summaryrefslogtreecommitdiff
path: root/arch/s390/kernel/nmi.c
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2015-10-09 13:48:03 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2015-10-14 14:32:18 +0200
commit29b0a8250ba63beffba4fe3a42fa75dddf4bd06a (patch)
tree07d567892411d28f22003f73193ae37a80cf4d17 /arch/s390/kernel/nmi.c
parent7cc8944e13c73374b6f33b39ca24c0891c87b077 (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.c16
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 */