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

s390/airq: simplify adapter interrupt code

There are three users of adapter interrupts: AP, QDIO and PCI. Each
registers a single adapter interrupt with independent ISCs. Define
a "struct airq" with the interrupt handler, a pointer and a mask for
the local summary indicator and the ISC for the adapter interrupt
source. Convert the indicator array with its fixed number of adapter
interrupt sources per ISE to an array of hlists. This removes the
limitation to 32 adapter interrupts per ISC and allows for arbitrary
memory locations for the local summary indicator.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+130 -168
+12 -3
arch/s390/include/asm/airq.h
··· 9 9 #ifndef _ASM_S390_AIRQ_H 10 10 #define _ASM_S390_AIRQ_H 11 11 12 - typedef void (*adapter_int_handler_t)(void *, void *); 12 + struct airq_struct { 13 + struct hlist_node list; /* Handler queueing. */ 14 + void (*handler)(struct airq_struct *); /* Thin-interrupt handler */ 15 + u8 *lsi_ptr; /* Local-Summary-Indicator pointer */ 16 + u8 lsi_mask; /* Local-Summary-Indicator mask */ 17 + u8 isc; /* Interrupt-subclass */ 18 + u8 flags; 19 + }; 13 20 14 - void *s390_register_adapter_interrupt(adapter_int_handler_t, void *, u8); 15 - void s390_unregister_adapter_interrupt(void *, u8); 21 + #define AIRQ_PTR_ALLOCATED 0x01 22 + 23 + int register_adapter_interrupt(struct airq_struct *airq); 24 + void unregister_adapter_interrupt(struct airq_struct *airq); 16 25 17 26 #endif /* _ASM_S390_AIRQ_H */
+13 -14
arch/s390/pci/pci.c
··· 82 82 83 83 static struct intr_bucket *bucket; 84 84 85 - /* Adapter local summary indicator */ 86 - static u8 *zpci_irq_si; 85 + /* Adapter interrupt definitions */ 86 + static void zpci_irq_handler(struct airq_struct *airq); 87 + 88 + static struct airq_struct zpci_airq = { 89 + .handler = zpci_irq_handler, 90 + .isc = PCI_ISC, 91 + }; 87 92 88 93 /* I/O Map */ 89 94 static DEFINE_SPINLOCK(zpci_iomap_lock); ··· 407 402 /* store the last handled bit to implement fair scheduling of devices */ 408 403 static DEFINE_PER_CPU(unsigned long, next_sbit); 409 404 410 - static void zpci_irq_handler(void *dont, void *need) 405 + static void zpci_irq_handler(struct airq_struct *airq) 411 406 { 412 407 unsigned long sbit, mbit, last = 0, start = __get_cpu_var(next_sbit); 413 408 int rescan = 0, max = aisb_max; ··· 717 712 goto out_alloc; 718 713 } 719 714 720 - isc_register(PCI_ISC); 721 - zpci_irq_si = s390_register_adapter_interrupt(&zpci_irq_handler, NULL, PCI_ISC); 722 - if (IS_ERR(zpci_irq_si)) { 723 - rc = PTR_ERR(zpci_irq_si); 724 - zpci_irq_si = NULL; 715 + rc = register_adapter_interrupt(&zpci_airq); 716 + if (rc) 725 717 goto out_ai; 726 - } 718 + /* Set summary to 1 to be called every time for the ISC. */ 719 + *zpci_airq.lsi_ptr = 1; 727 720 728 721 for_each_online_cpu(cpu) 729 722 per_cpu(next_sbit, cpu) = 0; 730 723 731 724 spin_lock_init(&bucket->lock); 732 - /* set summary to 1 to be called every time for the ISC */ 733 - *zpci_irq_si = 1; 734 725 set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC); 735 726 return 0; 736 727 737 728 out_ai: 738 - isc_unregister(PCI_ISC); 739 729 free_page((unsigned long) bucket->alloc); 740 730 out_alloc: 741 731 free_page((unsigned long) bucket->aisb); ··· 743 743 { 744 744 free_page((unsigned long) bucket->alloc); 745 745 free_page((unsigned long) bucket->aisb); 746 - s390_unregister_adapter_interrupt(zpci_irq_si, PCI_ISC); 747 - isc_unregister(PCI_ISC); 746 + unregister_adapter_interrupt(&zpci_airq); 748 747 kfree(bucket); 749 748 } 750 749
+54 -109
drivers/s390/cio/airq.c
··· 9 9 */ 10 10 11 11 #include <linux/init.h> 12 + #include <linux/irq.h> 13 + #include <linux/kernel_stat.h> 12 14 #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/rculist.h> 13 17 #include <linux/slab.h> 14 - #include <linux/rcupdate.h> 15 18 16 19 #include <asm/airq.h> 17 20 #include <asm/isc.h> 18 21 19 22 #include "cio.h" 20 23 #include "cio_debug.h" 24 + #include "ioasm.h" 21 25 22 - #define NR_AIRQS 32 23 - #define NR_AIRQS_PER_WORD sizeof(unsigned long) 24 - #define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) 25 - 26 - union indicator_t { 27 - unsigned long word[NR_AIRQ_WORDS]; 28 - unsigned char byte[NR_AIRQS]; 29 - } __attribute__((packed)); 30 - 31 - struct airq_t { 32 - adapter_int_handler_t handler; 33 - void *drv_data; 34 - }; 35 - 36 - static union indicator_t indicators[MAX_ISC+1]; 37 - static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS]; 38 - 39 - static int register_airq(struct airq_t *airq, u8 isc) 40 - { 41 - int i; 42 - 43 - for (i = 0; i < NR_AIRQS; i++) 44 - if (!cmpxchg(&airqs[isc][i], NULL, airq)) 45 - return i; 46 - return -ENOMEM; 47 - } 26 + static DEFINE_SPINLOCK(airq_lists_lock); 27 + static struct hlist_head airq_lists[MAX_ISC+1]; 48 28 49 29 /** 50 - * s390_register_adapter_interrupt() - register adapter interrupt handler 51 - * @handler: adapter handler to be registered 52 - * @drv_data: driver data passed with each call to the handler 53 - * @isc: isc for which the handler should be called 30 + * register_adapter_interrupt() - register adapter interrupt handler 31 + * @airq: pointer to adapter interrupt descriptor 54 32 * 55 - * Returns: 56 - * Pointer to the indicator to be used on success 57 - * ERR_PTR() if registration failed 33 + * Returns 0 on success, or -EINVAL. 58 34 */ 59 - void *s390_register_adapter_interrupt(adapter_int_handler_t handler, 60 - void *drv_data, u8 isc) 35 + int register_adapter_interrupt(struct airq_struct *airq) 61 36 { 62 - struct airq_t *airq; 63 - char dbf_txt[16]; 64 - int ret; 37 + char dbf_txt[32]; 65 38 66 - if (isc > MAX_ISC) 67 - return ERR_PTR(-EINVAL); 68 - airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); 69 - if (!airq) { 70 - ret = -ENOMEM; 71 - goto out; 39 + if (!airq->handler || airq->isc > MAX_ISC) 40 + return -EINVAL; 41 + if (!airq->lsi_ptr) { 42 + airq->lsi_ptr = kzalloc(1, GFP_KERNEL); 43 + if (!airq->lsi_ptr) 44 + return -ENOMEM; 45 + airq->flags |= AIRQ_PTR_ALLOCATED; 72 46 } 73 - airq->handler = handler; 74 - airq->drv_data = drv_data; 75 - 76 - ret = register_airq(airq, isc); 77 - out: 78 - snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); 47 + if (!airq->lsi_mask) 48 + airq->lsi_mask = 0xff; 49 + snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq); 79 50 CIO_TRACE_EVENT(4, dbf_txt); 80 - if (ret < 0) { 81 - kfree(airq); 82 - return ERR_PTR(ret); 83 - } else 84 - return &indicators[isc].byte[ret]; 51 + isc_register(airq->isc); 52 + spin_lock(&airq_lists_lock); 53 + hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]); 54 + spin_unlock(&airq_lists_lock); 55 + return 0; 85 56 } 86 - EXPORT_SYMBOL(s390_register_adapter_interrupt); 57 + EXPORT_SYMBOL(register_adapter_interrupt); 87 58 88 59 /** 89 - * s390_unregister_adapter_interrupt - unregister adapter interrupt handler 90 - * @ind: indicator for which the handler is to be unregistered 91 - * @isc: interruption subclass 60 + * unregister_adapter_interrupt - unregister adapter interrupt handler 61 + * @airq: pointer to adapter interrupt descriptor 92 62 */ 93 - void s390_unregister_adapter_interrupt(void *ind, u8 isc) 63 + void unregister_adapter_interrupt(struct airq_struct *airq) 94 64 { 95 - struct airq_t *airq; 96 - char dbf_txt[16]; 97 - int i; 65 + char dbf_txt[32]; 98 66 99 - i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); 100 - snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); 67 + if (hlist_unhashed(&airq->list)) 68 + return; 69 + snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq); 101 70 CIO_TRACE_EVENT(4, dbf_txt); 102 - indicators[isc].byte[i] = 0; 103 - airq = xchg(&airqs[isc][i], NULL); 104 - /* 105 - * Allow interrupts to complete. This will ensure that the airq handle 106 - * is no longer referenced by any interrupt handler. 107 - */ 108 - synchronize_sched(); 109 - kfree(airq); 71 + spin_lock(&airq_lists_lock); 72 + hlist_del_rcu(&airq->list); 73 + spin_unlock(&airq_lists_lock); 74 + synchronize_rcu(); 75 + isc_unregister(airq->isc); 76 + if (airq->flags & AIRQ_PTR_ALLOCATED) { 77 + kfree(airq->lsi_ptr); 78 + airq->lsi_ptr = NULL; 79 + airq->flags &= ~AIRQ_PTR_ALLOCATED; 80 + } 110 81 } 111 - EXPORT_SYMBOL(s390_unregister_adapter_interrupt); 112 - 113 - #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) 82 + EXPORT_SYMBOL(unregister_adapter_interrupt); 114 83 115 84 void do_adapter_IO(u8 isc) 116 85 { 117 - int w; 118 - int i; 119 - unsigned long word; 120 - struct airq_t *airq; 86 + struct airq_struct *airq; 87 + struct hlist_head *head; 121 88 122 - /* 123 - * Access indicator array in word-sized chunks to minimize storage 124 - * fetch operations. 125 - */ 126 - for (w = 0; w < NR_AIRQ_WORDS; w++) { 127 - word = indicators[isc].word[w]; 128 - i = w * NR_AIRQS_PER_WORD; 129 - /* 130 - * Check bytes within word for active indicators. 131 - */ 132 - while (word) { 133 - if (word & INDICATOR_MASK) { 134 - airq = airqs[isc][i]; 135 - /* Make sure gcc reads from airqs only once. */ 136 - barrier(); 137 - if (likely(airq)) 138 - airq->handler(&indicators[isc].byte[i], 139 - airq->drv_data); 140 - else 141 - /* 142 - * Reset ill-behaved indicator. 143 - */ 144 - indicators[isc].byte[i] = 0; 145 - } 146 - word <<= 8; 147 - i++; 148 - } 149 - } 89 + head = &airq_lists[isc]; 90 + rcu_read_lock(); 91 + hlist_for_each_entry_rcu(airq, head, list) 92 + if ((*airq->lsi_ptr & airq->lsi_mask) != 0) 93 + airq->handler(airq); 94 + rcu_read_unlock(); 150 95 }
+16 -17
drivers/s390/cio/qdio_thinint.c
··· 36 36 static LIST_HEAD(tiq_list); 37 37 static DEFINE_MUTEX(tiq_list_lock); 38 38 39 - /* adapter local summary indicator */ 40 - static u8 *tiqdio_alsi; 39 + /* Adapter interrupt definitions */ 40 + static void tiqdio_thinint_handler(struct airq_struct *airq); 41 + 42 + static struct airq_struct tiqdio_airq = { 43 + .handler = tiqdio_thinint_handler, 44 + .isc = QDIO_AIRQ_ISC, 45 + }; 41 46 42 47 static struct indicator_t *q_indicators; 43 48 ··· 181 176 * @alsi: pointer to adapter local summary indicator 182 177 * @data: NULL 183 178 */ 184 - static void tiqdio_thinint_handler(void *alsi, void *data) 179 + static void tiqdio_thinint_handler(struct airq_struct *airq) 185 180 { 186 181 u32 si_used = clear_shared_ind(); 187 182 struct qdio_q *q; ··· 221 216 summary_indicator_addr = 0; 222 217 subchannel_indicator_addr = 0; 223 218 } else { 224 - summary_indicator_addr = virt_to_phys(tiqdio_alsi); 219 + summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr); 225 220 subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci); 226 221 } 227 222 ··· 257 252 258 253 int __init tiqdio_register_thinints(void) 259 254 { 260 - isc_register(QDIO_AIRQ_ISC); 261 - tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, 262 - NULL, QDIO_AIRQ_ISC); 263 - if (IS_ERR(tiqdio_alsi)) { 264 - DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); 265 - tiqdio_alsi = NULL; 266 - isc_unregister(QDIO_AIRQ_ISC); 267 - return -ENOMEM; 255 + int rc; 256 + 257 + rc = register_adapter_interrupt(&tiqdio_airq); 258 + if (rc) { 259 + DBF_EVENT("RTI:%x", rc); 260 + return rc; 268 261 } 269 262 return 0; 270 263 } ··· 295 292 void __exit tiqdio_unregister_thinints(void) 296 293 { 297 294 WARN_ON(!list_empty(&tiq_list)); 298 - 299 - if (tiqdio_alsi) { 300 - s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); 301 - isc_unregister(QDIO_AIRQ_ISC); 302 - } 295 + unregister_adapter_interrupt(&tiqdio_airq); 303 296 }
+35 -25
drivers/s390/crypto/ap_bus.c
··· 58 58 static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); 59 59 static int ap_device_remove(struct device *dev); 60 60 static int ap_device_probe(struct device *dev); 61 - static void ap_interrupt_handler(void *unused1, void *unused2); 61 + static void ap_interrupt_handler(struct airq_struct *airq); 62 62 static void ap_reset(struct ap_device *ap_dev); 63 63 static void ap_config_timeout(unsigned long ptr); 64 64 static int ap_select_domain(void); ··· 106 106 static struct task_struct *ap_poll_kthread = NULL; 107 107 static DEFINE_MUTEX(ap_poll_thread_mutex); 108 108 static DEFINE_SPINLOCK(ap_poll_timer_lock); 109 - static void *ap_interrupt_indicator; 110 109 static struct hrtimer ap_poll_timer; 111 110 /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. 112 111 * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ ··· 119 120 static int user_set_domain = 0; 120 121 static struct bus_type ap_bus_type; 121 122 123 + /* Adapter interrupt definitions */ 124 + static int ap_airq_flag; 125 + 126 + static struct airq_struct ap_airq = { 127 + .handler = ap_interrupt_handler, 128 + .isc = AP_ISC, 129 + }; 130 + 122 131 /** 123 132 * ap_using_interrupts() - Returns non-zero if interrupt support is 124 133 * available. 125 134 */ 126 135 static inline int ap_using_interrupts(void) 127 136 { 128 - return ap_interrupt_indicator != NULL; 137 + return ap_airq_flag; 129 138 } 130 139 131 140 /** ··· 595 588 } 596 589 } 597 590 if (rc == 0 && ap_using_interrupts()) { 598 - rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); 591 + rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr); 599 592 /* If interruption mode is supported by the machine, 600 593 * but an AP can not be enabled for interruption then 601 594 * the AP will be discarded. */ ··· 828 821 829 822 static int ap_bus_resume(struct device *dev) 830 823 { 831 - int rc = 0; 832 824 struct ap_device *ap_dev = to_ap_dev(dev); 825 + int rc; 833 826 834 827 if (ap_suspend_flag) { 835 828 ap_suspend_flag = 0; 836 - if (!ap_interrupts_available()) 837 - ap_interrupt_indicator = NULL; 829 + if (ap_interrupts_available()) { 830 + if (!ap_using_interrupts()) { 831 + rc = register_adapter_interrupt(&ap_airq); 832 + ap_airq_flag = (rc == 0); 833 + } 834 + } else { 835 + if (ap_using_interrupts()) { 836 + unregister_adapter_interrupt(&ap_airq); 837 + ap_airq_flag = 0; 838 + } 839 + } 838 840 ap_query_configuration(); 839 841 if (!user_set_domain) { 840 842 ap_domain_index = -1; ··· 864 848 tasklet_schedule(&ap_tasklet); 865 849 if (ap_thread_flag) 866 850 rc = ap_poll_thread_start(); 867 - } 851 + else 852 + rc = 0; 853 + } else 854 + rc = 0; 868 855 if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) { 869 856 spin_lock_bh(&ap_dev->lock); 870 857 ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), ··· 1285 1266 return rc; 1286 1267 } 1287 1268 1288 - static void ap_interrupt_handler(void *unused1, void *unused2) 1269 + static void ap_interrupt_handler(struct airq_struct *airq) 1289 1270 { 1290 1271 inc_irq_stat(IRQIO_APB); 1291 1272 tasklet_schedule(&ap_tasklet); ··· 1741 1722 * important that no requests on any AP get lost. 1742 1723 */ 1743 1724 if (ap_using_interrupts()) 1744 - xchg((u8 *)ap_interrupt_indicator, 0); 1725 + xchg(ap_airq.lsi_ptr, 0); 1745 1726 do { 1746 1727 flags = 0; 1747 1728 spin_lock(&ap_device_list_lock); ··· 1900 1881 return -ENODEV; 1901 1882 } 1902 1883 if (ap_interrupts_available()) { 1903 - isc_register(AP_ISC); 1904 - ap_interrupt_indicator = s390_register_adapter_interrupt( 1905 - &ap_interrupt_handler, NULL, AP_ISC); 1906 - if (IS_ERR(ap_interrupt_indicator)) { 1907 - ap_interrupt_indicator = NULL; 1908 - isc_unregister(AP_ISC); 1909 - } 1884 + rc = register_adapter_interrupt(&ap_airq); 1885 + ap_airq_flag = (rc == 0); 1910 1886 } 1911 1887 1912 1888 register_reset_call(&ap_reset_call); ··· 1969 1955 bus_unregister(&ap_bus_type); 1970 1956 out: 1971 1957 unregister_reset_call(&ap_reset_call); 1972 - if (ap_using_interrupts()) { 1973 - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 1974 - isc_unregister(AP_ISC); 1975 - } 1958 + if (ap_using_interrupts()) 1959 + unregister_adapter_interrupt(&ap_airq); 1976 1960 return rc; 1977 1961 } 1978 1962 ··· 2006 1994 bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); 2007 1995 bus_unregister(&ap_bus_type); 2008 1996 unregister_reset_call(&ap_reset_call); 2009 - if (ap_using_interrupts()) { 2010 - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 2011 - isc_unregister(AP_ISC); 2012 - } 1997 + if (ap_using_interrupts()) 1998 + unregister_adapter_interrupt(&ap_airq); 2013 1999 } 2014 2000 2015 2001 module_init(ap_module_init);