Changes in kernel/generic/src/ddi/irq.c [82cbf8c6:9d58539] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ddi/irq.c
r82cbf8c6 r9d58539 32 32 /** 33 33 * @file 34 * @brief IRQ dispatcher 35 * 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. 34 * @brief IRQ dispatcher. 35 * 36 * This file provides means of connecting IRQs with particular 37 * devices and logic for dispatching interrupts to IRQ handlers 38 * defined by those devices. 39 * 40 * This code is designed to support: 41 * - multiple devices sharing single IRQ 42 * - multiple IRQs per single device 43 * - multiple instances of the same device 44 * 45 * 46 * Note about architectures. 47 * 48 * Some architectures has the term IRQ well defined. Examples 49 * of such architectures include amd64, ia32 and mips32. Some 50 * other architectures, such as sparc64, don't use the term 51 * at all. In those cases, we boldly step forward and define what 52 * an IRQ is. 53 * 54 * The implementation is generic enough and still allows the 55 * architectures to use the hardware layout effectively. 56 * For instance, on amd64 and ia32, where there is only 16 57 * IRQs, the irq_hash_table can be optimized to a one-dimensional 58 * array. Next, when it is known that the IRQ numbers (aka INR's) 59 * are unique, the claim functions can always return IRQ_ACCEPT. 60 * 61 * 62 * Note about the irq_hash_table. 63 * 64 * The hash table is configured to use two keys: inr and devno. 65 * However, the hash index is computed only from inr. Moreover, 66 * if devno is -1, the match is based on the return value of 67 * the claim() function instead of on devno. 39 68 */ 40 69 41 70 #include <ddi/irq.h> 42 #include <adt/hash.h>43 71 #include <adt/hash_table.h> 44 72 #include <mm/slab.h> … … 47 75 #include <console/console.h> 48 76 #include <interrupt.h> 49 #include <mem .h>77 #include <memstr.h> 50 78 #include <arch.h> 51 79 52 slab_cache_t *irq_slab = NULL; 53 54 /** Spinlock protecting the kernel IRQ hash table 80 #define KEY_INR 0 81 #define KEY_DEVNO 1 82 83 /** Spinlock protecting the kernel IRQ hash table. 55 84 * 56 85 * This lock must be taken only when interrupts are disabled. … … 62 91 static hash_table_t irq_kernel_hash_table; 63 92 64 /** Spinlock protecting the uspace IRQ hash table 93 /** Spinlock protecting the uspace IRQ hash table. 65 94 * 66 95 * This lock must be taken only when interrupts are disabled. … … 69 98 IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock); 70 99 71 /** The uspace IRQ hash table */100 /** The uspace IRQ hash table. */ 72 101 hash_table_t irq_uspace_hash_table; 73 102 74 static size_t irq_ht_hash(const ht_link_t *); 75 static size_t irq_ht_key_hash(void *); 76 static bool irq_ht_equal(const ht_link_t *, const ht_link_t *); 77 static bool irq_ht_key_equal(void *, const ht_link_t *); 78 79 static hash_table_ops_t irq_ht_ops = { 103 /** 104 * Hash table operations for cases when we know that 105 * there will be collisions between different keys. 106 * 107 */ 108 static size_t irq_ht_hash(sysarg_t *key); 109 static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item); 110 static void irq_ht_remove(link_t *item); 111 112 static hash_table_operations_t irq_ht_ops = { 80 113 .hash = irq_ht_hash, 81 .key_hash = irq_ht_key_hash, 82 .equal = irq_ht_equal, 83 .key_equal = irq_ht_key_equal 114 .compare = irq_ht_compare, 115 .remove_callback = irq_ht_remove, 84 116 }; 85 117 86 /** Last valid INR */ 118 /** 119 * Hash table operations for cases when we know that 120 * there will be no collisions between different keys. 121 * However, there might be still collisions among 122 * elements with single key (sharing of one IRQ). 123 * 124 */ 125 static size_t irq_lin_hash(sysarg_t *key); 126 static bool irq_lin_compare(sysarg_t *key, size_t keys, link_t *item); 127 static void irq_lin_remove(link_t *item); 128 129 static hash_table_operations_t irq_lin_ops = { 130 .hash = irq_lin_hash, 131 .compare = irq_lin_compare, 132 .remove_callback = irq_lin_remove, 133 }; 134 135 /** Number of buckets in either of the hash tables. */ 136 static size_t buckets; 137 138 /** Last valid INR. */ 87 139 inr_t last_inr = 0; 88 140 89 /** Initialize IRQ subsystem 90 * 91 * @param inrs 92 * @param chains Number of buckets in the hash table.141 /** Initialize IRQ subsystem. 142 * 143 * @param inrs Numbers of unique IRQ numbers or INRs. 144 * @param chains Number of chains in the hash table. 93 145 * 94 146 */ 95 147 void irq_init(size_t inrs, size_t chains) 96 148 { 149 buckets = chains; 97 150 last_inr = inrs - 1; 98 151 99 irq_slab = slab_cache_create("irq_t", sizeof(irq_t), 0, NULL, NULL, 100 FRAME_ATOMIC); 101 assert(irq_slab); 102 103 hash_table_create(&irq_uspace_hash_table, chains, 0, &irq_ht_ops); 104 hash_table_create(&irq_kernel_hash_table, chains, 0, &irq_ht_ops); 105 } 106 107 /** Initialize one IRQ structure 108 * 109 * @param irq Pointer to the IRQ structure to be initialized. 152 /* 153 * Be smart about the choice of the hash table operations. 154 * In cases in which inrs equals the requested number of 155 * chains (i.e. where there is no collision between 156 * different keys), we can use optimized set of operations. 157 */ 158 if (inrs == chains) { 159 hash_table_create(&irq_uspace_hash_table, chains, 2, 160 &irq_lin_ops); 161 hash_table_create(&irq_kernel_hash_table, chains, 2, 162 &irq_lin_ops); 163 } else { 164 hash_table_create(&irq_uspace_hash_table, chains, 2, 165 &irq_ht_ops); 166 hash_table_create(&irq_kernel_hash_table, chains, 2, 167 &irq_ht_ops); 168 } 169 } 170 171 /** Initialize one IRQ structure. 172 * 173 * @param irq Pointer to the IRQ structure to be initialized. 110 174 * 111 175 */ … … 113 177 { 114 178 memsetb(irq, sizeof(irq_t), 0); 179 link_initialize(&irq->link); 115 180 irq_spinlock_initialize(&irq->lock, "irq.lock"); 181 link_initialize(&irq->notif_cfg.link); 116 182 irq->inr = -1; 183 irq->devno = -1; 117 184 118 185 irq_initialize_arch(irq); 119 186 } 120 187 121 /** Register IRQ for device 122 * 123 * The irq structure must be filled with information about the interrupt source 124 * and with the claim() function pointer and handler() function pointer. 125 * 126 * @param irq IRQ structure belonging to a device. 188 /** Register IRQ for device. 189 * 190 * The irq structure must be filled with information 191 * about the interrupt source and with the claim() 192 * function pointer and handler() function pointer. 193 * 194 * @param irq IRQ structure belonging to a device. 195 * 196 * @return True on success, false on failure. 127 197 * 128 198 */ 129 199 void irq_register(irq_t *irq) 130 200 { 201 sysarg_t key[] = { 202 (sysarg_t) irq->inr, 203 (sysarg_t) irq->devno 204 }; 205 131 206 irq_spinlock_lock(&irq_kernel_hash_table_lock, true); 132 207 irq_spinlock_lock(&irq->lock, false); 133 hash_table_insert(&irq_kernel_hash_table, &irq->link);208 hash_table_insert(&irq_kernel_hash_table, key, &irq->link); 134 209 irq_spinlock_unlock(&irq->lock, false); 135 210 irq_spinlock_unlock(&irq_kernel_hash_table_lock, true); 136 211 } 137 212 138 /** Search and lock an IRQ hash table */ 139 static irq_t * 140 irq_dispatch_and_lock_table(hash_table_t *h, irq_spinlock_t *l, inr_t inr) 141 { 142 irq_spinlock_lock(l, false); 143 for (ht_link_t *lnk = hash_table_find(h, &inr); lnk; 144 lnk = hash_table_find_next(h, lnk)) { 145 irq_t *irq = hash_table_get_inst(lnk, irq_t, link); 146 irq_spinlock_lock(&irq->lock, false); 147 if (irq->claim(irq) == IRQ_ACCEPT) { 148 /* leave irq locked */ 149 irq_spinlock_unlock(l, false); 150 return irq; 151 } 152 irq_spinlock_unlock(&irq->lock, false); 153 } 154 irq_spinlock_unlock(l, false); 213 /** Search and lock the uspace IRQ hash table. 214 * 215 */ 216 static irq_t *irq_dispatch_and_lock_uspace(inr_t inr) 217 { 218 link_t *lnk; 219 sysarg_t key[] = { 220 (sysarg_t) inr, 221 (sysarg_t) -1 /* Search will use claim() instead of devno */ 222 }; 223 224 irq_spinlock_lock(&irq_uspace_hash_table_lock, false); 225 lnk = hash_table_find(&irq_uspace_hash_table, key); 226 if (lnk) { 227 irq_t *irq = hash_table_get_instance(lnk, irq_t, link); 228 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false); 229 return irq; 230 } 231 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false); 155 232 156 233 return NULL; 157 234 } 158 235 159 /** Dispatch the IRQ 160 * 161 * We assume this function is only called from interrupt context (i.e. that 162 * interrupts are disabled prior to this call). 163 * 164 * This function attempts to lookup a fitting IRQ structure. In case of success, 165 * return with interrupts disabled and holding the respective structure. 166 * 167 * @param inr Interrupt number (aka inr or irq). 168 * 169 * @return IRQ structure of the respective device 170 * @return NULL if no IRQ structure found 236 /** Search and lock the kernel IRQ hash table. 237 * 238 */ 239 static irq_t *irq_dispatch_and_lock_kernel(inr_t inr) 240 { 241 link_t *lnk; 242 sysarg_t key[] = { 243 (sysarg_t) inr, 244 (sysarg_t) -1 /* Search will use claim() instead of devno */ 245 }; 246 247 irq_spinlock_lock(&irq_kernel_hash_table_lock, false); 248 lnk = hash_table_find(&irq_kernel_hash_table, key); 249 if (lnk) { 250 irq_t *irq = hash_table_get_instance(lnk, irq_t, link); 251 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false); 252 return irq; 253 } 254 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false); 255 256 return NULL; 257 } 258 259 /** Dispatch the IRQ. 260 * 261 * We assume this function is only called from interrupt 262 * context (i.e. that interrupts are disabled prior to 263 * this call). 264 * 265 * This function attempts to lookup a fitting IRQ 266 * structure. In case of success, return with interrupts 267 * disabled and holding the respective structure. 268 * 269 * @param inr Interrupt number (aka inr or irq). 270 * 271 * @return IRQ structure of the respective device or NULL. 171 272 * 172 273 */ … … 174 275 { 175 276 /* 176 * If the kernel console override is on, then try first the kernel 177 * handlers and eventually fall back to uspace handlers. 277 * If the kernel console override is on, 278 * then try first the kernel handlers 279 * and eventually fall back to uspace 280 * handlers. 178 281 * 179 * In the usual case the uspace handlers have precedence. 282 * In the usual case the uspace handlers 283 * have precedence. 180 284 */ 181 285 182 286 if (console_override) { 183 irq_t *irq = irq_dispatch_and_lock_table(&irq_kernel_hash_table, 184 &irq_kernel_hash_table_lock, inr); 287 irq_t *irq = irq_dispatch_and_lock_kernel(inr); 185 288 if (irq) 186 289 return irq; 187 290 188 return irq_dispatch_and_lock_table(&irq_uspace_hash_table, 189 &irq_uspace_hash_table_lock, inr); 190 } 191 192 irq_t *irq = irq_dispatch_and_lock_table(&irq_uspace_hash_table, 193 &irq_uspace_hash_table_lock, inr); 291 return irq_dispatch_and_lock_uspace(inr); 292 } 293 294 irq_t *irq = irq_dispatch_and_lock_uspace(inr); 194 295 if (irq) 195 296 return irq; 196 297 197 return irq_dispatch_and_lock_table(&irq_kernel_hash_table, 198 &irq_kernel_hash_table_lock, inr); 199 } 200 201 /** Return the hash of the key stored in the item. */ 202 size_t irq_ht_hash(const ht_link_t *item) 203 { 204 irq_t *irq = hash_table_get_inst(item, irq_t, link); 205 return hash_mix(irq->inr); 206 } 207 208 /** Return the hash of the key. */ 209 size_t irq_ht_key_hash(void *key) 210 { 211 inr_t *inr = (inr_t *) key; 212 return hash_mix(*inr); 213 } 214 215 /** Return true if the items have the same lookup key. */ 216 bool irq_ht_equal(const ht_link_t *item1, const ht_link_t *item2) 217 { 218 irq_t *irq1 = hash_table_get_inst(item1, irq_t, link); 219 irq_t *irq2 = hash_table_get_inst(item2, irq_t, link); 220 return irq1->inr == irq2->inr; 221 } 222 223 /** Return true if the key is equal to the item's lookup key. */ 224 bool irq_ht_key_equal(void *key, const ht_link_t *item) 225 { 226 inr_t *inr = (inr_t *) key; 227 irq_t *irq = hash_table_get_inst(item, irq_t, link); 228 return irq->inr == *inr; 298 return irq_dispatch_and_lock_kernel(inr); 299 } 300 301 /** Compute hash index for the key. 302 * 303 * This function computes hash index into 304 * the IRQ hash table for which there 305 * can be collisions between different 306 * INRs. 307 * 308 * The devno is not used to compute the hash. 309 * 310 * @param key The first of the keys is inr and the second is devno or -1. 311 * 312 * @return Index into the hash table. 313 * 314 */ 315 size_t irq_ht_hash(sysarg_t key[]) 316 { 317 inr_t inr = (inr_t) key[KEY_INR]; 318 return inr % buckets; 319 } 320 321 /** Compare hash table element with a key. 322 * 323 * There are two things to note about this function. 324 * First, it is used for the more complex architecture setup 325 * in which there are way too many interrupt numbers (i.e. inr's) 326 * to arrange the hash table so that collisions occur only 327 * among same inrs of different devnos. So the explicit check 328 * for inr match must be done. 329 * Second, if devno is -1, the second key (i.e. devno) is not 330 * used for the match and the result of the claim() function 331 * is used instead. 332 * 333 * This function assumes interrupts are already disabled. 334 * 335 * @param key Keys (i.e. inr and devno). 336 * @param keys This is 2. 337 * @param item The item to compare the key with. 338 * 339 * @return True on match or false otherwise. 340 * 341 */ 342 bool irq_ht_compare(sysarg_t key[], size_t keys, link_t *item) 343 { 344 irq_t *irq = hash_table_get_instance(item, irq_t, link); 345 inr_t inr = (inr_t) key[KEY_INR]; 346 devno_t devno = (devno_t) key[KEY_DEVNO]; 347 348 bool rv; 349 350 irq_spinlock_lock(&irq->lock, false); 351 if (devno == -1) { 352 /* Invoked by irq_dispatch_and_lock(). */ 353 rv = ((irq->inr == inr) && 354 (irq->claim(irq) == IRQ_ACCEPT)); 355 } else { 356 /* Invoked by irq_find_and_lock(). */ 357 rv = ((irq->inr == inr) && (irq->devno == devno)); 358 } 359 360 /* unlock only on non-match */ 361 if (!rv) 362 irq_spinlock_unlock(&irq->lock, false); 363 364 return rv; 365 } 366 367 /** Unlock IRQ structure after hash_table_remove(). 368 * 369 * @param lnk Link in the removed and locked IRQ structure. 370 */ 371 void irq_ht_remove(link_t *lnk) 372 { 373 irq_t *irq __attribute__((unused)) 374 = hash_table_get_instance(lnk, irq_t, link); 375 irq_spinlock_unlock(&irq->lock, false); 376 } 377 378 /** Compute hash index for the key. 379 * 380 * This function computes hash index into 381 * the IRQ hash table for which there 382 * are no collisions between different 383 * INRs. 384 * 385 * @param key The first of the keys is inr and the second is devno or -1. 386 * 387 * @return Index into the hash table. 388 * 389 */ 390 size_t irq_lin_hash(sysarg_t key[]) 391 { 392 inr_t inr = (inr_t) key[KEY_INR]; 393 return inr; 394 } 395 396 /** Compare hash table element with a key. 397 * 398 * There are two things to note about this function. 399 * First, it is used for the less complex architecture setup 400 * in which there are not too many interrupt numbers (i.e. inr's) 401 * to arrange the hash table so that collisions occur only 402 * among same inrs of different devnos. So the explicit check 403 * for inr match is not done. 404 * Second, if devno is -1, the second key (i.e. devno) is not 405 * used for the match and the result of the claim() function 406 * is used instead. 407 * 408 * This function assumes interrupts are already disabled. 409 * 410 * @param key Keys (i.e. inr and devno). 411 * @param keys This is 2. 412 * @param item The item to compare the key with. 413 * 414 * @return True on match or false otherwise. 415 * 416 */ 417 bool irq_lin_compare(sysarg_t key[], size_t keys, link_t *item) 418 { 419 irq_t *irq = list_get_instance(item, irq_t, link); 420 devno_t devno = (devno_t) key[KEY_DEVNO]; 421 bool rv; 422 423 irq_spinlock_lock(&irq->lock, false); 424 if (devno == -1) { 425 /* Invoked by irq_dispatch_and_lock() */ 426 rv = (irq->claim(irq) == IRQ_ACCEPT); 427 } else { 428 /* Invoked by irq_find_and_lock() */ 429 rv = (irq->devno == devno); 430 } 431 432 /* unlock only on non-match */ 433 if (!rv) 434 irq_spinlock_unlock(&irq->lock, false); 435 436 return rv; 437 } 438 439 /** Unlock IRQ structure after hash_table_remove(). 440 * 441 * @param lnk Link in the removed and locked IRQ structure. 442 * 443 */ 444 void irq_lin_remove(link_t *lnk) 445 { 446 irq_t *irq __attribute__((unused)) 447 = hash_table_get_instance(lnk, irq_t, link); 448 irq_spinlock_unlock(&irq->lock, false); 229 449 } 230 450
Note:
See TracChangeset
for help on using the changeset viewer.