[PATCH RFC v2 18/29] mm: asi: Map dynamic percpu memory as nonsensitive

Brendan Jackman
2025-01-10T18:41:32Z

From: Reiji Watanabe <reijiw@google.com>

Currently, all dynamic percpu memory is implicitly (and
unintentionally) treated as sensitive memory.

Unconditionally map pages for dynamically allocated percpu
memory as global nonsensitive memory, other than pages that
are allocated for pcpu_{first,reserved}_chunk during early
boot via memblock allocator (these will be taken care by the
following patch).

We don't support sensitive percpu memory allocation yet.

Co-developed-by: Junaid Shahid <junaids@google.com>
Signed-off-by: Junaid Shahid <junaids@google.com>
Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Brendan Jackman <jackmanb@google.com>

WIP: Drop VM_SENSITIVE checks from percpu code
---
mm/percpu-vm.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------
mm/percpu.c | 4 ++--
2 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index cd69caf6aa8d8eded2395eb4bc4051b78ec6aa33..2935d7fbac41548819a94dcc60566bd18cde819a 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -132,11 +132,20 @@ static void pcpu_pre_unmap_flush(struct pcpu_chunk *chunk,
pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
}

-static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
+static void ___pcpu_unmap_pages(unsigned long addr, int nr_pages)
{
vunmap_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT));
}

+static void __pcpu_unmap_pages(unsigned long addr, int nr_pages,
+ unsigned long vm_flags)
+{
+ unsigned long size = nr_pages << PAGE_SHIFT;
+
+ asi_unmap(ASI_GLOBAL_NONSENSITIVE, (void *)addr, size);
+ ___pcpu_unmap_pages(addr, nr_pages);
+}
+
/**
* pcpu_unmap_pages - unmap pages out of a pcpu_chunk
* @chunk: chunk of interest
@@ -153,6 +162,8 @@ static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
static void pcpu_unmap_pages(struct pcpu_chunk *chunk,
struct page **pages, int page_start, int page_end)
{
+ struct vm_struct **vms = (struct vm_struct **)chunk->data;
+ unsigned long vm_flags = vms ? vms[0]->flags : VM_ALLOC;
unsigned int cpu;
int i;

@@ -165,7 +176,7 @@ static void pcpu_unmap_pages(struct pcpu_chunk *chunk,
pages[pcpu_page_idx(cpu, i)] = page;
}
__pcpu_unmap_pages(pcpu_chunk_addr(chunk, cpu, page_start),
- page_end - page_start);
+ page_end - page_start, vm_flags);
}
}

@@ -190,13 +201,38 @@ static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,
pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
}

-static int __pcpu_map_pages(unsigned long addr, struct page **pages,
- int nr_pages)
+/*
+ * __pcpu_map_pages() should not be called during the percpu initialization,
+ * as asi_map() depends on the page allocator (which isn't available yet
+ * during percpu initialization). Instead, ___pcpu_map_pages() can be used
+ * during the percpu initialization. But, any pages that are mapped with
+ * ___pcpu_map_pages() will be treated as sensitive memory, unless
+ * they are explicitly mapped with asi_map() later.
+ */
+static int ___pcpu_map_pages(unsigned long addr, struct page **pages,
+ int nr_pages)
{
return vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT),
PAGE_KERNEL, pages, PAGE_SHIFT);
}

+static int __pcpu_map_pages(unsigned long addr, struct page **pages,
+ int nr_pages, unsigned long vm_flags)
+{
+ unsigned long size = nr_pages << PAGE_SHIFT;
+ int err;
+
+ err = ___pcpu_map_pages(addr, pages, nr_pages);
+ if (err)
+ return err;
+
+ /*
+ * If this fails, pcpu_map_pages()->__pcpu_unmap_pages() will call
+ * asi_unmap() and clean up any partial mappings.
+ */
+ return asi_map(ASI_GLOBAL_NONSENSITIVE, (void *)addr, size);
+}
+
/**
* pcpu_map_pages - map pages into a pcpu_chunk
* @chunk: chunk of interest
@@ -214,13 +250,15 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
static int pcpu_map_pages(struct pcpu_chunk *chunk,
struct page **pages, int page_start, int page_end)
{
+ struct vm_struct **vms = (struct vm_struct **)chunk->data;
+ unsigned long vm_flags = vms ? vms[0]->flags : VM_ALLOC;
unsigned int cpu, tcpu;
int i, err;

for_each_possible_cpu(cpu) {
err = __pcpu_map_pages(pcpu_chunk_addr(chunk, cpu, page_start),
&pages[pcpu_page_idx(cpu, page_start)],
- page_end - page_start);
+ page_end - page_start, vm_flags);
if (err < 0)
goto err;

@@ -232,7 +270,7 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk,
err:
for_each_possible_cpu(tcpu) {
__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
- page_end - page_start);
+ page_end - page_start, vm_flags);
if (tcpu == cpu)
break;
}
diff --git a/mm/percpu.c b/mm/percpu.c
index da21680ff294cb53dfb42bf0d3b3bbd2654d2cfa..c2d913c579bf07892957ac7f601a6a71defadc4b 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -3273,8 +3273,8 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
pcpu_populate_pte(unit_addr + (i << PAGE_SHIFT));

/* pte already populated, the following shouldn't fail */
- rc = __pcpu_map_pages(unit_addr, &pages[unit * unit_pages],
- unit_pages);
+ rc = ___pcpu_map_pages(unit_addr, &pages[unit * unit_pages],
+ unit_pages);
if (rc < 0)
panic("failed to map percpu area, err=%d\n", rc);

--
2.47.1.613.gc27f4b7a9f-goog