Ignore:
File:
1 edited

Legend:

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

    r8a637a4 r82cbf8c6  
    3737 *
    3838 * This framework allows applications to subscribe to receive a notification
    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).
     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).
    4243 *
    4344 * The structure of a notification message is as follows:
     
    5051 * - in_phone_hash: interrupt counter (may be needed to assure correct order
    5152 *                  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  *
    7153 */
    7254
    7355#include <arch.h>
     56#include <assert.h>
    7457#include <mm/slab.h>
    7558#include <mm/page.h>
     
    8366#include <print.h>
    8467#include <macros.h>
     68#include <cap/cap.h>
    8569
    8670static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
     
    117101        }
    118102       
    119         /* Rewrite the pseudocode addresses from physical to kernel virtual. */
     103        /* Rewrite the IRQ code addresses from physical to kernel virtual. */
    120104        for (size_t i = 0; i < cmdcount; i++) {
    121105                uintptr_t addr;
     
    175159}
    176160
    177 /** Statically check the top-half pseudocode
    178  *
    179  * Check the top-half pseudocode for invalid or unsafe
    180  * constructs.
     161/** Statically check the top-half IRQ code
     162 *
     163 * Check the top-half IRQ code for invalid or unsafe constructs.
    181164 *
    182165 */
     
    215198}
    216199
    217 /** Free the top-half pseudocode.
    218  *
    219  * @param code Pointer to the top-half pseudocode.
     200/** Free the top-half IRQ code.
     201 *
     202 * @param code Pointer to the top-half IRQ code.
    220203 *
    221204 */
     
    230213}
    231214
    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.
     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.
    237220 *
    238221 */
     
    288271}
    289272
     273static 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
     282static kobject_ops_t irq_kobject_ops = {
     283        .destroy = irq_destroy
     284};
     285
    290286/** Subscribe an answerbox as a receiving end for IRQ notifications.
    291287 *
    292288 * @param box     Receiving answerbox.
    293289 * @param inr     IRQ number.
    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        
     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 */
     297int ipc_irq_subscribe(answerbox_t *box, inr_t inr, sysarg_t imethod,
     298    irq_code_t *ucode)
     299{
    310300        if ((inr < 0) || (inr > last_inr))
    311301                return ELIMIT;
     
    320310       
    321311        /*
    322          * Allocate and populate the IRQ structure.
     312         * Allocate and populate the IRQ kernel object.
    323313         */
    324         irq_t *irq = malloc(sizeof(irq_t), 0);
     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        }
    325330       
    326331        irq_initialize(irq);
    327         irq->devno = devno;
    328332        irq->inr = inr;
    329333        irq->claim = ipc_irq_top_half_claim;
     
    336340       
    337341        /*
    338          * Enlist the IRQ structure in the uspace IRQ hash table and the
    339          * answerbox's list.
     342         * Insert the IRQ structure into the uspace IRQ hash table.
    340343         */
    341344        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 */
    357345        irq_spinlock_lock(&irq->lock, false);
    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);
     346       
     347        irq->notif_cfg.hashed_in = true;
     348        hash_table_insert(&irq_uspace_hash_table, &irq->link);
     349       
    364350        irq_spinlock_unlock(&irq->lock, false);
    365351        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;
     357}
     358
     359/** Unsubscribe task from IRQ notification.
     360 *
     361 * @param box     Answerbox associated with the notification.
     362 * @param handle  IRQ capability handle.
     363 *
     364 * @return EOK on success or a negative error code.
     365 *
     366 */
     367int ipc_irq_unsubscribe(answerbox_t *box, int handle)
     368{
     369        kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_IRQ);
     370        if (!kobj)
     371                return ENOENT;
     372       
     373        assert(kobj->irq->notif_cfg.answerbox == box);
     374
     375        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);
     386        irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
     387
     388        kobject_put(kobj);
     389        cap_free(TASK, handle);
    366390       
    367391        return EOK;
    368 }
    369 
    370 /** Unsubscribe task from IRQ notification.
    371  *
    372  * @param box   Answerbox associated with the notification.
    373  * @param inr   IRQ number.
    374  * @param devno Device number.
    375  *
    376  * @return EOK on success or a negative error code.
    377  *
    378  */
    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);
    393                 return ENOENT;
    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:
    442         irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
    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);
    497         irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
    498392}
    499393
     
    515409}
    516410
    517 /** Apply the top-half pseudo code to find out whether to accept the IRQ or not.
     411/** Apply the top-half IRQ code to find out whether to accept the IRQ or not.
    518412 *
    519413 * @param irq IRQ structure.
    520414 *
    521  * @return IRQ_ACCEPT if the interrupt is accepted by the
    522  *         pseudocode, IRQ_DECLINE otherwise.
     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.
    523417 *
    524418 */
     
    607501void ipc_irq_top_half_handler(irq_t *irq)
    608502{
    609         ASSERT(irq);
    610        
    611         ASSERT(interrupts_disabled());
    612         ASSERT(irq_spinlock_locked(&irq->lock));
     503        assert(irq);
     504       
     505        assert(interrupts_disabled());
     506        assert(irq_spinlock_locked(&irq->lock));
    613507       
    614508        if (irq->notif_cfg.answerbox) {
Note: See TracChangeset for help on using the changeset viewer.