]> www.infradead.org Git - users/hch/misc.git/commitdiff
mm: decline to manipulate the refcount on a slab page
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Mon, 10 Mar 2025 14:35:24 +0000 (14:35 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Mon, 17 Mar 2025 00:40:26 +0000 (17:40 -0700)
Slab pages now have a refcount of 0, so nobody should be trying to
manipulate the refcount on them.  Doing so has little effect; the object
could be freed and reallocated to a different purpose, although the slab
itself would not be until the refcount was put making it behave rather
like TYPESAFE_BY_RCU.

Unfortunately, __iov_iter_get_pages_alloc() does take a refcount.  Fix
that to not change the refcount, and make put_page() silently not change
the refcount.  get_page() warns so that we can fix any other callers that
need to be changed.

Long-term, networking needs to stop taking a refcount on the pages that it
uses and rely on the caller to hold whatever references are necessary to
make the memory stable.  In the medium term, more page types are going to
hav a zero refcount, so we'll want to move get_page() and put_page() out
of line.

Link: https://lkml.kernel.org/r/20250310143544.1216127-1-willy@infradead.org
Fixes: 9aec2fb0fd5e (slab: allocate frozen pages)
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reported-by: Hannes Reinecke <hare@suse.de>
Closes: https://lore.kernel.org/all/08c29e4b-2f71-4b6d-8046-27e407214d8c@suse.com/
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/mm.h
lib/iov_iter.c

index 7b1068ddcbb70b732a4f2843e6b55fd73cc3e320..fd1e85b4b48a23216313137d9c05a054f720b56d 100644 (file)
@@ -1458,7 +1458,10 @@ static inline void folio_get(struct folio *folio)
 
 static inline void get_page(struct page *page)
 {
-       folio_get(page_folio(page));
+       struct folio *folio = page_folio(page);
+       if (WARN_ON_ONCE(folio_test_slab(folio)))
+               return;
+       folio_get(folio);
 }
 
 static inline __must_check bool try_get_page(struct page *page)
@@ -1552,6 +1555,9 @@ static inline void put_page(struct page *page)
 {
        struct folio *folio = page_folio(page);
 
+       if (folio_test_slab(folio))
+               return;
+
        /*
         * For some devmap managed pages we need to catch refcount transition
         * from 2 to 1:
index 65f550cb5081b9e2e724d19c0de812a8f4230297..8c7fdb7d8c8fa3daf70671818cb92c7d5b79605c 100644 (file)
@@ -1190,8 +1190,12 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
                if (!n)
                        return -ENOMEM;
                p = *pages;
-               for (int k = 0; k < n; k++)
-                       get_page(p[k] = page + k);
+               for (int k = 0; k < n; k++) {
+                       struct folio *folio = page_folio(page);
+                       p[k] = page + k;
+                       if (!folio_test_slab(folio))
+                               folio_get(folio);
+               }
                maxsize = min_t(size_t, maxsize, n * PAGE_SIZE - *start);
                i->count -= maxsize;
                i->iov_offset += maxsize;