aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/qdio_thinint.c
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/s390/cio/qdio_thinint.c
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r--drivers/s390/cio/qdio_thinint.c104
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 */
31static LIST_HEAD(tiq_list); 31static LIST_HEAD(tiq_list);
32DEFINE_MUTEX(tiq_list_lock); 32DEFINE_MUTEX(tiq_list_lock);
33 33
34/* adapter local summary indicator */ 34/* adapter local summary indicator */
35static unsigned char *tiqdio_alsi; 35static u8 *tiqdio_alsi;
36 36
37/* device state change indicators */ 37struct indicator_t *q_indicators;
38struct 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};
42static struct indicator_t *q_indicators;
43 38
44static int css_qdio_omit_svs; 39static u64 last_ai_time;
45
46static 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 */
59static u32 *get_indicator(void) 42static 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
119static inline int shared_ind(struct qdio_irq *irq_ptr) 98static 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 */
129static void tiqdio_thinint_handler(void *ind, void *drv_data) 108static 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
180static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 162static 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
289void __exit tiqdio_unregister_thinints(void) 265void __exit tiqdio_unregister_thinints(void)