]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/extmem: Add workaround for DCSS unload diag
authorGerald Schaefer <gerald.schaefer@linux.ibm.com>
Mon, 12 May 2025 13:37:55 +0000 (15:37 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Sat, 17 May 2025 08:55:00 +0000 (10:55 +0200)
When calling the diag for DCSS unload on a non-IPL CPU, the sclp maximum
memory detection on the next IPL would falsely return the end of the
previously loaded DCSS.

This is because of an issue in z/VM, so work around it by always calling
the diag for DCSS unload on IPL CPU 0. That CPU cannot be set offline,
so the dcss_diag() call can directly be scheduled to CPU 0.

The wrong maximum memory value returned by sclp would only affect KASAN
kernels. When a DCSS within the falsely reported extra memory range is
loaded and accessed again, it would result in a kernel crash:

Unable to handle kernel pointer dereference in virtual kernel address space
Failing address: 001c0000a3ffe000 TEID: 001c0000a3ffe803
Fault in home space mode while using kernel ASCE.
AS:000000039955400b R2:00000003fe3b400b R3:000000037a2a8007 S:0000000000000020
Oops: 0010 ilc:3 [#1]SMP
[...]
CPU: 2 UID: 0 PID: 1563 Comm: mount Kdump: loaded Not tainted 6.15.0-rc5-11546-g3ea93fb3d026-dirty #7 NONE
Hardware name: IBM 3931 A01 704 (z/VM 7.4.0)
Krnl PSW : 0704c00180000000 000da6f2b338faf2 (kasan_check_range+0x172/0x310)
           R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 RI:0 EA:3
Krnl GPRS: 0000000000000040 001c0000a3ffe000 000000051fff0000 0000000000001000
           0000000000000000 000da6f233380ff6 00000000000001f8 0000000000000000
           001c0000a3ffe200 0000000000000040 001c0000a3ffe200 0000000000000200
           000003ff97a2cfa8 0000000000000000 0000000000000010 000da672b58af070
Krnl Code: 000da6f2b338fae241101008            la      %r1,8(%r1)
           000da6f2b338fae6eca100268064       cgrj    %r10,%r1,8,000da6f2b338fb32
          #000da6f2b338faecebe00002000c       srlg    %r14,%r0,2
          >000da6f2b338faf2e3b010000002       ltg     %r11,0(%r1)
           000da6f2b338faf8a77400a8           brc     7,000da6f2b338fc48
           000da6f2b338fafc41b01008           la      %r11,8(%r1)
           000da6f2b338fb00b904001b           lgr     %r1,%r11
           000da6f2b338fb04e3a0b0000002       ltg     %r10,0(%r11)
Call Trace:
 [<000da6f2b338faf2>] kasan_check_range+0x172/0x310
 [<000da6f2b3390b3c>] __asan_memcpy+0x3c/0x90
 [<000da6f233380ff6>] dcssblk_submit_bio+0x3a6/0x620 [dcssblk]
 [<000da6f2b3eb403c>] __submit_bio+0x25c/0x4a0
 [<000da6f2b3eb43bc>] __submit_bio_noacct+0x13c/0x450
 [<000da6f2b3eb4bde>] submit_bio_noacct_nocheck+0x50e/0x620
 [<000da6f2b34f4978>] mpage_readahead+0x318/0x3f0
 [<000da6f2b31edbe6>] read_pages+0x156/0x740
 [<000da6f2b31ee594>] page_cache_ra_unbounded+0x3c4/0x610
 [<000da6f2b31ef094>] force_page_cache_ra+0x1f4/0x2d0
 [<000da6f2b31d092e>] filemap_get_pages+0x2ce/0xaa0
 [<000da6f2b31d1428>] filemap_read+0x328/0x9a0
 [<000da6f2b3e9b7e8>] blkdev_read_iter+0x228/0x3b0
 [<000da6f2b340f7a6>] vfs_read+0x5b6/0x7f0
 [<000da6f2b34110be>] ksys_read+0x10e/0x1e0
 [<000da6f2b4e7acb2>] __do_syscall+0x122/0x1f0
 [<000da6f2b4e93ffe>] system_call+0x6e/0x90
Last Breaking-Event-Address:
 [<000da6f2b338faac>] kasan_check_range+0x12c/0x310
Kernel panic - not syncing: Fatal exception: panic_on_oops

Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/mm/extmem.c

index a6b8b8ea90864eebb63a3a8bce161614f3637ea2..f7da53e212f554d545c8b06a1d3382e59fbf9253 100644 (file)
@@ -530,6 +530,14 @@ segment_modify_shared (char *name, int do_nonshared)
        return rc;
 }
 
+static void __dcss_diag_purge_on_cpu_0(void *data)
+{
+       struct dcss_segment *seg = (struct dcss_segment *)data;
+       unsigned long dummy;
+
+       dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
+}
+
 /*
  * Decrease the use count of a DCSS segment and remove
  * it from the address space if nobody is using it
@@ -538,7 +546,6 @@ segment_modify_shared (char *name, int do_nonshared)
 void
 segment_unload(char *name)
 {
-       unsigned long dummy;
        struct dcss_segment *seg;
 
        if (!machine_is_vm())
@@ -556,7 +563,14 @@ segment_unload(char *name)
        kfree(seg->res);
        vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
        list_del(&seg->list);
-       dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
+       /*
+        * Workaround for z/VM issue, where calling the DCSS unload diag on
+        * a non-IPL CPU would cause bogus sclp maximum memory detection on
+        * next IPL.
+        * IPL CPU 0 cannot be set offline, so the dcss_diag() call can
+        * directly be scheduled to that CPU.
+        */
+       smp_call_function_single(0, __dcss_diag_purge_on_cpu_0, seg, 1);
        kfree(seg);
 out_unlock:
        mutex_unlock(&dcss_lock);