Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/ipc/irq.c

    r82cbf8c6 r8a637a4  
    3737 *
    3838 * 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).
    4342 *
    4443 * The structure of a notification message is as follows:
     
    5150 * - in_phone_hash: interrupt counter (may be needed to assure correct order
    5251 *                  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 *
    5371 */
    5472
    5573#include <arch.h>
    56 #include <assert.h>
    5774#include <mm/slab.h>
    5875#include <mm/page.h>
     
    6683#include <print.h>
    6784#include <macros.h>
    68 #include <cap/cap.h>
    6985
    7086static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
     
    101117        }
    102118       
    103         /* Rewrite the IRQ code addresses from physical to kernel virtual. */
     119        /* Rewrite the pseudocode addresses from physical to kernel virtual. */
    104120        for (size_t i = 0; i < cmdcount; i++) {
    105121                uintptr_t addr;
     
    159175}
    160176
    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.
    164181 *
    165182 */
     
    198215}
    199216
    200 /** Free the top-half IRQ code.
    201  *
    202  * @param code Pointer to the top-half IRQ code.
     217/** Free the top-half pseudocode.
     218 *
     219 * @param code Pointer to the top-half pseudocode.
    203220 *
    204221 */
     
    213230}
    214231
    215 /** Copy the top-half IRQ code from userspace into the kernel.
    216  *
    217  * @param ucode Userspace address of the top-half IRQ code.
    218  *
    219  * @return Kernel address of the copied IRQ code.
     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.
    220237 *
    221238 */
     
    271288}
    272289
    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_destroy
    284 };
    285 
    286290/** Subscribe an answerbox as a receiving end for IRQ notifications.
    287291 *
    288292 * @param box     Receiving answerbox.
    289293 * @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 */
     302int 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       
    300310        if ((inr < 0) || (inr > last_inr))
    301311                return ELIMIT;
     
    310320       
    311321        /*
    312          * Allocate and populate the IRQ kernel object.
     322         * Allocate and populate the IRQ structure.
    313323         */
    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);
    330325       
    331326        irq_initialize(irq);
     327        irq->devno = devno;
    332328        irq->inr = inr;
    333329        irq->claim = ipc_irq_top_half_claim;
     
    340336       
    341337        /*
    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.
    343340         */
    344341        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 */
    345357        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);
    350364        irq_spinlock_unlock(&irq->lock, false);
    351365        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;
    357368}
    358369
    359370/** Unsubscribe task from IRQ notification.
    360371 *
    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.
    363375 *
    364376 * @return EOK on success or a negative error code.
    365377 *
    366378 */
    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)
     379int 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);
    371393                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 */
     439void ipc_irq_cleanup(answerbox_t *box)
     440{
     441loop:
    375442        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);
    386497        irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
    387 
    388         kobject_put(kobj);
    389         cap_free(TASK, handle);
    390        
    391         return EOK;
    392498}
    393499
     
    409515}
    410516
    411 /** Apply the top-half IRQ code 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.
    412518 *
    413519 * @param irq IRQ structure.
    414520 *
    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.
    417523 *
    418524 */
     
    501607void ipc_irq_top_half_handler(irq_t *irq)
    502608{
    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));
    507613       
    508614        if (irq->notif_cfg.answerbox) {
Note: See TracChangeset for help on using the changeset viewer.