Commit df7997ab authored by Heiko Carstens's avatar Heiko Carstens

[S390] irq: fix service signal external interrupt handling

Interrupt sources like pfault, sclp, dasd_diag and virtio all use the
service signal external interrupt subclass mask in control register 0
to enable and disable the corresponding interrupt.
Because no reference counting is implemented each subsystem thinks it
is the only user of subclass and sets and clears the bit like it wants.
This leads to case that unloading the dasd diag module under z/VM
causes both sclp and pfault interrupts to be masked. The result will
be locked up system sooner or later.
Fix this by introducing a new way to set (register) and clear
(unregister) the service signal subclass mask bit in cr0.
Also convert all drivers.
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 902050bc
...@@ -13,5 +13,7 @@ typedef void (*ext_int_handler_t)(unsigned int, unsigned int, unsigned long); ...@@ -13,5 +13,7 @@ typedef void (*ext_int_handler_t)(unsigned int, unsigned int, unsigned long);
int register_external_interrupt(__u16 code, ext_int_handler_t handler); int register_external_interrupt(__u16 code, ext_int_handler_t handler);
int unregister_external_interrupt(__u16 code, ext_int_handler_t handler); int unregister_external_interrupt(__u16 code, ext_int_handler_t handler);
void service_subclass_irq_register(void);
void service_subclass_irq_unregister(void);
#endif /* _S390_EXTINT_H */ #endif /* _S390_EXTINT_H */
...@@ -106,3 +106,26 @@ void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code, ...@@ -106,3 +106,26 @@ void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code,
irq_exit(); irq_exit();
set_irq_regs(old_regs); set_irq_regs(old_regs);
} }
static DEFINE_SPINLOCK(sc_irq_lock);
static int sc_irq_refcount;
void service_subclass_irq_register(void)
{
spin_lock(&sc_irq_lock);
if (!sc_irq_refcount)
ctl_set_bit(0, 9);
sc_irq_refcount++;
spin_unlock(&sc_irq_lock);
}
EXPORT_SYMBOL(service_subclass_irq_register);
void service_subclass_irq_unregister(void)
{
spin_lock(&sc_irq_lock);
sc_irq_refcount--;
if (!sc_irq_refcount)
ctl_clear_bit(0, 9);
spin_unlock(&sc_irq_lock);
}
EXPORT_SYMBOL(service_subclass_irq_unregister);
...@@ -613,7 +613,7 @@ static int __init pfault_irq_init(void) ...@@ -613,7 +613,7 @@ static int __init pfault_irq_init(void)
rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP;
if (rc) if (rc)
goto out_pfault; goto out_pfault;
ctl_set_bit(0, 9); service_subclass_irq_register();
hotcpu_notifier(pfault_cpu_notify, 0); hotcpu_notifier(pfault_cpu_notify, 0);
return 0; return 0;
......
...@@ -642,7 +642,7 @@ dasd_diag_init(void) ...@@ -642,7 +642,7 @@ dasd_diag_init(void)
} }
ASCEBC(dasd_diag_discipline.ebcname, 4); ASCEBC(dasd_diag_discipline.ebcname, 4);
ctl_set_bit(0, 9); service_subclass_irq_register();
register_external_interrupt(0x2603, dasd_ext_handler); register_external_interrupt(0x2603, dasd_ext_handler);
dasd_diag_discipline_pointer = &dasd_diag_discipline; dasd_diag_discipline_pointer = &dasd_diag_discipline;
return 0; return 0;
...@@ -652,7 +652,7 @@ static void __exit ...@@ -652,7 +652,7 @@ static void __exit
dasd_diag_cleanup(void) dasd_diag_cleanup(void)
{ {
unregister_external_interrupt(0x2603, dasd_ext_handler); unregister_external_interrupt(0x2603, dasd_ext_handler);
ctl_clear_bit(0, 9); service_subclass_irq_unregister();
dasd_diag_discipline_pointer = NULL; dasd_diag_discipline_pointer = NULL;
} }
......
...@@ -885,12 +885,12 @@ sclp_check_interface(void) ...@@ -885,12 +885,12 @@ sclp_check_interface(void)
spin_unlock_irqrestore(&sclp_lock, flags); spin_unlock_irqrestore(&sclp_lock, flags);
/* Enable service-signal interruption - needs to happen /* Enable service-signal interruption - needs to happen
* with IRQs enabled. */ * with IRQs enabled. */
ctl_set_bit(0, 9); service_subclass_irq_register();
/* Wait for signal from interrupt or timeout */ /* Wait for signal from interrupt or timeout */
sclp_sync_wait(); sclp_sync_wait();
/* Disable service-signal interruption - needs to happen /* Disable service-signal interruption - needs to happen
* with IRQs enabled. */ * with IRQs enabled. */
ctl_clear_bit(0,9); service_subclass_irq_unregister();
spin_lock_irqsave(&sclp_lock, flags); spin_lock_irqsave(&sclp_lock, flags);
del_timer(&sclp_request_timer); del_timer(&sclp_request_timer);
if (sclp_init_req.status == SCLP_REQ_DONE && if (sclp_init_req.status == SCLP_REQ_DONE &&
...@@ -1070,7 +1070,7 @@ sclp_init(void) ...@@ -1070,7 +1070,7 @@ sclp_init(void)
spin_unlock_irqrestore(&sclp_lock, flags); spin_unlock_irqrestore(&sclp_lock, flags);
/* Enable service-signal external interruption - needs to happen with /* Enable service-signal external interruption - needs to happen with
* IRQs enabled. */ * IRQs enabled. */
ctl_set_bit(0, 9); service_subclass_irq_register();
sclp_init_mask(1); sclp_init_mask(1);
return 0; return 0;
......
...@@ -441,7 +441,7 @@ static int __init kvm_devices_init(void) ...@@ -441,7 +441,7 @@ static int __init kvm_devices_init(void)
INIT_WORK(&hotplug_work, hotplug_devices); INIT_WORK(&hotplug_work, hotplug_devices);
ctl_set_bit(0, 9); service_subclass_irq_register();
register_external_interrupt(0x2603, kvm_extint_handler); register_external_interrupt(0x2603, kvm_extint_handler);
scan_devices(); scan_devices();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment