]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
s390: fix transactional execution control register handling
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Thu, 9 Nov 2017 11:29:34 +0000 (12:29 +0100)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Fri, 10 Nov 2017 17:58:00 +0000 (18:58 +0100)
Dan Horák reported the following crash related to transactional execution:

User process fault: interruption code 0013 ilc:3 in libpthread-2.26.so[3ff93c00000+1b000]
CPU: 2 PID: 1 Comm: /init Not tainted 4.13.4-300.fc27.s390x #1
Hardware name: IBM 2827 H43 400 (z/VM 6.4.0)
task: 00000000fafc8000 task.stack: 00000000fafc4000
User PSW : 0705200180000000 000003ff93c14e70
           R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:1 AS:0 CC:2 PM:0 RI:0 EA:3
User GPRS: 0000000000000077 000003ff00000000 000003ff93144d48 000003ff93144d5e
           0000000000000000 0000000000000002 0000000000000000 000003ff00000000
           0000000000000000 0000000000000418 0000000000000000 000003ffcc9fe770
           000003ff93d28f50 000003ff9310acf0 000003ff92b0319a 000003ffcc9fe6d0
User Code: 000003ff93c14e6260e0b030            std     %f14,48(%r11)
           000003ff93c14e6660f0b038            std     %f15,56(%r11)
          #000003ff93c14e6ae5600000ff0e        tbegin  0,65294
          >000003ff93c14e70a7740006            brc     7,3ff93c14e7c
           000003ff93c14e74a7080000            lhi     %r0,0
           000003ff93c14e78a7f40023            brc     15,3ff93c14ebe
           000003ff93c14e7cb2220000            ipm     %r0
           000003ff93c14e808800001c            srl     %r0,28

There are several bugs with control register handling with respect to
transactional execution:

- on task switch update_per_regs() is only called if the next task has
  an mm (is not a kernel thread). This however is incorrect. This
  breaks e.g. for user mode helper handling, where the kernel creates
  a kernel thread and then execve's a user space program. Control
  register contents related to transactional execution won't be
  updated on execve. If the previous task ran with transactional
  execution disabled then the new task will also run with
  transactional execution disabled, which is incorrect. Therefore call
  update_per_regs() unconditionally within switch_to().

- on startup the transactional execution facility is not enabled for
  the idle thread. This is not really a bug, but an inconsistency to
  other facilities. Therefore enable the facility if it is available.

- on fork the new thread's per_flags field is not cleared. This means
  that a child process inherits the PER_FLAG_NO_TE flag. This flag can
  be set with a ptrace request to disable transactional execution for
  the current process. It should not be inherited by new child
  processes in order to be consistent with the handling of all other
  PER related debugging options. Therefore clear the per_flags field in
  copy_thread_tls().

Reported-and-tested-by: Dan Horák <dan@danny.cz>
Fixes: d35339a42dd1 ("s390: add support for transactional memory")
Cc: <stable@vger.kernel.org> # v3.7+
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
arch/s390/include/asm/switch_to.h
arch/s390/kernel/early.c
arch/s390/kernel/process.c

index f6c2b5814ab0d14cf0f70977d8d29607a6ca7c00..8e6b07609ff44202f82d8da0f2b289158f7ab9d9 100644 (file)
@@ -36,8 +36,8 @@ static inline void restore_access_regs(unsigned int *acrs)
                save_ri_cb(prev->thread.ri_cb);                         \
                save_gs_cb(prev->thread.gs_cb);                         \
        }                                                               \
+       update_cr_regs(next);                                           \
        if (next->mm) {                                                 \
-               update_cr_regs(next);                                   \
                set_cpu_flag(CIF_FPU);                                  \
                restore_access_regs(&next->thread.acrs[0]);             \
                restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb);  \
index 389e9f61c76f8a13bb1bb97e4731353bc4c48f43..5096875f7822ef69af2ce141c2cce97488d9e526 100644 (file)
@@ -238,8 +238,10 @@ static __init void detect_machine_facilities(void)
                S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
        if (test_facility(40))
                S390_lowcore.machine_flags |= MACHINE_FLAG_LPP;
-       if (test_facility(50) && test_facility(73))
+       if (test_facility(50) && test_facility(73)) {
                S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
+               __ctl_set_bit(0, 55);
+       }
        if (test_facility(51))
                S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
        if (test_facility(129)) {
index 080c851dd9a5fa0f4c8d217c08829848c8204a39..cee658e27732f0859d85bead00eec8e17d788fa8 100644 (file)
@@ -86,6 +86,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long new_stackp,
        memset(&p->thread.per_user, 0, sizeof(p->thread.per_user));
        memset(&p->thread.per_event, 0, sizeof(p->thread.per_event));
        clear_tsk_thread_flag(p, TIF_SINGLE_STEP);
+       p->thread.per_flags = 0;
        /* Initialize per thread user and system timer values */
        p->thread.user_timer = 0;
        p->thread.guest_timer = 0;