Changeset cecb0789 in mainline for kernel/generic/src/ipc/irq.c
- Timestamp:
- 2009-02-21T17:27:59Z (16 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 9688513
- Parents:
- 0cb9fa0
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/irq.c
r0cb9fa0 rcecb0789 45 45 * - ARG2: payload modified by a 'top-half' handler 46 46 * - 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 47 49 * - in_phone_hash: interrupt counter (may be needed to assure correct order 48 50 * 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. 49 69 */ 50 70 … … 59 79 #include <print.h> 60 80 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. 120 82 * 121 83 * @param code Pointer to the top-half pseudocode. … … 129 91 } 130 92 131 /** Copy t op-half pseudocode from userspace into the kernel.93 /** Copy the top-half pseudocode from userspace into the kernel. 132 94 * 133 95 * @param ucode Userspace address of the top-half pseudocode. … … 165 127 } 166 128 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 199 129 /** Register an answerbox as a receiving end for IRQ notifications. 200 130 * … … 213 143 irq_code_t *code; 214 144 irq_t *irq; 145 unative_t key[] = { 146 (unative_t) inr, 147 (unative_t) devno 148 }; 215 149 216 150 if (ucode) { … … 222 156 } 223 157 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; 239 167 irq->notif_cfg.notify = true; 240 168 irq->notif_cfg.answerbox = box; … … 243 171 irq->notif_cfg.counter = 0; 244 172 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); 245 180 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); 246 191 list_append(&irq->notif_cfg.link, &box->irq_head); 247 192 spinlock_unlock(&box->irq_lock); 248 249 193 spinlock_unlock(&irq->lock); 194 spinlock_unlock(&irq_uspace_hash_table_lock); 195 250 196 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 */ 206 int 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 */ 259 void ipc_irq_cleanup(answerbox_t *box) 260 { 261 ipl_t ipl; 262 263 loop: 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); 253 307 } 254 308 … … 267 321 268 322 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 */ 332 irq_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 */ 414 void 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 } 269 439 } 270 440 … … 292 462 } 293 463 call->flags |= IPC_CALL_NOTIF; 464 /* Put a counter to the message */ 465 call->priv = ++irq->notif_cfg.counter; 466 294 467 IPC_SET_METHOD(call->data, irq->notif_cfg.method); 295 468 IPC_SET_ARG1(call->data, a1); … … 298 471 IPC_SET_ARG4(call->data, a4); 299 472 IPC_SET_ARG5(call->data, a5); 300 /* Put a counter to the message */301 call->priv = ++irq->notif_cfg.counter;302 473 303 474 send_call(irq, call); … … 306 477 } 307 478 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 contains341 * list of all irq_t structures that are registered to342 * 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 392 479 /** @} 393 480 */
Note:
See TracChangeset
for help on using the changeset viewer.