diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2010-09-07 17:14:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-08 17:31:00 -0400 |
commit | d36deae75011a7890f0e730dd0f867c64081cb50 (patch) | |
tree | d24672cf5cc40c637186362187450362cabefd24 /drivers/s390/cio/qdio_thinint.c | |
parent | e508be174ad36b0cf9b324cd04978c2b13c21502 (diff) |
qdio: extend API to allow polling
Extend the qdio API to allow polling in the upper-layer driver. This
is needed by qeth to use NAPI.
To use the new interface the upper-layer driver must specify the
queue_start_poll(). This callback is used to signal the upper-layer
driver that is has initiative and must process the inbound queue by
calling qdio_get_next_buffers(). If the upper-layer driver wants to
stop polling it calls qdio_start_irq().
Since adapter interrupts are not completely stoppable qdio implements
a software bit QDIO_QUEUE_IRQS_DISABLED to safely disable interrupts for an
input queue.
The old interface is preserved and will be used as is by zfcp.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 66 |
1 files changed, 38 insertions, 28 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 8daf1b99f153..752dbee06af5 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -25,24 +25,20 @@ | |||
25 | */ | 25 | */ |
26 | #define TIQDIO_NR_NONSHARED_IND 63 | 26 | #define TIQDIO_NR_NONSHARED_IND 63 |
27 | #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) | 27 | #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) |
28 | #define TIQDIO_SHARED_IND 63 | ||
29 | 28 | ||
30 | /* list of thin interrupt input queues */ | 29 | /* list of thin interrupt input queues */ |
31 | static LIST_HEAD(tiq_list); | 30 | static LIST_HEAD(tiq_list); |
32 | DEFINE_MUTEX(tiq_list_lock); | 31 | DEFINE_MUTEX(tiq_list_lock); |
33 | 32 | ||
34 | /* adapter local summary indicator */ | 33 | /* adapter local summary indicator */ |
35 | static unsigned char *tiqdio_alsi; | 34 | static u8 *tiqdio_alsi; |
36 | 35 | ||
37 | /* device state change indicators */ | 36 | struct indicator_t *q_indicators; |
38 | struct indicator_t { | ||
39 | u32 ind; /* u32 because of compare-and-swap performance */ | ||
40 | atomic_t count; /* use count, 0 or 1 for non-shared indicators */ | ||
41 | }; | ||
42 | static struct indicator_t *q_indicators; | ||
43 | 37 | ||
44 | static int css_qdio_omit_svs; | 38 | static int css_qdio_omit_svs; |
45 | 39 | ||
40 | static u64 last_ai_time; | ||
41 | |||
46 | static inline unsigned long do_clear_global_summary(void) | 42 | static inline unsigned long do_clear_global_summary(void) |
47 | { | 43 | { |
48 | register unsigned long __fn asm("1") = 3; | 44 | register unsigned long __fn asm("1") = 3; |
@@ -116,59 +112,73 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | |||
116 | } | 112 | } |
117 | } | 113 | } |
118 | 114 | ||
119 | static inline int shared_ind(struct qdio_irq *irq_ptr) | 115 | static inline int shared_ind_used(void) |
120 | { | 116 | { |
121 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; | 117 | return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); |
122 | } | 118 | } |
123 | 119 | ||
124 | /** | 120 | /** |
125 | * tiqdio_thinint_handler - thin interrupt handler for qdio | 121 | * tiqdio_thinint_handler - thin interrupt handler for qdio |
126 | * @ind: pointer to adapter local summary indicator | 122 | * @alsi: pointer to adapter local summary indicator |
127 | * @drv_data: NULL | 123 | * @data: NULL |
128 | */ | 124 | */ |
129 | static void tiqdio_thinint_handler(void *ind, void *drv_data) | 125 | static void tiqdio_thinint_handler(void *alsi, void *data) |
130 | { | 126 | { |
131 | struct qdio_q *q; | 127 | struct qdio_q *q; |
132 | 128 | ||
129 | last_ai_time = S390_lowcore.int_clock; | ||
130 | |||
133 | /* | 131 | /* |
134 | * SVS only when needed: issue SVS to benefit from iqdio interrupt | 132 | * SVS only when needed: issue SVS to benefit from iqdio interrupt |
135 | * avoidance (SVS clears adapter interrupt suppression overwrite) | 133 | * avoidance (SVS clears adapter interrupt suppression overwrite). |
136 | */ | 134 | */ |
137 | if (!css_qdio_omit_svs) | 135 | if (!css_qdio_omit_svs) |
138 | do_clear_global_summary(); | 136 | do_clear_global_summary(); |
139 | 137 | ||
140 | /* | 138 | /* reset local summary indicator */ |
141 | * reset local summary indicator (tiqdio_alsi) to stop adapter | 139 | if (shared_ind_used()) |
142 | * interrupts for now | 140 | xchg(tiqdio_alsi, 0); |
143 | */ | ||
144 | xchg((u8 *)ind, 0); | ||
145 | 141 | ||
146 | /* protect tiq_list entries, only changed in activate or shutdown */ | 142 | /* protect tiq_list entries, only changed in activate or shutdown */ |
147 | rcu_read_lock(); | 143 | rcu_read_lock(); |
148 | 144 | ||
149 | /* check for work on all inbound thinint queues */ | 145 | /* check for work on all inbound thinint queues */ |
150 | list_for_each_entry_rcu(q, &tiq_list, entry) | 146 | list_for_each_entry_rcu(q, &tiq_list, entry) { |
147 | |||
151 | /* only process queues from changed sets */ | 148 | /* only process queues from changed sets */ |
152 | if (*q->irq_ptr->dsci) { | 149 | if (!*q->irq_ptr->dsci) |
153 | qperf_inc(q, adapter_int); | 150 | continue; |
154 | 151 | ||
152 | if (q->u.in.queue_start_poll) { | ||
153 | /* skip if polling is enabled or already in work */ | ||
154 | if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, | ||
155 | &q->u.in.queue_irq_state)) { | ||
156 | qperf_inc(q, int_discarded); | ||
157 | continue; | ||
158 | } | ||
159 | |||
160 | /* avoid dsci clear here, done after processing */ | ||
161 | q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, | ||
162 | q->irq_ptr->int_parm); | ||
163 | } else { | ||
155 | /* only clear it if the indicator is non-shared */ | 164 | /* only clear it if the indicator is non-shared */ |
156 | if (!shared_ind(q->irq_ptr)) | 165 | if (!shared_ind(q->irq_ptr)) |
157 | xchg(q->irq_ptr->dsci, 0); | 166 | xchg(q->irq_ptr->dsci, 0); |
158 | /* | 167 | /* |
159 | * don't call inbound processing directly since | 168 | * Call inbound processing but not directly |
160 | * that could starve other thinint queues | 169 | * since that could starve other thinint queues. |
161 | */ | 170 | */ |
162 | tasklet_schedule(&q->tasklet); | 171 | tasklet_schedule(&q->tasklet); |
163 | } | 172 | } |
164 | 173 | qperf_inc(q, adapter_int); | |
174 | } | ||
165 | rcu_read_unlock(); | 175 | rcu_read_unlock(); |
166 | 176 | ||
167 | /* | 177 | /* |
168 | * if we used the shared indicator clear it now after all queues | 178 | * If the shared indicator was used clear it now after all queues |
169 | * were processed | 179 | * were processed. |
170 | */ | 180 | */ |
171 | if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) { | 181 | if (shared_ind_used()) { |
172 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); | 182 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); |
173 | 183 | ||
174 | /* prevent racing */ | 184 | /* prevent racing */ |