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

[S390] Fix reboot hang on LPARs

Reboot hangs on LPARs without diag308 support. The reason for this is,
that before the reboot is done, the channel subsystem is shut down.
During the reset on each possible subchannel a "store subchannel" is
done. This operation can end in a program check interruption, if the
specified subchannel set is not implemented by the hardware. During
the reset, currently we do not have a program check handler, which
leads to the described kernel bug. We install now a new program check
handler for the reboot code to fix this problem.

Signed-off-by: Michael Holzheu <holzheu@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Michael Holzheu and committed by
Martin Schwidefsky
a45e1414 b3c14d0b

+75 -3
+9 -1
arch/s390/kernel/ipl.c
··· 1037 1037 } 1038 1038 1039 1039 extern void reset_mcck_handler(void); 1040 + extern void reset_pgm_handler(void); 1040 1041 1041 1042 void s390_reset_system(void) 1042 1043 { 1043 1044 struct _lowcore *lc; 1044 1045 1045 - /* Stack for interrupt/machine check handler */ 1046 1046 lc = (struct _lowcore *)(unsigned long) store_prefix(); 1047 + 1048 + /* Stack for interrupt/machine check handler */ 1047 1049 lc->panic_stack = S390_lowcore.panic_stack; 1048 1050 1049 1051 /* Disable prefixing */ ··· 1058 1056 S390_lowcore.mcck_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; 1059 1057 S390_lowcore.mcck_new_psw.addr = 1060 1058 PSW_ADDR_AMODE | (unsigned long) &reset_mcck_handler; 1059 + 1060 + /* Set new program check handler */ 1061 + S390_lowcore.program_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; 1062 + S390_lowcore.program_new_psw.addr = 1063 + PSW_ADDR_AMODE | (unsigned long) &reset_pgm_handler; 1064 + 1061 1065 do_reset_calls(); 1062 1066 }
+42
arch/s390/kernel/reset.S
··· 3 3 * 4 4 * Copyright (C) IBM Corp. 2006 5 5 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> 6 + * Michael Holzheu <holzheu@de.ibm.com> 6 7 */ 7 8 8 9 #include <asm/ptrace.h> ··· 28 27 s390_reset_mcck_handler: 29 28 .quad 0 30 29 30 + .globl reset_pgm_handler 31 + reset_pgm_handler: 32 + stmg %r0,%r15,__LC_SAVE_AREA 33 + basr %r13,0 34 + 0: lg %r15,__LC_PANIC_STACK # load panic stack 35 + aghi %r15,-STACK_FRAME_OVERHEAD 36 + lg %r1,s390_reset_pgm_handler-0b(%r13) 37 + ltgr %r1,%r1 38 + jz 1f 39 + basr %r14,%r1 40 + lmg %r0,%r15,__LC_SAVE_AREA 41 + lpswe __LC_PGM_OLD_PSW 42 + 1: lpswe disabled_wait_psw-0b(%r13) 43 + .globl s390_reset_pgm_handler 44 + s390_reset_pgm_handler: 45 + .quad 0 46 + .align 8 47 + disabled_wait_psw: 48 + .quad 0x0002000180000000,0x0000000000000000 + reset_pgm_handler 49 + 31 50 #else /* CONFIG_64BIT */ 32 51 33 52 .globl reset_mcck_handler ··· 65 44 .globl s390_reset_mcck_handler 66 45 s390_reset_mcck_handler: 67 46 .long 0 47 + 48 + .globl reset_pgm_handler 49 + reset_pgm_handler: 50 + stm %r0,%r15,__LC_SAVE_AREA 51 + basr %r13,0 52 + 0: l %r15,__LC_PANIC_STACK # load panic stack 53 + ahi %r15,-STACK_FRAME_OVERHEAD 54 + l %r1,s390_reset_pgm_handler-0b(%r13) 55 + ltr %r1,%r1 56 + jz 1f 57 + basr %r14,%r1 58 + lm %r0,%r15,__LC_SAVE_AREA 59 + lpsw __LC_PGM_OLD_PSW 60 + 61 + 1: lpsw disabled_wait_psw-0b(%r13) 62 + .globl s390_reset_pgm_handler 63 + s390_reset_pgm_handler: 64 + .long 0 65 + disabled_wait_psw: 66 + .align 8 67 + .long 0x000a0000,0x00000000 + reset_pgm_handler 68 68 69 69 #endif /* CONFIG_64BIT */
+23 -2
drivers/s390/cio/cio.c
··· 871 871 return -EBUSY; 872 872 } 873 873 874 + static int pgm_check_occured; 875 + 876 + static void cio_reset_pgm_check_handler(void) 877 + { 878 + pgm_check_occured = 1; 879 + } 880 + 881 + static int stsch_reset(struct subchannel_id schid, volatile struct schib *addr) 882 + { 883 + int rc; 884 + 885 + pgm_check_occured = 0; 886 + s390_reset_pgm_handler = cio_reset_pgm_check_handler; 887 + rc = stsch(schid, addr); 888 + s390_reset_pgm_handler = NULL; 889 + if (pgm_check_occured) 890 + return -EIO; 891 + else 892 + return rc; 893 + } 894 + 874 895 static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) 875 896 { 876 897 struct schib schib; 877 898 878 - if (stsch_err(schid, &schib)) 899 + if (stsch_reset(schid, &schib)) 879 900 return -ENXIO; 880 901 if (!schib.pmcw.ena) 881 902 return 0; ··· 993 972 struct schib schib; 994 973 struct sch_match_id *match_id = data; 995 974 996 - if (stsch_err(schid, &schib)) 975 + if (stsch_reset(schid, &schib)) 997 976 return -ENXIO; 998 977 if (schib.pmcw.dnv && 999 978 (schib.pmcw.dev == match_id->devid.devno) &&
+1
include/asm-s390/reset.h
··· 19 19 extern void unregister_reset_call(struct reset_call *reset); 20 20 extern void s390_reset_system(void); 21 21 extern void (*s390_reset_mcck_handler)(void); 22 + extern void (*s390_reset_pgm_handler)(void); 22 23 23 24 #endif /* _ASM_S390_RESET_H */