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

s390/nmi: improve revalidation of fpu / vector registers

The machine check handler will do one of two things if the floating-point
control, a floating point register or a vector register can not be
revalidated:
1) if the PSW indicates user mode the process is terminated
2) if the PSW indicates kernel mode the system is stopped

To unconditionally stop the system for 2) is incorrect.

There are three possible outcomes if the floating-point control, a
floating point register or a vector registers can not be revalidated:
1) The kernel is inside a kernel_fpu_begin/kernel_fpu_end block and
needs the register. The system is stopped.
2) No active kernel_fpu_begin/kernel_fpu_end block and the CIF_CPU bit
is not set. The user space process needs the register and is killed.
3) No active kernel_fpu_begin/kernel_fpu_end block and the CIF_FPU bit
is set. Neither the kernel nor the user space process needs the
lost register. Just revalidate it and continue.

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

+39 -28
+39 -28
arch/s390/kernel/nmi.c
··· 98 98 * returns 0 if all registers could be validated 99 99 * returns 1 otherwise 100 100 */ 101 - static int notrace s390_validate_registers(union mci mci) 101 + static int notrace s390_validate_registers(union mci mci, int umode) 102 102 { 103 103 int kill_task; 104 104 u64 zero; ··· 110 110 if (!mci.gr) { 111 111 /* 112 112 * General purpose registers couldn't be restored and have 113 - * unknown contents. Process needs to be terminated. 113 + * unknown contents. Stop system or terminate process. 114 114 */ 115 + if (!umode) 116 + s390_handle_damage(); 115 117 kill_task = 1; 116 118 } 117 119 if (!mci.fp) { 118 120 /* 119 - * Floating point registers can't be restored and 120 - * therefore the process needs to be terminated. 121 + * Floating point registers can't be restored. If the 122 + * kernel currently uses floating point registers the 123 + * system is stopped. If the process has its floating 124 + * pointer registers loaded it is terminated. 125 + * Otherwise just revalidate the registers. 121 126 */ 122 - kill_task = 1; 127 + if (S390_lowcore.fpu_flags & KERNEL_VXR_V0V7) 128 + s390_handle_damage(); 129 + if (!test_cpu_flag(CIF_FPU)) 130 + kill_task = 1; 123 131 } 124 132 fpt_save_area = &S390_lowcore.floating_pt_save_area; 125 133 fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area; 126 134 if (!mci.fc) { 127 135 /* 128 136 * Floating point control register can't be restored. 129 - * Task will be terminated. 137 + * If the kernel currently uses the floating pointer 138 + * registers and needs the FPC register the system is 139 + * stopped. If the process has its floating pointer 140 + * registers loaded it is terminated. Otherwiese the 141 + * FPC is just revalidated. 130 142 */ 143 + if (S390_lowcore.fpu_flags & KERNEL_FPC) 144 + s390_handle_damage(); 131 145 asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero)); 132 - kill_task = 1; 146 + if (!test_cpu_flag(CIF_FPU)) 147 + kill_task = 1; 133 148 } else 134 149 asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area)); 135 150 ··· 174 159 175 160 if (!mci.vr) { 176 161 /* 177 - * Vector registers can't be restored and therefore 178 - * the process needs to be terminated. 162 + * Vector registers can't be restored. If the kernel 163 + * currently uses vector registers the system is 164 + * stopped. If the process has its vector registers 165 + * loaded it is terminated. Otherwise just revalidate 166 + * the registers. 179 167 */ 180 - kill_task = 1; 168 + if (S390_lowcore.fpu_flags & KERNEL_VXR) 169 + s390_handle_damage(); 170 + if (!test_cpu_flag(CIF_FPU)) 171 + kill_task = 1; 181 172 } 182 173 cr0.val = S390_lowcore.cregs_save_area[0]; 183 174 cr0.afp = cr0.vx = 1; ··· 271 250 struct mcck_struct *mcck; 272 251 unsigned long long tmp; 273 252 union mci mci; 274 - int umode; 275 253 276 254 nmi_enter(); 277 255 inc_irq_stat(NMI_NMI); 278 256 mci.val = S390_lowcore.mcck_interruption_code; 279 257 mcck = this_cpu_ptr(&cpu_mcck); 280 - umode = user_mode(regs); 281 258 282 259 if (mci.sd) { 283 260 /* System damage -> stopping machine */ ··· 316 297 s390_handle_damage(); 317 298 } 318 299 } 319 - if (s390_validate_registers(mci)) { 320 - if (umode) { 321 - /* 322 - * Couldn't restore all register contents while in 323 - * user mode -> mark task for termination. 324 - */ 325 - mcck->kill_task = 1; 326 - mcck->mcck_code = mci.val; 327 - set_cpu_flag(CIF_MCCK_PENDING); 328 - } else { 329 - /* 330 - * Couldn't restore all register contents while in 331 - * kernel mode -> stopping machine. 332 - */ 333 - s390_handle_damage(); 334 - } 300 + if (s390_validate_registers(mci, user_mode(regs))) { 301 + /* 302 + * Couldn't restore all register contents for the 303 + * user space process -> mark task for termination. 304 + */ 305 + mcck->kill_task = 1; 306 + mcck->mcck_code = mci.val; 307 + set_cpu_flag(CIF_MCCK_PENDING); 335 308 } 336 309 if (mci.cd) { 337 310 /* Timing facility damage */