diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2015-10-09 13:48:03 +0200 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2015-10-14 14:32:18 +0200 |
commit | 29b0a8250ba63beffba4fe3a42fa75dddf4bd06a (patch) | |
tree | 07d567892411d28f22003f73193ae37a80cf4d17 /arch/s390/kernel/nmi.c | |
parent | 7cc8944e13c73374b6f33b39ca24c0891c87b077 (diff) |
s390/etr,stp: fix possible deadlock on machine check
The first level machine check handler for etr and stp machine checks may
call queue_work() while in nmi context. This may deadlock e.g. if the
machine check happened when the interrupted context did hold a lock, that
also will be acquired by queue_work().
Therefore split etr and stp machine check handling into first and second
level handling. The second level handling will then issue the queue_work()
call in process context which avoids the potential deadlock.
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/nmi.c')
-rw-r--r-- | arch/s390/kernel/nmi.c | 16 |
1 files changed, 12 insertions, 4 deletions
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 0ae6f8e74840..4082885dd4fb 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -28,6 +28,8 @@ struct mcck_struct { int kill_task; int channel_report; int warning; + unsigned int etr_queue : 1; + unsigned int stp_queue : 1; unsigned long long mcck_code; }; @@ -81,6 +83,10 @@ void s390_handle_mcck(void) if (xchg(&mchchk_wng_posted, 1) == 0) kill_cad_pid(SIGPWR, 1); } + if (mcck.etr_queue) + etr_queue_work(); + if (mcck.stp_queue) + stp_queue_work(); if (mcck.kill_task) { local_irq_enable(); printk(KERN_EMERG "mcck: Terminating task because of machine " @@ -323,13 +329,15 @@ void notrace s390_do_machine_check(struct pt_regs *regs) if (mci->ed && mci->ec) { /* External damage */ if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC)) - etr_sync_check(); + mcck->etr_queue |= etr_sync_check(); if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH)) - etr_switch_to_local(); + mcck->etr_queue |= etr_switch_to_local(); if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC)) - stp_sync_check(); + mcck->stp_queue |= stp_sync_check(); if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) - stp_island_check(); + mcck->stp_queue |= stp_island_check(); + if (mcck->etr_queue || mcck->stp_queue) + set_cpu_flag(CIF_MCCK_PENDING); } if (mci->se) /* Storage error uncorrected */ |