Changes in kernel/generic/src/ipc/ipc.c [cd529c4:e9fe33b] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/ipc.c
rcd529c4 re9fe33b 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); 73 call->sender = TASK; 75 spinlock_initialize(&call->forget_lock, "forget_lock"); 76 call->active = false; 77 call->forget = false; 78 call->sender = NULL; 74 79 call->buffer = NULL; 80 } 81 82 void ipc_call_hold(call_t *call) 83 { 84 atomic_inc(&call->refcnt); 85 } 86 87 void ipc_call_release(call_t *call) 88 { 89 if (atomic_predec(&call->refcnt) == 0) { 90 if (call->buffer) 91 free(call->buffer); 92 slab_free(ipc_call_slab, call); 93 } 75 94 } 76 95 … … 83 102 * 84 103 * @return If flags permit it, return NULL, or initialized kernel 85 * call structure .104 * call structure with one reference. 86 105 * 87 106 */ … … 89 108 { 90 109 call_t *call = slab_alloc(ipc_call_slab, flags); 91 if (call) 110 if (call) { 92 111 _ipc_call_init(call); 112 ipc_call_hold(call); 113 } 93 114 94 115 return call; … … 102 123 void ipc_call_free(call_t *call) 103 124 { 104 /* Check to see if we have data in the IPC_M_DATA_SEND buffer. */ 105 if (call->buffer) 106 free(call->buffer); 107 slab_free(ipc_call_slab, call); 125 ipc_call_release(call); 108 126 } 109 127 … … 132 150 * @param phone Initialized phone structure. 133 151 * @param box Initialized answerbox structure. 134 * 135 */ 136 void ipc_phone_connect(phone_t *phone, answerbox_t *box) 137 { 152 * @return True if the phone was connected, false otherwise. 153 */ 154 bool ipc_phone_connect(phone_t *phone, answerbox_t *box) 155 { 156 bool active; 157 138 158 mutex_lock(&phone->lock); 139 140 phone->state = IPC_PHONE_CONNECTED;141 phone->callee = box;142 143 159 irq_spinlock_lock(&box->lock, true); 144 list_append(&phone->link, &box->connected_phones); 160 161 active = box->active; 162 if (active) { 163 phone->state = IPC_PHONE_CONNECTED; 164 phone->callee = box; 165 list_append(&phone->link, &box->connected_phones); 166 } 167 145 168 irq_spinlock_unlock(&box->lock, true); 146 147 169 mutex_unlock(&phone->lock); 170 171 return active; 148 172 } 149 173 … … 151 175 * 152 176 * @param phone Phone structure to be initialized. 153 * 154 */ 155 void ipc_phone_init(phone_t *phone) 177 * @param caller Owning task. 178 * 179 */ 180 void ipc_phone_init(phone_t *phone, task_t *caller) 156 181 { 157 182 mutex_initialize(&phone->lock, MUTEX_PASSIVE); 183 phone->caller = caller; 158 184 phone->callee = NULL; 159 185 phone->state = IPC_PHONE_FREE; … … 167 193 * 168 194 */ 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 195 void _ipc_answer_free_call(call_t *call, bool selflocked) 196 { 174 197 /* Count sent answer */ 175 198 irq_spinlock_lock(&TASK->lock, true); 176 199 TASK->ipc_info.answer_sent++; 177 200 irq_spinlock_unlock(&TASK->lock, true); 201 202 spinlock_lock(&call->forget_lock); 203 if (call->forget) { 204 /* This is a forgotten call and call->sender is not valid. */ 205 spinlock_unlock(&call->forget_lock); 206 ipc_call_free(call); 207 return; 208 } else { 209 /* 210 * If the call is still active, i.e. it was answered 211 * in a non-standard way, remove the call from the 212 * sender's active call list. 213 */ 214 if (call->active) { 215 spinlock_lock(&call->sender->active_calls_lock); 216 list_remove(&call->ta_link); 217 spinlock_unlock(&call->sender->active_calls_lock); 218 } 219 } 220 spinlock_unlock(&call->forget_lock); 221 222 answerbox_t *callerbox = &call->sender->answerbox; 223 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox)); 178 224 179 225 call->flags |= IPC_CALL_ANSWERED; 180 226 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 227 call->data.task_id = TASK->taskid; 189 228 … … 191 230 irq_spinlock_lock(&callerbox->lock, true); 192 231 193 list_append(&call-> link, &callerbox->answers);232 list_append(&call->ab_link, &callerbox->answers); 194 233 195 234 if (do_lock) … … 209 248 /* Remove from active box */ 210 249 irq_spinlock_lock(&box->lock, true); 211 list_remove(&call-> link);250 list_remove(&call->ab_link); 212 251 irq_spinlock_unlock(&box->lock, true); 213 252 … … 228 267 void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err) 229 268 { 269 task_t *caller = phone->caller; 270 271 atomic_inc(&phone->active_calls); 272 call->caller_phone = phone; 273 call->sender = caller; 274 275 call->active = true; 276 spinlock_lock(&caller->active_calls_lock); 277 list_append(&call->ta_link, &caller->active_calls); 278 spinlock_unlock(&caller->active_calls_lock); 279 230 280 call->data.phone = phone; 231 atomic_inc(&phone->active_calls); 281 232 282 IPC_SET_RETVAL(call->data, err); 233 283 _ipc_answer_free_call(call, false); … … 243 293 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call) 244 294 { 295 task_t *caller = phone->caller; 296 245 297 /* Count sent ipc call */ 246 irq_spinlock_lock(& TASK->lock, true);247 TASK->ipc_info.call_sent++;248 irq_spinlock_unlock(& TASK->lock, true);298 irq_spinlock_lock(&caller->lock, true); 299 caller->ipc_info.call_sent++; 300 irq_spinlock_unlock(&caller->lock, true); 249 301 250 302 if (!(call->flags & IPC_CALL_FORWARDED)) { 251 303 atomic_inc(&phone->active_calls); 304 call->caller_phone = phone; 305 call->sender = caller; 306 307 call->active = true; 308 spinlock_lock(&caller->active_calls_lock); 309 list_append(&call->ta_link, &caller->active_calls); 310 spinlock_unlock(&caller->active_calls_lock); 311 252 312 call->data.phone = phone; 253 call->data.task_id = TASK->taskid;313 call->data.task_id = caller->taskid; 254 314 } 255 315 256 316 irq_spinlock_lock(&box->lock, true); 257 list_append(&call-> link, &box->calls);317 list_append(&call->ab_link, &box->calls); 258 318 irq_spinlock_unlock(&box->lock, true); 259 319 … … 275 335 if (phone->state != IPC_PHONE_CONNECTED) { 276 336 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 { 337 if (!(call->flags & IPC_CALL_FORWARDED)) { 281 338 if (phone->state == IPC_PHONE_HUNGUP) 282 339 ipc_backsend_err(phone, call, EHANGUP); … … 325 382 call_t *call = ipc_call_alloc(0); 326 383 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP); 384 call->request_method = IPC_M_PHONE_HUNGUP; 327 385 call->flags |= IPC_CALL_DISCARD_ANSWER; 328 386 _ipc_call(phone, box, call); … … 356 414 TASK->ipc_info.forwarded++; 357 415 irq_spinlock_pass(&TASK->lock, &oldbox->lock); 358 list_remove(&call-> link);416 list_remove(&call->ab_link); 359 417 irq_spinlock_unlock(&oldbox->lock, true); 360 418 361 419 if (mode & IPC_FF_ROUTE_FROM_ME) { 362 if (!call->caller_phone)363 call->caller_phone = call->data.phone;364 420 call->data.phone = newphone; 365 421 call->data.task_id = TASK->taskid; … … 406 462 407 463 request = list_get_instance(list_first(&box->irq_notifs), 408 call_t, link);409 list_remove(&request-> link);464 call_t, ab_link); 465 list_remove(&request->ab_link); 410 466 411 467 irq_spinlock_unlock(&box->irq_lock, false); … … 416 472 /* Handle asynchronous answers */ 417 473 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);474 call_t, ab_link); 475 list_remove(&request->ab_link); 476 atomic_dec(&request->caller_phone->active_calls); 421 477 } else if (!list_empty(&box->calls)) { 422 478 /* Count received call */ … … 425 481 /* Handle requests */ 426 482 request = list_get_instance(list_first(&box->calls), 427 call_t, link);428 list_remove(&request-> link);483 call_t, ab_link); 484 list_remove(&request->ab_link); 429 485 430 486 /* Append request to dispatch queue */ 431 list_append(&request-> link, &box->dispatched_calls);487 list_append(&request->ab_link, &box->dispatched_calls); 432 488 } else { 433 489 /* This can happen regularly after ipc_cleanup */ … … 449 505 /** Answer all calls from list with EHANGUP answer. 450 506 * 507 * @param box Answerbox with the list. 451 508 * @param lst Head of the list to be cleaned up. 452 * 453 */ 454 void ipc_cleanup_call_list(list_t *lst) 455 { 509 */ 510 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst) 511 { 512 irq_spinlock_lock(&box->lock, true); 456 513 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 514 call_t *call = list_get_instance(list_first(lst), call_t, 515 ab_link); 516 517 list_remove(&call->ab_link); 518 519 irq_spinlock_unlock(&box->lock, true); 520 521 ipc_data_t old = call->data; 463 522 IPC_SET_RETVAL(call->data, EHANGUP); 523 answer_preprocess(call, &old); 464 524 _ipc_answer_free_call(call, true); 465 } 525 526 irq_spinlock_lock(&box->lock, true); 527 } 528 irq_spinlock_unlock(&box->lock, true); 466 529 } 467 530 … … 501 564 mutex_unlock(&phone->lock); 502 565 irq_spinlock_unlock(&box->lock, true); 503 566 567 // FIXME: phone can become deallocated at any time now 568 504 569 /* 505 570 * Send one message to the answerbox for each … … 509 574 */ 510 575 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP); 576 call->request_method = IPC_M_PHONE_HUNGUP; 511 577 call->flags |= IPC_CALL_DISCARD_ANSWER; 512 578 _ipc_call(phone, box, call); … … 529 595 } 530 596 597 static void ipc_forget_all_active_calls(void) 598 { 599 call_t *call; 600 601 restart: 602 spinlock_lock(&TASK->active_calls_lock); 603 if (list_empty(&TASK->active_calls)) { 604 /* 605 * We are done, there are no more active calls. 606 * Nota bene: there may still be answers waiting for pick up. 607 */ 608 spinlock_unlock(&TASK->active_calls_lock); 609 return; 610 } 611 612 call = list_get_instance(list_first(&TASK->active_calls), call_t, 613 ta_link); 614 615 if (!spinlock_trylock(&call->forget_lock)) { 616 /* 617 * Avoid deadlock and let async_answer() or 618 * _ipc_answer_free_call() win the race to dequeue the first 619 * call on the list. 620 */ 621 spinlock_unlock(&TASK->active_calls_lock); 622 goto restart; 623 } 624 625 /* 626 * Forget the call and donate it to the task which holds up the answer. 627 */ 628 629 call->forget = true; 630 call->sender = NULL; 631 list_remove(&call->ta_link); 632 633 /* 634 * The call may be freed by _ipc_answer_free_call() before we are done 635 * with it; to avoid working with a destroyed call_t structure, we 636 * must hold a reference to it. 637 */ 638 ipc_call_hold(call); 639 640 spinlock_unlock(&call->forget_lock); 641 spinlock_unlock(&TASK->active_calls_lock); 642 643 atomic_dec(&call->caller_phone->active_calls); 644 645 sysipc_ops_t *ops = sysipc_ops_get(call->request_method); 646 if (ops->request_forget) 647 ops->request_forget(call); 648 649 ipc_call_release(call); 650 651 goto restart; 652 } 653 654 /** Wait for all answers to asynchronous calls to arrive. */ 655 static void ipc_wait_for_all_answered_calls(void) 656 { 657 call_t *call; 658 size_t i; 659 660 restart: 661 /* 662 * Go through all phones, until they are all free. 663 * Locking is needed as there may be connection handshakes in progress. 664 */ 665 for (i = 0; i < IPC_MAX_PHONES; i++) { 666 phone_t *phone = &TASK->phones[i]; 667 668 mutex_lock(&phone->lock); 669 if ((phone->state == IPC_PHONE_HUNGUP) && 670 (atomic_get(&phone->active_calls) == 0)) { 671 phone->state = IPC_PHONE_FREE; 672 phone->callee = NULL; 673 } 674 675 /* 676 * We might have had some IPC_PHONE_CONNECTING phones at the 677 * beginning of ipc_cleanup(). Depending on whether these were 678 * forgotten or answered, they will eventually enter the 679 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively. 680 * In the latter case, the other side may slam the open phones 681 * at any time, in which case we will get an IPC_PHONE_SLAMMED 682 * phone. 683 */ 684 if ((phone->state == IPC_PHONE_CONNECTED) || 685 (phone->state == IPC_PHONE_SLAMMED)) { 686 mutex_unlock(&phone->lock); 687 ipc_phone_hangup(phone); 688 /* 689 * Now there may be one extra active call, which needs 690 * to be forgotten. 691 */ 692 ipc_forget_all_active_calls(); 693 goto restart; 694 } 695 696 /* 697 * If the hangup succeeded, it has sent a HANGUP message, the 698 * IPC is now in HUNGUP state, we wait for the reply to come 699 */ 700 if (phone->state != IPC_PHONE_FREE) { 701 mutex_unlock(&phone->lock); 702 break; 703 } 704 705 mutex_unlock(&phone->lock); 706 } 707 708 /* Got into cleanup */ 709 if (i == IPC_MAX_PHONES) 710 return; 711 712 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, 713 SYNCH_FLAGS_NONE); 714 ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF)); 715 ipc_call_free(call); 716 goto restart; 717 } 718 531 719 /** Clean up all IPC communication of the current task. 532 720 * … … 537 725 void ipc_cleanup(void) 538 726 { 727 /* 728 * Mark the answerbox as inactive. 729 * 730 * The main purpose for doing this is to prevent any pending callback 731 * connections from getting established beyond this point. 732 */ 733 irq_spinlock_lock(&TASK->answerbox.lock, true); 734 TASK->answerbox.active = false; 735 irq_spinlock_unlock(&TASK->answerbox.lock, true); 736 539 737 /* Disconnect all our phones ('ipc_phone_hangup') */ 540 size_t i; 541 for (i = 0; i < IPC_MAX_PHONES; i++) 738 for (size_t i = 0; i < IPC_MAX_PHONES; i++) 542 739 ipc_phone_hangup(&TASK->phones[i]); 543 740 … … 557 754 558 755 /* 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 } 756 ipc_cleanup_call_list(&TASK->answerbox, 757 &TASK->answerbox.dispatched_calls); 758 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls); 759 760 ipc_forget_all_active_calls(); 761 ipc_wait_for_all_answered_calls(); 606 762 } 607 763 … … 615 771 ipc_answerbox_slab = slab_cache_create("answerbox_t", 616 772 sizeof(answerbox_t), 0, NULL, NULL, 0); 773 } 774 775 776 static void ipc_print_call_list(list_t *list) 777 { 778 list_foreach(*list, cur) { 779 call_t *call = list_get_instance(cur, call_t, ab_link); 780 781 #ifdef __32_BITS__ 782 printf("%10p ", call); 783 #endif 784 785 #ifdef __64_BITS__ 786 printf("%18p ", call); 787 #endif 788 789 spinlock_lock(&call->forget_lock); 790 791 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun 792 " %-6" PRIun " %-6" PRIun " %-7x", 793 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), 794 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), 795 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), 796 call->flags); 797 798 if (call->forget) { 799 printf(" ? (call forgotten)\n"); 800 } else { 801 printf(" %" PRIu64 " (%s)\n", 802 call->sender->taskid, call->sender->name); 803 } 804 805 spinlock_unlock(&call->forget_lock); 806 } 617 807 } 618 808 … … 688 878 689 879 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 880 ipc_print_call_list(&task->answerbox.calls); 709 881 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 882 ipc_print_call_list(&task->answerbox.dispatched_calls); 729 883 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 } 884 ipc_print_call_list(&task->answerbox.answers); 748 885 749 886 irq_spinlock_unlock(&task->answerbox.lock, false);
Note:
See TracChangeset
for help on using the changeset viewer.