Subject: Re: [PATCH v2 05/18] mm/gup: introduce pin_user_pages*() and FOLL_PIN

From: Jerome Glisse <[email-masked]>
Date: Mon, 4 Nov 2019 14:52:48 -0500

On Mon, Nov 04, 2019 at 11:30:32AM -0800, John Hubbard wrote:
> On 11/4/19 11:18 AM, Jerome Glisse wrote:
> > On Mon, Nov 04, 2019 at 11:04:38AM -0800, John Hubbard wrote:
> >> On 11/4/19 9:33 AM, Jerome Glisse wrote:
> >> ...
> >>>
> >>> Few nitpick belows, nonetheless:
> >>>
> >>> Reviewed-by: Jérôme Glisse <[email-masked]>
> >>> [...]
> >>>> +
> >>>> +CASE 3: ODP
> >>>> +-----------
> >>>> +(Mellanox/Infiniband On Demand Paging: the hardware supports
> >>>> +replayable page faulting). There are GUP references to pages serving as DMA
> >>>> +buffers. For ODP, MMU notifiers are used to synchronize with page_mkclean()
> >>>> +and munmap(). Therefore, normal GUP calls are sufficient, so neither flag
> >>>> +needs to be set.
> >>>
> >>> I would not include ODP or anything like it here, they do not use
> >>> GUP anymore and i believe it is more confusing here. I would how-
> >>> ever include some text in this documentation explaining that hard-
> >>> ware that support page fault is superior as it does not incur any
> >>> of the issues described here.
> >>
> >> OK, agreed, here's a new write up that I'll put in v3:
> >>
> >>
> >> CASE 3: ODP
> >> -----------
> > 
> > ODP is RDMA, maybe Hardware with page fault support instead
> > 
> >> Advanced, but non-CPU (DMA) hardware that supports replayable page faults.
> 
> OK, so:
> 
>     "RDMA hardware with page faulting support."
> 
> for the first sentence.

I would drop RDMA completely, RDMA is just one example, they are GPU, FPGA and
others that are in that category. See below

> 
> 
> >> Here, a well-written driver doesn't normally need to pin pages at all. However,
> >> if the driver does choose to do so, it can register MMU notifiers for the range,
> >> and will be called back upon invalidation. Either way (avoiding page pinning, or
> >> using MMU notifiers to unpin upon request), there is proper synchronization with 
> >> both filesystem and mm (page_mkclean(), munmap(), etc).
> >>
> >> Therefore, neither flag needs to be set.
> > 
> > In fact GUP should never be use with those.
> 
> 
> Yes. The next paragraph says that, but maybe not strong enough.
> 
> 
> >>
> >> It's worth mentioning here that pinning pages should not be the first design
> >> choice. If page fault capable hardware is available, then the software should
> >> be written so that it does not pin pages. This allows mm and filesystems to
> >> operate more efficiently and reliably.
> 
> Here's what we have after the above changes:
> 
> CASE 3: ODP
> -----------
> RDMA hardware with page faulting support. Here, a well-written driver doesn't

CASE3: Hardware with page fault support
---------------------------------------

Here, a well-written ....

> normally need to pin pages at all. However, if the driver does choose to do so,
> it can register MMU notifiers for the range, and will be called back upon
> invalidation. Either way (avoiding page pinning, or using MMU notifiers to unpin
> upon request), there is proper synchronization with both filesystem and mm
> (page_mkclean(), munmap(), etc).
> 
> Therefore, neither flag needs to be set.
> 
> In this case, ideally, neither get_user_pages() nor pin_user_pages() should be 
> called. Instead, the software should be written so that it does not pin pages. 
> This allows mm and filesystems to operate more efficiently and reliably.
> 
> >>> [...]
> >>>
> >>>> @@ -1014,7 +1018,16 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
> >>>>  		BUG_ON(*locked != 1);
> >>>>  	}
> >>>>  
> >>>> -	if (pages)
> >>>> +	/*
> >>>> +	 * FOLL_PIN and FOLL_GET are mutually exclusive. Traditional behavior
> >>>> +	 * is to set FOLL_GET if the caller wants pages[] filled in (but has
> >>>> +	 * carelessly failed to specify FOLL_GET), so keep doing that, but only
> >>>> +	 * for FOLL_GET, not for the newer FOLL_PIN.
> >>>> +	 *
> >>>> +	 * FOLL_PIN always expects pages to be non-null, but no need to assert
> >>>> +	 * that here, as any failures will be obvious enough.
> >>>> +	 */
> >>>> +	if (pages && !(flags & FOLL_PIN))
> >>>>  		flags |= FOLL_GET;
> >>>
> >>> Did you look at user that have pages and not FOLL_GET set ?
> >>> I believe it would be better to first fix them to end up
> >>> with FOLL_GET set and then error out if pages is != NULL but
> >>> nor FOLL_GET or FOLL_PIN is set.
> >>>
> >>
> >> I was perhaps overly cautious, and didn't go there. However, it's probably
> >> doable, given that there was already the following in __get_user_pages():
> >>
> >>     VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
> >>
> >> ...which will have conditioned people and code to set FOLL_GET together with
> >> pages. So I agree that the time is right.
> >>
> >> In order to make bisecting future failures simpler, I can insert a patch right 
> >> before this one, that changes the FOLL_GET setting into an assert, like this:
> >>
> >> diff --git a/mm/gup.c b/mm/gup.c
> >> index 8f236a335ae9..be338961e80d 100644
> >> --- a/mm/gup.c
> >> +++ b/mm/gup.c
> >> @@ -1014,8 +1014,8 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
> >>                 BUG_ON(*locked != 1);
> >>         }
> >>  
> >> -       if (pages)
> >> -               flags |= FOLL_GET;
> >> +       if (pages && WARN_ON_ONCE(!(gup_flags & FOLL_GET)))
> >> +               return -EINVAL;
> >>  
> >>         pages_done = 0;
> >>         lock_dropped = false;
> >>
> >>
> >> ...and then add in FOLL_PIN, with this patch.
> > 
> > looks good but double check that it should not happens, i will try
> > to check on my side too.
> 
> Yes, I'll look.
> 
> ...
> >>>> +	 */
> >>>> +	gup_flags |= FOLL_REMOTE | FOLL_PIN;
> >>>
> >>> Wouldn't it be better to not add pin_longterm_pages_remote() until
> >>> it can be properly implemented ?
> >>>
> >>
> >> Well, the problem is that I need each call site that requires FOLL_PIN
> >> to use a proper wrapper. It's the FOLL_PIN that is the focus here, because
> >> there is a hard, bright rule, which is: if and only if a caller sets
> >> FOLL_PIN, then the dma-page tracking happens, and put_user_page() must
> >> be called.
> >>
> >> So this leaves me with only two reasonable choices:
> >>
> >> a) Convert the call site as above: pin_longterm_pages_remote(), which sets
> >> FOLL_PIN (the key point!), and leaves the FOLL_LONGTERM situation exactly
> >> as it has been so far. When the FOLL_LONGTERM situation is fixed, the call
> >> site *might* not need any changes to adopt the working gup.c code.
> >>
> >> b) Convert the call site to pin_user_pages_remote(), which also sets
> >> FOLL_PIN, and also leaves the FOLL_LONGTERM situation exactly as before.
> >> There would also be a comment at the call site, to the effect of, "this
> >> is the wrong call to make: it really requires FOLL_LONGTERM behavior".
> >>
> >> When the FOLL_LONGTERM situation is fixed, the call site will need to be
> >> changed to pin_longterm_pages_remote().
> >>
> >> So you can probably see why I picked (a).
> > 
> > But right now nobody has FOLL_LONGTERM and FOLL_REMOTE. So you should
> > never have the need for pin_longterm_pages_remote(). My fear is that
> > longterm has implication and it would be better to not drop this implication
> > by adding a wrapper that does not do what the name says.
> > 
> > So do not introduce pin_longterm_pages_remote() until its first user
> > happens. This is option c)
> > 
> 
> Almost forgot, though: there is already another user: Infiniband:
> 
> drivers/infiniband/core/umem_odp.c:646:         npages = pin_longterm_pages_remote(owning_process, owning_mm,

odp do not need that, i thought the HMM convertion was already upstream
but seems not, in any case odp do not need the longterm case it only
so best is to revert that user to gup_fast or something until it get
converted to HMM.

Cheers,
Jérôme