s390/nmi: improve revalidation of fpu / vector registers
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 22 Aug 2016 12:40:06 +0000 (14:40 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 29 Aug 2016 09:05:03 +0000 (11:05 +0200)
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>
arch/s390/kernel/nmi.c

index 29376f0e725c985e7427b0bdbd3da60937d8f97b..9a32f7419d783f6c7c0977690ddb304fbe83c878 100644 (file)
@@ -98,7 +98,7 @@ EXPORT_SYMBOL_GPL(s390_handle_mcck);
  * returns 0 if all registers could be validated
  * returns 1 otherwise
  */
-static int notrace s390_validate_registers(union mci mci)
+static int notrace s390_validate_registers(union mci mci, int umode)
 {
        int kill_task;
        u64 zero;
@@ -110,26 +110,41 @@ static int notrace s390_validate_registers(union mci mci)
        if (!mci.gr) {
                /*
                 * General purpose registers couldn't be restored and have
-                * unknown contents. Process needs to be terminated.
+                * unknown contents. Stop system or terminate process.
                 */
+               if (!umode)
+                       s390_handle_damage();
                kill_task = 1;
        }
        if (!mci.fp) {
                /*
-                * Floating point registers can't be restored and
-                * therefore the process needs to be terminated.
+                * Floating point registers can't be restored. If the
+                * kernel currently uses floating point registers the
+                * system is stopped. If the process has its floating
+                * pointer registers loaded it is terminated.
+                * Otherwise just revalidate the registers.
                 */
-               kill_task = 1;
+               if (S390_lowcore.fpu_flags & KERNEL_VXR_V0V7)
+                       s390_handle_damage();
+               if (!test_cpu_flag(CIF_FPU))
+                       kill_task = 1;
        }
        fpt_save_area = &S390_lowcore.floating_pt_save_area;
        fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
        if (!mci.fc) {
                /*
                 * Floating point control register can't be restored.
-                * Task will be terminated.
+                * If the kernel currently uses the floating pointer
+                * registers and needs the FPC register the system is
+                * stopped. If the process has its floating pointer
+                * registers loaded it is terminated. Otherwiese the
+                * FPC is just revalidated.
                 */
+               if (S390_lowcore.fpu_flags & KERNEL_FPC)
+                       s390_handle_damage();
                asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
-               kill_task = 1;
+               if (!test_cpu_flag(CIF_FPU))
+                       kill_task = 1;
        } else
                asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
 
@@ -159,10 +174,16 @@ static int notrace s390_validate_registers(union mci mci)
 
                if (!mci.vr) {
                        /*
-                        * Vector registers can't be restored and therefore
-                        * the process needs to be terminated.
+                        * Vector registers can't be restored. If the kernel
+                        * currently uses vector registers the system is
+                        * stopped. If the process has its vector registers
+                        * loaded it is terminated. Otherwise just revalidate
+                        * the registers.
                         */
-                       kill_task = 1;
+                       if (S390_lowcore.fpu_flags & KERNEL_VXR)
+                               s390_handle_damage();
+                       if (!test_cpu_flag(CIF_FPU))
+                               kill_task = 1;
                }
                cr0.val = S390_lowcore.cregs_save_area[0];
                cr0.afp = cr0.vx = 1;
@@ -250,13 +271,11 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
        struct mcck_struct *mcck;
        unsigned long long tmp;
        union mci mci;
-       int umode;
 
        nmi_enter();
        inc_irq_stat(NMI_NMI);
        mci.val = S390_lowcore.mcck_interruption_code;
        mcck = this_cpu_ptr(&cpu_mcck);
-       umode = user_mode(regs);
 
        if (mci.sd) {
                /* System damage -> stopping machine */
@@ -297,22 +316,14 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
                        s390_handle_damage();
                }
        }
-       if (s390_validate_registers(mci)) {
-               if (umode) {
-                       /*
-                        * Couldn't restore all register contents while in
-                        * user mode -> mark task for termination.
-                        */
-                       mcck->kill_task = 1;
-                       mcck->mcck_code = mci.val;
-                       set_cpu_flag(CIF_MCCK_PENDING);
-               } else {
-                       /*
-                        * Couldn't restore all register contents while in
-                        * kernel mode -> stopping machine.
-                        */
-                       s390_handle_damage();
-               }
+       if (s390_validate_registers(mci, user_mode(regs))) {
+               /*
+                * Couldn't restore all register contents for the
+                * user space process -> mark task for termination.
+                */
+               mcck->kill_task = 1;
+               mcck->mcck_code = mci.val;
+               set_cpu_flag(CIF_MCCK_PENDING);
        }
        if (mci.cd) {
                /* Timing facility damage */