Changes in kernel/generic/src/ipc/ipc.c [cd529c4:c33f39f] in mainline
- File:
-
- 1 edited
-
kernel/generic/src/ipc/ipc.c (modified) (20 diffs)
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/ipc.c
rcd529c4 rc33f39f 45 45 #include <ipc/kbox.h> 46 46 #include <ipc/event.h> 47 #include <ipc/sysipc_ops.h> 48 #include <ipc/sysipc_priv.h> 47 49 #include <errno.h> 48 50 #include <mm/slab.h> … … 71 73 { 72 74 memsetb(call, sizeof(*call), 0); 75 spinlock_initialize(&call->forget_lock, "forget_lock"); 76 call->active = false; 77 call->forget = false; 73 78 call->sender = TASK; 74 79 call->buffer = NULL; … … 132 137 * @param phone Initialized phone structure. 133 138 * @param box Initialized answerbox structure. 134 * 135 */ 136 void ipc_phone_connect(phone_t *phone, answerbox_t *box) 137 { 139 * @return True if the phone was connected, false otherwise. 140 */ 141 bool ipc_phone_connect(phone_t *phone, answerbox_t *box) 142 { 143 bool active; 144 138 145 mutex_lock(&phone->lock); 139 140 phone->state = IPC_PHONE_CONNECTED;141 phone->callee = box;142 143 146 irq_spinlock_lock(&box->lock, true); 144 list_append(&phone->link, &box->connected_phones); 147 148 active = box->active; 149 if (active) { 150 phone->state = IPC_PHONE_CONNECTED; 151 phone->callee = box; 152 list_append(&phone->link, &box->connected_phones); 153 } 154 145 155 irq_spinlock_unlock(&box->lock, true); 146 147 156 mutex_unlock(&phone->lock); 157 158 return active; 148 159 } 149 160 … … 167 178 * 168 179 */ 169 static void _ipc_answer_free_call(call_t *call, bool selflocked) 170 { 171 answerbox_t *callerbox = &call->sender->answerbox; 172 bool do_lock = ((!selflocked) || callerbox != (&TASK->answerbox)); 173 180 void _ipc_answer_free_call(call_t *call, bool selflocked) 181 { 174 182 /* Count sent answer */ 175 183 irq_spinlock_lock(&TASK->lock, true); 176 184 TASK->ipc_info.answer_sent++; 177 185 irq_spinlock_unlock(&TASK->lock, true); 186 187 spinlock_lock(&call->forget_lock); 188 if (call->forget) { 189 /* This is a forgotten call and call->sender is not valid. */ 190 spinlock_unlock(&call->forget_lock); 191 ipc_call_free(call); 192 return; 193 } else { 194 /* 195 * If the call is still active, i.e. it was answered 196 * in a non-standard way, remove the call from the 197 * sender's active call list. 198 */ 199 if (call->active) { 200 spinlock_lock(&call->sender->active_calls_lock); 201 list_remove(&call->ta_link); 202 spinlock_unlock(&call->sender->active_calls_lock); 203 } 204 } 205 spinlock_unlock(&call->forget_lock); 206 207 answerbox_t *callerbox = &call->sender->answerbox; 208 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox)); 178 209 179 210 call->flags |= IPC_CALL_ANSWERED; 180 211 181 if (call->flags & IPC_CALL_FORWARDED) {182 if (call->caller_phone) {183 /* Demasquerade the caller phone. */184 call->data.phone = call->caller_phone;185 }186 }187 188 212 call->data.task_id = TASK->taskid; 189 213 … … 191 215 irq_spinlock_lock(&callerbox->lock, true); 192 216 193 list_append(&call-> link, &callerbox->answers);217 list_append(&call->ab_link, &callerbox->answers); 194 218 195 219 if (do_lock) … … 209 233 /* Remove from active box */ 210 234 irq_spinlock_lock(&box->lock, true); 211 list_remove(&call-> link);235 list_remove(&call->ab_link); 212 236 irq_spinlock_unlock(&box->lock, true); 213 237 … … 228 252 void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err) 229 253 { 254 call->caller_phone = phone; 230 255 call->data.phone = phone; 231 256 atomic_inc(&phone->active_calls); 257 258 spinlock_lock(&TASK->active_calls_lock); 259 list_append(&call->ta_link, &TASK->active_calls); 260 spinlock_unlock(&TASK->active_calls_lock); 261 232 262 IPC_SET_RETVAL(call->data, err); 233 263 _ipc_answer_free_call(call, false); … … 250 280 if (!(call->flags & IPC_CALL_FORWARDED)) { 251 281 atomic_inc(&phone->active_calls); 282 283 call->caller_phone = phone; 284 call->active = true; 285 286 spinlock_lock(&TASK->active_calls_lock); 287 list_append(&call->ta_link, &TASK->active_calls); 288 spinlock_unlock(&TASK->active_calls_lock); 289 252 290 call->data.phone = phone; 253 291 call->data.task_id = TASK->taskid; … … 255 293 256 294 irq_spinlock_lock(&box->lock, true); 257 list_append(&call-> link, &box->calls);295 list_append(&call->ab_link, &box->calls); 258 296 irq_spinlock_unlock(&box->lock, true); 259 297 … … 275 313 if (phone->state != IPC_PHONE_CONNECTED) { 276 314 mutex_unlock(&phone->lock); 277 if (call->flags & IPC_CALL_FORWARDED) { 278 IPC_SET_RETVAL(call->data, EFORWARD); 279 _ipc_answer_free_call(call, false); 280 } else { 315 if (!(call->flags & IPC_CALL_FORWARDED)) { 281 316 if (phone->state == IPC_PHONE_HUNGUP) 282 317 ipc_backsend_err(phone, call, EHANGUP); … … 356 391 TASK->ipc_info.forwarded++; 357 392 irq_spinlock_pass(&TASK->lock, &oldbox->lock); 358 list_remove(&call-> link);393 list_remove(&call->ab_link); 359 394 irq_spinlock_unlock(&oldbox->lock, true); 360 395 361 396 if (mode & IPC_FF_ROUTE_FROM_ME) { 362 if (!call->caller_phone)363 call->caller_phone = call->data.phone;364 397 call->data.phone = newphone; 365 398 call->data.task_id = TASK->taskid; … … 406 439 407 440 request = list_get_instance(list_first(&box->irq_notifs), 408 call_t, link);409 list_remove(&request-> link);441 call_t, ab_link); 442 list_remove(&request->ab_link); 410 443 411 444 irq_spinlock_unlock(&box->irq_lock, false); … … 416 449 /* Handle asynchronous answers */ 417 450 request = list_get_instance(list_first(&box->answers), 418 call_t, link);419 list_remove(&request-> link);420 atomic_dec(&request-> data.phone->active_calls);451 call_t, ab_link); 452 list_remove(&request->ab_link); 453 atomic_dec(&request->caller_phone->active_calls); 421 454 } else if (!list_empty(&box->calls)) { 422 455 /* Count received call */ … … 425 458 /* Handle requests */ 426 459 request = list_get_instance(list_first(&box->calls), 427 call_t, link);428 list_remove(&request-> link);460 call_t, ab_link); 461 list_remove(&request->ab_link); 429 462 430 463 /* Append request to dispatch queue */ 431 list_append(&request-> link, &box->dispatched_calls);464 list_append(&request->ab_link, &box->dispatched_calls); 432 465 } else { 433 466 /* This can happen regularly after ipc_cleanup */ … … 449 482 /** Answer all calls from list with EHANGUP answer. 450 483 * 484 * @param box Answerbox with the list. 451 485 * @param lst Head of the list to be cleaned up. 452 * 453 */ 454 void ipc_cleanup_call_list(list_t *lst) 455 { 486 */ 487 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst) 488 { 489 irq_spinlock_lock(&box->lock, true); 456 490 while (!list_empty(lst)) { 457 call_t *call = list_get_instance(list_first(lst), call_t, link); 458 if (call->buffer) 459 free(call->buffer); 460 461 list_remove(&call->link); 462 491 call_t *call = list_get_instance(list_first(lst), call_t, 492 ab_link); 493 494 list_remove(&call->ab_link); 495 496 irq_spinlock_unlock(&box->lock, true); 497 498 ipc_data_t old = call->data; 463 499 IPC_SET_RETVAL(call->data, EHANGUP); 500 answer_preprocess(call, &old); 464 501 _ipc_answer_free_call(call, true); 465 } 502 503 irq_spinlock_lock(&box->lock, true); 504 } 505 irq_spinlock_unlock(&box->lock, true); 466 506 } 467 507 … … 529 569 } 530 570 571 static void ipc_forget_all_active_calls(void) 572 { 573 call_t *call; 574 575 restart: 576 spinlock_lock(&TASK->active_calls_lock); 577 if (list_empty(&TASK->active_calls)) { 578 /* 579 * We are done, there are no more active calls. 580 * Nota bene: there may still be answers waiting for pick up. 581 */ 582 spinlock_unlock(&TASK->active_calls_lock); 583 return; 584 } 585 586 call = list_get_instance(list_first(&TASK->active_calls), call_t, 587 ta_link); 588 589 if (!spinlock_trylock(&call->forget_lock)) { 590 /* 591 * Avoid deadlock and let async_answer() or 592 * _ipc_answer_free_call() win the race to dequeue the first 593 * call on the list. 594 */ 595 spinlock_unlock(&TASK->active_calls_lock); 596 goto restart; 597 } 598 599 /* 600 * Forget the call and donate it to the task which holds up the answer. 601 */ 602 603 call->forget = true; 604 call->sender = NULL; 605 list_remove(&call->ta_link); 606 607 spinlock_unlock(&call->forget_lock); 608 spinlock_unlock(&TASK->active_calls_lock); 609 610 atomic_dec(&call->caller_phone->active_calls); 611 612 sysipc_ops_t *ops = sysipc_ops_get(call->request_method); 613 if (ops->request_forget) 614 ops->request_forget(call); 615 616 goto restart; 617 } 618 619 /** Wait for all answers to asynchronous calls to arrive. */ 620 static void ipc_wait_for_all_answered_calls(void) 621 { 622 call_t *call; 623 size_t i; 624 625 restart: 626 /* 627 * Go through all phones, until they are all free. 628 * Locking is needed as there may be connection handshakes in progress. 629 */ 630 for (i = 0; i < IPC_MAX_PHONES; i++) { 631 phone_t *phone = &TASK->phones[i]; 632 633 mutex_lock(&phone->lock); 634 if ((phone->state == IPC_PHONE_HUNGUP) && 635 (atomic_get(&phone->active_calls) == 0)) { 636 phone->state = IPC_PHONE_FREE; 637 phone->callee = NULL; 638 } 639 640 /* 641 * We might have had some IPC_PHONE_CONNECTING phones at the 642 * beginning of ipc_cleanup(). Depending on whether these were 643 * forgotten or answered, they will eventually enter the 644 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively. 645 * In the latter case, the other side may slam the open phones 646 * at any time, in which case we will get an IPC_PHONE_SLAMMED 647 * phone. 648 */ 649 if ((phone->state == IPC_PHONE_CONNECTED) || 650 (phone->state == IPC_PHONE_SLAMMED)) { 651 mutex_unlock(&phone->lock); 652 ipc_phone_hangup(phone); 653 /* 654 * Now there may be one extra active call, which needs 655 * to be forgotten. 656 */ 657 ipc_forget_all_active_calls(); 658 goto restart; 659 } 660 661 /* 662 * If the hangup succeeded, it has sent a HANGUP message, the 663 * IPC is now in HUNGUP state, we wait for the reply to come 664 */ 665 if (phone->state != IPC_PHONE_FREE) { 666 mutex_unlock(&phone->lock); 667 break; 668 } 669 670 mutex_unlock(&phone->lock); 671 } 672 673 /* Got into cleanup */ 674 if (i == IPC_MAX_PHONES) 675 return; 676 677 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, 678 SYNCH_FLAGS_NONE); 679 ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF)); 680 ipc_call_free(call); 681 goto restart; 682 } 683 531 684 /** Clean up all IPC communication of the current task. 532 685 * … … 537 690 void ipc_cleanup(void) 538 691 { 692 /* 693 * Mark the answerbox as inactive. 694 * 695 * The main purpose for doing this is to prevent any pending callback 696 * connections from getting established beyond this point. 697 */ 698 irq_spinlock_lock(&TASK->answerbox.lock, true); 699 TASK->answerbox.active = false; 700 irq_spinlock_unlock(&TASK->answerbox.lock, true); 701 539 702 /* Disconnect all our phones ('ipc_phone_hangup') */ 540 size_t i; 541 for (i = 0; i < IPC_MAX_PHONES; i++) 703 for (size_t i = 0; i < IPC_MAX_PHONES; i++) 542 704 ipc_phone_hangup(&TASK->phones[i]); 543 705 … … 557 719 558 720 /* Answer all messages in 'calls' and 'dispatched_calls' queues */ 559 irq_spinlock_lock(&TASK->answerbox.lock, true); 560 ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls); 561 ipc_cleanup_call_list(&TASK->answerbox.calls); 562 irq_spinlock_unlock(&TASK->answerbox.lock, true); 563 564 /* Wait for all answers to asynchronous calls to arrive */ 565 while (true) { 566 /* 567 * Go through all phones, until they are all FREE 568 * Locking is not needed, no one else should modify 569 * it when we are in cleanup 570 */ 571 for (i = 0; i < IPC_MAX_PHONES; i++) { 572 if (TASK->phones[i].state == IPC_PHONE_HUNGUP && 573 atomic_get(&TASK->phones[i].active_calls) == 0) { 574 TASK->phones[i].state = IPC_PHONE_FREE; 575 TASK->phones[i].callee = NULL; 576 } 577 578 /* 579 * Just for sure, we might have had some 580 * IPC_PHONE_CONNECTING phones 581 */ 582 if (TASK->phones[i].state == IPC_PHONE_CONNECTED) 583 ipc_phone_hangup(&TASK->phones[i]); 584 585 /* 586 * If the hangup succeeded, it has sent a HANGUP 587 * message, the IPC is now in HUNGUP state, we 588 * wait for the reply to come 589 */ 590 591 if (TASK->phones[i].state != IPC_PHONE_FREE) 592 break; 593 } 594 595 /* Got into cleanup */ 596 if (i == IPC_MAX_PHONES) 597 break; 598 599 call_t *call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, 600 SYNCH_FLAGS_NONE); 601 ASSERT((call->flags & IPC_CALL_ANSWERED) || 602 (call->flags & IPC_CALL_NOTIF)); 603 604 ipc_call_free(call); 605 } 721 ipc_cleanup_call_list(&TASK->answerbox, 722 &TASK->answerbox.dispatched_calls); 723 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls); 724 725 ipc_forget_all_active_calls(); 726 ipc_wait_for_all_answered_calls(); 606 727 } 607 728 … … 615 736 ipc_answerbox_slab = slab_cache_create("answerbox_t", 616 737 sizeof(answerbox_t), 0, NULL, NULL, 0); 738 } 739 740 741 static void ipc_print_call_list(list_t *list) 742 { 743 list_foreach(*list, cur) { 744 call_t *call = list_get_instance(cur, call_t, ab_link); 745 746 #ifdef __32_BITS__ 747 printf("%10p ", call); 748 #endif 749 750 #ifdef __64_BITS__ 751 printf("%18p ", call); 752 #endif 753 754 spinlock_lock(&call->forget_lock); 755 756 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun 757 " %-6" PRIun " %-6" PRIun " %-7x", 758 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), 759 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), 760 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), 761 call->flags); 762 763 if (call->forget) { 764 printf(" ? (call forgotten)\n"); 765 } else { 766 printf(" %" PRIu64 " (%s)\n", 767 call->sender->taskid, call->sender->name); 768 } 769 770 spinlock_unlock(&call->forget_lock); 771 } 617 772 } 618 773 … … 688 843 689 844 printf(" --- incomming calls ---\n"); 690 list_foreach(task->answerbox.calls, cur) { 691 call_t *call = list_get_instance(cur, call_t, link); 692 693 #ifdef __32_BITS__ 694 printf("%10p ", call); 695 #endif 696 697 #ifdef __64_BITS__ 698 printf("%18p ", call); 699 #endif 700 701 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun 702 " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n", 703 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), 704 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), 705 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), 706 call->flags, call->sender->taskid, call->sender->name); 707 } 708 845 ipc_print_call_list(&task->answerbox.calls); 709 846 printf(" --- dispatched calls ---\n"); 710 list_foreach(task->answerbox.dispatched_calls, cur) { 711 call_t *call = list_get_instance(cur, call_t, link); 712 713 #ifdef __32_BITS__ 714 printf("%10p ", call); 715 #endif 716 717 #ifdef __64_BITS__ 718 printf("%18p ", call); 719 #endif 720 721 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun 722 " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n", 723 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), 724 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), 725 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), 726 call->flags, call->sender->taskid, call->sender->name); 727 } 728 847 ipc_print_call_list(&task->answerbox.dispatched_calls); 729 848 printf(" --- incoming answers ---\n"); 730 list_foreach(task->answerbox.answers, cur) { 731 call_t *call = list_get_instance(cur, call_t, link); 732 733 #ifdef __32_BITS__ 734 printf("%10p ", call); 735 #endif 736 737 #ifdef __64_BITS__ 738 printf("%18p ", call); 739 #endif 740 741 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun 742 " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n", 743 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), 744 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), 745 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), 746 call->flags, call->sender->taskid, call->sender->name); 747 } 849 ipc_print_call_list(&task->answerbox.answers); 748 850 749 851 irq_spinlock_unlock(&task->answerbox.lock, false);
Note:
See TracChangeset
for help on using the changeset viewer.
