Subject: xenpaging: handle HVMCOPY_gfn_paged_out in copy_from/to_user copy_from_user_hvm can fail when __hvm_copy returns HVMCOPY_gfn_paged_out for a referenced gfn, for example during guests pagetable walk. This has to be handled in some way. One hypercall that failed was do_memory_op/XENMEM_decrease_reservation which lead to a BUG_ON balloon.c. Since do_memory_op already has restart support for the hypercall, copy_from_guest uses this existing retry code. In addition, cleanup on error was added to increase_reservation and populate_physmap. Signed-off-by: Olaf Hering --- xen/arch/x86/hvm/hvm.c | 4 ++++ xen/common/memory.c | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) Index: xen-4.0.2-testing/xen/arch/x86/hvm/hvm.c =================================================================== --- xen-4.0.2-testing.orig/xen/arch/x86/hvm/hvm.c +++ xen-4.0.2-testing/xen/arch/x86/hvm/hvm.c @@ -1843,6 +1843,8 @@ unsigned long copy_to_user_hvm(void *to, rc = hvm_copy_to_guest_virt_nofault((unsigned long)to, (void *)from, len, 0); + if ( rc == HVMCOPY_gfn_paged_out ) + return -EAGAIN; return rc ? len : 0; /* fake a copy_to_user() return code */ } @@ -1859,6 +1861,8 @@ unsigned long copy_from_user_hvm(void *t #endif rc = hvm_copy_from_guest_virt_nofault(to, (unsigned long)from, len, 0); + if ( rc == HVMCOPY_gfn_paged_out ) + return -EAGAIN; return rc ? len : 0; /* fake a copy_from_user() return code */ } Index: xen-4.0.2-testing/xen/common/memory.c =================================================================== --- xen-4.0.2-testing.orig/xen/common/memory.c +++ xen-4.0.2-testing/xen/common/memory.c @@ -47,6 +47,7 @@ static void increase_reservation(struct { struct page_info *page; unsigned long i; + unsigned long ctg_ret; xen_pfn_t mfn; struct domain *d = a->domain; @@ -80,8 +81,13 @@ static void increase_reservation(struct if ( !guest_handle_is_null(a->extent_list) ) { mfn = page_to_mfn(page); - if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) ) + ctg_ret = __copy_to_guest_offset(a->extent_list, i, &mfn, 1); + if ( unlikely(ctg_ret) ) + { + if ( (long)ctg_ret == -EAGAIN ) + a->preempted = 1; goto out; + } } } @@ -93,6 +99,7 @@ static void populate_physmap(struct memo { struct page_info *page; unsigned long i, j; + unsigned long cftg_ret; xen_pfn_t gpfn, mfn; struct domain *d = a->domain; @@ -111,8 +118,13 @@ static void populate_physmap(struct memo goto out; } - if ( unlikely(__copy_from_guest_offset(&gpfn, a->extent_list, i, 1)) ) + cftg_ret = __copy_from_guest_offset(&gpfn, a->extent_list, i, 1); + if ( unlikely(cftg_ret) ) + { + if ( (long)cftg_ret == -EAGAIN ) + a->preempted = 1; goto out; + } if ( a->memflags & MEMF_populate_on_demand ) { @@ -142,8 +154,13 @@ static void populate_physmap(struct memo set_gpfn_from_mfn(mfn + j, gpfn + j); /* Inform the domain of the new page's machine address. */ - if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) ) + cftg_ret = __copy_to_guest_offset(a->extent_list, i, &mfn, 1); + if ( unlikely(cftg_ret) ) + { + if ( (long)cftg_ret == -EAGAIN ) + a->preempted = 1; goto out; + } } } } @@ -212,6 +229,7 @@ int guest_remove_page(struct domain *d, static void decrease_reservation(struct memop_args *a) { unsigned long i, j; + unsigned long cfg_ret; xen_pfn_t gmfn; if ( !guest_handle_subrange_okay(a->extent_list, a->nr_done, @@ -226,8 +244,13 @@ static void decrease_reservation(struct goto out; } - if ( unlikely(__copy_from_guest_offset(&gmfn, a->extent_list, i, 1)) ) + cfg_ret = __copy_from_guest_offset(&gmfn, a->extent_list, i, 1); + if ( unlikely(cfg_ret) ) + { + if ( (long)cfg_ret == -EAGAIN ) + a->preempted = 1; goto out; + } if ( tb_init_done ) { @@ -511,6 +534,7 @@ long do_memory_op(unsigned long cmd, XEN int rc, op; unsigned int address_bits; unsigned long start_extent; + unsigned long cfg_ret; struct xen_memory_reservation reservation; struct memop_args args; domid_t domid; @@ -524,8 +548,13 @@ long do_memory_op(unsigned long cmd, XEN case XENMEM_populate_physmap: start_extent = cmd >> MEMOP_EXTENT_SHIFT; - if ( copy_from_guest(&reservation, arg, 1) ) + cfg_ret = copy_from_guest(&reservation, arg, 1); + if ( unlikely(cfg_ret) ) + { + if ( (long)cfg_ret == -EAGAIN ) + return hypercall_create_continuation(__HYPERVISOR_memory_op, "lh", cmd, arg); return start_extent; + } /* Is size too large for us to encode a continuation? */ if ( reservation.nr_extents > (ULONG_MAX >> MEMOP_EXTENT_SHIFT) )