Changes in kernel/generic/src/ipc/ipc.c [466e95f7:cd529c4] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/ipc.c
r466e95f7 rcd529c4 45 45 #include <ipc/kbox.h> 46 46 #include <ipc/event.h> 47 #include <ipc/sysipc_ops.h>48 #include <ipc/sysipc_priv.h>49 47 #include <errno.h> 50 48 #include <mm/slab.h> … … 73 71 { 74 72 memsetb(call, sizeof(*call), 0); 75 spinlock_initialize(&call->forget_lock, "forget_lock"); 76 call->active = false; 77 call->forget = false; 78 call->sender = NULL; 73 call->sender = TASK; 79 74 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 }94 75 } 95 76 … … 102 83 * 103 84 * @return If flags permit it, return NULL, or initialized kernel 104 * call structure with one reference.85 * call structure. 105 86 * 106 87 */ … … 108 89 { 109 90 call_t *call = slab_alloc(ipc_call_slab, flags); 110 if (call) {91 if (call) 111 92 _ipc_call_init(call); 112 ipc_call_hold(call);113 }114 93 115 94 return call; … … 123 102 void ipc_call_free(call_t *call) 124 103 { 125 ipc_call_release(call); 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); 126 108 } 127 109 … … 150 132 * @param phone Initialized phone structure. 151 133 * @param box Initialized answerbox structure. 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 134 * 135 */ 136 void ipc_phone_connect(phone_t *phone, answerbox_t *box) 137 { 158 138 mutex_lock(&phone->lock); 139 140 phone->state = IPC_PHONE_CONNECTED; 141 phone->callee = box; 142 159 143 irq_spinlock_lock(&box->lock, true); 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 144 list_append(&phone->link, &box->connected_phones); 168 145 irq_spinlock_unlock(&box->lock, true); 146 169 147 mutex_unlock(&phone->lock); 170 171 return active;172 148 } 173 149 … … 175 151 * 176 152 * @param phone Phone structure to be initialized. 177 * @param caller Owning task. 178 * 179 */ 180 void ipc_phone_init(phone_t *phone, task_t *caller) 153 * 154 */ 155 void ipc_phone_init(phone_t *phone) 181 156 { 182 157 mutex_initialize(&phone->lock, MUTEX_PASSIVE); 183 phone->caller = caller;184 158 phone->callee = NULL; 185 159 phone->state = IPC_PHONE_FREE; … … 193 167 * 194 168 */ 195 void _ipc_answer_free_call(call_t *call, bool selflocked) 196 { 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 197 174 /* Count sent answer */ 198 175 irq_spinlock_lock(&TASK->lock, true); 199 176 TASK->ipc_info.answer_sent++; 200 177 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); 178 179 call->flags |= IPC_CALL_ANSWERED; 180 181 if (call->flags & IPC_CALL_FORWARDED) { 182 if (call->caller_phone) { 183 /* Demasquerade the caller phone. */ 184 call->data.phone = call->caller_phone; 218 185 } 219 186 } 220 spinlock_unlock(&call->forget_lock); 221 222 answerbox_t *callerbox = &call->sender->answerbox; 223 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox)); 224 225 call->flags |= IPC_CALL_ANSWERED; 226 187 227 188 call->data.task_id = TASK->taskid; 228 189 … … 230 191 irq_spinlock_lock(&callerbox->lock, true); 231 192 232 list_append(&call-> ab_link, &callerbox->answers);193 list_append(&call->link, &callerbox->answers); 233 194 234 195 if (do_lock) … … 248 209 /* Remove from active box */ 249 210 irq_spinlock_lock(&box->lock, true); 250 list_remove(&call-> ab_link);211 list_remove(&call->link); 251 212 irq_spinlock_unlock(&box->lock, true); 252 213 253 214 /* Send back answer */ 254 215 _ipc_answer_free_call(call, false); 255 }256 257 static void _ipc_call_actions_internal(phone_t *phone, call_t *call)258 {259 task_t *caller = phone->caller;260 261 atomic_inc(&phone->active_calls);262 call->caller_phone = phone;263 call->sender = caller;264 265 call->active = true;266 spinlock_lock(&caller->active_calls_lock);267 list_append(&call->ta_link, &caller->active_calls);268 spinlock_unlock(&caller->active_calls_lock);269 270 call->data.phone = phone;271 call->data.task_id = caller->taskid;272 216 } 273 217 … … 284 228 void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err) 285 229 { 286 _ipc_call_actions_internal(phone, call); 230 call->data.phone = phone; 231 atomic_inc(&phone->active_calls); 287 232 IPC_SET_RETVAL(call->data, err); 288 233 _ipc_answer_free_call(call, false); … … 298 243 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call) 299 244 { 300 task_t *caller = phone->caller;301 302 245 /* Count sent ipc call */ 303 irq_spinlock_lock(&caller->lock, true); 304 caller->ipc_info.call_sent++; 305 irq_spinlock_unlock(&caller->lock, true); 306 307 if (!(call->flags & IPC_CALL_FORWARDED)) 308 _ipc_call_actions_internal(phone, call); 246 irq_spinlock_lock(&TASK->lock, true); 247 TASK->ipc_info.call_sent++; 248 irq_spinlock_unlock(&TASK->lock, true); 249 250 if (!(call->flags & IPC_CALL_FORWARDED)) { 251 atomic_inc(&phone->active_calls); 252 call->data.phone = phone; 253 call->data.task_id = TASK->taskid; 254 } 309 255 310 256 irq_spinlock_lock(&box->lock, true); 311 list_append(&call-> ab_link, &box->calls);257 list_append(&call->link, &box->calls); 312 258 irq_spinlock_unlock(&box->lock, true); 313 259 … … 329 275 if (phone->state != IPC_PHONE_CONNECTED) { 330 276 mutex_unlock(&phone->lock); 331 if (!(call->flags & IPC_CALL_FORWARDED)) { 277 if (call->flags & IPC_CALL_FORWARDED) { 278 IPC_SET_RETVAL(call->data, EFORWARD); 279 _ipc_answer_free_call(call, false); 280 } else { 332 281 if (phone->state == IPC_PHONE_HUNGUP) 333 282 ipc_backsend_err(phone, call, EHANGUP); … … 376 325 call_t *call = ipc_call_alloc(0); 377 326 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP); 378 call->request_method = IPC_M_PHONE_HUNGUP;379 327 call->flags |= IPC_CALL_DISCARD_ANSWER; 380 328 _ipc_call(phone, box, call); … … 408 356 TASK->ipc_info.forwarded++; 409 357 irq_spinlock_pass(&TASK->lock, &oldbox->lock); 410 list_remove(&call-> ab_link);358 list_remove(&call->link); 411 359 irq_spinlock_unlock(&oldbox->lock, true); 412 360 413 361 if (mode & IPC_FF_ROUTE_FROM_ME) { 362 if (!call->caller_phone) 363 call->caller_phone = call->data.phone; 414 364 call->data.phone = newphone; 415 365 call->data.task_id = TASK->taskid; … … 456 406 457 407 request = list_get_instance(list_first(&box->irq_notifs), 458 call_t, ab_link);459 list_remove(&request-> ab_link);408 call_t, link); 409 list_remove(&request->link); 460 410 461 411 irq_spinlock_unlock(&box->irq_lock, false); … … 466 416 /* Handle asynchronous answers */ 467 417 request = list_get_instance(list_first(&box->answers), 468 call_t, ab_link);469 list_remove(&request-> ab_link);470 atomic_dec(&request-> caller_phone->active_calls);418 call_t, link); 419 list_remove(&request->link); 420 atomic_dec(&request->data.phone->active_calls); 471 421 } else if (!list_empty(&box->calls)) { 472 422 /* Count received call */ … … 475 425 /* Handle requests */ 476 426 request = list_get_instance(list_first(&box->calls), 477 call_t, ab_link);478 list_remove(&request-> ab_link);427 call_t, link); 428 list_remove(&request->link); 479 429 480 430 /* Append request to dispatch queue */ 481 list_append(&request-> ab_link, &box->dispatched_calls);431 list_append(&request->link, &box->dispatched_calls); 482 432 } else { 483 433 /* This can happen regularly after ipc_cleanup */ … … 499 449 /** Answer all calls from list with EHANGUP answer. 500 450 * 501 * @param box Answerbox with the list.502 451 * @param lst Head of the list to be cleaned up. 503 * /504 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst) 505 { 506 irq_spinlock_lock(&box->lock, true); 452 * 453 */ 454 void ipc_cleanup_call_list(list_t *lst) 455 { 507 456 while (!list_empty(lst)) { 508 call_t *call = list_get_instance(list_first(lst), call_t, 509 ab_link); 510 511 list_remove(&call->ab_link); 512 513 irq_spinlock_unlock(&box->lock, true); 514 515 if (lst == &box->calls) 516 SYSIPC_OP(request_process, call, box); 517 518 ipc_data_t old = call->data; 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 519 463 IPC_SET_RETVAL(call->data, EHANGUP); 520 answer_preprocess(call, &old);521 464 _ipc_answer_free_call(call, true); 522 523 irq_spinlock_lock(&box->lock, true); 524 } 525 irq_spinlock_unlock(&box->lock, true); 465 } 526 466 } 527 467 … … 561 501 mutex_unlock(&phone->lock); 562 502 irq_spinlock_unlock(&box->lock, true); 563 564 // FIXME: phone can become deallocated at any time now 565 503 566 504 /* 567 505 * Send one message to the answerbox for each … … 571 509 */ 572 510 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP); 573 call->request_method = IPC_M_PHONE_HUNGUP;574 511 call->flags |= IPC_CALL_DISCARD_ANSWER; 575 512 _ipc_call(phone, box, call); … … 592 529 } 593 530 594 static void ipc_forget_all_active_calls(void)595 {596 call_t *call;597 598 restart:599 spinlock_lock(&TASK->active_calls_lock);600 if (list_empty(&TASK->active_calls)) {601 /*602 * We are done, there are no more active calls.603 * Nota bene: there may still be answers waiting for pick up.604 */605 spinlock_unlock(&TASK->active_calls_lock);606 return;607 }608 609 call = list_get_instance(list_first(&TASK->active_calls), call_t,610 ta_link);611 612 if (!spinlock_trylock(&call->forget_lock)) {613 /*614 * Avoid deadlock and let async_answer() or615 * _ipc_answer_free_call() win the race to dequeue the first616 * call on the list.617 */618 spinlock_unlock(&TASK->active_calls_lock);619 goto restart;620 }621 622 /*623 * Forget the call and donate it to the task which holds up the answer.624 */625 626 call->forget = true;627 call->sender = NULL;628 list_remove(&call->ta_link);629 630 /*631 * The call may be freed by _ipc_answer_free_call() before we are done632 * with it; to avoid working with a destroyed call_t structure, we633 * must hold a reference to it.634 */635 ipc_call_hold(call);636 637 spinlock_unlock(&call->forget_lock);638 spinlock_unlock(&TASK->active_calls_lock);639 640 atomic_dec(&call->caller_phone->active_calls);641 642 SYSIPC_OP(request_forget, call);643 644 ipc_call_release(call);645 646 goto restart;647 }648 649 /** Wait for all answers to asynchronous calls to arrive. */650 static void ipc_wait_for_all_answered_calls(void)651 {652 call_t *call;653 size_t i;654 655 restart:656 /*657 * Go through all phones, until they are all free.658 * Locking is needed as there may be connection handshakes in progress.659 */660 for (i = 0; i < IPC_MAX_PHONES; i++) {661 phone_t *phone = &TASK->phones[i];662 663 mutex_lock(&phone->lock);664 if ((phone->state == IPC_PHONE_HUNGUP) &&665 (atomic_get(&phone->active_calls) == 0)) {666 phone->state = IPC_PHONE_FREE;667 phone->callee = NULL;668 }669 670 /*671 * We might have had some IPC_PHONE_CONNECTING phones at the672 * beginning of ipc_cleanup(). Depending on whether these were673 * forgotten or answered, they will eventually enter the674 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.675 * In the latter case, the other side may slam the open phones676 * at any time, in which case we will get an IPC_PHONE_SLAMMED677 * phone.678 */679 if ((phone->state == IPC_PHONE_CONNECTED) ||680 (phone->state == IPC_PHONE_SLAMMED)) {681 mutex_unlock(&phone->lock);682 ipc_phone_hangup(phone);683 /*684 * Now there may be one extra active call, which needs685 * to be forgotten.686 */687 ipc_forget_all_active_calls();688 goto restart;689 }690 691 /*692 * If the hangup succeeded, it has sent a HANGUP message, the693 * IPC is now in HUNGUP state, we wait for the reply to come694 */695 if (phone->state != IPC_PHONE_FREE) {696 mutex_unlock(&phone->lock);697 break;698 }699 700 mutex_unlock(&phone->lock);701 }702 703 /* Got into cleanup */704 if (i == IPC_MAX_PHONES)705 return;706 707 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,708 SYNCH_FLAGS_NONE);709 ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));710 711 SYSIPC_OP(answer_process, call);712 713 ipc_call_free(call);714 goto restart;715 }716 717 531 /** Clean up all IPC communication of the current task. 718 532 * … … 723 537 void ipc_cleanup(void) 724 538 { 725 /*726 * Mark the answerbox as inactive.727 *728 * The main purpose for doing this is to prevent any pending callback729 * connections from getting established beyond this point.730 */731 irq_spinlock_lock(&TASK->answerbox.lock, true);732 TASK->answerbox.active = false;733 irq_spinlock_unlock(&TASK->answerbox.lock, true);734 735 539 /* Disconnect all our phones ('ipc_phone_hangup') */ 736 for (size_t i = 0; i < IPC_MAX_PHONES; i++) 540 size_t i; 541 for (i = 0; i < IPC_MAX_PHONES; i++) 737 542 ipc_phone_hangup(&TASK->phones[i]); 738 543 … … 752 557 753 558 /* Answer all messages in 'calls' and 'dispatched_calls' queues */ 754 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls); 755 ipc_cleanup_call_list(&TASK->answerbox, 756 &TASK->answerbox.dispatched_calls); 757 758 ipc_forget_all_active_calls(); 759 ipc_wait_for_all_answered_calls(); 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 } 760 606 } 761 607 … … 769 615 ipc_answerbox_slab = slab_cache_create("answerbox_t", 770 616 sizeof(answerbox_t), 0, NULL, NULL, 0); 771 }772 773 774 static void ipc_print_call_list(list_t *list)775 {776 list_foreach(*list, cur) {777 call_t *call = list_get_instance(cur, call_t, ab_link);778 779 #ifdef __32_BITS__780 printf("%10p ", call);781 #endif782 783 #ifdef __64_BITS__784 printf("%18p ", call);785 #endif786 787 spinlock_lock(&call->forget_lock);788 789 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun790 " %-6" PRIun " %-6" PRIun " %-7x",791 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),792 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),793 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),794 call->flags);795 796 if (call->forget) {797 printf(" ? (call forgotten)\n");798 } else {799 printf(" %" PRIu64 " (%s)\n",800 call->sender->taskid, call->sender->name);801 }802 803 spinlock_unlock(&call->forget_lock);804 }805 617 } 806 618 … … 876 688 877 689 printf(" --- incomming calls ---\n"); 878 ipc_print_call_list(&task->answerbox.calls); 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 879 709 printf(" --- dispatched calls ---\n"); 880 ipc_print_call_list(&task->answerbox.dispatched_calls); 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 881 729 printf(" --- incoming answers ---\n"); 882 ipc_print_call_list(&task->answerbox.answers); 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 } 883 748 884 749 irq_spinlock_unlock(&task->answerbox.lock, false);
Note:
See TracChangeset
for help on using the changeset viewer.