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 void do_adapter_IO(u8 isc) 85 { 86 struct airq_struct *airq; 87 struct hlist_head *head; 88 89 head = &airq_lists[isc]; 90 rcu_read_lock(); 91 hlist_for_each_entry_rcu(airq, head, list) 92 if ((*airq->lsi_ptr & airq->lsi_mask) != 0) 93 airq->handler(airq); 94 rcu_read_unlock(); 95 } 96