|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v4 12/16] xen: implement new foreign copy hypercall
On 03.06.2026 15:05, Frediano Ziglio wrote:
> --- a/xen/common/memory.c
> +++ b/xen/common/memory.c
> @@ -1545,6 +1545,132 @@ static int acquire_resource(
> return rc;
> }
>
> +/*
> + * The "noinline" qualifier avoid the compiler to create a large function
> + * consuming quite a lot of stack.
> + */
> +static int noinline mem_foreigncopy(
> + XEN_GUEST_HANDLE_PARAM(xen_foreigncopy_t) arg)
> +{
> + struct domain *d, *const currd = current->domain;
> + xen_foreigncopy_t copy;
> + int rc, direction;
> +
> + if ( !arch_acquire_resource_check(currd) )
> + return -EACCES;
How is the new sub-op related to acquire-resource? And aren't you instead
lacking an XSM check?
> + if ( copy_from_guest(©, arg, 1) )
> + return -EFAULT;
> +
> + if ( copy.flags & ~1u )
If a suffix is needed here in the first place, please use an upper-case one.
Misra only demands L suffixes to be upper-case, but iirc we decided that
then we want all suffixes that way.
> + return -EINVAL;
> +
> + direction = copy.flags & XENMEM_foreigncopy_direction;
> +
> + if ( copy.nr_frames == 0 )
> + return 0;
> +
> + rc = rcu_lock_remote_domain_by_id(copy.domid, &d);
Why not rcu_lock_domain_by_any_id()? IOW why would a self-copy need
prohibiting?
> + if ( rc )
> + return rc;
> +
> + /*
> + * Check we are allowed to map and access these foreign pages.
> + */
> + rc = xsm_map_gmfn_foreign(XSM_TARGET, currd, d);
> + if ( rc )
> + goto out;
> +
> + do {
> + /*
> + * Arbitrary size. Not too much stack space, and a reasonable stride
> + * for continuation checks.
> + */
> + xen_pfn_t gfn_list[32];
> + unsigned int todo = MIN(ARRAY_SIZE(gfn_list), copy.nr_frames);
> +
> + rc = -EFAULT;
> + if ( copy_from_guest(gfn_list, copy.frame_list, todo) )
> + goto out;
> +
> + for ( unsigned i = 0; i < todo; i++ )
> + {
> + struct page_info *foreign_page;
> + void *foreign;
> + p2m_type_t p2mt;
> +
> + foreign_page = get_page_from_gfn(d, gfn_list[i], &p2mt,
> P2M_ALLOC);
> +
> + if ( unlikely(p2mt != p2m_ram_rw
> +#ifdef CONFIG_X86
> + && p2mt != p2m_ram_logdirty
> +#endif
> + ) && foreign_page )
This is ugly formatting wise, and the use of unlikely() isn't very likely
to have the effect you intend: As long as the compiler can't translate the
&& expression to something involving only a single conditional branch,
which of the branches is it that is unlikely to be taken?
> + {
> + put_page(foreign_page);
> + foreign_page = NULL;
> + }
> + if ( unlikely(!foreign_page) )
> + {
> + gdprintk(XENLOG_WARNING,
> + "Error accessing foreign mfn %" PRI_mfn "\n",
> + gfn_list[i]);
As per get_page_from_gfn() and gfn_list[] it's a GFN, not an MFN.
> + rc = -EINVAL;
> + copy.nr_frames -= i;
> + guest_handle_add_offset(copy.frame_list, i);
> + goto out;
> + }
> +
> + /* A page is dirtied when it's being copied to. */
> + if ( direction == XENMEM_foreigncopy_to )
> + paging_mark_dirty(d, page_to_mfn(foreign_page));
> +
> + foreign = map_domain_page(page_to_mfn(foreign_page));
> + if ( direction == XENMEM_foreigncopy_from )
> + rc = copy_to_guest(copy.buffer, foreign, PAGE_SIZE);
> + else
> + rc = copy_from_guest(foreign, copy.buffer, PAGE_SIZE);
> + unmap_domain_page(foreign);
> + put_page(foreign_page);
> +
> + if ( unlikely(rc) )
> + {
> + gdprintk(XENLOG_WARNING,
> + "Error copying to mfn %" PRI_mfn "\n", gfn_list[i]);
"to" isn't always correct here, and the problem wasn't with the GFN (not
MFN) anyway, but with the buffer.
> + copy.nr_frames -= i;
> + guest_handle_add_offset(copy.frame_list, i);
> + goto out;
> + }
> +
> + guest_handle_add_offset(copy.buffer, PAGE_SIZE);
> + }
> +
> + copy.nr_frames -= todo;
> + guest_handle_add_offset(copy.frame_list, todo);
> +
> + if ( copy.nr_frames && hypercall_preempt_check() )
> + {
> + rc = hypercall_create_continuation(
> + __HYPERVISOR_memory_op, "lh", XENMEM_foreigncopy, arg);
> + goto out;
> + }
> + } while ( copy.nr_frames );
> +
> + rc = 0;
> +
> + out:
> + rcu_unlock_domain(d);
> +
> + /* Update in all cases, it allows the caller to know how many
> + * frames were successfully copied and the continuation to
> + * continue correctly.
> + */
> + if ( copy_to_guest(arg, ©, 1) )
Since you already used copy_from_guest() one the way in, __copy_to_guest()
will do here.
> + rc = -EFAULT;
> +
> + return rc;
> +}
Before looking at the implementation in yet more detail: This is quite a
bit of new code. Did you at least consider extending MMUEXT_COPY_PAGE
along the lines of what c6b8bdfe3b47 ("x86: extend mmu_update hypercall
to allow update of foreign pagetables") did, allowing two domains to be
specified in the foreigndom argument?
> @@ -2012,6 +2138,13 @@ long do_memory_op(unsigned long cmd,
> XEN_GUEST_HANDLE_PARAM(void) arg)
> start_extent);
> break;
>
> + case XENMEM_foreigncopy:
> + if ( unlikely(start_extent) )
> + return -EINVAL;
Why make this different from other continuable sub-ops?
> --- a/xen/include/public/memory.h
> +++ b/xen/include/public/memory.h
> @@ -740,7 +740,45 @@ struct xen_vnuma_topology_info {
> typedef struct xen_vnuma_topology_info xen_vnuma_topology_info_t;
> DEFINE_XEN_GUEST_HANDLE(xen_vnuma_topology_info_t);
>
> -/* Next available subop number is 29 */
> +/*
> + * Copy memory from/to a given domain.
> + */
> +#define XENMEM_foreigncopy 29
> +struct xen_foreigncopy {
> + /* IN - The domain whose resource is to be copied. */
> + domid_t domid;
> +
> + /* IN - Flags. */
> +#define XENMEM_foreigncopy_from 0
> +#define XENMEM_foreigncopy_to 1
> +#define XENMEM_foreigncopy_direction 1
> + uint16_t flags;
> +
> + /*
> + * IN
> + *
> + * As an IN parameter number of frames of the domain to be copied.
> + */
> + uint32_t nr_frames;
> +
> + /*
> + * IN
> + *
> + * Frames to be copied.
> + */
> + XEN_GUEST_HANDLE(xen_pfn_t) frame_list;
> +
> + /*
> + * IN/OUT
> + *
> + * Userspace buffer to read/write from.
> + */
> + XEN_GUEST_HANDLE(uint8) buffer;
> +};
Seeing the two handles - what about compat_memory_op()?
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |