[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260130034818.472804-6-ziy@nvidia.com>
Date: Thu, 29 Jan 2026 22:48:18 -0500
From: Zi Yan <ziy@...dia.com>
To: Jason Gunthorpe <jgg@...dia.com>,
David Hildenbrand <david@...nel.org>,
Matthew Wilcox <willy@...radead.org>
Cc: Alistair Popple <apopple@...dia.com>,
Balbir Singh <balbirs@...dia.com>,
Andrew Morton <akpm@...ux-foundation.org>,
Lorenzo Stoakes <lorenzo.stoakes@...cle.com>,
"Liam R. Howlett" <Liam.Howlett@...cle.com>,
Vlastimil Babka <vbabka@...e.cz>,
Mike Rapoport <rppt@...nel.org>,
Suren Baghdasaryan <surenb@...gle.com>,
Michal Hocko <mhocko@...e.com>,
Jens Axboe <axboe@...nel.dk>,
Zi Yan <ziy@...dia.com>,
Baolin Wang <baolin.wang@...ux.alibaba.com>,
Nico Pache <npache@...hat.com>,
Ryan Roberts <ryan.roberts@....com>,
Dev Jain <dev.jain@....com>,
Barry Song <baohua@...nel.org>,
Lance Yang <lance.yang@...ux.dev>,
Muchun Song <muchun.song@...ux.dev>,
Oscar Salvador <osalvador@...e.de>,
Brendan Jackman <jackmanb@...gle.com>,
Johannes Weiner <hannes@...xchg.org>,
linux-mm@...ck.org,
linux-kernel@...r.kernel.org,
io-uring@...r.kernel.org
Subject: [RFC PATCH 5/5] mm: code separation for compound page and folio
A compound page is not a folio. Using struct folio in prep_compound_head()
causes confusion, since the input page is not a folio. The compound page to
folio conversion happens in page_rmappable_folio(). So move folio code from
prep_compound_head() to page_rmappable_folio().
After the change, a compound page no longer has the following folio field
set:
1. folio->_nr_pages,
2. folio->_large_mapcount,
3. folio->_nr_pages_mapped,
4. folio->_mm_ids,
5. folio->_mm_id_mapcount,
6. folio->_pincount,
7. folio->_entire_mapcount,
8. folio->_deferred_list.
The page freeing path for compound pages does not need to check these
fields and now just checks ->mapping == TAIL_MAPPING for all subpages.
So free_tail_page_prepare() has a new large_rmappable input to distinguish
between a compound page and a folio.
Signed-off-by: Zi Yan <ziy@...dia.com>
---
mm/hugetlb.c | 2 +-
mm/internal.h | 44 ++++++++++++++++++++++++++------------------
mm/mm_init.c | 2 +-
mm/page_alloc.c | 23 ++++++++++++++++++-----
4 files changed, 46 insertions(+), 25 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 7466c7bf41a1..231c91c3d93b 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3204,7 +3204,7 @@ static void __init hugetlb_folio_init_vmemmap(struct folio *folio,
ret = folio_ref_freeze(folio, 1);
VM_BUG_ON(!ret);
hugetlb_folio_init_tail_vmemmap(folio, 1, nr_pages);
- prep_compound_head(&folio->page, huge_page_order(h));
+ set_compound_order(&folio->page, huge_page_order(h));
}
static bool __init hugetlb_bootmem_page_prehvo(struct huge_bootmem_page *m)
diff --git a/mm/internal.h b/mm/internal.h
index 8bb22fb9a0e1..4d72e915d623 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -854,30 +854,38 @@ static inline struct folio *page_rmappable_folio(struct page *page)
{
struct folio *folio = (struct folio *)page;
- if (folio && folio_test_large(folio))
+ if (folio && folio_test_large(folio)) {
+ unsigned int order = compound_order(page);
+
+#ifdef NR_PAGES_IN_LARGE_FOLIO
+ folio->_nr_pages = 1U << order;
+#endif
+ atomic_set(&folio->_large_mapcount, -1);
+ if (IS_ENABLED(CONFIG_PAGE_MAPCOUNT))
+ atomic_set(&folio->_nr_pages_mapped, 0);
+ if (IS_ENABLED(CONFIG_MM_ID)) {
+ folio->_mm_ids = 0;
+ folio->_mm_id_mapcount[0] = -1;
+ folio->_mm_id_mapcount[1] = -1;
+ }
+ if (IS_ENABLED(CONFIG_64BIT) || order > 1) {
+ atomic_set(&folio->_pincount, 0);
+ atomic_set(&folio->_entire_mapcount, -1);
+ }
+ if (order > 1)
+ INIT_LIST_HEAD(&folio->_deferred_list);
folio_set_large_rmappable(folio);
+ }
return folio;
}
-static inline void prep_compound_head(struct page *page, unsigned int order)
+static inline void set_compound_order(struct page *page, unsigned int order)
{
- struct folio *folio = (struct folio *)page;
+ if (WARN_ON_ONCE(!order || !PageHead(page)))
+ return;
+ VM_WARN_ON_ONCE(order > MAX_FOLIO_ORDER);
- folio_set_order(folio, order);
- atomic_set(&folio->_large_mapcount, -1);
- if (IS_ENABLED(CONFIG_PAGE_MAPCOUNT))
- atomic_set(&folio->_nr_pages_mapped, 0);
- if (IS_ENABLED(CONFIG_MM_ID)) {
- folio->_mm_ids = 0;
- folio->_mm_id_mapcount[0] = -1;
- folio->_mm_id_mapcount[1] = -1;
- }
- if (IS_ENABLED(CONFIG_64BIT) || order > 1) {
- atomic_set(&folio->_pincount, 0);
- atomic_set(&folio->_entire_mapcount, -1);
- }
- if (order > 1)
- INIT_LIST_HEAD(&folio->_deferred_list);
+ page[1].flags.f = (page[1].flags.f & ~0xffUL) | order;
}
static inline void prep_compound_tail(struct page *head, int tail_idx)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 1a29a719af58..23a42a4af77b 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1102,7 +1102,7 @@ static void __ref memmap_init_compound(struct page *head,
prep_compound_tail(head, pfn - head_pfn);
set_page_count(page, 0);
}
- prep_compound_head(head, order);
+ set_compound_order(head, order);
}
void __ref memmap_init_zone_device(struct zone *zone,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index e4104973e22f..2194a6b3a062 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -746,7 +746,7 @@ void prep_compound_page(struct page *page, unsigned int order)
for (i = 1; i < nr_pages; i++)
prep_compound_tail(page, i);
- prep_compound_head(page, order);
+ set_compound_order(page, order);
}
static inline void set_buddy_order(struct page *page, unsigned int order)
@@ -1126,7 +1126,8 @@ static inline bool is_check_pages_enabled(void)
return static_branch_unlikely(&check_pages_enabled);
}
-static int free_tail_page_prepare(struct page *head_page, struct page *page)
+static int free_tail_page_prepare(struct page *head_page, struct page *page,
+ bool large_rmappable)
{
struct folio *folio = (struct folio *)head_page;
int ret = 1;
@@ -1141,6 +1142,13 @@ static int free_tail_page_prepare(struct page *head_page, struct page *page)
ret = 0;
goto out;
}
+ if (!large_rmappable) {
+ if (page->mapping != TAIL_MAPPING) {
+ bad_page(page, "corrupted mapping in compound page's tail page");
+ goto out;
+ }
+ goto skip_rmappable_checks;
+ }
switch (page - head_page) {
case 1:
/* the first tail page: these may be in place of ->mapping */
@@ -1198,11 +1206,12 @@ static int free_tail_page_prepare(struct page *head_page, struct page *page)
fallthrough;
default:
if (page->mapping != TAIL_MAPPING) {
- bad_page(page, "corrupted mapping in tail page");
+ bad_page(page, "corrupted mapping in folio's tail page");
goto out;
}
break;
}
+skip_rmappable_checks:
if (unlikely(!PageTail(page))) {
bad_page(page, "PageTail not set");
goto out;
@@ -1392,17 +1401,21 @@ __always_inline bool free_pages_prepare(struct page *page,
* avoid checking PageCompound for order-0 pages.
*/
if (unlikely(order)) {
+ bool large_rmappable = false;
int i;
if (compound) {
+ large_rmappable = folio_test_large_rmappable(folio);
+ /* clear compound order */
page[1].flags.f &= ~PAGE_FLAGS_SECOND;
#ifdef NR_PAGES_IN_LARGE_FOLIO
- folio->_nr_pages = 0;
+ if (large_rmappable)
+ folio->_nr_pages = 0;
#endif
}
for (i = 1; i < (1 << order); i++) {
if (compound)
- bad += free_tail_page_prepare(page, page + i);
+ bad += free_tail_page_prepare(page, page + i, large_rmappable);
if (is_check_pages_enabled()) {
if (free_page_is_bad(page + i)) {
bad++;
--
2.51.0
Powered by blists - more mailing lists