root/drivers/s390/cio/qdio_thinint.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_indicator
  2. put_indicator
  3. tiqdio_add_input_queues
  4. tiqdio_remove_input_queues
  5. has_multiple_inq_on_dsci
  6. references_shared_dsci
  7. shared_ind
  8. clear_nonshared_ind
  9. test_nonshared_ind
  10. clear_shared_ind
  11. tiqdio_call_inq_handlers
  12. tiqdio_thinint_handler
  13. set_subchannel_ind
  14. tiqdio_allocate_memory
  15. tiqdio_free_memory
  16. tiqdio_register_thinints
  17. qdio_establish_thinint
  18. qdio_setup_thinint
  19. qdio_shutdown_thinint
  20. tiqdio_unregister_thinints

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright IBM Corp. 2000, 2009
   4  * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
   5  *            Cornelia Huck <cornelia.huck@de.ibm.com>
   6  *            Jan Glauber <jang@linux.vnet.ibm.com>
   7  */
   8 #include <linux/io.h>
   9 #include <linux/slab.h>
  10 #include <linux/kernel_stat.h>
  11 #include <linux/atomic.h>
  12 #include <linux/rculist.h>
  13 
  14 #include <asm/debug.h>
  15 #include <asm/qdio.h>
  16 #include <asm/airq.h>
  17 #include <asm/isc.h>
  18 
  19 #include "cio.h"
  20 #include "ioasm.h"
  21 #include "qdio.h"
  22 #include "qdio_debug.h"
  23 
  24 /*
  25  * Restriction: only 63 iqdio subchannels would have its own indicator,
  26  * after that, subsequent subchannels share one indicator
  27  */
  28 #define TIQDIO_NR_NONSHARED_IND         63
  29 #define TIQDIO_NR_INDICATORS            (TIQDIO_NR_NONSHARED_IND + 1)
  30 #define TIQDIO_SHARED_IND               63
  31 
  32 /* device state change indicators */
  33 struct indicator_t {
  34         u32 ind;        /* u32 because of compare-and-swap performance */
  35         atomic_t count; /* use count, 0 or 1 for non-shared indicators */
  36 };
  37 
  38 /* list of thin interrupt input queues */
  39 static LIST_HEAD(tiq_list);
  40 static DEFINE_MUTEX(tiq_list_lock);
  41 
  42 /* Adapter interrupt definitions */
  43 static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating);
  44 
  45 static struct airq_struct tiqdio_airq = {
  46         .handler = tiqdio_thinint_handler,
  47         .isc = QDIO_AIRQ_ISC,
  48 };
  49 
  50 static struct indicator_t *q_indicators;
  51 
  52 u64 last_ai_time;
  53 
  54 /* returns addr for the device state change indicator */
  55 static u32 *get_indicator(void)
  56 {
  57         int i;
  58 
  59         for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
  60                 if (!atomic_cmpxchg(&q_indicators[i].count, 0, 1))
  61                         return &q_indicators[i].ind;
  62 
  63         /* use the shared indicator */
  64         atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
  65         return &q_indicators[TIQDIO_SHARED_IND].ind;
  66 }
  67 
  68 static void put_indicator(u32 *addr)
  69 {
  70         struct indicator_t *ind = container_of(addr, struct indicator_t, ind);
  71 
  72         if (!addr)
  73                 return;
  74         atomic_dec(&ind->count);
  75 }
  76 
  77 void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
  78 {
  79         mutex_lock(&tiq_list_lock);
  80         list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list);
  81         mutex_unlock(&tiq_list_lock);
  82 }
  83 
  84 void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
  85 {
  86         struct qdio_q *q;
  87 
  88         q = irq_ptr->input_qs[0];
  89         if (!q)
  90                 return;
  91 
  92         mutex_lock(&tiq_list_lock);
  93         list_del_rcu(&q->entry);
  94         mutex_unlock(&tiq_list_lock);
  95         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;
 102 }
 103 
 104 static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
 105 {
 106         return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
 107 }
 108 
 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 int test_nonshared_ind(struct qdio_irq *irq_ptr)
 125 {
 126         if (!is_thinint_irq(irq_ptr))
 127                 return 0;
 128         if (shared_ind(irq_ptr))
 129                 return 0;
 130         if (*irq_ptr->dsci)
 131                 return 1;
 132         else
 133                 return 0;
 134 }
 135 
 136 static inline u32 clear_shared_ind(void)
 137 {
 138         if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
 139                 return 0;
 140         return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
 141 }
 142 
 143 static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
 144 {
 145         struct qdio_q *q;
 146         int i;
 147 
 148         if (!references_shared_dsci(irq) &&
 149             has_multiple_inq_on_dsci(irq))
 150                 xchg(irq->dsci, 0);
 151 
 152         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                 }
 174         }
 175 }
 176 
 177 /**
 178  * tiqdio_thinint_handler - thin interrupt handler for qdio
 179  * @airq: pointer to adapter interrupt descriptor
 180  * @floating: flag to recognize floating vs. directed interrupts (unused)
 181  */
 182 static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating)
 183 {
 184         u32 si_used = clear_shared_ind();
 185         struct qdio_q *q;
 186 
 187         last_ai_time = S390_lowcore.int_clock;
 188         inc_irq_stat(IRQIO_QAI);
 189 
 190         /* protect tiq_list entries, only changed in activate or shutdown */
 191         rcu_read_lock();
 192 
 193         /* check for work on all inbound thinint queues */
 194         list_for_each_entry_rcu(q, &tiq_list, entry) {
 195                 struct qdio_irq *irq;
 196 
 197                 /* only process queues from changed sets */
 198                 irq = q->irq_ptr;
 199                 if (unlikely(references_shared_dsci(irq))) {
 200                         if (!si_used)
 201                                 continue;
 202                 } else if (!*irq->dsci)
 203                         continue;
 204 
 205                 tiqdio_call_inq_handlers(irq);
 206 
 207                 qperf_inc(q, adapter_int);
 208         }
 209         rcu_read_unlock();
 210 }
 211 
 212 static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
 213 {
 214         struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
 215         u64 summary_indicator_addr, subchannel_indicator_addr;
 216         int rc;
 217 
 218         if (reset) {
 219                 summary_indicator_addr = 0;
 220                 subchannel_indicator_addr = 0;
 221         } else {
 222                 summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr);
 223                 subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci);
 224         }
 225 
 226         rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr,
 227                        subchannel_indicator_addr);
 228         if (rc) {
 229                 DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
 230                           scssc->response.code);
 231                 goto out;
 232         }
 233 
 234         DBF_EVENT("setscind");
 235         DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr));
 236         DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr));
 237 out:
 238         return rc;
 239 }
 240 
 241 /* allocate non-shared indicators and shared indicator */
 242 int __init tiqdio_allocate_memory(void)
 243 {
 244         q_indicators = kcalloc(TIQDIO_NR_INDICATORS,
 245                                sizeof(struct indicator_t),
 246                                GFP_KERNEL);
 247         if (!q_indicators)
 248                 return -ENOMEM;
 249         return 0;
 250 }
 251 
 252 void tiqdio_free_memory(void)
 253 {
 254         kfree(q_indicators);
 255 }
 256 
 257 int __init tiqdio_register_thinints(void)
 258 {
 259         int rc;
 260 
 261         rc = register_adapter_interrupt(&tiqdio_airq);
 262         if (rc) {
 263                 DBF_EVENT("RTI:%x", rc);
 264                 return rc;
 265         }
 266         return 0;
 267 }
 268 
 269 int qdio_establish_thinint(struct qdio_irq *irq_ptr)
 270 {
 271         if (!is_thinint_irq(irq_ptr))
 272                 return 0;
 273         return set_subchannel_ind(irq_ptr, 0);
 274 }
 275 
 276 void qdio_setup_thinint(struct qdio_irq *irq_ptr)
 277 {
 278         if (!is_thinint_irq(irq_ptr))
 279                 return;
 280         irq_ptr->dsci = get_indicator();
 281         DBF_HEX(&irq_ptr->dsci, sizeof(void *));
 282 }
 283 
 284 void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
 285 {
 286         if (!is_thinint_irq(irq_ptr))
 287                 return;
 288 
 289         /* reset adapter interrupt indicators */
 290         set_subchannel_ind(irq_ptr, 1);
 291         put_indicator(irq_ptr->dsci);
 292 }
 293 
 294 void __exit tiqdio_unregister_thinints(void)
 295 {
 296         WARN_ON(!list_empty(&tiq_list));
 297         unregister_adapter_interrupt(&tiqdio_airq);
 298 }

/* [<][>][^][v][top][bottom][index][help] */