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 jglisse@redhat.com [...]
+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.
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 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,
thanks,
John Hubbard NVIDIA