Ignore:
File:
1 edited

Legend:

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

    r9d58539 r82cbf8c6  
    3232/**
    3333 * @file
    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.
     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.
    6839 */
    6940
    7041#include <ddi/irq.h>
     42#include <adt/hash.h>
    7143#include <adt/hash_table.h>
    7244#include <mm/slab.h>
     
    7547#include <console/console.h>
    7648#include <interrupt.h>
    77 #include <memstr.h>
     49#include <mem.h>
    7850#include <arch.h>
    7951
    80 #define KEY_INR    0
    81 #define KEY_DEVNO  1
    82 
    83 /** Spinlock protecting the kernel IRQ hash table.
     52slab_cache_t *irq_slab = NULL;
     53
     54/** Spinlock protecting the kernel IRQ hash table
    8455 *
    8556 * This lock must be taken only when interrupts are disabled.
     
    9162static hash_table_t irq_kernel_hash_table;
    9263
    93 /** Spinlock protecting the uspace IRQ hash table.
     64/** Spinlock protecting the uspace IRQ hash table
    9465 *
    9566 * This lock must be taken only when interrupts are disabled.
     
    9869IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
    9970
    100 /** The uspace IRQ hash table. */
     71/** The uspace IRQ hash table */
    10172hash_table_t irq_uspace_hash_table;
    10273
    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 = {
     74static size_t irq_ht_hash(const ht_link_t *);
     75static size_t irq_ht_key_hash(void *);
     76static bool irq_ht_equal(const ht_link_t *, const ht_link_t *);
     77static bool irq_ht_key_equal(void *, const ht_link_t *);
     78
     79static hash_table_ops_t irq_ht_ops = {
    11380        .hash = irq_ht_hash,
    114         .compare = irq_ht_compare,
    115         .remove_callback = irq_ht_remove,
     81        .key_hash = irq_ht_key_hash,
     82        .equal = irq_ht_equal,
     83        .key_equal = irq_ht_key_equal
    11684};
    11785
    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. */
     86/** Last valid INR */
    13987inr_t last_inr = 0;
    14088
    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.
     89/** Initialize IRQ subsystem
     90 *
     91 * @param inrs    Numbers of unique IRQ numbers or INRs.
     92 * @param chains  Number of buckets in the hash table.
    14593 *
    14694 */
    14795void irq_init(size_t inrs, size_t chains)
    14896{
    149         buckets = chains;
    15097        last_inr = inrs - 1;
    15198
    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.
     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.
    174110 *
    175111 */
     
    177113{
    178114        memsetb(irq, sizeof(irq_t), 0);
    179         link_initialize(&irq->link);
    180115        irq_spinlock_initialize(&irq->lock, "irq.lock");
    181         link_initialize(&irq->notif_cfg.link);
    182116        irq->inr = -1;
    183         irq->devno = -1;
    184117       
    185118        irq_initialize_arch(irq);
    186119}
    187120
    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.
     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.
    197127 *
    198128 */
    199129void irq_register(irq_t *irq)
    200130{
    201         sysarg_t key[] = {
    202                 (sysarg_t) irq->inr,
    203                 (sysarg_t) irq->devno
    204         };
    205        
    206131        irq_spinlock_lock(&irq_kernel_hash_table_lock, true);
    207132        irq_spinlock_lock(&irq->lock, false);
    208         hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
     133        hash_table_insert(&irq_kernel_hash_table, &irq->link);
    209134        irq_spinlock_unlock(&irq->lock, false);
    210135        irq_spinlock_unlock(&irq_kernel_hash_table_lock, true);
    211136}
    212137
    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;
     138/** Search and lock an IRQ hash table */
     139static irq_t *
     140irq_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);
    230153        }
    231         irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
     154        irq_spinlock_unlock(l, false);
    232155       
    233156        return NULL;
    234157}
    235158
    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.
     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
    272171 *
    273172 */
     
    275174{
    276175        /*
    277          * If the kernel console override is on,
    278          * then try first the kernel handlers
    279          * and eventually fall back to uspace
    280          * handlers.
     176         * If the kernel console override is on, then try first the kernel
     177         * handlers and eventually fall back to uspace handlers.
    281178         *
    282          * In the usual case the uspace handlers
    283          * have precedence.
     179         * In the usual case the uspace handlers have precedence.
    284180         */
    285181       
    286182        if (console_override) {
    287                 irq_t *irq = irq_dispatch_and_lock_kernel(inr);
     183                irq_t *irq = irq_dispatch_and_lock_table(&irq_kernel_hash_table,
     184                    &irq_kernel_hash_table_lock, inr);
    288185                if (irq)
    289186                        return irq;
    290187               
    291                 return irq_dispatch_and_lock_uspace(inr);
     188                return irq_dispatch_and_lock_table(&irq_uspace_hash_table,
     189                    &irq_uspace_hash_table_lock, inr);
    292190        }
    293191       
    294         irq_t *irq = irq_dispatch_and_lock_uspace(inr);
     192        irq_t *irq = irq_dispatch_and_lock_table(&irq_uspace_hash_table,
     193            &irq_uspace_hash_table_lock, inr);
    295194        if (irq)
    296195                return irq;
    297196       
    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);
     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. */
     202size_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. */
     209size_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. */
     216bool 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. */
     224bool 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;
    449229}
    450230
Note: See TracChangeset for help on using the changeset viewer.