]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
s390: Unmap early KASAN shadow on memory offlining
authorVasily Gorbik <gor@linux.ibm.com>
Fri, 28 Nov 2025 22:01:08 +0000 (23:01 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Sun, 7 Dec 2025 15:15:19 +0000 (16:15 +0100)
Teach the memory hotplug path to tear down KASAN shadow that
was mapped during early boot when a memory block is offlined.

Track for each sclp_mem whether its range was covered by the early
KASAN shadow via an early_shadow_mapped flag. When such a block is
deconfigured and removed via sclp_config_mem_store(), compute the
corresponding shadow range and call vmemmap_free() to unmap the
boot mapped shadow, then clear the flag.

Using vmemmap_free() for the early shadow is safe despite the use
of large mappings in the boot-time KASAN setup. The initial shadow
is mapped with 1M and 2G pages, where possible. The minimum hotplug
memory block size is 128M and always aligned (the identity mapping
is at least 2G aligned), which corresponds to a 16M chunk of at
least 1M aligned shadow. PMD-mapped 1M shadow pages therefore
never need splitting, and PUD-mapped 2G shadow pages can now be
split following the preceding changes.

Relax the modify_pagetable() sanity check in vmem so that, with
KASAN enabled, it may also operate on the KASAN shadow region in
addition to the 1:1 mapping and vmemmap area. This allows the KASAN
shadow unmapping to reuse the common vmem helpers.

Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/mm/vmem.c
drivers/s390/char/sclp_mem.c

index faed09531499b1635b44e8b2ff2d8ebb4a248972..eeadff45e0e102165be6decd1fe7074d6285a23a 100644 (file)
@@ -437,9 +437,15 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add,
 
        if (WARN_ON_ONCE(!PAGE_ALIGNED(start | end)))
                return -EINVAL;
-       /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */
+       /* Don't mess with any tables not fully in 1:1 mapping, vmemmap & kasan area */
+#ifdef CONFIG_KASAN
+       if (WARN_ON_ONCE(!(start >= KASAN_SHADOW_START && end <= KASAN_SHADOW_END) &&
+                        end > __abs_lowcore))
+               return -EINVAL;
+#else
        if (WARN_ON_ONCE(end > __abs_lowcore))
                return -EINVAL;
+#endif
        for (addr = start; addr < end; addr = next) {
                next = pgd_addr_end(addr, end);
                pgd = pgd_offset_k(addr);
index 676c085b4f8a9298c611408f59ba892a9a355908..27f0d2f12a8ba11814a9b7670a833a559f32522e 100644 (file)
@@ -44,6 +44,9 @@ struct sclp_mem {
        unsigned int id;
        unsigned int memmap_on_memory;
        unsigned int config;
+#ifdef CONFIG_KASAN
+       unsigned int early_shadow_mapped;
+#endif
 };
 
 struct sclp_mem_arg {
@@ -244,6 +247,16 @@ static ssize_t sclp_config_mem_store(struct kobject *kobj, struct kobj_attribute
                put_device(&mem->dev);
                sclp_mem_change_state(addr, block_size, 0);
                __remove_memory(addr, block_size);
+#ifdef CONFIG_KASAN
+               if (sclp_mem->early_shadow_mapped) {
+                       unsigned long start, end;
+
+                       start = (unsigned long)kasan_mem_to_shadow(__va(addr));
+                       end = start + (block_size >> KASAN_SHADOW_SCALE_SHIFT);
+                       vmemmap_free(start, end, NULL);
+                       sclp_mem->early_shadow_mapped = 0;
+               }
+#endif
                WRITE_ONCE(sclp_mem->config, 0);
        }
 out_unlock:
@@ -316,6 +329,9 @@ static int sclp_create_mem(struct sclp_mem *sclp_mem, struct kset *kset,
 
        sclp_mem->memmap_on_memory = memmap_on_memory;
        sclp_mem->config = config;
+#ifdef CONFIG_KASAN
+       sclp_mem->early_shadow_mapped = config;
+#endif
        sclp_mem->id = id;
        kobject_init(&sclp_mem->kobj, &ktype);
        rc = kobject_add(&sclp_mem->kobj, &kset->kobj, "memory%d", id);