From e3890b3fce4ad7f968cc0c569c3565b3df89e8c8 Mon Sep 17 00:00:00 2001 From: Jakub Jermar Date: Sun, 15 Oct 2006 12:46:29 +0000 Subject: [PATCH] More IRQ stuff. Modify the IRQ hash table functions to support lookup based on inr and devno. Add method member to irq_t. --- kernel/arch/sparc64/src/trap/interrupt.c | 4 +- kernel/generic/include/ddi/irq.h | 5 +- kernel/generic/src/ddi/irq.c | 176 ++++++++++++++++++++++++------- 3 files changed, 144 insertions(+), 41 deletions(-) diff --git a/kernel/arch/sparc64/src/trap/interrupt.c b/kernel/arch/sparc64/src/trap/interrupt.c index f1de09c18..54615992e 100644 --- a/kernel/arch/sparc64/src/trap/interrupt.c +++ b/kernel/arch/sparc64/src/trap/interrupt.c @@ -46,6 +46,7 @@ #include #include #include +#include /* * To be removed once we get rid of the dependency in ipc_irq_bind_arch(). @@ -88,12 +89,13 @@ void interrupt(int n, istate_t *istate) intrcv = asi_u64_read(ASI_INTR_RECEIVE, 0); data0 = asi_u64_read(ASI_UDB_INTR_R, ASI_UDB_INTR_R_DATA_0); - irq_t *irq = irq_dispatch(data0); + irq_t *irq = irq_dispatch_and_lock(data0); if (irq) { /* * The IRQ handler was found. */ irq->handler(irq, irq->arg); + spinlock_unlock(&irq->lock); } else if (data0 > config.base) { /* * This is a cross-call. diff --git a/kernel/generic/include/ddi/irq.h b/kernel/generic/include/ddi/irq.h index 00ff1c0cb..ee1e51a68 100644 --- a/kernel/generic/include/ddi/irq.h +++ b/kernel/generic/include/ddi/irq.h @@ -92,6 +92,8 @@ struct irq { /** Pseudo-code to be performed by the top-half * before a notification is sent. */ irq_code_t *code; + /** Method of the notification. */ + unative_t method; /** Counter of IRQ notifications. */ atomic_t counter; }; @@ -99,7 +101,8 @@ struct irq { extern void irq_init(count_t inrs, count_t chains); extern void irq_initialize(irq_t *irq); extern void irq_register(irq_t *irq); -extern irq_t *irq_dispatch(inr_t inr); +extern irq_t *irq_dispatch_and_lock(inr_t inr); +extern irq_t *irq_find_and_lock(inr_t inr, devno_t devno); #endif diff --git a/kernel/generic/src/ddi/irq.c b/kernel/generic/src/ddi/irq.c index 69c429cd0..2797c541c 100644 --- a/kernel/generic/src/ddi/irq.c +++ b/kernel/generic/src/ddi/irq.c @@ -31,7 +31,7 @@ */ /** * @file - * @brief IRQ redirector. + * @brief IRQ dispatcher. * * This file provides means of connecting IRQs with particular * devices and logic for dispatching interrupts to IRQ handlers @@ -56,6 +56,14 @@ * IRQs, the irq_hash_table can be optimized to a one-dimensional * array. Next, when it is known that the IRQ numbers (aka INR's) * are unique, the claim functions can always return IRQ_ACCEPT. + * + * + * Note about the irq_hash_table. + * + * The hash table is configured to use two keys: inr and devno. + * However, the hash index is computed only from inr. Moreover, + * if devno is -1, the match is based on the return value of + * the claim() function instead of on devno. */ #include @@ -66,6 +74,9 @@ #include #include +#define KEY_INR 0 +#define KEY_DEVNO 1 + /** * Spinlock protecting the hash table. * This lock must be taken only when interrupts are disabled. @@ -115,9 +126,9 @@ void irq_init(count_t inrs, count_t chains) * different keys), we can use optimized set of operations. */ if (inrs == chains) - hash_table_create(&irq_hash_table, chains, 1, &irq_lin_ops); + hash_table_create(&irq_hash_table, chains, 2, &irq_lin_ops); else - hash_table_create(&irq_hash_table, chains, 1, &irq_ht_ops); + hash_table_create(&irq_hash_table, chains, 2, &irq_ht_ops); } /** Initialize one IRQ structure. @@ -137,6 +148,7 @@ void irq_initialize(irq_t *irq) irq->arg = NULL; irq->notif_answerbox = NULL; irq->code = NULL; + irq->method = 0; atomic_set(&irq->counter, 0); } @@ -151,41 +163,92 @@ void irq_initialize(irq_t *irq) void irq_register(irq_t *irq) { ipl_t ipl; + unative_t key[] = { + (unative_t) irq->inr, + (unative_t) irq->devno + }; ipl = interrupts_disable(); spinlock_lock(&irq_hash_table_lock); - hash_table_insert(&irq_hash_table, (void *) &irq->inr, &irq->link); + hash_table_insert(&irq_hash_table, key, &irq->link); spinlock_unlock(&irq_hash_table_lock); interrupts_restore(ipl); } /** Dispatch the IRQ. * + * We assume this function is only called from interrupt + * context (i.e. that interrupts are disabled prior to + * this call). + * + * This function attempts to lookup a fitting IRQ + * structure. In case of success, return with interrupts + * disabled and holding the respective structure. + * * @param inr Interrupt number (aka inr or irq). * * @return IRQ structure of the respective device or NULL. */ -irq_t *irq_dispatch(inr_t inr) +irq_t *irq_dispatch_and_lock(inr_t inr) { - ipl_t ipl; link_t *lnk; + unative_t key[] = { + (unative_t) inr, + (unative_t) -1 /* search will use claim() instead of devno */ + }; - ipl = interrupts_disable(); spinlock_lock(&irq_hash_table_lock); - lnk = hash_table_find(&irq_hash_table, (void *) &inr); + lnk = hash_table_find(&irq_hash_table, key); + if (lnk) { + irq_t *irq; + + irq = hash_table_get_instance(lnk, irq_t, link); + + spinlock_unlock(&irq_hash_table_lock); + return irq; + } + + spinlock_unlock(&irq_hash_table_lock); + + return NULL; +} + +/** Find the IRQ structure corresponding to inr and devno. + * + * This functions attempts to lookup the IRQ structure + * corresponding to its arguments. On success, this + * function returns with interrups disabled, holding + * the lock of the respective IRQ structure. + * + * This function assumes interrupts are already disabled. + * + * @param inr INR being looked up. + * @param devno Devno being looked up. + * + * @return Locked IRQ structure on success or NULL on failure. + */ +irq_t *irq_find_and_lock(inr_t inr, devno_t devno) +{ + link_t *lnk; + unative_t keys[] = { + (unative_t) inr, + (unative_t) devno + }; + + spinlock_lock(&irq_hash_table_lock); + + lnk = hash_table_find(&irq_hash_table, keys); if (lnk) { irq_t *irq; irq = hash_table_get_instance(lnk, irq_t, link); spinlock_unlock(&irq_hash_table_lock); - interrupts_restore(ipl); return irq; } spinlock_unlock(&irq_hash_table_lock); - interrupts_restore(ipl); return NULL; } @@ -197,38 +260,58 @@ irq_t *irq_dispatch(inr_t inr) * can be collisions between different * INRs. * - * @param key Pointer to INR. + * The devno is not used to compute the hash. + * + * @param key The first of the keys is inr and the second is devno or -1. * * @return Index into the hash table. */ -index_t irq_ht_hash(unative_t *key) +index_t irq_ht_hash(unative_t key[]) { - inr_t *inr = (inr_t *) key; - return *inr % irq_hash_table.entries; + inr_t inr = (inr_t) key[KEY_INR]; + return inr % irq_hash_table.entries; } /** Compare hash table element with a key. * - * As usually, we do sort of a hack here. - * Even when the key matches the inr member, - * we ask the device to either accept - * or decline to service the interrupt. + * There are two things to note about this function. + * First, it is used for the more complex architecture setup + * in which there are way too many interrupt numbers (i.e. inr's) + * to arrange the hash table so that collisions occur only + * among same inrs of different devnos. So the explicit check + * for inr match must be done. + * Second, if devno is -1, the second key (i.e. devno) is not + * used for the match and the result of the claim() function + * is used instead. * - * @param key Pointer to key (i.e. inr). - * @param keys This is 1. + * This function assumes interrupts are already disabled. + * + * @param key Keys (i.e. inr and devno). + * @param keys This is 2. * @param item The item to compare the key with. * * @return True on match or false otherwise. */ -bool irq_ht_compare(unative_t *key, count_t keys, link_t *item) +bool irq_ht_compare(unative_t key[], count_t keys, link_t *item) { irq_t *irq = hash_table_get_instance(item, irq_t, link); - inr_t *inr = (inr_t *) key; + inr_t inr = (inr_t) key[KEY_INR]; + devno_t devno = (devno_t) key[KEY_DEVNO]; + bool rv; spinlock_lock(&irq->lock); - rv = ((irq->inr == *inr) && (irq->claim() == IRQ_ACCEPT)); - spinlock_unlock(&irq->lock); + if (devno == -1) { + /* Invoked by irq_dispatch(). */ + rv = ((irq->inr == inr) && (irq->claim() == IRQ_ACCEPT)); + } else { + /* Invoked by irq_find(). */ + rv = ((irq->inr == inr) && (irq->devno == devno)); + } + + /* unlock only on non-match */ + if (!rv) + spinlock_unlock(&irq->lock); return rv; } @@ -240,39 +323,54 @@ bool irq_ht_compare(unative_t *key, count_t keys, link_t *item) * are no collisions between different * INRs. * - * @param key INR. + * @param key The first of the keys is inr and the second is devno or -1. * * @return Index into the hash table. */ -index_t irq_lin_hash(unative_t *key) +index_t irq_lin_hash(unative_t key[]) { - inr_t *inr = (inr_t *) key; - return *inr; + inr_t inr = (inr_t) key[KEY_INR]; + return inr; } /** Compare hash table element with a key. * - * As usually, we do sort of a hack here. - * We don't compare the inr member with - * the key because we know that there are - * no collision between different keys. - * We only ask the device to either accept - * or decline to service the interrupt. + * There are two things to note about this function. + * First, it is used for the less complex architecture setup + * in which there are not too many interrupt numbers (i.e. inr's) + * to arrange the hash table so that collisions occur only + * among same inrs of different devnos. So the explicit check + * for inr match is not done. + * Second, if devno is -1, the second key (i.e. devno) is not + * used for the match and the result of the claim() function + * is used instead. + * + * This function assumes interrupts are already disabled. * - * @param key Pointer to key (i.e. inr). - * @param keys This is 1. + * @param key Keys (i.e. inr and devno). + * @param keys This is 2. * @param item The item to compare the key with. * * @return True on match or false otherwise. */ -bool irq_lin_compare(unative_t *key, count_t keys, link_t *item) +bool irq_lin_compare(unative_t key[], count_t keys, link_t *item) { irq_t *irq = list_get_instance(item, irq_t, link); + devno_t devno = (devno_t) key[KEY_DEVNO]; bool rv; spinlock_lock(&irq->lock); - rv = (irq->claim() == IRQ_ACCEPT); - spinlock_unlock(&irq->lock); + if (devno == -1) { + /* Invoked by irq_dispatch() */ + rv = (irq->claim() == IRQ_ACCEPT); + } else { + /* Invoked by irq_find() */ + rv = (irq->devno == devno); + } + + /* unlock only on non-match */ + if (!rv) + spinlock_unlock(&irq->lock); return rv; } -- 2.11.4.GIT