Re: [PATCH v3 16/26] mm: add definitions for allocating unmapped pages

From: Brendan Jackman

Date: Fri Aug 14 2026 - 07:23:33 EST


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) <vbabka@xxxxxxxxxx>
>> Signed-off-by: Brendan Jackman <jackmanb@xxxxxxxxxx>
>> ---
>> 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...