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

Re: [Xen-devel] [PATCH 1/2] x86/mm: factor out the code for shattering an l3 PTE



Hi Andrew,

On Fri, 2019-12-06 at 17:50 +0000, Andrew Cooper wrote:
> On 06/12/2019 15:53, Hongyan Xia wrote:
> > map_pages_to_xen and modify_xen_mappings are performing almost
> > exactly
> > the same operations when shattering an l3 PTE, the only difference
> > being whether we want to flush.
> > 
> > Signed-off-by: Hongyan Xia <hongyxia@xxxxxxxxxx>
> 
> Just for reviewing purposes, how does this relate to your other
> posted
> series.  Is it independent, a prerequisite, or does it depend on that
> series?

This is independent. Other series I posted will touch a lot of PTE
functions, and as Jan suggested, it would be nice to refactor some of
the long and complicated ones before changing them, which could also
prepare us for 5-level paging in the future. Although if these
refactoring patches get in, I need to rebase the series I posted
before.

> 
> > ---
> >  xen/arch/x86/mm.c | 85 ++++++++++++++++++++++---------------------
> > ----
> >  1 file changed, 40 insertions(+), 45 deletions(-)
> > 
> > diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
> > index 7d4dd80a85..42aaaa1083 100644
> > --- a/xen/arch/x86/mm.c
> > +++ b/xen/arch/x86/mm.c
> > @@ -5151,6 +5151,43 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long
> > v)
> >                           flush_area_local((const void *)v, f) : \
> >                           flush_area_all((const void *)v, f))
> >  
> > +/* Shatter an l3 entry and populate l2. If virt is passed in, also
> > do flush. */
> > +static void shatter_l3e(l3_pgentry_t *pl3e, l2_pgentry_t *l2t,
> > +        unsigned long virt, bool locking)
> > +{
> > +    unsigned int i;
> > +    l3_pgentry_t ol3e = *pl3e;
> > +
> > +    for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ )
> > +        l2e_write(l2t + i,
> > +                  l2e_from_pfn(l3e_get_pfn(ol3e) +
> > +                               (i << PAGETABLE_ORDER),
> > +                               l3e_get_flags(ol3e)));
> 
> The PTE macros are especially poor for generated asm, and in cases
> like
> this, I'd like to improve things.
> 
> In particular, I believe the following code has identical behaviour:
> 
> l2_pgentry_t nl2e = l2e_from_intpte(l3e_get_intpte(ol3e));
> 
> for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++, nl2e.l2 +=
> PAGETABLE_ORDER )
>     l2e_write(l2t + i, nl2e);
> 
> (although someone please double check my logic) and rather better asm
> generation.  (I also expect there to be some discussion on whether
> using
> n2le.l2 directly is something we'd want to start doing.)
> 

I believe it should be nl2e.l2 += 1<<(PAGETABLE_ORDER+PAGE_SHIFT) ?
Although the code rarely touches the field (.l2) directly, so maybe use
the macros (get_intpte -> add -> from_intpte) for consistency? They
should produce the same code if the compiler is not too stupid.

> > +
> > +    if ( locking )
> > +        spin_lock(&map_pgdir_lock);
> > +    if ( (l3e_get_flags(ol3e) & _PAGE_PRESENT) &&
> > +         (l3e_get_flags(ol3e) & _PAGE_PSE) )
> 
> There is a subtle difference between the original code, and the
> refactored code, and it depends on the memory barrier from
> spin_lock().
> 
> Previously, it was re-read from memory after the lock, whereas now it
> is
> likely the stale version from before map_pgdir was locked.
> 
> Either you can go back to the old version and use *pl3e, or
> alternatively use:
> 
>     if ( locking )
>         spin_lock(&map_pgdir_lock);
>     ol3e = ACCESS_ONCE(*pl3e);
>     if ( ...
> 
> to make it clear that a reread from memory is required.
> 

Good point. Thanks.

> > +    {
> > +        l3e_write_atomic(pl3e, l3e_from_mfn(virt_to_mfn(l2t),
> 
> This would probably generate better asm by using the maddr variants
> so
> we don't have a double shift.
> 

Right. I can change that.

> > +                                            __PAGE_HYPERVISOR));
> > +        l2t = NULL;
> > +    }
> > +    if ( locking )
> > +        spin_unlock(&map_pgdir_lock);
> > +    if ( virt )
> > +    {
> > +        unsigned int flush_flags =
> > +            FLUSH_TLB | FLUSH_ORDER(2 * PAGETABLE_ORDER);
> > +
> > +        if ( (l3e_get_flags(ol3e) & _PAGE_GLOBAL) )
> > +                flush_flags |= FLUSH_TLB_GLOBAL;
> > +        flush_area(virt, flush_flags);
> > +    }
> > +    if ( l2t )
> > +        free_xen_pagetable(l2t);
> 
> This surely needs to NULL out l2t, just so the caller doesn't get any
> clever ideas and ends up with a use-after-free?
> 
> ~Andrew

hmm... if we want to make the nullification visible to the caller we
might need to pass &. I wonder if it makes sense to simply move the
memory allocation of pl2e into shatter_l3e as well, so that the caller
cannot have any ideas.

Thanks,
Hongyan
_______________________________________________
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®.