forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
7cc8944e13
commit
29b0a8250b
@ -211,8 +211,9 @@ static inline int etr_ptff(void *ptff_block, unsigned int func)
|
||||
#define ETR_PTFF_SGS 0x43 /* set gross steering rate */
|
||||
|
||||
/* Functions needed by the machine check handler */
|
||||
void etr_switch_to_local(void);
|
||||
void etr_sync_check(void);
|
||||
int etr_switch_to_local(void);
|
||||
int etr_sync_check(void);
|
||||
void etr_queue_work(void);
|
||||
|
||||
/* notifier for syncs */
|
||||
extern struct atomic_notifier_head s390_epoch_delta_notifier;
|
||||
@ -253,7 +254,8 @@ struct stp_sstpi {
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Functions needed by the machine check handler */
|
||||
void stp_sync_check(void);
|
||||
void stp_island_check(void);
|
||||
int stp_sync_check(void);
|
||||
int stp_island_check(void);
|
||||
void stp_queue_work(void);
|
||||
|
||||
#endif /* __S390_ETR_H */
|
||||
|
@ -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 */
|
||||
|
@ -542,16 +542,17 @@ arch_initcall(etr_init);
|
||||
* Switch to local machine check. This is called when the last usable
|
||||
* ETR port goes inactive. After switch to local the clock is not in sync.
|
||||
*/
|
||||
void etr_switch_to_local(void)
|
||||
int etr_switch_to_local(void)
|
||||
{
|
||||
if (!etr_eacr.sl)
|
||||
return;
|
||||
return 0;
|
||||
disable_sync_clock(NULL);
|
||||
if (!test_and_set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events)) {
|
||||
etr_eacr.es = etr_eacr.sl = 0;
|
||||
etr_setr(&etr_eacr);
|
||||
queue_work(time_sync_wq, &etr_work);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -560,16 +561,22 @@ void etr_switch_to_local(void)
|
||||
* After a ETR sync check the clock is not in sync. The machine check
|
||||
* is broadcasted to all cpus at the same time.
|
||||
*/
|
||||
void etr_sync_check(void)
|
||||
int etr_sync_check(void)
|
||||
{
|
||||
if (!etr_eacr.es)
|
||||
return;
|
||||
return 0;
|
||||
disable_sync_clock(NULL);
|
||||
if (!test_and_set_bit(ETR_EVENT_SYNC_CHECK, &etr_events)) {
|
||||
etr_eacr.es = 0;
|
||||
etr_setr(&etr_eacr);
|
||||
queue_work(time_sync_wq, &etr_work);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void etr_queue_work(void)
|
||||
{
|
||||
queue_work(time_sync_wq, &etr_work);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1504,10 +1511,10 @@ static void stp_timing_alert(struct stp_irq_parm *intparm)
|
||||
* After a STP sync check the clock is not in sync. The machine check
|
||||
* is broadcasted to all cpus at the same time.
|
||||
*/
|
||||
void stp_sync_check(void)
|
||||
int stp_sync_check(void)
|
||||
{
|
||||
disable_sync_clock(NULL);
|
||||
queue_work(time_sync_wq, &stp_work);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1516,12 +1523,16 @@ void stp_sync_check(void)
|
||||
* have matching CTN ids and have a valid stratum-1 configuration
|
||||
* but the configurations do not match.
|
||||
*/
|
||||
void stp_island_check(void)
|
||||
int stp_island_check(void)
|
||||
{
|
||||
disable_sync_clock(NULL);
|
||||
queue_work(time_sync_wq, &stp_work);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void stp_queue_work(void)
|
||||
{
|
||||
queue_work(time_sync_wq, &stp_work);
|
||||
}
|
||||
|
||||
static int stp_sync_clock(void *data)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user