Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
airq.c
Go to the documentation of this file.
1 /*
2  * Support for adapter interruptions
3  *
4  * Copyright IBM Corp. 1999, 2007
5  * Author(s): Ingo Adlung <[email protected]>
6  * Cornelia Huck <[email protected]>
7  * Arnd Bergmann <[email protected]>
8  * Peter Oberparleiter <[email protected]>
9  */
10 
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/rcupdate.h>
15 
16 #include <asm/airq.h>
17 #include <asm/isc.h>
18 
19 #include "cio.h"
20 #include "cio_debug.h"
21 
22 #define NR_AIRQS 32
23 #define NR_AIRQS_PER_WORD sizeof(unsigned long)
24 #define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD)
25 
26 union indicator_t {
27  unsigned long word[NR_AIRQ_WORDS];
28  unsigned char byte[NR_AIRQS];
29 } __attribute__((packed));
30 
31 struct airq_t {
33  void *drv_data;
34 };
35 
36 static union indicator_t indicators[MAX_ISC+1];
37 static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS];
38 
39 static int register_airq(struct airq_t *airq, u8 isc)
40 {
41  int i;
42 
43  for (i = 0; i < NR_AIRQS; i++)
44  if (!cmpxchg(&airqs[isc][i], NULL, airq))
45  return i;
46  return -ENOMEM;
47 }
48 
60  void *drv_data, u8 isc)
61 {
62  struct airq_t *airq;
63  char dbf_txt[16];
64  int ret;
65 
66  if (isc > MAX_ISC)
67  return ERR_PTR(-EINVAL);
68  airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
69  if (!airq) {
70  ret = -ENOMEM;
71  goto out;
72  }
73  airq->handler = handler;
74  airq->drv_data = drv_data;
75 
76  ret = register_airq(airq, isc);
77 out:
78  snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
79  CIO_TRACE_EVENT(4, dbf_txt);
80  if (ret < 0) {
81  kfree(airq);
82  return ERR_PTR(ret);
83  } else
84  return &indicators[isc].byte[ret];
85 }
87 
94 {
95  struct airq_t *airq;
96  char dbf_txt[16];
97  int i;
98 
99  i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
100  snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
101  CIO_TRACE_EVENT(4, dbf_txt);
102  indicators[isc].byte[i] = 0;
103  airq = xchg(&airqs[isc][i], NULL);
104  /*
105  * Allow interrupts to complete. This will ensure that the airq handle
106  * is no longer referenced by any interrupt handler.
107  */
109  kfree(airq);
110 }
112 
113 #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
114 
115 void do_adapter_IO(u8 isc)
116 {
117  int w;
118  int i;
119  unsigned long word;
120  struct airq_t *airq;
121 
122  /*
123  * Access indicator array in word-sized chunks to minimize storage
124  * fetch operations.
125  */
126  for (w = 0; w < NR_AIRQ_WORDS; w++) {
127  word = indicators[isc].word[w];
128  i = w * NR_AIRQS_PER_WORD;
129  /*
130  * Check bytes within word for active indicators.
131  */
132  while (word) {
133  if (word & INDICATOR_MASK) {
134  airq = airqs[isc][i];
135  /* Make sure gcc reads from airqs only once. */
136  barrier();
137  if (likely(airq))
138  airq->handler(&indicators[isc].byte[i],
139  airq->drv_data);
140  else
141  /*
142  * Reset ill-behaved indicator.
143  */
144  indicators[isc].byte[i] = 0;
145  }
146  word <<= 8;
147  i++;
148  }
149  }
150 }