| .. | .. |
|---|
| 39 | 39 | static LIST_HEAD(tiq_list); |
|---|
| 40 | 40 | static DEFINE_MUTEX(tiq_list_lock); |
|---|
| 41 | 41 | |
|---|
| 42 | | -/* Adapter interrupt definitions */ |
|---|
| 43 | | -static void tiqdio_thinint_handler(struct airq_struct *airq); |
|---|
| 44 | | - |
|---|
| 45 | | -static struct airq_struct tiqdio_airq = { |
|---|
| 46 | | - .handler = tiqdio_thinint_handler, |
|---|
| 47 | | - .isc = QDIO_AIRQ_ISC, |
|---|
| 48 | | -}; |
|---|
| 49 | | - |
|---|
| 50 | 42 | static struct indicator_t *q_indicators; |
|---|
| 51 | 43 | |
|---|
| 52 | 44 | u64 last_ai_time; |
|---|
| .. | .. |
|---|
| 74 | 66 | atomic_dec(&ind->count); |
|---|
| 75 | 67 | } |
|---|
| 76 | 68 | |
|---|
| 77 | | -void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) |
|---|
| 69 | +void tiqdio_add_device(struct qdio_irq *irq_ptr) |
|---|
| 78 | 70 | { |
|---|
| 79 | 71 | mutex_lock(&tiq_list_lock); |
|---|
| 80 | | - list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list); |
|---|
| 72 | + list_add_rcu(&irq_ptr->entry, &tiq_list); |
|---|
| 81 | 73 | mutex_unlock(&tiq_list_lock); |
|---|
| 82 | 74 | } |
|---|
| 83 | 75 | |
|---|
| 84 | | -void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) |
|---|
| 76 | +void tiqdio_remove_device(struct qdio_irq *irq_ptr) |
|---|
| 85 | 77 | { |
|---|
| 86 | | - struct qdio_q *q; |
|---|
| 87 | | - |
|---|
| 88 | | - q = irq_ptr->input_qs[0]; |
|---|
| 89 | | - if (!q) |
|---|
| 90 | | - return; |
|---|
| 91 | | - |
|---|
| 92 | 78 | mutex_lock(&tiq_list_lock); |
|---|
| 93 | | - list_del_rcu(&q->entry); |
|---|
| 79 | + list_del_rcu(&irq_ptr->entry); |
|---|
| 94 | 80 | mutex_unlock(&tiq_list_lock); |
|---|
| 95 | 81 | synchronize_rcu(); |
|---|
| 96 | | - INIT_LIST_HEAD(&q->entry); |
|---|
| 97 | | -} |
|---|
| 98 | | - |
|---|
| 99 | | -static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr) |
|---|
| 100 | | -{ |
|---|
| 101 | | - return irq_ptr->nr_input_qs > 1; |
|---|
| 82 | + INIT_LIST_HEAD(&irq_ptr->entry); |
|---|
| 102 | 83 | } |
|---|
| 103 | 84 | |
|---|
| 104 | 85 | static inline int references_shared_dsci(struct qdio_irq *irq_ptr) |
|---|
| .. | .. |
|---|
| 106 | 87 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; |
|---|
| 107 | 88 | } |
|---|
| 108 | 89 | |
|---|
| 109 | | -static inline int shared_ind(struct qdio_irq *irq_ptr) |
|---|
| 110 | | -{ |
|---|
| 111 | | - return references_shared_dsci(irq_ptr) || |
|---|
| 112 | | - has_multiple_inq_on_dsci(irq_ptr); |
|---|
| 113 | | -} |
|---|
| 114 | | - |
|---|
| 115 | | -void clear_nonshared_ind(struct qdio_irq *irq_ptr) |
|---|
| 116 | | -{ |
|---|
| 117 | | - if (!is_thinint_irq(irq_ptr)) |
|---|
| 118 | | - return; |
|---|
| 119 | | - if (shared_ind(irq_ptr)) |
|---|
| 120 | | - return; |
|---|
| 121 | | - xchg(irq_ptr->dsci, 0); |
|---|
| 122 | | -} |
|---|
| 123 | | - |
|---|
| 124 | 90 | int test_nonshared_ind(struct qdio_irq *irq_ptr) |
|---|
| 125 | 91 | { |
|---|
| 126 | 92 | if (!is_thinint_irq(irq_ptr)) |
|---|
| 127 | 93 | return 0; |
|---|
| 128 | | - if (shared_ind(irq_ptr)) |
|---|
| 94 | + if (references_shared_dsci(irq_ptr)) |
|---|
| 129 | 95 | return 0; |
|---|
| 130 | 96 | if (*irq_ptr->dsci) |
|---|
| 131 | 97 | return 1; |
|---|
| .. | .. |
|---|
| 145 | 111 | struct qdio_q *q; |
|---|
| 146 | 112 | int i; |
|---|
| 147 | 113 | |
|---|
| 148 | | - if (!references_shared_dsci(irq) && |
|---|
| 149 | | - has_multiple_inq_on_dsci(irq)) |
|---|
| 114 | + if (!references_shared_dsci(irq)) |
|---|
| 150 | 115 | xchg(irq->dsci, 0); |
|---|
| 151 | 116 | |
|---|
| 117 | + if (irq->irq_poll) { |
|---|
| 118 | + if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq->poll_state)) |
|---|
| 119 | + irq->irq_poll(irq->cdev, irq->int_parm); |
|---|
| 120 | + else |
|---|
| 121 | + QDIO_PERF_STAT_INC(irq, int_discarded); |
|---|
| 122 | + |
|---|
| 123 | + return; |
|---|
| 124 | + } |
|---|
| 125 | + |
|---|
| 152 | 126 | for_each_input_queue(irq, q, i) { |
|---|
| 153 | | - if (q->u.in.queue_start_poll) { |
|---|
| 154 | | - /* skip if polling is enabled or already in work */ |
|---|
| 155 | | - if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, |
|---|
| 156 | | - &q->u.in.queue_irq_state)) { |
|---|
| 157 | | - qperf_inc(q, int_discarded); |
|---|
| 158 | | - continue; |
|---|
| 159 | | - } |
|---|
| 160 | | - |
|---|
| 161 | | - /* avoid dsci clear here, done after processing */ |
|---|
| 162 | | - q->u.in.queue_start_poll(irq->cdev, q->nr, |
|---|
| 163 | | - irq->int_parm); |
|---|
| 164 | | - } else { |
|---|
| 165 | | - if (!shared_ind(irq)) |
|---|
| 166 | | - xchg(irq->dsci, 0); |
|---|
| 167 | | - |
|---|
| 168 | | - /* |
|---|
| 169 | | - * Call inbound processing but not directly |
|---|
| 170 | | - * since that could starve other thinint queues. |
|---|
| 171 | | - */ |
|---|
| 172 | | - tasklet_schedule(&q->tasklet); |
|---|
| 173 | | - } |
|---|
| 127 | + /* |
|---|
| 128 | + * Call inbound processing but not directly |
|---|
| 129 | + * since that could starve other thinint queues. |
|---|
| 130 | + */ |
|---|
| 131 | + tasklet_schedule(&q->tasklet); |
|---|
| 174 | 132 | } |
|---|
| 175 | 133 | } |
|---|
| 176 | 134 | |
|---|
| 177 | 135 | /** |
|---|
| 178 | 136 | * tiqdio_thinint_handler - thin interrupt handler for qdio |
|---|
| 179 | 137 | * @airq: pointer to adapter interrupt descriptor |
|---|
| 138 | + * @floating: flag to recognize floating vs. directed interrupts (unused) |
|---|
| 180 | 139 | */ |
|---|
| 181 | | -static void tiqdio_thinint_handler(struct airq_struct *airq) |
|---|
| 140 | +static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) |
|---|
| 182 | 141 | { |
|---|
| 183 | 142 | u32 si_used = clear_shared_ind(); |
|---|
| 184 | | - struct qdio_q *q; |
|---|
| 143 | + struct qdio_irq *irq; |
|---|
| 185 | 144 | |
|---|
| 186 | 145 | last_ai_time = S390_lowcore.int_clock; |
|---|
| 187 | 146 | inc_irq_stat(IRQIO_QAI); |
|---|
| .. | .. |
|---|
| 189 | 148 | /* protect tiq_list entries, only changed in activate or shutdown */ |
|---|
| 190 | 149 | rcu_read_lock(); |
|---|
| 191 | 150 | |
|---|
| 192 | | - /* check for work on all inbound thinint queues */ |
|---|
| 193 | | - list_for_each_entry_rcu(q, &tiq_list, entry) { |
|---|
| 194 | | - struct qdio_irq *irq; |
|---|
| 195 | | - |
|---|
| 151 | + list_for_each_entry_rcu(irq, &tiq_list, entry) { |
|---|
| 196 | 152 | /* only process queues from changed sets */ |
|---|
| 197 | | - irq = q->irq_ptr; |
|---|
| 198 | 153 | if (unlikely(references_shared_dsci(irq))) { |
|---|
| 199 | 154 | if (!si_used) |
|---|
| 200 | 155 | continue; |
|---|
| .. | .. |
|---|
| 203 | 158 | |
|---|
| 204 | 159 | tiqdio_call_inq_handlers(irq); |
|---|
| 205 | 160 | |
|---|
| 206 | | - qperf_inc(q, adapter_int); |
|---|
| 161 | + QDIO_PERF_STAT_INC(irq, adapter_int); |
|---|
| 207 | 162 | } |
|---|
| 208 | 163 | rcu_read_unlock(); |
|---|
| 209 | 164 | } |
|---|
| 165 | + |
|---|
| 166 | +static struct airq_struct tiqdio_airq = { |
|---|
| 167 | + .handler = tiqdio_thinint_handler, |
|---|
| 168 | + .isc = QDIO_AIRQ_ISC, |
|---|
| 169 | +}; |
|---|
| 210 | 170 | |
|---|
| 211 | 171 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |
|---|
| 212 | 172 | { |
|---|
| .. | .. |
|---|
| 223 | 183 | } |
|---|
| 224 | 184 | |
|---|
| 225 | 185 | rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr, |
|---|
| 226 | | - subchannel_indicator_addr); |
|---|
| 186 | + subchannel_indicator_addr, tiqdio_airq.isc); |
|---|
| 227 | 187 | if (rc) { |
|---|
| 228 | 188 | DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, |
|---|
| 229 | 189 | scssc->response.code); |
|---|
| .. | .. |
|---|
| 235 | 195 | DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr)); |
|---|
| 236 | 196 | out: |
|---|
| 237 | 197 | return rc; |
|---|
| 238 | | -} |
|---|
| 239 | | - |
|---|
| 240 | | -/* allocate non-shared indicators and shared indicator */ |
|---|
| 241 | | -int __init tiqdio_allocate_memory(void) |
|---|
| 242 | | -{ |
|---|
| 243 | | - q_indicators = kcalloc(TIQDIO_NR_INDICATORS, |
|---|
| 244 | | - sizeof(struct indicator_t), |
|---|
| 245 | | - GFP_KERNEL); |
|---|
| 246 | | - if (!q_indicators) |
|---|
| 247 | | - return -ENOMEM; |
|---|
| 248 | | - return 0; |
|---|
| 249 | | -} |
|---|
| 250 | | - |
|---|
| 251 | | -void tiqdio_free_memory(void) |
|---|
| 252 | | -{ |
|---|
| 253 | | - kfree(q_indicators); |
|---|
| 254 | | -} |
|---|
| 255 | | - |
|---|
| 256 | | -int __init tiqdio_register_thinints(void) |
|---|
| 257 | | -{ |
|---|
| 258 | | - int rc; |
|---|
| 259 | | - |
|---|
| 260 | | - rc = register_adapter_interrupt(&tiqdio_airq); |
|---|
| 261 | | - if (rc) { |
|---|
| 262 | | - DBF_EVENT("RTI:%x", rc); |
|---|
| 263 | | - return rc; |
|---|
| 264 | | - } |
|---|
| 265 | | - return 0; |
|---|
| 266 | 198 | } |
|---|
| 267 | 199 | |
|---|
| 268 | 200 | int qdio_establish_thinint(struct qdio_irq *irq_ptr) |
|---|
| .. | .. |
|---|
| 292 | 224 | put_indicator(irq_ptr->dsci); |
|---|
| 293 | 225 | } |
|---|
| 294 | 226 | |
|---|
| 295 | | -void __exit tiqdio_unregister_thinints(void) |
|---|
| 227 | +int __init qdio_thinint_init(void) |
|---|
| 228 | +{ |
|---|
| 229 | + int rc; |
|---|
| 230 | + |
|---|
| 231 | + q_indicators = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t), |
|---|
| 232 | + GFP_KERNEL); |
|---|
| 233 | + if (!q_indicators) |
|---|
| 234 | + return -ENOMEM; |
|---|
| 235 | + |
|---|
| 236 | + rc = register_adapter_interrupt(&tiqdio_airq); |
|---|
| 237 | + if (rc) { |
|---|
| 238 | + DBF_EVENT("RTI:%x", rc); |
|---|
| 239 | + kfree(q_indicators); |
|---|
| 240 | + return rc; |
|---|
| 241 | + } |
|---|
| 242 | + return 0; |
|---|
| 243 | +} |
|---|
| 244 | + |
|---|
| 245 | +void __exit qdio_thinint_exit(void) |
|---|
| 296 | 246 | { |
|---|
| 297 | 247 | WARN_ON(!list_empty(&tiq_list)); |
|---|
| 298 | 248 | unregister_adapter_interrupt(&tiqdio_airq); |
|---|
| 249 | + kfree(q_indicators); |
|---|
| 299 | 250 | } |
|---|