Changes in kernel/generic/src/ipc/irq.c [82cbf8c6:8a637a4] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/irq.c
r82cbf8c6 r8a637a4 37 37 * 38 38 * This framework allows applications to subscribe to receive a notification 39 * when an interrupt is detected. The application may provide a simple 40 * 'top-half' handler as part of its registration, which can perform simple 41 * operations (read/write port/memory, add information to notification IPC 42 * message). 39 * when interrupt is detected. The application may provide a simple 'top-half' 40 * handler as part of its registration, which can perform simple operations 41 * (read/write port/memory, add information to notification IPC message). 43 42 * 44 43 * The structure of a notification message is as follows: … … 51 50 * - in_phone_hash: interrupt counter (may be needed to assure correct order 52 51 * in multithreaded drivers) 52 * 53 * Note on synchronization for ipc_irq_subscribe(), ipc_irq_unsubscribe(), 54 * ipc_irq_cleanup() and IRQ handlers: 55 * 56 * By always taking all of the uspace IRQ hash table lock, IRQ structure lock 57 * and answerbox lock, we can rule out race conditions between the 58 * registration functions and also the cleanup function. Thus the observer can 59 * either see the IRQ structure present in both the hash table and the 60 * answerbox list or absent in both. Views in which the IRQ structure would be 61 * linked in the hash table but not in the answerbox list, or vice versa, are 62 * not possible. 63 * 64 * By always taking the hash table lock and the IRQ structure lock, we can 65 * rule out a scenario in which we would free up an IRQ structure, which is 66 * still referenced by, for example, an IRQ handler. The locking scheme forces 67 * us to lock the IRQ structure only after any progressing IRQs on that 68 * structure are finished. Because we hold the hash table lock, we prevent new 69 * IRQs from taking new references to the IRQ structure. 70 * 53 71 */ 54 72 55 73 #include <arch.h> 56 #include <assert.h>57 74 #include <mm/slab.h> 58 75 #include <mm/page.h> … … 66 83 #include <print.h> 67 84 #include <macros.h> 68 #include <cap/cap.h>69 85 70 86 static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount) … … 101 117 } 102 118 103 /* Rewrite the IRQcode addresses from physical to kernel virtual. */119 /* Rewrite the pseudocode addresses from physical to kernel virtual. */ 104 120 for (size_t i = 0; i < cmdcount; i++) { 105 121 uintptr_t addr; … … 159 175 } 160 176 161 /** Statically check the top-half IRQ code 162 * 163 * Check the top-half IRQ code for invalid or unsafe constructs. 177 /** Statically check the top-half pseudocode 178 * 179 * Check the top-half pseudocode for invalid or unsafe 180 * constructs. 164 181 * 165 182 */ … … 198 215 } 199 216 200 /** Free the top-half IRQcode.201 * 202 * @param code Pointer to the top-half IRQcode.217 /** Free the top-half pseudocode. 218 * 219 * @param code Pointer to the top-half pseudocode. 203 220 * 204 221 */ … … 213 230 } 214 231 215 /** Copy the top-half IRQcode from userspace into the kernel.216 * 217 * @param ucode Userspace address of the top-half IRQcode.218 * 219 * @return Kernel address of the copied IRQcode.232 /** Copy the top-half pseudocode from userspace into the kernel. 233 * 234 * @param ucode Userspace address of the top-half pseudocode. 235 * 236 * @return Kernel address of the copied pseudocode. 220 237 * 221 238 */ … … 271 288 } 272 289 273 static void irq_destroy(void *arg)274 {275 irq_t *irq = (irq_t *) arg;276 277 /* Free up the IRQ code and associated structures. */278 code_free(irq->notif_cfg.code);279 slab_free(irq_slab, irq);280 }281 282 static kobject_ops_t irq_kobject_ops = {283 .destroy = irq_destroy284 };285 286 290 /** Subscribe an answerbox as a receiving end for IRQ notifications. 287 291 * 288 292 * @param box Receiving answerbox. 289 293 * @param inr IRQ number. 290 * @param imethod Interface and method to be associated with the notification. 291 * @param ucode Uspace pointer to top-half IRQ code. 292 * 293 * @return IRQ capability handle. 294 * @return Negative error code. 295 * 296 */ 297 int ipc_irq_subscribe(answerbox_t *box, inr_t inr, sysarg_t imethod, 298 irq_code_t *ucode) 299 { 294 * @param devno Device number. 295 * @param imethod Interface and method to be associated with the 296 * notification. 297 * @param ucode Uspace pointer to top-half pseudocode. 298 * 299 * @return EOK on success or a negative error code. 300 * 301 */ 302 int ipc_irq_subscribe(answerbox_t *box, inr_t inr, devno_t devno, 303 sysarg_t imethod, irq_code_t *ucode) 304 { 305 sysarg_t key[] = { 306 (sysarg_t) inr, 307 (sysarg_t) devno 308 }; 309 300 310 if ((inr < 0) || (inr > last_inr)) 301 311 return ELIMIT; … … 310 320 311 321 /* 312 * Allocate and populate the IRQ kernel object.322 * Allocate and populate the IRQ structure. 313 323 */ 314 cap_handle_t handle = cap_alloc(TASK); 315 if (handle < 0) 316 return handle; 317 318 irq_t *irq = (irq_t *) slab_alloc(irq_slab, FRAME_ATOMIC); 319 if (!irq) { 320 cap_free(TASK, handle); 321 return ENOMEM; 322 } 323 324 kobject_t *kobject = malloc(sizeof(kobject_t), FRAME_ATOMIC); 325 if (!kobject) { 326 cap_free(TASK, handle); 327 slab_free(irq_slab, irq); 328 return ENOMEM; 329 } 324 irq_t *irq = malloc(sizeof(irq_t), 0); 330 325 331 326 irq_initialize(irq); 327 irq->devno = devno; 332 328 irq->inr = inr; 333 329 irq->claim = ipc_irq_top_half_claim; … … 340 336 341 337 /* 342 * Insert the IRQ structure into the uspace IRQ hash table. 338 * Enlist the IRQ structure in the uspace IRQ hash table and the 339 * answerbox's list. 343 340 */ 344 341 irq_spinlock_lock(&irq_uspace_hash_table_lock, true); 342 343 link_t *hlp = hash_table_find(&irq_uspace_hash_table, key); 344 if (hlp) { 345 irq_t *hirq = hash_table_get_instance(hlp, irq_t, link); 346 347 /* hirq is locked */ 348 irq_spinlock_unlock(&hirq->lock, false); 349 code_free(code); 350 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 351 352 free(irq); 353 return EEXIST; 354 } 355 356 /* Locking is not really necessary, but paranoid */ 345 357 irq_spinlock_lock(&irq->lock, false); 346 347 irq->notif_cfg.hashed_in = true; 348 hash_table_insert(&irq_uspace_hash_table, &irq->link); 349 358 irq_spinlock_lock(&box->irq_lock, false); 359 360 hash_table_insert(&irq_uspace_hash_table, key, &irq->link); 361 list_append(&irq->notif_cfg.link, &box->irq_list); 362 363 irq_spinlock_unlock(&box->irq_lock, false); 350 364 irq_spinlock_unlock(&irq->lock, false); 351 365 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 352 353 kobject_initialize(kobject, KOBJECT_TYPE_IRQ, irq, &irq_kobject_ops); 354 cap_publish(TASK, handle, kobject); 355 356 return handle; 366 367 return EOK; 357 368 } 358 369 359 370 /** Unsubscribe task from IRQ notification. 360 371 * 361 * @param box Answerbox associated with the notification. 362 * @param handle IRQ capability handle. 372 * @param box Answerbox associated with the notification. 373 * @param inr IRQ number. 374 * @param devno Device number. 363 375 * 364 376 * @return EOK on success or a negative error code. 365 377 * 366 378 */ 367 int ipc_irq_unsubscribe(answerbox_t *box, int handle) 368 { 369 kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_IRQ); 370 if (!kobj) 379 int ipc_irq_unsubscribe(answerbox_t *box, inr_t inr, devno_t devno) 380 { 381 sysarg_t key[] = { 382 (sysarg_t) inr, 383 (sysarg_t) devno 384 }; 385 386 if ((inr < 0) || (inr > last_inr)) 387 return ELIMIT; 388 389 irq_spinlock_lock(&irq_uspace_hash_table_lock, true); 390 link_t *lnk = hash_table_find(&irq_uspace_hash_table, key); 391 if (!lnk) { 392 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 371 393 return ENOENT; 372 373 assert(kobj->irq->notif_cfg.answerbox == box); 374 394 } 395 396 irq_t *irq = hash_table_get_instance(lnk, irq_t, link); 397 398 /* irq is locked */ 399 irq_spinlock_lock(&box->irq_lock, false); 400 401 ASSERT(irq->notif_cfg.answerbox == box); 402 403 /* Remove the IRQ from the answerbox's list. */ 404 list_remove(&irq->notif_cfg.link); 405 406 /* 407 * We need to drop the IRQ lock now because hash_table_remove() will try 408 * to reacquire it. That basically violates the natural locking order, 409 * but a deadlock in hash_table_remove() is prevented by the fact that 410 * we already held the IRQ lock and didn't drop the hash table lock in 411 * the meantime. 412 */ 413 irq_spinlock_unlock(&irq->lock, false); 414 415 /* Remove the IRQ from the uspace IRQ hash table. */ 416 hash_table_remove(&irq_uspace_hash_table, key, 2); 417 418 irq_spinlock_unlock(&box->irq_lock, false); 419 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 420 421 /* Free up the pseudo code and associated structures. */ 422 code_free(irq->notif_cfg.code); 423 424 /* Free up the IRQ structure. */ 425 free(irq); 426 427 return EOK; 428 } 429 430 /** Disconnect all IRQ notifications from an answerbox. 431 * 432 * This function is effective because the answerbox contains 433 * list of all irq_t structures that are subscribed to 434 * send notifications to it. 435 * 436 * @param box Answerbox for which we want to carry out the cleanup. 437 * 438 */ 439 void ipc_irq_cleanup(answerbox_t *box) 440 { 441 loop: 375 442 irq_spinlock_lock(&irq_uspace_hash_table_lock, true); 376 irq_spinlock_lock(&kobj->irq->lock, false); 377 378 if (kobj->irq->notif_cfg.hashed_in) { 379 /* Remove the IRQ from the uspace IRQ hash table. */ 380 hash_table_remove_item(&irq_uspace_hash_table, 381 &kobj->irq->link); 382 kobj->irq->notif_cfg.hashed_in = false; 383 } 384 385 irq_spinlock_unlock(&kobj->irq->lock, false); 443 irq_spinlock_lock(&box->irq_lock, false); 444 445 while (!list_empty(&box->irq_list)) { 446 DEADLOCK_PROBE_INIT(p_irqlock); 447 448 irq_t *irq = list_get_instance(list_first(&box->irq_list), irq_t, 449 notif_cfg.link); 450 451 if (!irq_spinlock_trylock(&irq->lock)) { 452 /* 453 * Avoid deadlock by trying again. 454 */ 455 irq_spinlock_unlock(&box->irq_lock, false); 456 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 457 DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD); 458 goto loop; 459 } 460 461 sysarg_t key[2]; 462 key[0] = irq->inr; 463 key[1] = irq->devno; 464 465 ASSERT(irq->notif_cfg.answerbox == box); 466 467 /* Unlist from the answerbox. */ 468 list_remove(&irq->notif_cfg.link); 469 470 /* 471 * We need to drop the IRQ lock now because hash_table_remove() 472 * will try to reacquire it. That basically violates the natural 473 * locking order, but a deadlock in hash_table_remove() is 474 * prevented by the fact that we already held the IRQ lock and 475 * didn't drop the hash table lock in the meantime. 476 */ 477 irq_spinlock_unlock(&irq->lock, false); 478 479 /* Remove from the hash table. */ 480 hash_table_remove(&irq_uspace_hash_table, key, 2); 481 482 /* 483 * Release both locks so that we can free the pseudo code. 484 */ 485 irq_spinlock_unlock(&box->irq_lock, false); 486 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 487 488 code_free(irq->notif_cfg.code); 489 free(irq); 490 491 /* Reacquire both locks before taking another round. */ 492 irq_spinlock_lock(&irq_uspace_hash_table_lock, true); 493 irq_spinlock_lock(&box->irq_lock, false); 494 } 495 496 irq_spinlock_unlock(&box->irq_lock, false); 386 497 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true); 387 388 kobject_put(kobj);389 cap_free(TASK, handle);390 391 return EOK;392 498 } 393 499 … … 409 515 } 410 516 411 /** Apply the top-half IRQcode to find out whether to accept the IRQ or not.517 /** Apply the top-half pseudo code to find out whether to accept the IRQ or not. 412 518 * 413 519 * @param irq IRQ structure. 414 520 * 415 * @return IRQ_ACCEPT if the interrupt is accepted by the IRQ code.416 * @return IRQ_DECLINE if the interrupt is not accepted byt the IRQ code.521 * @return IRQ_ACCEPT if the interrupt is accepted by the 522 * pseudocode, IRQ_DECLINE otherwise. 417 523 * 418 524 */ … … 501 607 void ipc_irq_top_half_handler(irq_t *irq) 502 608 { 503 assert(irq);504 505 assert(interrupts_disabled());506 assert(irq_spinlock_locked(&irq->lock));609 ASSERT(irq); 610 611 ASSERT(interrupts_disabled()); 612 ASSERT(irq_spinlock_locked(&irq->lock)); 507 613 508 614 if (irq->notif_cfg.answerbox) {
Note:
See TracChangeset
for help on using the changeset viewer.