[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v10 2/3] x86/mem_sharing: reset a fork



On Tue, Feb 25, 2020 at 11:17:56AM -0800, Tamas K Lengyel wrote:
> Implement hypercall that allows a fork to shed all memory that got allocated
> for it during its execution and re-load its vCPU context from the parent VM.
> This allows the forked VM to reset into the same state the parent VM is in a
> faster way then creating a new fork would be. Measurements show about a 2x
> speedup during normal fuzzing operations. Performance may vary depending how
> much memory got allocated for the forked VM. If it has been completely
> deduplicated from the parent VM then creating a new fork would likely be more
> performant.
> 
> Signed-off-by: Tamas K Lengyel <tamas.lengyel@xxxxxxxxx>
> ---
> v10: implemented hypercall continuation similar to the existing range_share op
> ---
>  xen/arch/x86/mm/mem_sharing.c | 126 +++++++++++++++++++++++++++++++++-
>  xen/include/public/memory.h   |   4 ++
>  2 files changed, 129 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c
> index 8ee37e6943..aa4358aae4 100644
> --- a/xen/arch/x86/mm/mem_sharing.c
> +++ b/xen/arch/x86/mm/mem_sharing.c
> @@ -1673,7 +1673,6 @@ static int fork(struct domain *d, struct domain *cd)
>          domain_pause(d);
>          cd->parent_paused = true;
>          cd->max_pages = d->max_pages;
> -        cd->max_vcpus = d->max_vcpus;
>      }
>  
>      /* this is preemptible so it's the first to get done */
> @@ -1704,6 +1703,91 @@ static int fork(struct domain *d, struct domain *cd)
>      return rc;
>  }
>  
> +/*
> + * The fork reset operation is intended to be used on short-lived forks only.
> + */
> +static int fork_reset(struct domain *d, struct domain *cd,
> +                      struct mem_sharing_op_fork_reset *fr)
> +{
> +    int rc = 0;
> +    struct p2m_domain* p2m = p2m_get_hostp2m(cd);
> +    struct page_info *page, *tmp;
> +    unsigned long list_position = 0, preempt_count = 0, start = fr->opaque;
> +
> +    domain_pause(cd);
> +
> +    page_list_for_each_safe(page, tmp, &cd->page_list)
> +    {
> +        p2m_type_t p2mt;
> +        p2m_access_t p2ma;
> +        gfn_t gfn;
> +        mfn_t mfn;
> +        bool shared = false;
> +
> +        list_position++;
> +
> +        /* Resume were we left of before preemption */
> +        if ( start && list_position < start )
> +            continue;
> +
> +        mfn = page_to_mfn(page);
> +        if ( mfn_valid(mfn) )
> +        {
> +
> +            gfn = mfn_to_gfn(cd, mfn);
> +            mfn = __get_gfn_type_access(p2m, gfn_x(gfn), &p2mt, &p2ma,
> +                                        0, NULL, false);
> +
> +            if ( p2m_is_ram(p2mt) && !p2m_is_shared(p2mt) )
> +            {
> +                /* take an extra reference, must work for a shared page */
> +                if( !get_page(page, cd) )
> +                {
> +                    ASSERT_UNREACHABLE();
> +                    return -EINVAL;
> +                }
> +
> +                shared = true;
> +                preempt_count += 0x10;
> +
> +                /*
> +                 * Must succeed, it's a shared page that exists and
> +                 * thus its size is guaranteed to be 4k so we are not 
> splitting
> +                 * large pages.
> +                 */
> +                rc = p2m->set_entry(p2m, gfn, INVALID_MFN, PAGE_ORDER_4K,
> +                                    p2m_invalid, p2m_access_rwx, -1);
> +                ASSERT(!rc);
> +
> +                put_page_alloc_ref(page);
> +                put_page(page);
> +            }
> +        }
> +
> +        if ( !shared )
> +            preempt_count++;
> +
> +        /* Preempt every 2MiB (shared) or 32MiB (unshared) - arbitrary. */
> +        if ( preempt_count >= 0x2000 )
> +        {
> +            if ( hypercall_preempt_check() )
> +            {
> +                rc = -ERESTART;
> +                break;
> +            }
> +            preempt_count = 0;
> +        }
> +    }
> +
> +    if ( rc )
> +        fr->opaque = list_position;
> +    else if ( !(rc = hvm_copy_context_and_params(cd, d)) )
> +        fork_tsc(cd, d);

You also need to reset the contents of the special pages, the
vcpu_info pages and the shared_info page in order to match the state
the VM was in when forking.

PV timers should also be reset to parent's state AFAICT, or else you
will get spurious timer interrupts.

In fact you should check against the state of the parent, because the
fork might have changed the position of the shared info or any other
of those magic areas, and that should be reverted to the state they
are in the parent.

Thanks, Roger.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.