Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: diag288_wdt: Implement module autoload

The s390 specific diag288_wdt watchdog driver makes use of the virtual
watchdog timer, which is available in most machine configurations.
If executing the diagnose instruction with subcode 0x288 results in an
exception the watchdog timer is not available, otherwise it is available.

In order to allow module autoload of the diag288_wdt module, move the
detection of the virtual watchdog timer to early boot code, and provide
its availability as a cpu feature.

This allows to make use of module_cpu_feature_match() to automatically load
the module iff the virtual watchdog timer is available.

Suggested-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Tested-by: Mete Durlu <meted@linux.ibm.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20250410095036.1525057-1-hca@linux.ibm.com
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>

+68 -50
+17
arch/s390/boot/startup.c
··· 6 6 #include <asm/boot_data.h> 7 7 #include <asm/extmem.h> 8 8 #include <asm/sections.h> 9 + #include <asm/diag288.h> 9 10 #include <asm/maccess.h> 10 11 #include <asm/machine.h> 11 12 #include <asm/sysinfo.h> ··· 70 69 set_machine_feature(MFEATURE_KVM); 71 70 else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4)) 72 71 set_machine_feature(MFEATURE_VM); 72 + } 73 + 74 + static void detect_diag288(void) 75 + { 76 + /* "BEGIN" in EBCDIC character set */ 77 + static const char cmd[] = "\xc2\xc5\xc7\xc9\xd5"; 78 + unsigned long action, len; 79 + 80 + action = machine_is_vm() ? (unsigned long)cmd : LPARWDT_RESTART; 81 + len = machine_is_vm() ? sizeof(cmd) : 0; 82 + if (__diag288(WDT_FUNC_INIT, MIN_INTERVAL, action, len)) 83 + return; 84 + __diag288(WDT_FUNC_CANCEL, 0, 0, 0); 85 + set_machine_feature(MFEATURE_DIAG288); 73 86 } 74 87 75 88 static void detect_diag9c(void) ··· 534 519 detect_facilities(); 535 520 detect_diag9c(); 536 521 detect_machine_type(); 522 + /* detect_diag288() needs machine type */ 523 + detect_diag288(); 537 524 cmma_init(); 538 525 sanitize_prot_virt_host(); 539 526 max_physmem_end = detect_max_physmem_end();
+1
arch/s390/include/asm/cpufeature.h
··· 15 15 S390_CPU_FEATURE_MSA, 16 16 S390_CPU_FEATURE_VXRS, 17 17 S390_CPU_FEATURE_UV, 18 + S390_CPU_FEATURE_D288, 18 19 MAX_CPU_FEATURES 19 20 }; 20 21
+41
arch/s390/include/asm/diag288.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef _ASM_S390_DIAG288_H 4 + #define _ASM_S390_DIAG288_H 5 + 6 + #include <asm/asm-extable.h> 7 + #include <asm/types.h> 8 + 9 + #define MIN_INTERVAL 15 /* Minimal time supported by diag288 */ 10 + #define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ 11 + 12 + #define WDT_DEFAULT_TIMEOUT 30 13 + 14 + /* Function codes - init, change, cancel */ 15 + #define WDT_FUNC_INIT 0 16 + #define WDT_FUNC_CHANGE 1 17 + #define WDT_FUNC_CANCEL 2 18 + #define WDT_FUNC_CONCEAL 0x80000000 19 + 20 + /* Action codes for LPAR watchdog */ 21 + #define LPARWDT_RESTART 0 22 + 23 + static inline int __diag288(unsigned int func, unsigned int timeout, 24 + unsigned long action, unsigned int len) 25 + { 26 + union register_pair r1 = { .even = func, .odd = timeout, }; 27 + union register_pair r3 = { .even = action, .odd = len, }; 28 + int rc = -EINVAL; 29 + 30 + asm volatile( 31 + " diag %[r1],%[r3],0x288\n" 32 + "0: lhi %[rc],0\n" 33 + "1:" 34 + EX_TABLE(0b, 1b) 35 + : [rc] "+d" (rc) 36 + : [r1] "d" (r1.pair), [r3] "d" (r3.pair) 37 + : "cc", "memory"); 38 + return rc; 39 + } 40 + 41 + #endif /* _ASM_S390_DIAG288_H */
+1
arch/s390/include/asm/machine.h
··· 18 18 #define MFEATURE_VM 7 19 19 #define MFEATURE_KVM 8 20 20 #define MFEATURE_LPAR 9 21 + #define MFEATURE_DIAG288 10 21 22 22 23 #ifndef __ASSEMBLY__ 23 24
+5
arch/s390/kernel/cpufeature.c
··· 5 5 6 6 #include <linux/cpufeature.h> 7 7 #include <linux/bug.h> 8 + #include <asm/machine.h> 8 9 #include <asm/elf.h> 9 10 10 11 enum { 11 12 TYPE_HWCAP, 12 13 TYPE_FACILITY, 14 + TYPE_MACHINE, 13 15 }; 14 16 15 17 struct s390_cpu_feature { ··· 23 21 [S390_CPU_FEATURE_MSA] = {.type = TYPE_HWCAP, .num = HWCAP_NR_MSA}, 24 22 [S390_CPU_FEATURE_VXRS] = {.type = TYPE_HWCAP, .num = HWCAP_NR_VXRS}, 25 23 [S390_CPU_FEATURE_UV] = {.type = TYPE_FACILITY, .num = 158}, 24 + [S390_CPU_FEATURE_D288] = {.type = TYPE_MACHINE, .num = MFEATURE_DIAG288}, 26 25 }; 27 26 28 27 /* ··· 41 38 return !!(elf_hwcap & BIT(feature->num)); 42 39 case TYPE_FACILITY: 43 40 return test_facility(feature->num); 41 + case TYPE_MACHINE: 42 + return test_machine_feature(feature->num); 44 43 default: 45 44 WARN_ON_ONCE(1); 46 45 return 0;
+3 -50
drivers/watchdog/diag288_wdt.c
··· 29 29 #include <linux/watchdog.h> 30 30 #include <asm/machine.h> 31 31 #include <asm/ebcdic.h> 32 + #include <asm/diag288.h> 32 33 #include <asm/diag.h> 33 34 #include <linux/io.h> 34 35 35 36 #define MAX_CMDLEN 240 36 37 #define DEFAULT_CMD "SYSTEM RESTART" 37 - 38 - #define MIN_INTERVAL 15 /* Minimal time supported by diag88 */ 39 - #define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ 40 - 41 - #define WDT_DEFAULT_TIMEOUT 30 42 - 43 - /* Function codes - init, change, cancel */ 44 - #define WDT_FUNC_INIT 0 45 - #define WDT_FUNC_CHANGE 1 46 - #define WDT_FUNC_CANCEL 2 47 - #define WDT_FUNC_CONCEAL 0x80000000 48 - 49 - /* Action codes for LPAR watchdog */ 50 - #define LPARWDT_RESTART 0 51 38 52 39 static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; 53 40 static bool conceal_on; ··· 62 75 static int diag288(unsigned int func, unsigned int timeout, 63 76 unsigned long action, unsigned int len) 64 77 { 65 - union register_pair r1 = { .even = func, .odd = timeout, }; 66 - union register_pair r3 = { .even = action, .odd = len, }; 67 - int err; 68 - 69 78 diag_stat_inc(DIAG_STAT_X288); 70 - 71 - err = -EINVAL; 72 - asm volatile( 73 - " diag %[r1],%[r3],0x288\n" 74 - "0: la %[err],0\n" 75 - "1:\n" 76 - EX_TABLE(0b, 1b) 77 - : [err] "+d" (err) 78 - : [r1] "d" (r1.pair), [r3] "d" (r3.pair) 79 - : "cc", "memory"); 80 - return err; 79 + return __diag288(func, timeout, action, len); 81 80 } 82 81 83 82 static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) ··· 162 189 163 190 static int __init diag288_init(void) 164 191 { 165 - int ret; 166 - 167 192 watchdog_set_nowayout(&wdt_dev, nowayout_info); 168 193 169 194 if (machine_is_vm()) { ··· 170 199 pr_err("The watchdog cannot be initialized\n"); 171 200 return -ENOMEM; 172 201 } 173 - 174 - ret = diag288_str(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); 175 - if (ret != 0) { 176 - pr_err("The watchdog cannot be initialized\n"); 177 - kfree(cmd_buf); 178 - return -EINVAL; 179 - } 180 - } else { 181 - if (diag288(WDT_FUNC_INIT, WDT_DEFAULT_TIMEOUT, 182 - LPARWDT_RESTART, 0)) { 183 - pr_err("The watchdog cannot be initialized\n"); 184 - return -EINVAL; 185 - } 186 - } 187 - 188 - if (diag288(WDT_FUNC_CANCEL, 0, 0, 0)) { 189 - pr_err("The watchdog cannot be deactivated\n"); 190 - return -EINVAL; 191 202 } 192 203 193 204 return watchdog_register_device(&wdt_dev); ··· 181 228 kfree(cmd_buf); 182 229 } 183 230 184 - module_init(diag288_init); 231 + module_cpu_feature_match(S390_CPU_FEATURE_D288, diag288_init); 185 232 module_exit(diag288_exit);