Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+32 -7
+2
arch/s390/include/asm/s390_ext.h
··· 13 13 14 14 int register_external_interrupt(__u16 code, ext_int_handler_t handler); 15 15 int unregister_external_interrupt(__u16 code, ext_int_handler_t handler); 16 + void service_subclass_irq_register(void); 17 + void service_subclass_irq_unregister(void); 16 18 17 19 #endif /* _S390_EXTINT_H */
+23
arch/s390/kernel/s390_ext.c
··· 106 106 irq_exit(); 107 107 set_irq_regs(old_regs); 108 108 } 109 + 110 + static DEFINE_SPINLOCK(sc_irq_lock); 111 + static int sc_irq_refcount; 112 + 113 + void service_subclass_irq_register(void) 114 + { 115 + spin_lock(&sc_irq_lock); 116 + if (!sc_irq_refcount) 117 + ctl_set_bit(0, 9); 118 + sc_irq_refcount++; 119 + spin_unlock(&sc_irq_lock); 120 + } 121 + EXPORT_SYMBOL(service_subclass_irq_register); 122 + 123 + void service_subclass_irq_unregister(void) 124 + { 125 + spin_lock(&sc_irq_lock); 126 + sc_irq_refcount--; 127 + if (!sc_irq_refcount) 128 + ctl_clear_bit(0, 9); 129 + spin_unlock(&sc_irq_lock); 130 + } 131 + EXPORT_SYMBOL(service_subclass_irq_unregister);
+1 -1
arch/s390/mm/fault.c
··· 613 613 rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; 614 614 if (rc) 615 615 goto out_pfault; 616 - ctl_set_bit(0, 9); 616 + service_subclass_irq_register(); 617 617 hotcpu_notifier(pfault_cpu_notify, 0); 618 618 return 0; 619 619
+2 -2
drivers/s390/block/dasd_diag.c
··· 642 642 } 643 643 ASCEBC(dasd_diag_discipline.ebcname, 4); 644 644 645 - ctl_set_bit(0, 9); 645 + service_subclass_irq_register(); 646 646 register_external_interrupt(0x2603, dasd_ext_handler); 647 647 dasd_diag_discipline_pointer = &dasd_diag_discipline; 648 648 return 0; ··· 652 652 dasd_diag_cleanup(void) 653 653 { 654 654 unregister_external_interrupt(0x2603, dasd_ext_handler); 655 - ctl_clear_bit(0, 9); 655 + service_subclass_irq_unregister(); 656 656 dasd_diag_discipline_pointer = NULL; 657 657 } 658 658
+3 -3
drivers/s390/char/sclp.c
··· 885 885 spin_unlock_irqrestore(&sclp_lock, flags); 886 886 /* Enable service-signal interruption - needs to happen 887 887 * with IRQs enabled. */ 888 - ctl_set_bit(0, 9); 888 + service_subclass_irq_register(); 889 889 /* Wait for signal from interrupt or timeout */ 890 890 sclp_sync_wait(); 891 891 /* Disable service-signal interruption - needs to happen 892 892 * with IRQs enabled. */ 893 - ctl_clear_bit(0,9); 893 + service_subclass_irq_unregister(); 894 894 spin_lock_irqsave(&sclp_lock, flags); 895 895 del_timer(&sclp_request_timer); 896 896 if (sclp_init_req.status == SCLP_REQ_DONE && ··· 1070 1070 spin_unlock_irqrestore(&sclp_lock, flags); 1071 1071 /* Enable service-signal external interruption - needs to happen with 1072 1072 * IRQs enabled. */ 1073 - ctl_set_bit(0, 9); 1073 + service_subclass_irq_register(); 1074 1074 sclp_init_mask(1); 1075 1075 return 0; 1076 1076
+1 -1
drivers/s390/kvm/kvm_virtio.c
··· 441 441 442 442 INIT_WORK(&hotplug_work, hotplug_devices); 443 443 444 - ctl_set_bit(0, 9); 444 + service_subclass_irq_register(); 445 445 register_external_interrupt(0x2603, kvm_extint_handler); 446 446 447 447 scan_devices();