]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
[S390] irq: fix service signal external interrupt handling
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Thu, 26 May 2011 07:48:23 +0000 (09:48 +0200)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Thu, 26 May 2011 07:48:24 +0000 (09:48 +0200)
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: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/s390_ext.h
arch/s390/kernel/s390_ext.c
arch/s390/mm/fault.c
drivers/s390/block/dasd_diag.c
drivers/s390/char/sclp.c
drivers/s390/kvm/kvm_virtio.c

index 080876d5f19650aa8cdcbf3d85913fe953109db8..85b2154b899f8113ba669714403e77b8865ee521 100644 (file)
@@ -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 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 */
index 185029919c4d98fe38440547cf47c1730b8c5a11..87b5c532abf1faf132e85bf4158869c1677493f1 100644 (file)
@@ -106,3 +106,26 @@ void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code,
        irq_exit();
        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);
index e46ba2927424a4461acdd99b7963cd2b96b6a7c3..6e922b50efa45fb8f413d7c83c7265328622a70b 100644 (file)
@@ -613,7 +613,7 @@ static int __init pfault_irq_init(void)
        rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP;
        if (rc)
                goto out_pfault;
-       ctl_set_bit(0, 9);
+       service_subclass_irq_register();
        hotcpu_notifier(pfault_cpu_notify, 0);
        return 0;
 
index 85dddb1e4126be512d843ceef08123fd86cc1b0d..5e8e82db188622a7c7a18ffa50e1aff204676f71 100644 (file)
@@ -642,7 +642,7 @@ dasd_diag_init(void)
        }
        ASCEBC(dasd_diag_discipline.ebcname, 4);
 
-       ctl_set_bit(0, 9);
+       service_subclass_irq_register();
        register_external_interrupt(0x2603, dasd_ext_handler);
        dasd_diag_discipline_pointer = &dasd_diag_discipline;
        return 0;
@@ -652,7 +652,7 @@ static void __exit
 dasd_diag_cleanup(void)
 {
        unregister_external_interrupt(0x2603, dasd_ext_handler);
-       ctl_clear_bit(0, 9);
+       service_subclass_irq_unregister();
        dasd_diag_discipline_pointer = NULL;
 }
 
index b76c61f824857d4ed05d3a38e9657f078098ef90..b37b98cbbd0093b297558ee862c5884d21e3be9d 100644 (file)
@@ -885,12 +885,12 @@ sclp_check_interface(void)
                spin_unlock_irqrestore(&sclp_lock, flags);
                /* Enable service-signal interruption - needs to happen
                 * with IRQs enabled. */
-               ctl_set_bit(0, 9);
+               service_subclass_irq_register();
                /* Wait for signal from interrupt or timeout */
                sclp_sync_wait();
                /* Disable service-signal interruption - needs to happen
                 * with IRQs enabled. */
-               ctl_clear_bit(0,9);
+               service_subclass_irq_unregister();
                spin_lock_irqsave(&sclp_lock, flags);
                del_timer(&sclp_request_timer);
                if (sclp_init_req.status == SCLP_REQ_DONE &&
@@ -1070,7 +1070,7 @@ sclp_init(void)
        spin_unlock_irqrestore(&sclp_lock, flags);
        /* Enable service-signal external interruption - needs to happen with
         * IRQs enabled. */
-       ctl_set_bit(0, 9);
+       service_subclass_irq_register();
        sclp_init_mask(1);
        return 0;
 
index 607998f0b7d8580c05c4fec47cf672bfad9d3c14..724b5923b6e2daae397d9b79bb1900a25dc07444 100644 (file)
@@ -441,7 +441,7 @@ static int __init kvm_devices_init(void)
 
        INIT_WORK(&hotplug_work, hotplug_devices);
 
-       ctl_set_bit(0, 9);
+       service_subclass_irq_register();
        register_external_interrupt(0x2603, kvm_extint_handler);
 
        scan_devices();