Re: [PATCH v3 16/26] mm: add definitions for allocating unmapped pages
"Brendan Jackman" <[email protected]>
| Newsgroups | gmane.linux.kernel,gmane.linux.kernel.mm |
|---|---|
| Message-ID | <[email protected]> |
On Tue Aug 4, 2026 at 8:53 PM BST, Yosry Ahmed wrote: > On Sun, Jul 26, 2026 at 10:22:49PM +0000, Brendan Jackman wrote: >> Create ALLOC_UNMAPPED, which requests pages that are not present in the >> direct map. Since this feature has a cost (e.g. more freelists), it's >> behind a kconfig. Unlike other conditionally-defined alloc flags, it >> doesn't fall back to being 0. This prevents building code that uses >> ALLOC_UNMAPPED but doesn't depend on the necessary kconfig, since that >> would lead to invisible security issues. >> >> Create a freetype flag to record that pages on the freelists with this >> flag are unmapped. This is currently only needed for MIGRATE_UNMOVABLE >> pages, so the freetype encoding remains trivial. >> >> Also create the corresponding pageblock flag to record the same thing. >> >> To keep patches from being too overwhelming, the actual implementation >> is added separately, this is just types, Kconfig boilerplate, etc. >> >> Acked-by: Vlastimil Babka (SUSE) <[email protected]> >> Signed-off-by: Brendan Jackman <[email protected]> >> --- >> include/linux/freetype.h | 70 ++++++++++++++++++++++++++++++++++++++++-------- >> mm/Kconfig | 3 +++ >> mm/page_alloc.h | 18 +++++++++++++ >> 3 files changed, 80 insertions(+), 11 deletions(-) >> >> diff --git a/include/linux/freetype.h b/include/linux/freetype.h >> index 3b0d44023b6a1..37e88dcccdecc 100644 >> --- a/include/linux/freetype.h >> +++ b/include/linux/freetype.h >> @@ -2,6 +2,7 @@ >> #ifndef _LINUX_FREETYPE_H >> #define _LINUX_FREETYPE_H >> >> +#include <linux/log2.h> >> #include <linux/types.h> >> #include <linux/mmdebug.h> >> >> @@ -64,20 +65,47 @@ static inline bool migratetype_is_mergeable(int mt) >> return mt < MIGRATE_PCPTYPES; >> } >> >> +enum { >> + /* Defined unconditionally as a hack to avoid a zero-width bitfield. */ >> + FREETYPE_UNMAPPED_BIT, >> + NUM_FREETYPE_FLAGS, >> +}; >> + >> /* >> * A freetype is the identifier for a page freelist. This consists of a >> * migratetype, and other bits which encode orthogonal properties of memory. >> */ >> typedef struct { >> - int migratetype; >> + unsigned int migratetype : order_base_2(MIGRATE_TYPES); >> + unsigned int flags : NUM_FREETYPE_FLAGS; >> } freetype_t; >> >> +#ifdef CONFIG_PAGE_ALLOC_UNMAPPED >> +#define FREETYPE_UNMAPPED BIT(FREETYPE_UNMAPPED_BIT) >> +#define NUM_UNMAPPED_FREETYPES 1 >> +#else >> +#define FREETYPE_UNMAPPED 0 >> +#define NUM_UNMAPPED_FREETYPES 0 >> +#endif >> + >> +#define FREETYPE_FLAGS_MASK FREETYPE_UNMAPPED >> + >> /* >> * Return a dense linear index for freetypes that have lists in the free area. >> * Return -1 for other freetypes. >> */ >> static inline int freetype_idx(freetype_t freetype) >> { >> + /* For FREETYPE_UNMAPPED, only MIGRATE_UNMOVABLE has an index. */ >> + if (freetype.flags & FREETYPE_UNMAPPED) { >> + VM_WARN_ON_ONCE(freetype.flags & ~FREETYPE_UNMAPPED); > > If we move this to the beginning of the function we can drop the > VM_WARN_ON_ONCE() below, right? This one says "assert no flags are set that are incompatible with FREETYPE_UNMAPPED", the one below says "assert no completely invalid freetype flags are set". Right now those are the same thing so we _could_ combine them, but just happenstance. >> + if (freetype.migratetype != MIGRATE_UNMOVABLE) >> + return -1; >> + return MIGRATE_TYPES; >> + } >> + /* No other flags are supported. */ >> + VM_WARN_ON_ONCE(freetype.flags); >> + >> return freetype.migratetype; >> } >> >> @@ -85,33 +113,53 @@ static inline freetype_t freetype_from_idx(unsigned int idx) >> { >> freetype_t freetype; >> >> - freetype.migratetype = idx; > > Do we need a comment here? Something like this maybe: > > /* > * There is one freetype per migratetype, as well as one extra > * free type for unmovable unmapped pages. > */ > >> + if (idx == MIGRATE_TYPES) { Well we have basically that exact comment on the definition of NR_FREETYPE_IDXS. Do you think we should move it here? > >> + freetype.flags = FREETYPE_UNMAPPED; >> + freetype.migratetype = MIGRATE_UNMOVABLE; >> + } else { >> + VM_WARN_ON_ONCE(idx < 0 || idx > MIGRATE_TYPES); >> + freetype.flags = 0; >> + freetype.migratetype = idx; >> + } >> return freetype; >> } > [..] >> diff --git a/mm/page_alloc.h b/mm/page_alloc.h >> index 9928aa9012588..fac8e5304bb03 100644 >> --- a/mm/page_alloc.h >> +++ b/mm/page_alloc.h >> @@ -56,6 +56,24 @@ >> * alloc_tag_sub_check(). >> */ >> #define ALLOC_NO_CODETAG 0x1000 >> +#ifdef CONFIG_PAGE_ALLOC_UNMAPPED >> +/* >> + * Allocate pages that aren't present in the direct map. If the caller changes >> + * direct map presence, it must be restored to the previous state before freeing >> + * the page. (This is true regardless of ALLOC_UNMAPPED). >> + * >> + * This uses the mermap (when __GFP_ZERO), so it's only valid to allocate with >> + * this flag where that's valid, namely from process context after the mermap >> + * has been initialised for that process. This also means that the allocator >> + * leaves behind stale TLB entries in the mermap region. The caller is >> + * responsible for ensuring they are flushed as needed. > > I think this is no longer true as the allocator does not use the mermap > with __GFP_ZERO anymore? Oops, yep thanks. >> + * >> + * This is currently incompatible with __GFP_MOVABLE and __GFP_RECLAIMABLE, but >> + * only because of allocator implementation details, if a usecase arises this >> + * restriction could be dropped. > > It would help to explain why it's incompatible with __GFP_MOVABLE and > __GFP_RECLAIMABLE, here or in the changelog. I assume mainly because we > only have one freetype for unmapped unmovable, but there are also some > more interesting details like compaction needing to support copying > unmapped pages (e.g. via the mermap)? Yeah the latter is the reason I had in mind (also not just copying them when they're unmapped but also being aware of when it needs to "promote" a compaction to generate an entire block). And yeah it makes sense to have that in a comment. This is a kinda "API comment" so spiritually it doesn't really belong here but I'd probably put it here anyway in parans just coz that's where it will actually be seen...