diff options
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 104 |
1 files changed, 40 insertions, 64 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 8daf1b99f153..5c4e741d8221 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -8,6 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | #include <linux/io.h> | 9 | #include <linux/io.h> |
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <linux/kernel_stat.h> | ||
11 | #include <asm/atomic.h> | 12 | #include <asm/atomic.h> |
12 | #include <asm/debug.h> | 13 | #include <asm/debug.h> |
13 | #include <asm/qdio.h> | 14 | #include <asm/qdio.h> |
@@ -25,35 +26,17 @@ | |||
25 | */ | 26 | */ |
26 | #define TIQDIO_NR_NONSHARED_IND 63 | 27 | #define TIQDIO_NR_NONSHARED_IND 63 |
27 | #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) | 28 | #define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) |
28 | #define TIQDIO_SHARED_IND 63 | ||
29 | 29 | ||
30 | /* list of thin interrupt input queues */ | 30 | /* list of thin interrupt input queues */ |
31 | static LIST_HEAD(tiq_list); | 31 | static LIST_HEAD(tiq_list); |
32 | DEFINE_MUTEX(tiq_list_lock); | 32 | DEFINE_MUTEX(tiq_list_lock); |
33 | 33 | ||
34 | /* adapter local summary indicator */ | 34 | /* adapter local summary indicator */ |
35 | static unsigned char *tiqdio_alsi; | 35 | static u8 *tiqdio_alsi; |
36 | 36 | ||
37 | /* device state change indicators */ | 37 | 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 | 38 | ||
44 | static int css_qdio_omit_svs; | 39 | static u64 last_ai_time; |
45 | |||
46 | static inline unsigned long do_clear_global_summary(void) | ||
47 | { | ||
48 | register unsigned long __fn asm("1") = 3; | ||
49 | register unsigned long __tmp asm("2"); | ||
50 | register unsigned long __time asm("3"); | ||
51 | |||
52 | asm volatile( | ||
53 | " .insn rre,0xb2650000,2,0" | ||
54 | : "+d" (__fn), "=d" (__tmp), "=d" (__time)); | ||
55 | return __time; | ||
56 | } | ||
57 | 40 | ||
58 | /* returns addr for the device state change indicator */ | 41 | /* returns addr for the device state change indicator */ |
59 | static u32 *get_indicator(void) | 42 | static u32 *get_indicator(void) |
@@ -87,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) | |||
87 | struct qdio_q *q; | 70 | struct qdio_q *q; |
88 | int i; | 71 | int i; |
89 | 72 | ||
90 | /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ | ||
91 | if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) | ||
92 | css_qdio_omit_svs = 1; | ||
93 | |||
94 | mutex_lock(&tiq_list_lock); | 73 | mutex_lock(&tiq_list_lock); |
95 | for_each_input_queue(irq_ptr, q, i) | 74 | for_each_input_queue(irq_ptr, q, i) |
96 | list_add_rcu(&q->entry, &tiq_list); | 75 | list_add_rcu(&q->entry, &tiq_list); |
@@ -116,65 +95,68 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | |||
116 | } | 95 | } |
117 | } | 96 | } |
118 | 97 | ||
119 | static inline int shared_ind(struct qdio_irq *irq_ptr) | 98 | static inline u32 shared_ind_set(void) |
120 | { | 99 | { |
121 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; | 100 | return q_indicators[TIQDIO_SHARED_IND].ind; |
122 | } | 101 | } |
123 | 102 | ||
124 | /** | 103 | /** |
125 | * tiqdio_thinint_handler - thin interrupt handler for qdio | 104 | * tiqdio_thinint_handler - thin interrupt handler for qdio |
126 | * @ind: pointer to adapter local summary indicator | 105 | * @alsi: pointer to adapter local summary indicator |
127 | * @drv_data: NULL | 106 | * @data: NULL |
128 | */ | 107 | */ |
129 | static void tiqdio_thinint_handler(void *ind, void *drv_data) | 108 | static void tiqdio_thinint_handler(void *alsi, void *data) |
130 | { | 109 | { |
110 | u32 si_used = shared_ind_set(); | ||
131 | struct qdio_q *q; | 111 | struct qdio_q *q; |
132 | 112 | ||
133 | /* | 113 | last_ai_time = S390_lowcore.int_clock; |
134 | * SVS only when needed: issue SVS to benefit from iqdio interrupt | 114 | kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; |
135 | * avoidance (SVS clears adapter interrupt suppression overwrite) | ||
136 | */ | ||
137 | if (!css_qdio_omit_svs) | ||
138 | do_clear_global_summary(); | ||
139 | |||
140 | /* | ||
141 | * reset local summary indicator (tiqdio_alsi) to stop adapter | ||
142 | * interrupts for now | ||
143 | */ | ||
144 | xchg((u8 *)ind, 0); | ||
145 | 115 | ||
146 | /* protect tiq_list entries, only changed in activate or shutdown */ | 116 | /* protect tiq_list entries, only changed in activate or shutdown */ |
147 | rcu_read_lock(); | 117 | rcu_read_lock(); |
148 | 118 | ||
149 | /* check for work on all inbound thinint queues */ | 119 | /* check for work on all inbound thinint queues */ |
150 | list_for_each_entry_rcu(q, &tiq_list, entry) | 120 | list_for_each_entry_rcu(q, &tiq_list, entry) { |
121 | |||
151 | /* only process queues from changed sets */ | 122 | /* only process queues from changed sets */ |
152 | if (*q->irq_ptr->dsci) { | 123 | if (unlikely(shared_ind(q->irq_ptr->dsci))) { |
153 | qperf_inc(q, adapter_int); | 124 | if (!si_used) |
125 | continue; | ||
126 | } else if (!*q->irq_ptr->dsci) | ||
127 | continue; | ||
154 | 128 | ||
129 | if (q->u.in.queue_start_poll) { | ||
130 | /* skip if polling is enabled or already in work */ | ||
131 | if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, | ||
132 | &q->u.in.queue_irq_state)) { | ||
133 | qperf_inc(q, int_discarded); | ||
134 | continue; | ||
135 | } | ||
136 | |||
137 | /* avoid dsci clear here, done after processing */ | ||
138 | q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, | ||
139 | q->irq_ptr->int_parm); | ||
140 | } else { | ||
155 | /* only clear it if the indicator is non-shared */ | 141 | /* only clear it if the indicator is non-shared */ |
156 | if (!shared_ind(q->irq_ptr)) | 142 | if (!shared_ind(q->irq_ptr->dsci)) |
157 | xchg(q->irq_ptr->dsci, 0); | 143 | xchg(q->irq_ptr->dsci, 0); |
158 | /* | 144 | /* |
159 | * don't call inbound processing directly since | 145 | * Call inbound processing but not directly |
160 | * that could starve other thinint queues | 146 | * since that could starve other thinint queues. |
161 | */ | 147 | */ |
162 | tasklet_schedule(&q->tasklet); | 148 | tasklet_schedule(&q->tasklet); |
163 | } | 149 | } |
164 | 150 | qperf_inc(q, adapter_int); | |
151 | } | ||
165 | rcu_read_unlock(); | 152 | rcu_read_unlock(); |
166 | 153 | ||
167 | /* | 154 | /* |
168 | * if we used the shared indicator clear it now after all queues | 155 | * If the shared indicator was used clear it now after all queues |
169 | * were processed | 156 | * were processed. |
170 | */ | 157 | */ |
171 | if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) { | 158 | if (si_used && shared_ind_set()) |
172 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); | 159 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); |
173 | |||
174 | /* prevent racing */ | ||
175 | if (*tiqdio_alsi) | ||
176 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); | ||
177 | } | ||
178 | } | 160 | } |
179 | 161 | ||
180 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) | 162 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |
@@ -259,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr) | |||
259 | { | 241 | { |
260 | if (!is_thinint_irq(irq_ptr)) | 242 | if (!is_thinint_irq(irq_ptr)) |
261 | return 0; | 243 | return 0; |
262 | |||
263 | /* Check for aif time delay disablement. If installed, | ||
264 | * omit SVS even under LPAR | ||
265 | */ | ||
266 | if (css_general_characteristics.aif_tdd) | ||
267 | css_qdio_omit_svs = 1; | ||
268 | return set_subchannel_ind(irq_ptr, 0); | 244 | return set_subchannel_ind(irq_ptr, 0); |
269 | } | 245 | } |
270 | 246 | ||
@@ -282,8 +258,8 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) | |||
282 | return; | 258 | return; |
283 | 259 | ||
284 | /* reset adapter interrupt indicators */ | 260 | /* reset adapter interrupt indicators */ |
285 | put_indicator(irq_ptr->dsci); | ||
286 | set_subchannel_ind(irq_ptr, 1); | 261 | set_subchannel_ind(irq_ptr, 1); |
262 | put_indicator(irq_ptr->dsci); | ||
287 | } | 263 | } |
288 | 264 | ||
289 | void __exit tiqdio_unregister_thinints(void) | 265 | void __exit tiqdio_unregister_thinints(void) |