Changeset cecb0789 in mainline for kernel/generic/src/ipc/irq.c


Ignore:
Timestamp:
2009-02-21T17:27:59Z (16 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
9688513
Parents:
0cb9fa0
Message:

This is the evil commit. In particular, it does:

  • introduces more powerful pseudo code for userspace IRQ top-half handlers
  • changes the internals of IRQ dispatching
  • simplifies the kernel's i8042 driver
  • adapts the uspace i8042 driver to make use of the new pseudocode
  • breaks all other architectures except ia32
  • breaks almost all existing drivers
  • breaks switching between uspace and kernel drivers
File:
1 edited

Legend:

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

    r0cb9fa0 rcecb0789  
    4545 * - ARG2: payload modified by a 'top-half' handler
    4646 * - ARG3: payload modified by a 'top-half' handler
     47 * - ARG4: payload modified by a 'top-half' handler
     48 * - ARG5: payload modified by a 'top-half' handler
    4749 * - in_phone_hash: interrupt counter (may be needed to assure correct order
    4850 *         in multithreaded drivers)
     51 *
     52 * Note on synchronization for ipc_irq_register(), ipc_irq_unregister(),
     53 * ipc_irq_cleanup() and IRQ handlers:
     54 *
     55 *   By always taking all of the uspace IRQ hash table lock, IRQ structure lock
     56 *   and answerbox lock, we can rule out race conditions between the
     57 *   registration functions and also the cleanup function. Thus the observer can
     58 *   either see the IRQ structure present in both the hash table and the
     59 *   answerbox list or absent in both. Views in which the IRQ structure would be
     60 *   linked in the hash table but not in the answerbox list, or vice versa, are
     61 *   not possible.
     62 *
     63 *   By always taking the hash table lock and the IRQ structure lock, we can
     64 *   rule out a scenario in which we would free up an IRQ structure, which is
     65 *   still referenced by, for example, an IRQ handler. The locking scheme forces
     66 *   us to lock the IRQ structure only after any progressing IRQs on that
     67 *   structure are finished. Because we hold the hash table lock, we prevent new
     68 *   IRQs from taking new references to the IRQ structure.
    4969 */
    5070
     
    5979#include <print.h>
    6080
    61 /** Execute code associated with IRQ notification.
    62  *
    63  * @param call          Notification call.
    64  * @param code          Top-half pseudocode.
    65  */
    66 static void code_execute(call_t *call, irq_code_t *code)
    67 {
    68         unsigned int i;
    69         unative_t dstval = 0;
    70        
    71         if (!code)
    72                 return;
    73        
    74         for (i = 0; i < code->cmdcount; i++) {
    75                 switch (code->cmds[i].cmd) {
    76                 case CMD_MEM_READ_1:
    77                         dstval = *((uint8_t *) code->cmds[i].addr);
    78                         break;
    79                 case CMD_MEM_READ_2:
    80                         dstval = *((uint16_t *) code->cmds[i].addr);
    81                         break;
    82                 case CMD_MEM_READ_4:
    83                         dstval = *((uint32_t *) code->cmds[i].addr);
    84                         break;
    85                 case CMD_MEM_READ_8:
    86                         dstval = *((uint64_t *) code->cmds[i].addr);
    87                         break;
    88                 case CMD_MEM_WRITE_1:
    89                         *((uint8_t *) code->cmds[i].addr) = code->cmds[i].value;
    90                         break;
    91                 case CMD_MEM_WRITE_2:
    92                         *((uint16_t *) code->cmds[i].addr) =
    93                             code->cmds[i].value;
    94                         break;
    95                 case CMD_MEM_WRITE_4:
    96                         *((uint32_t *) code->cmds[i].addr) =
    97                             code->cmds[i].value;
    98                         break;
    99                 case CMD_MEM_WRITE_8:
    100                         *((uint64_t *) code->cmds[i].addr) =
    101                             code->cmds[i].value;
    102                         break;
    103                 case CMD_PORT_READ_1:
    104                         dstval = pio_read_8((ioport8_t *) code->cmds[i].addr);
    105                         break;
    106                 case CMD_PORT_WRITE_1:
    107                         pio_write_8((ioport8_t *) code->cmds[i].addr, code->cmds[i].value);
    108                         break;
    109                 default:
    110                         break;
    111                 }
    112                 if (code->cmds[i].dstarg && code->cmds[i].dstarg <
    113                     IPC_CALL_LEN) {
    114                         call->data.args[code->cmds[i].dstarg] = dstval;
    115                 }
    116         }
    117 }
    118 
    119 /** Free top-half pseudocode.
     81/** Free the top-half pseudocode.
    12082 *
    12183 * @param code          Pointer to the top-half pseudocode.
     
    12991}
    13092
    131 /** Copy top-half pseudocode from userspace into the kernel.
     93/** Copy the top-half pseudocode from userspace into the kernel.
    13294 *
    13395 * @param ucode         Userspace address of the top-half pseudocode.
     
    165127}
    166128
    167 /** Unregister task from IRQ notification.
    168  *
    169  * @param box           Answerbox associated with the notification.
    170  * @param inr           IRQ number.
    171  * @param devno         Device number.
    172  */
    173 void ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
    174 {
    175         ipl_t ipl;
    176         irq_t *irq;
    177 
    178         ipl = interrupts_disable();
    179         irq = irq_find_and_lock(inr, devno);
    180         if (irq) {
    181                 if (irq->notif_cfg.answerbox == box) {
    182                         code_free(irq->notif_cfg.code);
    183                         irq->notif_cfg.notify = false;
    184                         irq->notif_cfg.answerbox = NULL;
    185                         irq->notif_cfg.code = NULL;
    186                         irq->notif_cfg.method = 0;
    187                         irq->notif_cfg.counter = 0;
    188 
    189                         spinlock_lock(&box->irq_lock);
    190                         list_remove(&irq->notif_cfg.link);
    191                         spinlock_unlock(&box->irq_lock);
    192                        
    193                         spinlock_unlock(&irq->lock);
    194                 }
    195         }
    196         interrupts_restore(ipl);
    197 }
    198 
    199129/** Register an answerbox as a receiving end for IRQ notifications.
    200130 *
     
    213143        irq_code_t *code;
    214144        irq_t *irq;
     145        unative_t key[] = {
     146                (unative_t) inr,
     147                (unative_t) devno
     148        };
    215149
    216150        if (ucode) {
     
    222156        }
    223157
    224         ipl = interrupts_disable();
    225         irq = irq_find_and_lock(inr, devno);
    226         if (!irq) {
    227                 interrupts_restore(ipl);
    228                 code_free(code);
    229                 return ENOENT;
    230         }
    231        
    232         if (irq->notif_cfg.answerbox) {
    233                 spinlock_unlock(&irq->lock);
    234                 interrupts_restore(ipl);
    235                 code_free(code);
    236                 return EEXISTS;
    237         }
    238        
     158        /*
     159         * Allocate and populate the IRQ structure.
     160         */
     161        irq = malloc(sizeof(irq_t), 0);
     162        irq_initialize(irq);
     163        irq->devno = devno;
     164        irq->inr = inr;
     165        irq->claim = ipc_irq_top_half_claim;
     166        irq->handler = ipc_irq_top_half_handler;       
    239167        irq->notif_cfg.notify = true;
    240168        irq->notif_cfg.answerbox = box;
     
    243171        irq->notif_cfg.counter = 0;
    244172
     173        /*
     174         * Enlist the IRQ structure in the uspace IRQ hash table and the
     175         * answerbox's list.
     176         */
     177        ipl = interrupts_disable();
     178        spinlock_lock(&irq_uspace_hash_table_lock);
     179        spinlock_lock(&irq->lock);
    245180        spinlock_lock(&box->irq_lock);
     181        if (hash_table_find(&irq_uspace_hash_table, key)) {
     182                code_free(code);
     183                spinlock_unlock(&box->irq_lock);
     184                spinlock_unlock(&irq->lock);
     185                spinlock_unlock(&irq_uspace_hash_table_lock);
     186                free(irq);
     187                interrupts_restore(ipl);
     188                return EEXISTS;
     189        }
     190        hash_table_insert(&irq_uspace_hash_table, key, &irq->link);
    246191        list_append(&irq->notif_cfg.link, &box->irq_head);
    247192        spinlock_unlock(&box->irq_lock);
    248 
    249193        spinlock_unlock(&irq->lock);
     194        spinlock_unlock(&irq_uspace_hash_table_lock);
     195
    250196        interrupts_restore(ipl);
    251 
    252         return 0;
     197        return EOK;
     198}
     199
     200/** Unregister task from IRQ notification.
     201 *
     202 * @param box           Answerbox associated with the notification.
     203 * @param inr           IRQ number.
     204 * @param devno         Device number.
     205 */
     206int ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
     207{
     208        ipl_t ipl;
     209        unative_t key[] = {
     210                (unative_t) inr,
     211                (unative_t) devno
     212        };
     213        link_t *lnk;
     214        irq_t *irq;
     215
     216        ipl = interrupts_disable();
     217        spinlock_lock(&irq_uspace_hash_table_lock);
     218        lnk = hash_table_find(&irq_uspace_hash_table, key);
     219        if (!lnk) {
     220                spinlock_unlock(&irq_uspace_hash_table_lock);
     221                interrupts_restore(ipl);
     222                return ENOENT;
     223        }
     224        irq = hash_table_get_instance(lnk, irq_t, link);
     225        spinlock_lock(&irq->lock);
     226        spinlock_lock(&box->irq_lock);
     227       
     228        ASSERT(irq->notif_cfg.answerbox == box);
     229       
     230        /* Free up the pseudo code and associated structures. */
     231        code_free(irq->notif_cfg.code);
     232
     233        /* Remove the IRQ from the answerbox's list. */
     234        list_remove(&irq->notif_cfg.link);
     235
     236        /* Remove the IRQ from the uspace IRQ hash table. */
     237        hash_table_remove(&irq_uspace_hash_table, key, 2);
     238       
     239        spinlock_unlock(&irq_uspace_hash_table_lock);
     240        spinlock_unlock(&irq->lock);
     241        spinlock_unlock(&box->irq_lock);
     242       
     243        /* Free up the IRQ structure. */
     244        free(irq);
     245       
     246        interrupts_restore(ipl);
     247        return EOK;
     248}
     249
     250
     251/** Disconnect all IRQ notifications from an answerbox.
     252 *
     253 * This function is effective because the answerbox contains
     254 * list of all irq_t structures that are registered to
     255 * send notifications to it.
     256 *
     257 * @param box           Answerbox for which we want to carry out the cleanup.
     258 */
     259void ipc_irq_cleanup(answerbox_t *box)
     260{
     261        ipl_t ipl;
     262       
     263loop:
     264        ipl = interrupts_disable();
     265        spinlock_lock(&irq_uspace_hash_table_lock);
     266        spinlock_lock(&box->irq_lock);
     267       
     268        while (box->irq_head.next != &box->irq_head) {
     269                link_t *cur = box->irq_head.next;
     270                irq_t *irq;
     271                DEADLOCK_PROBE_INIT(p_irqlock);
     272                unative_t key[2];
     273               
     274                irq = list_get_instance(cur, irq_t, notif_cfg.link);
     275                if (!spinlock_trylock(&irq->lock)) {
     276                        /*
     277                         * Avoid deadlock by trying again.
     278                         */
     279                        spinlock_unlock(&box->irq_lock);
     280                        spinlock_unlock(&irq_uspace_hash_table_lock);
     281                        interrupts_restore(ipl);
     282                        DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
     283                        goto loop;
     284                }
     285                key[0] = irq->inr;
     286                key[1] = irq->devno;
     287               
     288               
     289                ASSERT(irq->notif_cfg.answerbox == box);
     290               
     291                /* Unlist from the answerbox. */
     292                list_remove(&irq->notif_cfg.link);
     293               
     294                /* Remove from the hash table. */
     295                hash_table_remove(&irq_uspace_hash_table, key, 2);
     296               
     297                /* Free up the pseudo code and associated structures. */
     298                code_free(irq->notif_cfg.code);
     299               
     300                spinlock_unlock(&irq->lock);
     301                free(irq);
     302        }
     303       
     304        spinlock_unlock(&box->irq_lock);
     305        spinlock_unlock(&irq_uspace_hash_table_lock);
     306        interrupts_restore(ipl);
    253307}
    254308
     
    267321               
    268322        waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
     323}
     324
     325/** Apply the top-half pseudo code to find out whether to accept the IRQ or not.
     326 *
     327 * @param irq           IRQ structure.
     328 *
     329 * @return              IRQ_ACCEPT if the interrupt is accepted by the
     330 *                      pseudocode. IRQ_DECLINE otherwise.
     331 */
     332irq_ownership_t ipc_irq_top_half_claim(irq_t *irq)
     333{
     334        unsigned int i;
     335        unative_t dstval;
     336        irq_code_t *code = irq->notif_cfg.code;
     337        unative_t *scratch = irq->notif_cfg.scratch;
     338
     339       
     340        if (!irq->notif_cfg.notify)
     341                return IRQ_DECLINE;
     342       
     343        if (!code)
     344                return IRQ_DECLINE;
     345       
     346        for (i = 0; i < code->cmdcount; i++) {
     347                unsigned int srcarg = code->cmds[i].srcarg;
     348                unsigned int dstarg = code->cmds[i].dstarg;
     349               
     350                if (srcarg >= IPC_CALL_LEN)
     351                        break;
     352                if (dstarg >= IPC_CALL_LEN)
     353                        break;
     354       
     355                switch (code->cmds[i].cmd) {
     356                case CMD_PIO_READ_8:
     357                        dstval = pio_read_8((ioport8_t *) code->cmds[i].addr);
     358                        if (dstarg)
     359                                scratch[dstarg] = dstval;
     360                        break;
     361                case CMD_PIO_READ_16:
     362                        dstval = pio_read_16((ioport16_t *) code->cmds[i].addr);
     363                        if (dstarg)
     364                                scratch[dstarg] = dstval;
     365                        break;
     366                case CMD_PIO_READ_32:
     367                        dstval = pio_read_32((ioport32_t *) code->cmds[i].addr);
     368                        if (dstarg)
     369                                scratch[dstarg] = dstval;
     370                        break;
     371                case CMD_PIO_WRITE_8:
     372                        pio_write_8((ioport8_t *) code->cmds[i].addr,
     373                            (uint8_t) code->cmds[i].value);
     374                        break;
     375                case CMD_PIO_WRITE_16:
     376                        pio_write_16((ioport16_t *) code->cmds[i].addr,
     377                            (uint16_t) code->cmds[i].value);
     378                        break;
     379                case CMD_PIO_WRITE_32:
     380                        pio_write_32((ioport32_t *) code->cmds[i].addr,
     381                            (uint32_t) code->cmds[i].value);
     382                        break;
     383                case CMD_BTEST:
     384                        if (srcarg && dstarg) {
     385                                dstval = scratch[srcarg] & code->cmds[i].value;
     386                                scratch[dstarg] = dstval;
     387                        }
     388                        break;
     389                case CMD_PREDICATE:
     390                        if (srcarg && !scratch[srcarg]) {
     391                                i += code->cmds[i].value;
     392                                continue;
     393                        }
     394                        break;
     395                case CMD_ACCEPT:
     396                        return IRQ_ACCEPT;
     397                        break;
     398                case CMD_DECLINE:
     399                default:
     400                        return IRQ_DECLINE;
     401                }
     402        }
     403       
     404        return IRQ_DECLINE;
     405}
     406
     407
     408/* IRQ top-half handler.
     409 *
     410 * We expect interrupts to be disabled and the irq->lock already held.
     411 *
     412 * @param irq           IRQ structure.
     413 */
     414void ipc_irq_top_half_handler(irq_t *irq)
     415{
     416        ASSERT(irq);
     417
     418        if (irq->notif_cfg.answerbox) {
     419                call_t *call;
     420
     421                call = ipc_call_alloc(FRAME_ATOMIC);
     422                if (!call)
     423                        return;
     424               
     425                call->flags |= IPC_CALL_NOTIF;
     426                /* Put a counter to the message */
     427                call->priv = ++irq->notif_cfg.counter;
     428
     429                /* Set up args */
     430                IPC_SET_METHOD(call->data, irq->notif_cfg.method);
     431                IPC_SET_ARG1(call->data, irq->notif_cfg.scratch[1]);
     432                IPC_SET_ARG2(call->data, irq->notif_cfg.scratch[2]);
     433                IPC_SET_ARG3(call->data, irq->notif_cfg.scratch[3]);
     434                IPC_SET_ARG4(call->data, irq->notif_cfg.scratch[4]);
     435                IPC_SET_ARG5(call->data, irq->notif_cfg.scratch[5]);
     436
     437                send_call(irq, call);
     438        }
    269439}
    270440
     
    292462                }
    293463                call->flags |= IPC_CALL_NOTIF;
     464                /* Put a counter to the message */
     465                call->priv = ++irq->notif_cfg.counter;
     466
    294467                IPC_SET_METHOD(call->data, irq->notif_cfg.method);
    295468                IPC_SET_ARG1(call->data, a1);
     
    298471                IPC_SET_ARG4(call->data, a4);
    299472                IPC_SET_ARG5(call->data, a5);
    300                 /* Put a counter to the message */
    301                 call->priv = ++irq->notif_cfg.counter;
    302473               
    303474                send_call(irq, call);
     
    306477}
    307478
    308 /** Notify a task that an IRQ had occurred.
    309  *
    310  * We expect interrupts to be disabled and the irq->lock already held.
    311  *
    312  * @param irq           IRQ structure.
    313  */
    314 void ipc_irq_send_notif(irq_t *irq)
    315 {
    316         call_t *call;
    317 
    318         ASSERT(irq);
    319 
    320         if (irq->notif_cfg.answerbox) {
    321                 call = ipc_call_alloc(FRAME_ATOMIC);
    322                 if (!call) {
    323                         return;
    324                 }
    325                 call->flags |= IPC_CALL_NOTIF;
    326                 /* Put a counter to the message */
    327                 call->priv = ++irq->notif_cfg.counter;
    328                 /* Set up args */
    329                 IPC_SET_METHOD(call->data, irq->notif_cfg.method);
    330 
    331                 /* Execute code to handle irq */
    332                 code_execute(call, irq->notif_cfg.code);
    333                
    334                 send_call(irq, call);
    335         }
    336 }
    337 
    338 /** Disconnect all IRQ notifications from an answerbox.
    339  *
    340  * This function is effective because the answerbox contains
    341  * list of all irq_t structures that are registered to
    342  * send notifications to it.
    343  *
    344  * @param box           Answerbox for which we want to carry out the cleanup.
    345  */
    346 void ipc_irq_cleanup(answerbox_t *box)
    347 {
    348         ipl_t ipl;
    349        
    350 loop:
    351         ipl = interrupts_disable();
    352         spinlock_lock(&box->irq_lock);
    353        
    354         while (box->irq_head.next != &box->irq_head) {
    355                 link_t *cur = box->irq_head.next;
    356                 irq_t *irq;
    357                 DEADLOCK_PROBE_INIT(p_irqlock);
    358                
    359                 irq = list_get_instance(cur, irq_t, notif_cfg.link);
    360                 if (!spinlock_trylock(&irq->lock)) {
    361                         /*
    362                          * Avoid deadlock by trying again.
    363                          */
    364                         spinlock_unlock(&box->irq_lock);
    365                         interrupts_restore(ipl);
    366                         DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
    367                         goto loop;
    368                 }
    369                
    370                 ASSERT(irq->notif_cfg.answerbox == box);
    371                
    372                 list_remove(&irq->notif_cfg.link);
    373                
    374                 /*
    375                  * Don't forget to free any top-half pseudocode.
    376                  */
    377                 code_free(irq->notif_cfg.code);
    378                
    379                 irq->notif_cfg.notify = false;
    380                 irq->notif_cfg.answerbox = NULL;
    381                 irq->notif_cfg.code = NULL;
    382                 irq->notif_cfg.method = 0;
    383                 irq->notif_cfg.counter = 0;
    384 
    385                 spinlock_unlock(&irq->lock);
    386         }
    387        
    388         spinlock_unlock(&box->irq_lock);
    389         interrupts_restore(ipl);
    390 }
    391 
    392479/** @}
    393480 */
Note: See TracChangeset for help on using the changeset viewer.