| From: Linus Torvalds <torvalds@linux-foundation.org> |
| Subject: mm: teach release_pages() to take an array of encoded page pointers too |
| Date: Wed, 9 Nov 2022 12:30:49 -0800 |
| |
| release_pages() already could take either an array of page pointers, or an |
| array of folio pointers. Expand it to also accept an array of encoded |
| page pointers, which is what both the existing mlock() use and the |
| upcoming mmu_gather use of encoded page pointers wants. |
| |
| Note that release_pages() won't actually use, or react to, any extra |
| encoded bits. Instead, this is very much a case of "I have walked the |
| array of encoded pages and done everything the extra bits tell me to do, |
| now release it all". |
| |
| Also, while the "either page or folio pointers" dual use was handled with |
| a cast of the pointer in "release_folios()", this takes a slightly |
| different approach and uses the "transparent union" attribute to describe |
| the set of arguments to the function: |
| |
| https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html |
| |
| which has been supported by gcc forever, but the kernel hasn't used |
| before. |
| |
| That allows us to avoid using various wrappers with casts, and just use |
| the same function regardless of use. |
| |
| Link: https://lkml.kernel.org/r/20221109203051.1835763-2-torvalds@linux-foundation.org |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Acked-by: Johannes Weiner <hannes@cmpxchg.org> |
| Acked-by: Hugh Dickins <hughd@google.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| include/linux/mm.h | 21 +++++++++++++++++++-- |
| mm/swap.c | 16 ++++++++++++---- |
| 2 files changed, 31 insertions(+), 6 deletions(-) |
| |
| --- a/include/linux/mm.h~mm-teach-release_pages-to-take-an-array-of-encoded-page-pointers-too |
| +++ a/include/linux/mm.h |
| @@ -1245,7 +1245,24 @@ static inline void folio_put_refs(struct |
| __folio_put(folio); |
| } |
| |
| -void release_pages(struct page **pages, int nr); |
| +/** |
| + * release_pages - release an array of pages or folios |
| + * |
| + * This just releases a simple array of multiple pages, and |
| + * accepts various different forms of said page array: either |
| + * a regular old boring array of pages, an array of folios, or |
| + * an array of encoded page pointers. |
| + * |
| + * The transparent union syntax for this kind of "any of these |
| + * argument types" is all kinds of ugly, so look away. |
| + */ |
| +typedef union { |
| + struct page **pages; |
| + struct folio **folios; |
| + struct encoded_page **encoded_pages; |
| +} release_pages_arg __attribute__ ((__transparent_union__)); |
| + |
| +void release_pages(release_pages_arg, int nr); |
| |
| /** |
| * folios_put - Decrement the reference count on an array of folios. |
| @@ -1261,7 +1278,7 @@ void release_pages(struct page **pages, |
| */ |
| static inline void folios_put(struct folio **folios, unsigned int nr) |
| { |
| - release_pages((struct page **)folios, nr); |
| + release_pages(folios, nr); |
| } |
| |
| static inline void put_page(struct page *page) |
| --- a/mm/swap.c~mm-teach-release_pages-to-take-an-array-of-encoded-page-pointers-too |
| +++ a/mm/swap.c |
| @@ -981,22 +981,30 @@ void lru_cache_disable(void) |
| |
| /** |
| * release_pages - batched put_page() |
| - * @pages: array of pages to release |
| + * @arg: array of pages to release |
| * @nr: number of pages |
| * |
| - * Decrement the reference count on all the pages in @pages. If it |
| + * Decrement the reference count on all the pages in @arg. If it |
| * fell to zero, remove the page from the LRU and free it. |
| + * |
| + * Note that the argument can be an array of pages, encoded pages, |
| + * or folio pointers. We ignore any encoded bits, and turn any of |
| + * them into just a folio that gets free'd. |
| */ |
| -void release_pages(struct page **pages, int nr) |
| +void release_pages(release_pages_arg arg, int nr) |
| { |
| int i; |
| + struct encoded_page **encoded = arg.encoded_pages; |
| LIST_HEAD(pages_to_free); |
| struct lruvec *lruvec = NULL; |
| unsigned long flags = 0; |
| unsigned int lock_batch; |
| |
| for (i = 0; i < nr; i++) { |
| - struct folio *folio = page_folio(pages[i]); |
| + struct folio *folio; |
| + |
| + /* Turn any of the argument types into a folio */ |
| + folio = page_folio(encoded_page_ptr(encoded[i])); |
| |
| /* |
| * Make sure the IRQ-safe lock-holding time does not get |
| _ |