Ignore:
File:
1 edited

Legend:

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

    r82cbf8c6 r9d58539  
    3232/**
    3333 * @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.
    3968 */
    4069
    4170#include <ddi/irq.h>
    42 #include <adt/hash.h>
    4371#include <adt/hash_table.h>
    4472#include <mm/slab.h>
     
    4775#include <console/console.h>
    4876#include <interrupt.h>
    49 #include <mem.h>
     77#include <memstr.h>
    5078#include <arch.h>
    5179
    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.
    5584 *
    5685 * This lock must be taken only when interrupts are disabled.
     
    6291static hash_table_t irq_kernel_hash_table;
    6392
    64 /** Spinlock protecting the uspace IRQ hash table
     93/** Spinlock protecting the uspace IRQ hash table.
    6594 *
    6695 * This lock must be taken only when interrupts are disabled.
     
    6998IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
    7099
    71 /** The uspace IRQ hash table */
     100/** The uspace IRQ hash table. */
    72101hash_table_t irq_uspace_hash_table;
    73102
    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 */
     108static size_t irq_ht_hash(sysarg_t *key);
     109static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item);
     110static void irq_ht_remove(link_t *item);
     111
     112static hash_table_operations_t irq_ht_ops = {
    80113        .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,
    84116};
    85117
    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 */
     125static size_t irq_lin_hash(sysarg_t *key);
     126static bool irq_lin_compare(sysarg_t *key, size_t keys, link_t *item);
     127static void irq_lin_remove(link_t *item);
     128
     129static 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. */
     136static size_t buckets;
     137
     138/** Last valid INR. */
    87139inr_t last_inr = 0;
    88140
    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.
     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.
    93145 *
    94146 */
    95147void irq_init(size_t inrs, size_t chains)
    96148{
     149        buckets = chains;
    97150        last_inr = inrs - 1;
    98151
    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.
    110174 *
    111175 */
     
    113177{
    114178        memsetb(irq, sizeof(irq_t), 0);
     179        link_initialize(&irq->link);
    115180        irq_spinlock_initialize(&irq->lock, "irq.lock");
     181        link_initialize(&irq->notif_cfg.link);
    116182        irq->inr = -1;
     183        irq->devno = -1;
    117184       
    118185        irq_initialize_arch(irq);
    119186}
    120187
    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.
    127197 *
    128198 */
    129199void irq_register(irq_t *irq)
    130200{
     201        sysarg_t key[] = {
     202                (sysarg_t) irq->inr,
     203                (sysarg_t) irq->devno
     204        };
     205       
    131206        irq_spinlock_lock(&irq_kernel_hash_table_lock, true);
    132207        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);
    134209        irq_spinlock_unlock(&irq->lock, false);
    135210        irq_spinlock_unlock(&irq_kernel_hash_table_lock, true);
    136211}
    137212
    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 */
     216static 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);
    155232       
    156233        return NULL;
    157234}
    158235
    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 */
     239static 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.
    171272 *
    172273 */
     
    174275{
    175276        /*
    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.
    178281         *
    179          * In the usual case the uspace handlers have precedence.
     282         * In the usual case the uspace handlers
     283         * have precedence.
    180284         */
    181285       
    182286        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);
    185288                if (irq)
    186289                        return irq;
    187290               
    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);
    194295        if (irq)
    195296                return irq;
    196297       
    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 */
     315size_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 */
     342bool 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 */
     371void 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 */
     390size_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 */
     417bool 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 */
     444void 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);
    229449}
    230450
Note: See TracChangeset for help on using the changeset viewer.