From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5C8A366823 for ; Fri, 27 Feb 2026 10:47:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772189269; cv=none; b=g7fKQ8n5CdW3eD4jtItDtm7Wce/+Tup5JM9VWLZvJzyWWjYujmDDu/86ba8wqwDMUKt8EZFNUDcwPfszOwQS6CsjpV0buezD76JdgDtyhvfRmJJ1/LRse1NUYC/BHxbwiR7HpcvnthJmzfwvkvkmTCqS5Uk5wVkUickvK/S5oLo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772189269; c=relaxed/simple; bh=e/JXdJfXwgx028pdZKDYfOOUOsClhbNJ9Qe9ZxZbLo0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uR+dL+w7rB+4IkTTG3jVuhQu4N6YJmBvjH0DsWEKzsEdMe5SVC2nPMhWNMKk0VrIboy7DuXTkPIcqWndl9sTv22C/PnccaGJLWzy45QQG+umPkrbBfD9MIAsnIEHpFFW/kJ5fnoZbhjQtsjNuHSwpL+tru/BKgPIJIs0wRTCK/8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--jackmanb.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=gj7KWSsh; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--jackmanb.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="gj7KWSsh" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-483bcfdaf7dso19021535e9.0 for ; Fri, 27 Feb 2026 02:47:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772189266; x=1772794066; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=MIZdtDXTxmY5bMoPqmkr1xnlX1DtFVenM/eaR2GkvVM=; b=gj7KWSshxoFhva+j93uO316arSwT+yO7rBSM6dIR33QTa5NKSxwke5vmUqsL7+gT7y MC9wt6CVG2i9U0HmeLUx4uc3W+SqL4PMRJL0938WvMTXZHeZFFtWi9xNIB9Vn6MgztWh Zh/8MRJXAmV54PhRjoKeAs9HiD15hB9r9/+FLGn1suJT4f3lCfsKVfgrwbAAK3xmU3q0 CRE+N0hO0J/chrlZwPkL3nXXXxpUoIXHc2D/D0aYUyODGHfxbseTKIbBDJJdU5O+dBgp HR8SQQoOsKFQhR9Za7uB8O0N1hmvDYnUt2MRITi8613h4H8iZfwbCcS+zoDrQwCqku/e LiGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772189266; x=1772794066; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MIZdtDXTxmY5bMoPqmkr1xnlX1DtFVenM/eaR2GkvVM=; b=VANW8CvJlKqvuufX2R8PPYmYX8FAQ2o9sHP5k/PIG0CE7xIM8k88Rpmlbtbd5dCX3X d8JJ60Jt9VMPoGtaJdVm8VwaL5C+soCeLAefZbADa3kpjk7XNIkXYnmFspZysKf3T7yU MFMkNFRiqhpoyNCQIHn33BoBL3MCCmuTNTxCzxVC/z6SwH6NiiQgaIA1yjEnSRBE9T3E u3nqGRC76649PGzIHk2T/q1ug9G59PXzveGQx1rxki0DRmrkIDPb9KwcBuHzUsQcRV2J njTcEVTRj59T9VipLXycNvoaUpV8hdlKuD77v74fc1CFHXLb+0PJX6JMAYHiz2m3GoHZ ay3g== X-Forwarded-Encrypted: i=1; AJvYcCUkZeyoEuW8fPrdgYFZhUpGs5mMBvv0IKnlXkEYbx9Ksnpf8lNgPiNNVKlyIO7OdV60iQaDMO2T0pFznNU=@vger.kernel.org X-Gm-Message-State: AOJu0YwwAwX8G5nEeXFHR7v4YepKs6UDnXHlZ9sHBtif+WtueH/B9xWF GBfl9aYbKt4aac9ZvfTH4pPhMcW/Nt3u5jnJ+u40bayRHCI1D9L7389EdTcC7gTg2s/qJbwSNmU Xoh/s3xTJ9Yw15w== X-Received: from wmjf8.prod.google.com ([2002:a7b:cd08:0:b0:483:291c:7f23]) (user=jackmanb job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600d:6447:20b0:477:98f7:2aec with SMTP id 5b1f17b1804b1-483c9ba7e34mr24732745e9.3.1772189265937; Fri, 27 Feb 2026 02:47:45 -0800 (PST) Date: Fri, 27 Feb 2026 10:47:45 +0000 In-Reply-To: <20260225-page_alloc-unmapped-v1-4-e8808a03cd66@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260225-page_alloc-unmapped-v1-0-e8808a03cd66@google.com> <20260225-page_alloc-unmapped-v1-4-e8808a03cd66@google.com> X-Mailer: aerc 0.21.0 Message-ID: Subject: Re: [PATCH RFC 04/19] x86/mm: introduce the mermap From: Brendan Jackman To: Brendan Jackman , Borislav Petkov , Dave Hansen , Peter Zijlstra , Andrew Morton , David Hildenbrand , Lorenzo Stoakes , Vlastimil Babka , Wei Xu , Johannes Weiner , Zi Yan Cc: , , , , Sumit Garg , , , Will Deacon , , "Kalyazin, Nikita" , , "Itazuri, Takahiro" , Andy Lutomirski , David Kaplan , Thomas Gleixner , Yosry Ahmed Content-Type: text/plain; charset="UTF-8" Relaying some code review from an AI that I wasn't able to run before sending... (This isn't the AI's verbatim output I'm filtering it and rephrasing). On Wed Feb 25, 2026 at 4:34 PM UTC, Brendan Jackman wrote: > + > +/* Call with migration disabled. */ > +static inline struct mermap_alloc *mermap_alloc(struct mm_struct *mm, > + unsigned long size, bool use_reserve) > +{ > + int cpu = raw_smp_processor_id(); > + struct mermap_cpu *mc = this_cpu_ptr(mm->mermap.cpu); > + unsigned long cpu_end = mermap_cpu_end(cpu); > + struct mermap_alloc *alloc = NULL; > + > + /* > + * This is an extremely stupid allocator, there can only ever be a small > + * number of allocations so everything just works on linear search. > + * > + * Allocations are "in order", i.e. if the whole region is free it > + * allocates from the beginning. If there are any existing allocations > + * it allocates from right after the last (highest address) one. Any > + * free space before that goes unused. > + * > + * Once an allocation has been freed, the space it occupied must be flushed > + * from the TLB before it can be reused. > + * > + * Visual example of how this is suppose to behave (A for allocated, T for > + * TLB-flush-pending): > + * > + * _______________ Start with everything free. > + * AaaA___________ Allocate something. > + * TttT___________ Free it. (Region needs a TLB flush now). > + * TttTAaaaaaaaA__ Allocate something else. > + * TttTAaaaaaaaAAA Allocate the remaining space. > + * TttTTtttttttTAA Free the allocation before last. > + * ^^^^^^^^^^^^^ This could all be reused now but for simplicity it > + * isn't. Another allocation at this point will fail. > + * TttTTtttttttTTT Free the last allocation. > + * _______________ Next time we allocate, first flush the TLB > + * AA_____________ Now we're back at the beginning. > + */ > + > + if (use_reserve) { > + if (WARN_ON_ONCE(size != PAGE_SIZE)) > + return NULL; > + lockdep_assert_preemption_disabled(); > + } else { > + cpu_end -= PAGE_SIZE; > + } > + > + if (WARN_ON_ONCE(!in_task())) > + return NULL; > + guard(preempt)(); > + > + /* Out of already-available space? */ > + if (mc->next_addr + size > cpu_end) { > + unsigned long new_next = mermap_cpu_base(cpu); > + > + /* Would we have space after a TLB flush? */ > + for (int i = 0; i < ARRAY_SIZE(mc->allocs); i++) { > + struct mermap_alloc *alloc = &mc->allocs[i]; > + > + /* > + * The space between the uppermost allocated alloc->end > + * (or the base of the CPU's region if there are no > + * current allocations) and mc->next_addr has been > + * unmapped in the pagetables, but not flushed from the > + * TLB. Set new_next to point to the beginning of that > + * space. > + */ > + if (READ_ONCE(alloc->in_use)) > + new_next = max(new_next, alloc->end); > + } > + if (size > cpu_end - new_next) > + return NULL; > + > + mermap_flush_tlb(cpu, mc); > + mc->next_addr = new_next; > + } > + > + /* Find an alloc-tracking structure to use */ > + for (int i = 0; i < ARRAY_SIZE(mc->allocs); i++) { > + if (!READ_ONCE(mc->allocs[i].in_use)) { > + alloc = &mc->allocs[i]; > + break; > + } > + } > + if (!alloc) > + return NULL; Oops, I forgot to account for @use_reserve here. The alloc-tracking structures should have a reservation like how the virtual address space does otherwise allocations can fail where they aren't supposed to. > + alloc->in_use = true; > + alloc->base = mc->next_addr; > + alloc->end = alloc->base + size; > + mc->next_addr += size; > + > + return alloc; > +} > + > +struct set_pte_ctx { > + pgprot_t prot; > + unsigned long next_pfn; > +}; > + > +static inline int do_set_pte(pte_t *pte, unsigned long addr, void *data) > +{ > + struct set_pte_ctx *ctx = data; > + > + set_pte(pte, pfn_pte(ctx->next_pfn, ctx->prot)); > + ctx->next_pfn++; > + > + return 0; > +} > + > +static struct mermap_alloc * > +__mermap_get(struct mm_struct *mm, struct page *page, > + unsigned long size, pgprot_t prot, bool use_reserve) > +{ > + struct mermap_alloc *alloc = NULL; > + struct set_pte_ctx ctx; > + int err; > + > + if (size > MERMAP_CPU_REGION_SIZE || WARN_ON_ONCE(!mm || !mm->mermap.cpu)) > + return NULL; > + if (WARN_ON_ONCE(!arch_mermap_pgprot_allowed(prot))) > + return NULL; > + > + size = PAGE_ALIGN(size); > + > + migrate_disable(); > + > + alloc = mermap_alloc(mm, size, use_reserve); > + if (!alloc) { > + migrate_enable(); > + return NULL; > + } > + > + /* This probably wants to be optimised. */ > + ctx.prot = prot; > + ctx.next_pfn = page_to_pfn(page); > + err = apply_to_existing_page_range(mm, alloc->base, size, do_set_pte, &ctx); This takes a PTE lock, and we may have preemption off here, so this may be broken on PREEMPT_RT? Haven't checked. (Maybe I can just test this by running it with PREEMPT_RT and lockdep enabled?) If that's indeed broken, this is yet another point to discuss in about requirements for a pagetable library [0]. The lock is not needed at all here - we need a way to modify pagetables that lets you take advantage of higher-level synchronization. [0] https://lore.kernel.org/all/20260219175113.618562-1-jackmanb@google.com/ > + if (err) { > + WRITE_ONCE(alloc->in_use, false); > + return NULL; Forgot migrate_enable(). (Is there a way to prevent this with the lovely new __attribute__((cleanup)) magic?) > + } > + > + return alloc; > +} > + > +/* > + * Allocate a region of virtual memory, and map the page into it. This tries > + * pretty hard to be fast but doesn't try very hard at all to actually succeed. > + * > + * The returned region is physically local to the current mm. It is _logically_ > + * local to the current CPU but this is not enforced by hardware so it can be > + * exploited to mitigate CPU vulns. This means the caller must not map memory > + * here that doesn't belong to the current process. The caller must also perform > + * a full TLB flush of the region before freeing the pages that have been mapped > + * here. > + * > + * This may only be called from process context, and the caller must arrange to > + * first call mermap_mm_prepare(). (It would be possible to support this in IRQ, > + * but it seems unlikely there's a valid usecase given the TLB flushing > + * requirements). If it succeeds, it disables migration until you call > + * mermap_put(). > + * > + * This is guaranteed not to allocate. This one isn't from AI, but I just realised that's a pretty confusing thing for an allocator to say. It should say it's guaranteed not to call the page allocator. (This is important coz I want to use it from the page allocator). > + * > + * Use mermap_addr() to get the actual address of the mapped region. > + */ > +struct mermap_alloc *mermap_get(struct page *page, unsigned long size, pgprot_t prot) > +{ > + return __mermap_get(current->mm, page, size, prot, false); > +} > +EXPORT_SYMBOL(mermap_get); > + > +/* > + * Allocate a single PAGE_SIZE page via mermap_get(), requiring preemption to be > + * off until it is freed. This always succeeds. > + */ > +void *mermap_get_reserved(struct page *page, pgprot_t prot) Oops, that should return mermap_alloc *. > +/* Clean up mermap stuff on mm teardown. */ > +void mermap_mm_teardown(struct mm_struct *mm) > +{ > + int cpu; > + > + if (!mm->mermap.cpu) > + return; > + > + for_each_possible_cpu(cpu) { > + struct mermap_cpu *mc = this_cpu_ptr(mm->mermap.cpu); Oops, this should be per_cpu_ptr(..., cpu) or whatever it is.