diff options
Diffstat (limited to 'drivers/s390/cio/airq.c')
-rw-r--r-- | drivers/s390/cio/airq.c | 171 |
1 files changed, 115 insertions, 56 deletions
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 5287631fbfc8..b7a07a866291 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
@@ -1,12 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/airq.c | 2 | * drivers/s390/cio/airq.c |
3 | * S/390 common I/O routines -- support for adapter interruptions | 3 | * Support for adapter interruptions |
4 | * | 4 | * |
5 | * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 1999,2007 |
6 | * IBM Corporation | 6 | * Author(s): Ingo Adlung <adlung@de.ibm.com> |
7 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 7 | * Cornelia Huck <cornelia.huck@de.ibm.com> |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 8 | * Arnd Bergmann <arndb@de.ibm.com> |
9 | * Arnd Bergmann (arndb@de.ibm.com) | 9 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
@@ -14,72 +14,131 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/rcupdate.h> | 15 | #include <linux/rcupdate.h> |
16 | 16 | ||
17 | #include <asm/airq.h> | ||
18 | |||
19 | #include "cio.h" | ||
17 | #include "cio_debug.h" | 20 | #include "cio_debug.h" |
18 | #include "airq.h" | ||
19 | 21 | ||
20 | static adapter_int_handler_t adapter_handler; | 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) | ||
21 | 25 | ||
22 | /* | 26 | union indicator_t { |
23 | * register for adapter interrupts | 27 | unsigned long word[NR_AIRQ_WORDS]; |
24 | * | 28 | unsigned char byte[NR_AIRQS]; |
25 | * With HiperSockets the zSeries architecture provides for | 29 | } __attribute__((packed)); |
26 | * means of adapter interrups, pseudo I/O interrupts that are | ||
27 | * not tied to an I/O subchannel, but to an adapter. However, | ||
28 | * it doesn't disclose the info how to enable/disable them, but | ||
29 | * to recognize them only. Perhaps we should consider them | ||
30 | * being shared interrupts, and thus build a linked list | ||
31 | * of adapter handlers ... to be evaluated ... | ||
32 | */ | ||
33 | int | ||
34 | s390_register_adapter_interrupt (adapter_int_handler_t handler) | ||
35 | { | ||
36 | int ret; | ||
37 | char dbf_txt[15]; | ||
38 | 30 | ||
39 | CIO_TRACE_EVENT (4, "rgaint"); | 31 | struct airq_t { |
32 | adapter_int_handler_t handler; | ||
33 | void *drv_data; | ||
34 | }; | ||
40 | 35 | ||
41 | if (handler == NULL) | 36 | static union indicator_t indicators; |
42 | ret = -EINVAL; | 37 | static struct airq_t *airqs[NR_AIRQS]; |
43 | else | ||
44 | ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0); | ||
45 | if (!ret) | ||
46 | synchronize_sched(); /* Allow interrupts to complete. */ | ||
47 | 38 | ||
48 | sprintf (dbf_txt, "ret:%d", ret); | 39 | static int register_airq(struct airq_t *airq) |
49 | CIO_TRACE_EVENT (4, dbf_txt); | 40 | { |
41 | int i; | ||
50 | 42 | ||
51 | return ret; | 43 | for (i = 0; i < NR_AIRQS; i++) |
44 | if (!cmpxchg(&airqs[i], NULL, airq)) | ||
45 | return i; | ||
46 | return -ENOMEM; | ||
52 | } | 47 | } |
53 | 48 | ||
54 | int | 49 | /** |
55 | s390_unregister_adapter_interrupt (adapter_int_handler_t handler) | 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 | * | ||
54 | * Returns: | ||
55 | * Pointer to the indicator to be used on success | ||
56 | * ERR_PTR() if registration failed | ||
57 | */ | ||
58 | void *s390_register_adapter_interrupt(adapter_int_handler_t handler, | ||
59 | void *drv_data) | ||
56 | { | 60 | { |
61 | struct airq_t *airq; | ||
62 | char dbf_txt[16]; | ||
57 | int ret; | 63 | int ret; |
58 | char dbf_txt[15]; | ||
59 | 64 | ||
60 | CIO_TRACE_EVENT (4, "urgaint"); | 65 | airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); |
61 | 66 | if (!airq) { | |
62 | if (handler == NULL) | 67 | ret = -ENOMEM; |
63 | ret = -EINVAL; | 68 | goto out; |
64 | else { | ||
65 | adapter_handler = NULL; | ||
66 | synchronize_sched(); /* Allow interrupts to complete. */ | ||
67 | ret = 0; | ||
68 | } | 69 | } |
69 | sprintf (dbf_txt, "ret:%d", ret); | 70 | airq->handler = handler; |
70 | CIO_TRACE_EVENT (4, dbf_txt); | 71 | airq->drv_data = drv_data; |
71 | 72 | ret = register_airq(airq); | |
72 | return ret; | 73 | if (ret < 0) |
74 | kfree(airq); | ||
75 | out: | ||
76 | snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); | ||
77 | CIO_TRACE_EVENT(4, dbf_txt); | ||
78 | if (ret < 0) | ||
79 | return ERR_PTR(ret); | ||
80 | else | ||
81 | return &indicators.byte[ret]; | ||
73 | } | 82 | } |
83 | EXPORT_SYMBOL(s390_register_adapter_interrupt); | ||
74 | 84 | ||
75 | void | 85 | /** |
76 | do_adapter_IO (void) | 86 | * s390_unregister_adapter_interrupt - unregister adapter interrupt handler |
87 | * @ind: indicator for which the handler is to be unregistered | ||
88 | */ | ||
89 | void s390_unregister_adapter_interrupt(void *ind) | ||
77 | { | 90 | { |
78 | CIO_TRACE_EVENT (6, "doaio"); | 91 | struct airq_t *airq; |
92 | char dbf_txt[16]; | ||
93 | int i; | ||
79 | 94 | ||
80 | if (adapter_handler) | 95 | i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); |
81 | (*adapter_handler) (); | 96 | snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); |
97 | CIO_TRACE_EVENT(4, dbf_txt); | ||
98 | indicators.byte[i] = 0; | ||
99 | airq = xchg(&airqs[i], NULL); | ||
100 | /* | ||
101 | * Allow interrupts to complete. This will ensure that the airq handle | ||
102 | * is no longer referenced by any interrupt handler. | ||
103 | */ | ||
104 | synchronize_sched(); | ||
105 | kfree(airq); | ||
82 | } | 106 | } |
107 | EXPORT_SYMBOL(s390_unregister_adapter_interrupt); | ||
108 | |||
109 | #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) | ||
83 | 110 | ||
84 | EXPORT_SYMBOL (s390_register_adapter_interrupt); | 111 | void do_adapter_IO(void) |
85 | EXPORT_SYMBOL (s390_unregister_adapter_interrupt); | 112 | { |
113 | int w; | ||
114 | int i; | ||
115 | unsigned long word; | ||
116 | struct airq_t *airq; | ||
117 | |||
118 | /* | ||
119 | * Access indicator array in word-sized chunks to minimize storage | ||
120 | * fetch operations. | ||
121 | */ | ||
122 | for (w = 0; w < NR_AIRQ_WORDS; w++) { | ||
123 | word = indicators.word[w]; | ||
124 | i = w * NR_AIRQS_PER_WORD; | ||
125 | /* | ||
126 | * Check bytes within word for active indicators. | ||
127 | */ | ||
128 | while (word) { | ||
129 | if (word & INDICATOR_MASK) { | ||
130 | airq = airqs[i]; | ||
131 | if (likely(airq)) | ||
132 | airq->handler(&indicators.byte[i], | ||
133 | airq->drv_data); | ||
134 | else | ||
135 | /* | ||
136 | * Reset ill-behaved indicator. | ||
137 | */ | ||
138 | indicators.byte[i] = 0; | ||
139 | } | ||
140 | word <<= 8; | ||
141 | i++; | ||
142 | } | ||
143 | } | ||
144 | } | ||