On 2/4/21 11:35 AM, Joao Martins wrote: > On 2/3/21 11:37 PM, John Hubbard wrote: >> On 2/3/21 2:00 PM, Joao Martins wrote: >>> -static inline unsigned int count_ntails(struct page **pages, unsigned long npages) >>> +static inline unsigned int count_ntails(struct page **pages, >>> + unsigned long npages, bool range) >>> { >>> - struct page *head = compound_head(pages[0]); >>> + struct page *page = pages[0], *head = compound_head(page); >>> unsigned int ntails; >>> >>> + if (range) >>> + return (!PageCompound(head) || compound_order(head) <= 1) ? 1 : >>> + min_t(unsigned int, (head + compound_nr(head) - page), npages); >> >> Here, you clearly should use a separate set of _range routines. Because you're basically >> creating two different routines here! Keep it simple. >> >> Once you're in a separate routine, you might feel more comfortable expanding that to >> a more readable form, too: >> >> if (!PageCompound(head) || compound_order(head) <= 1) >> return 1; >> >> return min_t(unsigned int, (head + compound_nr(head) - page), npages); >> > Yes. > > Let me also try instead to put move everything into two sole iterator helper routines, > compound_next() and compound_next_range(), and thus get rid of this count_ntails(). It > should also help in removing a compound_head() call which should save cycles. > As mentioned earlier, I got rid of count_ntails and the ugly boolean. Plus addressed the missing docs -- fwiw, I borrowed unpin_user_pages_dirty_lock() docs and modified a bit. Partial diff below, hopefully it is looking better now: diff --git a/mm/gup.c b/mm/gup.c index 5a3dd235017a..4ef36c8990e3 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -215,6 +215,34 @@ void unpin_user_page(struct page *page) } EXPORT_SYMBOL(unpin_user_page); +static inline void range_next(unsigned long i, unsigned long npages, + struct page **list, struct page **head, + unsigned int *ntails) +{ + struct page *next, *page; + unsigned int nr = 1; + + if (i >= npages) + return; + + npages -= i; + next = *list + i; + + page = compound_head(next); + if (PageCompound(page) && compound_order(page) > 1) + nr = min_t(unsigned int, + page + compound_nr(page) - next, npages); + + *head = page; + *ntails = nr; +} + +#define for_each_compound_range(__i, __list, __npages, __head, __ntails) \ + for (__i = 0, \ + range_next(__i, __npages, __list, &(__head), &(__ntails)); \ + __i < __npages; __i += __ntails, \ + range_next(__i, __npages, __list, &(__head), &(__ntails))) + static inline void compound_next(unsigned long i, unsigned long npages, struct page **list, struct page **head, unsigned int *ntails) @@ -306,6 +334,42 @@ void unpin_user_pages_dirty_lock(struct page **pages, unsigned long npages, } EXPORT_SYMBOL(unpin_user_pages_dirty_lock); +/** + * unpin_user_page_range_dirty_lock() - release and optionally dirty + * gup-pinned page range + * + * @page: the starting page of a range maybe marked dirty, and definitely released. + * @npages: number of consecutive pages to release. + * @make_dirty: whether to mark the pages dirty + * + * "gup-pinned page range" refers to a range of pages that has had one of the + * get_user_pages() variants called on that page. + * + * For the page ranges defined by [page .. page+npages], make that range (or + * its head pages, if a compound page) dirty, if @make_dirty is true, and if the + * page range was previously listed as clean. + * + * set_page_dirty_lock() is used internally. If instead, set_page_dirty() is + * required, then the caller should a) verify that this is really correct, + * because _lock() is usually required, and b) hand code it: + * set_page_dirty_lock(), unpin_user_page(). + * + */ +void unpin_user_page_range_dirty_lock(struct page *page, unsigned long npages, + bool make_dirty) +{ + unsigned long index; + struct page *head; + unsigned int ntails; + + for_each_compound_range(index, &page, npages, head, ntails) { + if (make_dirty && !PageDirty(head)) + set_page_dirty_lock(head); + put_compound_head(head, ntails, FOLL_PIN); + } +} +EXPORT_SYMBOL(unpin_user_page_range_dirty_lock); +