Remove the linear IRQ hash table optimization
[helenos.git] / kernel / generic / src / ddi / irq.c
blob7af8a1a6645e2121fd68de9cb8675417a4217ea2
1 /*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup genericddi
30 * @{
32 /**
33 * @file
34 * @brief IRQ dispatcher
36 * This file provides means of connecting IRQs with respective device drivers
37 * and logic for dispatching interrupts to IRQ handlers defined by those
38 * drivers.
41 #include <ddi/irq.h>
42 #include <adt/hash_table.h>
43 #include <mm/slab.h>
44 #include <typedefs.h>
45 #include <synch/spinlock.h>
46 #include <console/console.h>
47 #include <interrupt.h>
48 #include <mem.h>
49 #include <arch.h>
51 /** Spinlock protecting the kernel IRQ hash table
53 * This lock must be taken only when interrupts are disabled.
56 IRQ_SPINLOCK_STATIC_INITIALIZE(irq_kernel_hash_table_lock);
58 /** The kernel IRQ hash table. */
59 static hash_table_t irq_kernel_hash_table;
61 /** Spinlock protecting the uspace IRQ hash table
63 * This lock must be taken only when interrupts are disabled.
66 IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
68 /** The uspace IRQ hash table */
69 hash_table_t irq_uspace_hash_table;
71 static size_t irq_ht_hash(sysarg_t *key);
72 static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item);
73 static void irq_ht_remove(link_t *item);
75 static hash_table_operations_t irq_ht_ops = {
76 .hash = irq_ht_hash,
77 .compare = irq_ht_compare,
78 .remove_callback = irq_ht_remove,
81 /** Number of buckets in either of the hash tables */
82 static size_t buckets;
84 /** Last valid INR */
85 inr_t last_inr = 0;
87 /** Initialize IRQ subsystem
89 * @param inrs Numbers of unique IRQ numbers or INRs.
90 * @param chains Number of buckets in the hash table.
93 void irq_init(size_t inrs, size_t chains)
95 buckets = chains;
96 last_inr = inrs - 1;
98 hash_table_create(&irq_uspace_hash_table, chains, 2, &irq_ht_ops);
99 hash_table_create(&irq_kernel_hash_table, chains, 2, &irq_ht_ops);
102 /** Initialize one IRQ structure
104 * @param irq Pointer to the IRQ structure to be initialized.
107 void irq_initialize(irq_t *irq)
109 memsetb(irq, sizeof(irq_t), 0);
110 link_initialize(&irq->link);
111 irq_spinlock_initialize(&irq->lock, "irq.lock");
112 link_initialize(&irq->notif_cfg.link);
113 irq->inr = -1;
115 irq_initialize_arch(irq);
118 /** Register IRQ for device
120 * The irq structure must be filled with information about the interrupt source
121 * and with the claim() function pointer and handler() function pointer.
123 * @param irq IRQ structure belonging to a device.
126 void irq_register(irq_t *irq)
128 sysarg_t key[] = {
129 [IRQ_HT_KEY_INR] = (sysarg_t) irq->inr,
130 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_NO_CLAIM
133 irq_spinlock_lock(&irq_kernel_hash_table_lock, true);
134 irq_spinlock_lock(&irq->lock, false);
135 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
136 irq_spinlock_unlock(&irq->lock, false);
137 irq_spinlock_unlock(&irq_kernel_hash_table_lock, true);
140 /** Search and lock the uspace IRQ hash table */
141 static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
143 link_t *lnk;
144 sysarg_t key[] = {
145 [IRQ_HT_KEY_INR] = (sysarg_t) inr,
146 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
149 irq_spinlock_lock(&irq_uspace_hash_table_lock, false);
150 lnk = hash_table_find(&irq_uspace_hash_table, key);
151 if (lnk) {
152 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
153 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
154 return irq;
156 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
158 return NULL;
161 /** Search and lock the kernel IRQ hash table */
162 static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
164 link_t *lnk;
165 sysarg_t key[] = {
166 [IRQ_HT_KEY_INR] = (sysarg_t) inr,
167 [IRQ_HT_KEY_MODE] = (sysarg_t) IRQ_HT_MODE_CLAIM
170 irq_spinlock_lock(&irq_kernel_hash_table_lock, false);
171 lnk = hash_table_find(&irq_kernel_hash_table, key);
172 if (lnk) {
173 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
174 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
175 return irq;
177 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
179 return NULL;
182 /** Dispatch the IRQ
184 * We assume this function is only called from interrupt context (i.e. that
185 * interrupts are disabled prior to this call).
187 * This function attempts to lookup a fitting IRQ structure. In case of success,
188 * return with interrupts disabled and holding the respective structure.
190 * @param inr Interrupt number (aka inr or irq).
192 * @return IRQ structure of the respective device
193 * @return NULL if no IRQ structure found
196 irq_t *irq_dispatch_and_lock(inr_t inr)
199 * If the kernel console override is on, then try first the kernel
200 * handlers and eventually fall back to uspace handlers.
202 * In the usual case the uspace handlers have precedence.
205 if (console_override) {
206 irq_t *irq = irq_dispatch_and_lock_kernel(inr);
207 if (irq)
208 return irq;
210 return irq_dispatch_and_lock_uspace(inr);
213 irq_t *irq = irq_dispatch_and_lock_uspace(inr);
214 if (irq)
215 return irq;
217 return irq_dispatch_and_lock_kernel(inr);
220 /** Compute hash index for the key
222 * @param key The first of the keys is inr and the second is mode. Only inr is
223 * used to compute the hash.
225 * @return Index into the hash table.
228 size_t irq_ht_hash(sysarg_t key[])
230 inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
231 return inr % buckets;
234 /** Compare hash table element with a key
236 * If mode is IRQ_HT_MODE_CLAIM, the result of the claim() function is used for
237 * the match. Otherwise the key does not match.
239 * This function assumes interrupts are already disabled.
241 * @param key Keys (i.e. inr and mode).
242 * @param keys This is 2.
243 * @param item The item to compare the key with.
245 * @return True on match
246 * @return False on no match
249 bool irq_ht_compare(sysarg_t key[], size_t keys, link_t *item)
251 irq_t *irq = hash_table_get_instance(item, irq_t, link);
252 inr_t inr = (inr_t) key[IRQ_HT_KEY_INR];
253 irq_ht_mode_t mode = (irq_ht_mode_t) key[IRQ_HT_KEY_MODE];
255 bool rv;
257 irq_spinlock_lock(&irq->lock, false);
258 if (mode == IRQ_HT_MODE_CLAIM) {
259 /* Invoked by irq_dispatch_and_lock(). */
260 rv = ((irq->inr == inr) && (irq->claim(irq) == IRQ_ACCEPT));
261 } else {
262 /* Invoked by irq_find_and_lock(). */
263 rv = false;
266 /* unlock only on non-match */
267 if (!rv)
268 irq_spinlock_unlock(&irq->lock, false);
270 return rv;
273 /** Unlock IRQ structure after hash_table_remove()
275 * @param lnk Link in the removed and locked IRQ structure.
277 void irq_ht_remove(link_t *lnk)
279 irq_t *irq __attribute__((unused))
280 = hash_table_get_instance(lnk, irq_t, link);
281 irq_spinlock_unlock(&irq->lock, false);
284 /** @}