Re: [PATCHv6 07/17] mm: Rework compound_head() for power-of-2 sizeof(struct page)
From: David Hildenbrand (Arm)
Date: Thu Feb 05 2026 - 09:12:51 EST
On 2/2/26 16:56, Kiryl Shutsemau wrote:
For tail pages, the kernel uses the 'compound_info' field to get to the
head page. The bit 0 of the field indicates whether the page is a
tail page, and if set, the remaining bits represent a pointer to the
head page.
For cases when size of struct page is power-of-2, change the encoding of
compound_info to store a mask that can be applied to the virtual address
of the tail page in order to access the head page. It is possible
because struct page of the head page is naturally aligned with regards
to order of the page.
The significant impact of this modification is that all tail pages of
the same order will now have identical 'compound_info', regardless of
the compound page they are associated with. This paves the way for
eliminating fake heads.
The HugeTLB Vmemmap Optimization (HVO) creates fake heads and it is only
applied when the sizeof(struct page) is power-of-2. Having identical
tail pages allows the same page to be mapped into the vmemmap of all
pages, maintaining memory savings without fake heads.
If sizeof(struct page) is not power-of-2, there is no functional
changes.
Limit mask usage to HugeTLB vmemmap optimization (HVO) where it makes
a difference. The approach with mask would work in the wider set of
conditions, but it requires validating that struct pages are naturally
aligned for all orders up to the MAX_FOLIO_ORDER, which can be tricky.
Signed-off-by: Kiryl Shutsemau <kas@xxxxxxxxxx>
Reviewed-by: Muchun Song <muchun.song@xxxxxxxxx>
Reviewed-by: Zi Yan <ziy@xxxxxxxxxx>
[...]
struct folio *foliop;
int loops = 5;
@@ -1247,8 +1247,8 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page)
again:
memset(&ps->folio_snapshot, 0, sizeof(struct folio));
memcpy(&ps->page_snapshot, page, sizeof(*page));
- head = ps->page_snapshot.compound_info;
- if ((head & 1) == 0) {
+ info = ps->page_snapshot.compound_info;
+ if (!(info & 1)) {
ps->idx = 0;
foliop = (struct folio *)&ps->page_snapshot;
if (!folio_test_large(foliop)) {
@@ -1259,7 +1259,15 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page)
}
foliop = (struct folio *)page;
} else {
- foliop = (struct folio *)(head - 1);
+ /* See compound_head() */
+ if (compound_info_has_mask()) {
+ unsigned long p = (unsigned long)page;
+
+ foliop = (struct folio *)(p & info);
IIUC, we don't care about clearing bit0 before the & as the page pointer shouldn't have set it in the first page.
Pretty neat
Acked-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
--
Cheers,
David