1 /* 2 * Support for adapter interruptions 3 * 4 * Copyright IBM Corp. 1999, 2007 5 * Author(s): Ingo Adlung <adlung@de.ibm.com> 6 * Cornelia Huck <cornelia.huck@de.ibm.com> 7 * Arnd Bergmann <arndb@de.ibm.com> 8 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 9 */ 10 11 #include <linux/init.h> 12 #include <linux/irq.h> 13 #include <linux/kernel_stat.h> 14 #include <linux/module.h> 15 #include <linux/mutex.h> 16 #include <linux/rculist.h> 17 #include <linux/slab.h> 18 19 #include <asm/airq.h> 20 #include <asm/isc.h> 21 22 #include "cio.h" 23 #include "cio_debug.h" 24 #include "ioasm.h" 25 26 static DEFINE_SPINLOCK(airq_lists_lock); 27 static struct hlist_head airq_lists[MAX_ISC+1]; 28 29 /** 30 * register_adapter_interrupt() - register adapter interrupt handler 31 * @airq: pointer to adapter interrupt descriptor 32 * 33 * Returns 0 on success, or -EINVAL. 34 */ 35 int register_adapter_interrupt(struct airq_struct *airq) 36 { 37 char dbf_txt[32]; 38 39 if (!airq->handler || airq->isc > MAX_ISC) 40 return -EINVAL; 41 if (!airq->lsi_ptr) { 42 airq->lsi_ptr = kzalloc(1, GFP_KERNEL); 43 if (!airq->lsi_ptr) 44 return -ENOMEM; 45 airq->flags |= AIRQ_PTR_ALLOCATED; 46 } 47 if (!airq->lsi_mask) 48 airq->lsi_mask = 0xff; 49 snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq); 50 CIO_TRACE_EVENT(4, dbf_txt); 51 isc_register(airq->isc); 52 spin_lock(&airq_lists_lock); 53 hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]); 54 spin_unlock(&airq_lists_lock); 55 return 0; 56 } 57 EXPORT_SYMBOL(register_adapter_interrupt); 58 59 /** 60 * unregister_adapter_interrupt - unregister adapter interrupt handler 61 * @airq: pointer to adapter interrupt descriptor 62 */ 63 void unregister_adapter_interrupt(struct airq_struct *airq) 64 { 65 char dbf_txt[32]; 66 67 if (hlist_unhashed(&airq->list)) 68 return; 69 snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq); 70 CIO_TRACE_EVENT(4, dbf_txt); 71 spin_lock(&airq_lists_lock); 72 hlist_del_rcu(&airq->list); 73 spin_unlock(&airq_lists_lock); 74 synchronize_rcu(); 75 isc_unregister(airq->isc); 76 if (airq->flags & AIRQ_PTR_ALLOCATED) { 77 kfree(airq->lsi_ptr); 78 airq->lsi_ptr = NULL; 79 airq->flags &= ~AIRQ_PTR_ALLOCATED; 80 } 81 } 82 EXPORT_SYMBOL(unregister_adapter_interrupt); 83 84 static irqreturn_t do_airq_interrupt(int irq, void *dummy) 85 { 86 struct tpi_info *tpi_info; 87 struct airq_struct *airq; 88 struct hlist_head *head; 89 90 __this_cpu_write(s390_idle.nohz_delay, 1); 91 tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; 92 head = &airq_lists[tpi_info->isc]; 93 rcu_read_lock(); 94 hlist_for_each_entry_rcu(airq, head, list) 95 if ((*airq->lsi_ptr & airq->lsi_mask) != 0) 96 airq->handler(airq); 97 rcu_read_unlock(); 98 99 return IRQ_HANDLED; 100 } 101 102 static struct irqaction airq_interrupt = { 103 .name = "AIO", 104 .handler = do_airq_interrupt, 105 }; 106 107 void __init init_airq_interrupts(void) 108 { 109 irq_set_chip_and_handler(THIN_INTERRUPT, 110 &dummy_irq_chip, handle_percpu_irq); 111 setup_irq(THIN_INTERRUPT, &airq_interrupt); 112 } 113 114 /** 115 * airq_iv_create - create an interrupt vector 116 * @bits: number of bits in the interrupt vector 117 * @flags: allocation flags 118 * 119 * Returns a pointer to an interrupt vector structure 120 */ 121 struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags) 122 { 123 struct airq_iv *iv; 124 unsigned long size; 125 126 iv = kzalloc(sizeof(*iv), GFP_KERNEL); 127 if (!iv) 128 goto out; 129 iv->bits = bits; 130 size = BITS_TO_LONGS(bits) * sizeof(unsigned long); 131 iv->vector = kzalloc(size, GFP_KERNEL); 132 if (!iv->vector) 133 goto out_free; 134 if (flags & AIRQ_IV_ALLOC) { 135 iv->avail = kmalloc(size, GFP_KERNEL); 136 if (!iv->avail) 137 goto out_free; 138 memset(iv->avail, 0xff, size); 139 iv->end = 0; 140 } else 141 iv->end = bits; 142 if (flags & AIRQ_IV_BITLOCK) { 143 iv->bitlock = kzalloc(size, GFP_KERNEL); 144 if (!iv->bitlock) 145 goto out_free; 146 } 147 if (flags & AIRQ_IV_PTR) { 148 size = bits * sizeof(unsigned long); 149 iv->ptr = kzalloc(size, GFP_KERNEL); 150 if (!iv->ptr) 151 goto out_free; 152 } 153 if (flags & AIRQ_IV_DATA) { 154 size = bits * sizeof(unsigned int); 155 iv->data = kzalloc(size, GFP_KERNEL); 156 if (!iv->data) 157 goto out_free; 158 } 159 spin_lock_init(&iv->lock); 160 return iv; 161 162 out_free: 163 kfree(iv->ptr); 164 kfree(iv->bitlock); 165 kfree(iv->avail); 166 kfree(iv->vector); 167 kfree(iv); 168 out: 169 return NULL; 170 } 171 EXPORT_SYMBOL(airq_iv_create); 172 173 /** 174 * airq_iv_release - release an interrupt vector 175 * @iv: pointer to interrupt vector structure 176 */ 177 void airq_iv_release(struct airq_iv *iv) 178 { 179 kfree(iv->data); 180 kfree(iv->ptr); 181 kfree(iv->bitlock); 182 kfree(iv->vector); 183 kfree(iv->avail); 184 kfree(iv); 185 } 186 EXPORT_SYMBOL(airq_iv_release); 187 188 /** 189 * airq_iv_alloc_bit - allocate an irq bit from an interrupt vector 190 * @iv: pointer to an interrupt vector structure 191 * 192 * Returns the bit number of the allocated irq, or -1UL if no bit 193 * is available or the AIRQ_IV_ALLOC flag has not been specified 194 */ 195 unsigned long airq_iv_alloc_bit(struct airq_iv *iv) 196 { 197 unsigned long bit; 198 199 if (!iv->avail) 200 return -1UL; 201 spin_lock(&iv->lock); 202 bit = find_first_bit_inv(iv->avail, iv->bits); 203 if (bit < iv->bits) { 204 clear_bit_inv(bit, iv->avail); 205 if (bit >= iv->end) 206 iv->end = bit + 1; 207 } else 208 bit = -1UL; 209 spin_unlock(&iv->lock); 210 return bit; 211 212 } 213 EXPORT_SYMBOL(airq_iv_alloc_bit); 214 215 /** 216 * airq_iv_free_bit - free an irq bit of an interrupt vector 217 * @iv: pointer to interrupt vector structure 218 * @bit: number of the irq bit to free 219 */ 220 void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) 221 { 222 if (!iv->avail) 223 return; 224 spin_lock(&iv->lock); 225 /* Clear (possibly left over) interrupt bit */ 226 clear_bit_inv(bit, iv->vector); 227 /* Make the bit position available again */ 228 set_bit_inv(bit, iv->avail); 229 if (bit == iv->end - 1) { 230 /* Find new end of bit-field */ 231 while (--iv->end > 0) 232 if (!test_bit_inv(iv->end - 1, iv->avail)) 233 break; 234 } 235 spin_unlock(&iv->lock); 236 } 237 EXPORT_SYMBOL(airq_iv_free_bit); 238 239 /** 240 * airq_iv_scan - scan interrupt vector for non-zero bits 241 * @iv: pointer to interrupt vector structure 242 * @start: bit number to start the search 243 * @end: bit number to end the search 244 * 245 * Returns the bit number of the next non-zero interrupt bit, or 246 * -1UL if the scan completed without finding any more any non-zero bits. 247 */ 248 unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start, 249 unsigned long end) 250 { 251 unsigned long bit; 252 253 /* Find non-zero bit starting from 'ivs->next'. */ 254 bit = find_next_bit_inv(iv->vector, end, start); 255 if (bit >= end) 256 return -1UL; 257 clear_bit_inv(bit, iv->vector); 258 return bit; 259 } 260 EXPORT_SYMBOL(airq_iv_scan); 261