aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2013-06-24 04:30:41 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-06-26 15:10:28 -0400
commitf4eae94f71372ea5ec1ba17a85f3aebedc516ca8 (patch)
treeec3d6a527bf276a92e8befd559d9a0f0866ab7bd /drivers/s390
parent386aa051fb4b6298c23996e68b7c92f186e484b6 (diff)
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>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/cio/airq.c167
-rw-r--r--drivers/s390/cio/qdio_thinint.c33
-rw-r--r--drivers/s390/crypto/ap_bus.c60
3 files changed, 107 insertions, 153 deletions
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index bc10220f6847..91edbd7ee806 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -9,142 +9,87 @@
9 */ 9 */
10 10
11#include <linux/init.h> 11#include <linux/init.h>
12#include <linux/irq.h>
13#include <linux/kernel_stat.h>
12#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/rculist.h>
13#include <linux/slab.h> 17#include <linux/slab.h>
14#include <linux/rcupdate.h>
15 18
16#include <asm/airq.h> 19#include <asm/airq.h>
17#include <asm/isc.h> 20#include <asm/isc.h>
18 21
19#include "cio.h" 22#include "cio.h"
20#include "cio_debug.h" 23#include "cio_debug.h"
24#include "ioasm.h"
21 25
22#define NR_AIRQS 32 26static DEFINE_SPINLOCK(airq_lists_lock);
23#define NR_AIRQS_PER_WORD sizeof(unsigned long) 27static struct hlist_head airq_lists[MAX_ISC+1];
24#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD)
25
26union indicator_t {
27 unsigned long word[NR_AIRQ_WORDS];
28 unsigned char byte[NR_AIRQS];
29} __attribute__((packed));
30
31struct airq_t {
32 adapter_int_handler_t handler;
33 void *drv_data;
34};
35
36static union indicator_t indicators[MAX_ISC+1];
37static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS];
38
39static 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}
48 28
49/** 29/**
50 * s390_register_adapter_interrupt() - register adapter interrupt handler 30 * register_adapter_interrupt() - register adapter interrupt handler
51 * @handler: adapter handler to be registered 31 * @airq: pointer to adapter interrupt descriptor
52 * @drv_data: driver data passed with each call to the handler
53 * @isc: isc for which the handler should be called
54 * 32 *
55 * Returns: 33 * Returns 0 on success, or -EINVAL.
56 * Pointer to the indicator to be used on success
57 * ERR_PTR() if registration failed
58 */ 34 */
59void *s390_register_adapter_interrupt(adapter_int_handler_t handler, 35int register_adapter_interrupt(struct airq_struct *airq)
60 void *drv_data, u8 isc)
61{ 36{
62 struct airq_t *airq; 37 char dbf_txt[32];
63 char dbf_txt[16]; 38
64 int ret; 39 if (!airq->handler || airq->isc > MAX_ISC)
65 40 return -EINVAL;
66 if (isc > MAX_ISC) 41 if (!airq->lsi_ptr) {
67 return ERR_PTR(-EINVAL); 42 airq->lsi_ptr = kzalloc(1, GFP_KERNEL);
68 airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); 43 if (!airq->lsi_ptr)
69 if (!airq) { 44 return -ENOMEM;
70 ret = -ENOMEM; 45 airq->flags |= AIRQ_PTR_ALLOCATED;
71 goto out;
72 } 46 }
73 airq->handler = handler; 47 if (!airq->lsi_mask)
74 airq->drv_data = drv_data; 48 airq->lsi_mask = 0xff;
75 49 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
76 ret = register_airq(airq, isc);
77out:
78 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
79 CIO_TRACE_EVENT(4, dbf_txt); 50 CIO_TRACE_EVENT(4, dbf_txt);
80 if (ret < 0) { 51 isc_register(airq->isc);
81 kfree(airq); 52 spin_lock(&airq_lists_lock);
82 return ERR_PTR(ret); 53 hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
83 } else 54 spin_unlock(&airq_lists_lock);
84 return &indicators[isc].byte[ret]; 55 return 0;
85} 56}
86EXPORT_SYMBOL(s390_register_adapter_interrupt); 57EXPORT_SYMBOL(register_adapter_interrupt);
87 58
88/** 59/**
89 * s390_unregister_adapter_interrupt - unregister adapter interrupt handler 60 * unregister_adapter_interrupt - unregister adapter interrupt handler
90 * @ind: indicator for which the handler is to be unregistered 61 * @airq: pointer to adapter interrupt descriptor
91 * @isc: interruption subclass
92 */ 62 */
93void s390_unregister_adapter_interrupt(void *ind, u8 isc) 63void unregister_adapter_interrupt(struct airq_struct *airq)
94{ 64{
95 struct airq_t *airq; 65 char dbf_txt[32];
96 char dbf_txt[16];
97 int i;
98 66
99 i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); 67 if (hlist_unhashed(&airq->list))
100 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); 68 return;
69 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
101 CIO_TRACE_EVENT(4, dbf_txt); 70 CIO_TRACE_EVENT(4, dbf_txt);
102 indicators[isc].byte[i] = 0; 71 spin_lock(&airq_lists_lock);
103 airq = xchg(&airqs[isc][i], NULL); 72 hlist_del_rcu(&airq->list);
104 /* 73 spin_unlock(&airq_lists_lock);
105 * Allow interrupts to complete. This will ensure that the airq handle 74 synchronize_rcu();
106 * is no longer referenced by any interrupt handler. 75 isc_unregister(airq->isc);
107 */ 76 if (airq->flags & AIRQ_PTR_ALLOCATED) {
108 synchronize_sched(); 77 kfree(airq->lsi_ptr);
109 kfree(airq); 78 airq->lsi_ptr = NULL;
79 airq->flags &= ~AIRQ_PTR_ALLOCATED;
80 }
110} 81}
111EXPORT_SYMBOL(s390_unregister_adapter_interrupt); 82EXPORT_SYMBOL(unregister_adapter_interrupt);
112
113#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
114 83
115void do_adapter_IO(u8 isc) 84void do_adapter_IO(u8 isc)
116{ 85{
117 int w; 86 struct airq_struct *airq;
118 int i; 87 struct hlist_head *head;
119 unsigned long word; 88
120 struct airq_t *airq; 89 head = &airq_lists[isc];
121 90 rcu_read_lock();
122 /* 91 hlist_for_each_entry_rcu(airq, head, list)
123 * Access indicator array in word-sized chunks to minimize storage 92 if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
124 * fetch operations. 93 airq->handler(airq);
125 */ 94 rcu_read_unlock();
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 }
150} 95}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 417b2557d83e..5d06253c2a7a 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -36,8 +36,13 @@ struct indicator_t {
36static LIST_HEAD(tiq_list); 36static LIST_HEAD(tiq_list);
37static DEFINE_MUTEX(tiq_list_lock); 37static DEFINE_MUTEX(tiq_list_lock);
38 38
39/* adapter local summary indicator */ 39/* Adapter interrupt definitions */
40static u8 *tiqdio_alsi; 40static void tiqdio_thinint_handler(struct airq_struct *airq);
41
42static struct airq_struct tiqdio_airq = {
43 .handler = tiqdio_thinint_handler,
44 .isc = QDIO_AIRQ_ISC,
45};
41 46
42static struct indicator_t *q_indicators; 47static struct indicator_t *q_indicators;
43 48
@@ -176,7 +181,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
176 * @alsi: pointer to adapter local summary indicator 181 * @alsi: pointer to adapter local summary indicator
177 * @data: NULL 182 * @data: NULL
178 */ 183 */
179static void tiqdio_thinint_handler(void *alsi, void *data) 184static void tiqdio_thinint_handler(struct airq_struct *airq)
180{ 185{
181 u32 si_used = clear_shared_ind(); 186 u32 si_used = clear_shared_ind();
182 struct qdio_q *q; 187 struct qdio_q *q;
@@ -216,7 +221,7 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
216 summary_indicator_addr = 0; 221 summary_indicator_addr = 0;
217 subchannel_indicator_addr = 0; 222 subchannel_indicator_addr = 0;
218 } else { 223 } else {
219 summary_indicator_addr = virt_to_phys(tiqdio_alsi); 224 summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr);
220 subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci); 225 subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci);
221 } 226 }
222 227
@@ -252,14 +257,12 @@ void tiqdio_free_memory(void)
252 257
253int __init tiqdio_register_thinints(void) 258int __init tiqdio_register_thinints(void)
254{ 259{
255 isc_register(QDIO_AIRQ_ISC); 260 int rc;
256 tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, 261
257 NULL, QDIO_AIRQ_ISC); 262 rc = register_adapter_interrupt(&tiqdio_airq);
258 if (IS_ERR(tiqdio_alsi)) { 263 if (rc) {
259 DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); 264 DBF_EVENT("RTI:%x", rc);
260 tiqdio_alsi = NULL; 265 return rc;
261 isc_unregister(QDIO_AIRQ_ISC);
262 return -ENOMEM;
263 } 266 }
264 return 0; 267 return 0;
265} 268}
@@ -292,9 +295,5 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
292void __exit tiqdio_unregister_thinints(void) 295void __exit tiqdio_unregister_thinints(void)
293{ 296{
294 WARN_ON(!list_empty(&tiq_list)); 297 WARN_ON(!list_empty(&tiq_list));
295 298 unregister_adapter_interrupt(&tiqdio_airq);
296 if (tiqdio_alsi) {
297 s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);
298 isc_unregister(QDIO_AIRQ_ISC);
299 }
300} 299}
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 0b116a49ee53..f446a7705c3b 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -58,7 +58,7 @@ static inline void ap_schedule_poll_timer(void);
58static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); 58static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
59static int ap_device_remove(struct device *dev); 59static int ap_device_remove(struct device *dev);
60static int ap_device_probe(struct device *dev); 60static int ap_device_probe(struct device *dev);
61static void ap_interrupt_handler(void *unused1, void *unused2); 61static void ap_interrupt_handler(struct airq_struct *airq);
62static void ap_reset(struct ap_device *ap_dev); 62static void ap_reset(struct ap_device *ap_dev);
63static void ap_config_timeout(unsigned long ptr); 63static void ap_config_timeout(unsigned long ptr);
64static int ap_select_domain(void); 64static int ap_select_domain(void);
@@ -106,7 +106,6 @@ static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
106static struct task_struct *ap_poll_kthread = NULL; 106static struct task_struct *ap_poll_kthread = NULL;
107static DEFINE_MUTEX(ap_poll_thread_mutex); 107static DEFINE_MUTEX(ap_poll_thread_mutex);
108static DEFINE_SPINLOCK(ap_poll_timer_lock); 108static DEFINE_SPINLOCK(ap_poll_timer_lock);
109static void *ap_interrupt_indicator;
110static struct hrtimer ap_poll_timer; 109static struct hrtimer ap_poll_timer;
111/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. 110/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
112 * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ 111 * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
@@ -120,13 +119,21 @@ static int ap_suspend_flag;
120static int user_set_domain = 0; 119static int user_set_domain = 0;
121static struct bus_type ap_bus_type; 120static struct bus_type ap_bus_type;
122 121
122/* Adapter interrupt definitions */
123static int ap_airq_flag;
124
125static struct airq_struct ap_airq = {
126 .handler = ap_interrupt_handler,
127 .isc = AP_ISC,
128};
129
123/** 130/**
124 * ap_using_interrupts() - Returns non-zero if interrupt support is 131 * ap_using_interrupts() - Returns non-zero if interrupt support is
125 * available. 132 * available.
126 */ 133 */
127static inline int ap_using_interrupts(void) 134static inline int ap_using_interrupts(void)
128{ 135{
129 return ap_interrupt_indicator != NULL; 136 return ap_airq_flag;
130} 137}
131 138
132/** 139/**
@@ -588,7 +595,7 @@ static int ap_init_queue(ap_qid_t qid)
588 } 595 }
589 } 596 }
590 if (rc == 0 && ap_using_interrupts()) { 597 if (rc == 0 && ap_using_interrupts()) {
591 rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); 598 rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr);
592 /* If interruption mode is supported by the machine, 599 /* If interruption mode is supported by the machine,
593 * but an AP can not be enabled for interruption then 600 * but an AP can not be enabled for interruption then
594 * the AP will be discarded. */ 601 * the AP will be discarded. */
@@ -821,13 +828,22 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state)
821 828
822static int ap_bus_resume(struct device *dev) 829static int ap_bus_resume(struct device *dev)
823{ 830{
824 int rc = 0;
825 struct ap_device *ap_dev = to_ap_dev(dev); 831 struct ap_device *ap_dev = to_ap_dev(dev);
832 int rc;
826 833
827 if (ap_suspend_flag) { 834 if (ap_suspend_flag) {
828 ap_suspend_flag = 0; 835 ap_suspend_flag = 0;
829 if (!ap_interrupts_available()) 836 if (ap_interrupts_available()) {
830 ap_interrupt_indicator = NULL; 837 if (!ap_using_interrupts()) {
838 rc = register_adapter_interrupt(&ap_airq);
839 ap_airq_flag = (rc == 0);
840 }
841 } else {
842 if (ap_using_interrupts()) {
843 unregister_adapter_interrupt(&ap_airq);
844 ap_airq_flag = 0;
845 }
846 }
831 ap_query_configuration(); 847 ap_query_configuration();
832 if (!user_set_domain) { 848 if (!user_set_domain) {
833 ap_domain_index = -1; 849 ap_domain_index = -1;
@@ -848,7 +864,10 @@ static int ap_bus_resume(struct device *dev)
848 tasklet_schedule(&ap_tasklet); 864 tasklet_schedule(&ap_tasklet);
849 if (ap_thread_flag) 865 if (ap_thread_flag)
850 rc = ap_poll_thread_start(); 866 rc = ap_poll_thread_start();
851 } 867 else
868 rc = 0;
869 } else
870 rc = 0;
852 if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) { 871 if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {
853 spin_lock_bh(&ap_dev->lock); 872 spin_lock_bh(&ap_dev->lock);
854 ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), 873 ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid),
@@ -1266,7 +1285,7 @@ out:
1266 return rc; 1285 return rc;
1267} 1286}
1268 1287
1269static void ap_interrupt_handler(void *unused1, void *unused2) 1288static void ap_interrupt_handler(struct airq_struct *airq)
1270{ 1289{
1271 inc_irq_stat(IRQIO_APB); 1290 inc_irq_stat(IRQIO_APB);
1272 tasklet_schedule(&ap_tasklet); 1291 tasklet_schedule(&ap_tasklet);
@@ -1722,7 +1741,7 @@ static void ap_poll_all(unsigned long dummy)
1722 * important that no requests on any AP get lost. 1741 * important that no requests on any AP get lost.
1723 */ 1742 */
1724 if (ap_using_interrupts()) 1743 if (ap_using_interrupts())
1725 xchg((u8 *)ap_interrupt_indicator, 0); 1744 xchg(ap_airq.lsi_ptr, 0);
1726 do { 1745 do {
1727 flags = 0; 1746 flags = 0;
1728 spin_lock(&ap_device_list_lock); 1747 spin_lock(&ap_device_list_lock);
@@ -1881,13 +1900,8 @@ int __init ap_module_init(void)
1881 return -ENODEV; 1900 return -ENODEV;
1882 } 1901 }
1883 if (ap_interrupts_available()) { 1902 if (ap_interrupts_available()) {
1884 isc_register(AP_ISC); 1903 rc = register_adapter_interrupt(&ap_airq);
1885 ap_interrupt_indicator = s390_register_adapter_interrupt( 1904 ap_airq_flag = (rc == 0);
1886 &ap_interrupt_handler, NULL, AP_ISC);
1887 if (IS_ERR(ap_interrupt_indicator)) {
1888 ap_interrupt_indicator = NULL;
1889 isc_unregister(AP_ISC);
1890 }
1891 } 1905 }
1892 1906
1893 register_reset_call(&ap_reset_call); 1907 register_reset_call(&ap_reset_call);
@@ -1955,10 +1969,8 @@ out_bus:
1955 bus_unregister(&ap_bus_type); 1969 bus_unregister(&ap_bus_type);
1956out: 1970out:
1957 unregister_reset_call(&ap_reset_call); 1971 unregister_reset_call(&ap_reset_call);
1958 if (ap_using_interrupts()) { 1972 if (ap_using_interrupts())
1959 s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 1973 unregister_adapter_interrupt(&ap_airq);
1960 isc_unregister(AP_ISC);
1961 }
1962 return rc; 1974 return rc;
1963} 1975}
1964 1976
@@ -1994,10 +2006,8 @@ void ap_module_exit(void)
1994 bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); 2006 bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
1995 bus_unregister(&ap_bus_type); 2007 bus_unregister(&ap_bus_type);
1996 unregister_reset_call(&ap_reset_call); 2008 unregister_reset_call(&ap_reset_call);
1997 if (ap_using_interrupts()) { 2009 if (ap_using_interrupts())
1998 s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 2010 unregister_adapter_interrupt(&ap_airq);
1999 isc_unregister(AP_ISC);
2000 }
2001} 2011}
2002 2012
2003module_init(ap_module_init); 2013module_init(ap_module_init);