Implement interruptible wait for messages sent by the kernel
[helenos.git] / kernel / generic / src / ipc / ipc.c
blob2309946daa37832ee01e9fcc84f5105862d77346
1 /*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup genericipc
30 * @{
32 /** @file
35 /* Lock ordering
37 * First the answerbox, then the phone.
40 #include <synch/spinlock.h>
41 #include <synch/mutex.h>
42 #include <synch/waitq.h>
43 #include <ipc/ipc.h>
44 #include <abi/ipc/methods.h>
45 #include <ipc/kbox.h>
46 #include <ipc/event.h>
47 #include <ipc/sysipc_ops.h>
48 #include <ipc/sysipc_priv.h>
49 #include <errno.h>
50 #include <mm/slab.h>
51 #include <arch.h>
52 #include <proc/task.h>
53 #include <memstr.h>
54 #include <debug.h>
55 #include <print.h>
56 #include <console/console.h>
57 #include <proc/thread.h>
58 #include <arch/interrupt.h>
59 #include <ipc/irq.h>
61 static void ipc_forget_call(call_t *);
63 /** Open channel that is assigned automatically to new tasks */
64 answerbox_t *ipc_phone_0 = NULL;
66 static slab_cache_t *ipc_call_slab;
67 static slab_cache_t *ipc_answerbox_slab;
69 /** Initialize a call structure.
71 * @param call Call structure to be initialized.
74 static void _ipc_call_init(call_t *call)
76 memsetb(call, sizeof(*call), 0);
77 spinlock_initialize(&call->forget_lock, "forget_lock");
78 call->active = false;
79 call->forget = false;
80 call->sender = NULL;
81 call->callerbox = &TASK->answerbox;
82 call->buffer = NULL;
85 void ipc_call_hold(call_t *call)
87 atomic_inc(&call->refcnt);
90 void ipc_call_release(call_t *call)
92 if (atomic_predec(&call->refcnt) == 0) {
93 if (call->buffer)
94 free(call->buffer);
95 slab_free(ipc_call_slab, call);
99 /** Allocate and initialize a call structure.
101 * The call is initialized, so that the reply will be directed to
102 * TASK->answerbox.
104 * @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
106 * @return If flags permit it, return NULL, or initialized kernel
107 * call structure with one reference.
110 call_t *ipc_call_alloc(unsigned int flags)
112 call_t *call = slab_alloc(ipc_call_slab, flags);
113 if (call) {
114 _ipc_call_init(call);
115 ipc_call_hold(call);
118 return call;
121 /** Deallocate a call structure.
123 * @param call Call structure to be freed.
126 void ipc_call_free(call_t *call)
128 ipc_call_release(call);
131 /** Initialize an answerbox structure.
133 * @param box Answerbox structure to be initialized.
134 * @param task Task to which the answerbox belongs.
137 void ipc_answerbox_init(answerbox_t *box, task_t *task)
139 irq_spinlock_initialize(&box->lock, "ipc.box.lock");
140 irq_spinlock_initialize(&box->irq_lock, "ipc.box.irqlock");
141 waitq_initialize(&box->wq);
142 list_initialize(&box->connected_phones);
143 list_initialize(&box->calls);
144 list_initialize(&box->dispatched_calls);
145 list_initialize(&box->answers);
146 list_initialize(&box->irq_notifs);
147 list_initialize(&box->irq_list);
148 box->task = task;
151 /** Connect a phone to an answerbox.
153 * @param phone Initialized phone structure.
154 * @param box Initialized answerbox structure.
155 * @return True if the phone was connected, false otherwise.
157 bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
159 bool active;
161 mutex_lock(&phone->lock);
162 irq_spinlock_lock(&box->lock, true);
164 active = box->active;
165 if (active) {
166 phone->state = IPC_PHONE_CONNECTED;
167 phone->callee = box;
168 list_append(&phone->link, &box->connected_phones);
171 irq_spinlock_unlock(&box->lock, true);
172 mutex_unlock(&phone->lock);
174 return active;
177 /** Initialize a phone structure.
179 * @param phone Phone structure to be initialized.
180 * @param caller Owning task.
183 void ipc_phone_init(phone_t *phone, task_t *caller)
185 mutex_initialize(&phone->lock, MUTEX_PASSIVE);
186 phone->caller = caller;
187 phone->callee = NULL;
188 phone->state = IPC_PHONE_FREE;
189 atomic_set(&phone->active_calls, 0);
192 /** Helper function to facilitate synchronous calls.
194 * @param phone Destination kernel phone structure.
195 * @param request Call structure with request.
197 * @return EOK on success or a negative error code.
200 int ipc_call_sync(phone_t *phone, call_t *request)
202 answerbox_t *mybox = slab_alloc(ipc_answerbox_slab, 0);
203 ipc_answerbox_init(mybox, TASK);
205 /* We will receive data in a special box. */
206 request->callerbox = mybox;
208 int rc = ipc_call(phone, request);
209 if (rc != EOK) {
210 slab_free(ipc_answerbox_slab, mybox);
211 return rc;
214 call_t *answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
215 SYNCH_FLAGS_INTERRUPTIBLE);
216 if (!answer) {
219 * The sleep was interrupted.
221 * There are two possibilities now:
222 * 1) the call gets answered before we manage to forget it
223 * 2) we manage to forget the call before it gets answered
226 spinlock_lock(&request->forget_lock);
227 spinlock_lock(&TASK->active_calls_lock);
229 ASSERT(!request->forget);
231 bool answered = !request->active;
232 if (!answered) {
234 * The call is not yet answered and we won the race to
235 * forget it.
237 ipc_forget_call(request); /* releases locks */
238 rc = EINTR;
240 } else {
241 spinlock_unlock(&TASK->active_calls_lock);
242 spinlock_unlock(&request->forget_lock);
245 if (answered) {
247 * The other side won the race to answer the call.
248 * It is safe to wait for the answer uninterruptibly
249 * now.
251 answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
252 SYNCH_FLAGS_NONE);
255 ASSERT(!answer || request == answer);
257 slab_free(ipc_answerbox_slab, mybox);
258 return rc;
261 /** Answer a message which was not dispatched and is not listed in any queue.
263 * @param call Call structure to be answered.
264 * @param selflocked If true, then TASK->answebox is locked.
267 void _ipc_answer_free_call(call_t *call, bool selflocked)
269 /* Count sent answer */
270 irq_spinlock_lock(&TASK->lock, true);
271 TASK->ipc_info.answer_sent++;
272 irq_spinlock_unlock(&TASK->lock, true);
274 spinlock_lock(&call->forget_lock);
275 if (call->forget) {
276 /* This is a forgotten call and call->sender is not valid. */
277 spinlock_unlock(&call->forget_lock);
278 ipc_call_free(call);
279 return;
280 } else {
282 * If the call is still active, i.e. it was answered
283 * in a non-standard way, remove the call from the
284 * sender's active call list.
286 if (call->active) {
287 spinlock_lock(&call->sender->active_calls_lock);
288 list_remove(&call->ta_link);
289 spinlock_unlock(&call->sender->active_calls_lock);
292 spinlock_unlock(&call->forget_lock);
294 answerbox_t *callerbox = call->callerbox;
295 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
297 call->flags |= IPC_CALL_ANSWERED;
299 call->data.task_id = TASK->taskid;
301 if (do_lock)
302 irq_spinlock_lock(&callerbox->lock, true);
304 list_append(&call->ab_link, &callerbox->answers);
306 if (do_lock)
307 irq_spinlock_unlock(&callerbox->lock, true);
309 waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
312 /** Answer a message which is in a callee queue.
314 * @param box Answerbox that is answering the message.
315 * @param call Modified request that is being sent back.
318 void ipc_answer(answerbox_t *box, call_t *call)
320 /* Remove from active box */
321 irq_spinlock_lock(&box->lock, true);
322 list_remove(&call->ab_link);
323 irq_spinlock_unlock(&box->lock, true);
325 /* Send back answer */
326 _ipc_answer_free_call(call, false);
329 static void _ipc_call_actions_internal(phone_t *phone, call_t *call)
331 task_t *caller = phone->caller;
333 atomic_inc(&phone->active_calls);
334 call->caller_phone = phone;
335 call->sender = caller;
337 call->active = true;
338 spinlock_lock(&caller->active_calls_lock);
339 list_append(&call->ta_link, &caller->active_calls);
340 spinlock_unlock(&caller->active_calls_lock);
342 call->data.phone = phone;
343 call->data.task_id = caller->taskid;
346 /** Simulate sending back a message.
348 * Most errors are better handled by forming a normal backward
349 * message and sending it as a normal answer.
351 * @param phone Phone structure the call should appear to come from.
352 * @param call Call structure to be answered.
353 * @param err Return value to be used for the answer.
356 void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
358 _ipc_call_actions_internal(phone, call);
359 IPC_SET_RETVAL(call->data, err);
360 _ipc_answer_free_call(call, false);
363 /** Unsafe unchecking version of ipc_call.
365 * @param phone Phone structure the call comes from.
366 * @param box Destination answerbox structure.
367 * @param call Call structure with request.
370 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
372 task_t *caller = phone->caller;
374 /* Count sent ipc call */
375 irq_spinlock_lock(&caller->lock, true);
376 caller->ipc_info.call_sent++;
377 irq_spinlock_unlock(&caller->lock, true);
379 if (!(call->flags & IPC_CALL_FORWARDED))
380 _ipc_call_actions_internal(phone, call);
382 irq_spinlock_lock(&box->lock, true);
383 list_append(&call->ab_link, &box->calls);
384 irq_spinlock_unlock(&box->lock, true);
386 waitq_wakeup(&box->wq, WAKEUP_FIRST);
389 /** Send an asynchronous request using a phone to an answerbox.
391 * @param phone Phone structure the call comes from and which is
392 * connected to the destination answerbox.
393 * @param call Call structure with request.
395 * @return Return 0 on success, ENOENT on error.
398 int ipc_call(phone_t *phone, call_t *call)
400 mutex_lock(&phone->lock);
401 if (phone->state != IPC_PHONE_CONNECTED) {
402 mutex_unlock(&phone->lock);
403 if (!(call->flags & IPC_CALL_FORWARDED)) {
404 if (phone->state == IPC_PHONE_HUNGUP)
405 ipc_backsend_err(phone, call, EHANGUP);
406 else
407 ipc_backsend_err(phone, call, ENOENT);
410 return ENOENT;
413 answerbox_t *box = phone->callee;
414 _ipc_call(phone, box, call);
416 mutex_unlock(&phone->lock);
417 return 0;
420 /** Disconnect phone from answerbox.
422 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
423 * lazily later.
425 * @param phone Phone structure to be hung up.
427 * @return 0 if the phone is disconnected.
428 * @return -1 if the phone was already disconnected.
431 int ipc_phone_hangup(phone_t *phone)
433 mutex_lock(&phone->lock);
434 if (phone->state == IPC_PHONE_FREE ||
435 phone->state == IPC_PHONE_HUNGUP ||
436 phone->state == IPC_PHONE_CONNECTING) {
437 mutex_unlock(&phone->lock);
438 return -1;
441 answerbox_t *box = phone->callee;
442 if (phone->state != IPC_PHONE_SLAMMED) {
443 /* Remove myself from answerbox */
444 irq_spinlock_lock(&box->lock, true);
445 list_remove(&phone->link);
446 irq_spinlock_unlock(&box->lock, true);
448 call_t *call = ipc_call_alloc(0);
449 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
450 call->request_method = IPC_M_PHONE_HUNGUP;
451 call->flags |= IPC_CALL_DISCARD_ANSWER;
452 _ipc_call(phone, box, call);
455 phone->state = IPC_PHONE_HUNGUP;
456 mutex_unlock(&phone->lock);
458 return 0;
461 /** Forwards call from one answerbox to another one.
463 * @param call Call structure to be redirected.
464 * @param newphone Phone structure to target answerbox.
465 * @param oldbox Old answerbox structure.
466 * @param mode Flags that specify mode of the forward operation.
468 * @return 0 if forwarding succeeded or an error code if
469 * there was an error.
471 * The return value serves only as an information for the forwarder,
472 * the original caller is notified automatically with EFORWARD.
475 int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox,
476 unsigned int mode)
478 /* Count forwarded calls */
479 irq_spinlock_lock(&TASK->lock, true);
480 TASK->ipc_info.forwarded++;
481 irq_spinlock_pass(&TASK->lock, &oldbox->lock);
482 list_remove(&call->ab_link);
483 irq_spinlock_unlock(&oldbox->lock, true);
485 if (mode & IPC_FF_ROUTE_FROM_ME) {
486 call->data.phone = newphone;
487 call->data.task_id = TASK->taskid;
490 return ipc_call(newphone, call);
494 /** Wait for a phone call.
496 * @param box Answerbox expecting the call.
497 * @param usec Timeout in microseconds. See documentation for
498 * waitq_sleep_timeout() for decription of its special
499 * meaning.
500 * @param flags Select mode of sleep operation. See documentation for
501 * waitq_sleep_timeout() for description of its special
502 * meaning.
504 * @return Recived call structure or NULL.
506 * To distinguish between a call and an answer, have a look at call->flags.
509 call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, unsigned int flags)
511 call_t *request;
512 uint64_t irq_cnt = 0;
513 uint64_t answer_cnt = 0;
514 uint64_t call_cnt = 0;
515 int rc;
517 restart:
518 rc = waitq_sleep_timeout(&box->wq, usec, flags);
519 if (SYNCH_FAILED(rc))
520 return NULL;
522 irq_spinlock_lock(&box->lock, true);
523 if (!list_empty(&box->irq_notifs)) {
524 /* Count received IRQ notification */
525 irq_cnt++;
527 irq_spinlock_lock(&box->irq_lock, false);
529 request = list_get_instance(list_first(&box->irq_notifs),
530 call_t, ab_link);
531 list_remove(&request->ab_link);
533 irq_spinlock_unlock(&box->irq_lock, false);
534 } else if (!list_empty(&box->answers)) {
535 /* Count received answer */
536 answer_cnt++;
538 /* Handle asynchronous answers */
539 request = list_get_instance(list_first(&box->answers),
540 call_t, ab_link);
541 list_remove(&request->ab_link);
542 atomic_dec(&request->caller_phone->active_calls);
543 } else if (!list_empty(&box->calls)) {
544 /* Count received call */
545 call_cnt++;
547 /* Handle requests */
548 request = list_get_instance(list_first(&box->calls),
549 call_t, ab_link);
550 list_remove(&request->ab_link);
552 /* Append request to dispatch queue */
553 list_append(&request->ab_link, &box->dispatched_calls);
554 } else {
555 /* This can happen regularly after ipc_cleanup */
556 irq_spinlock_unlock(&box->lock, true);
557 goto restart;
560 irq_spinlock_pass(&box->lock, &TASK->lock);
562 TASK->ipc_info.irq_notif_received += irq_cnt;
563 TASK->ipc_info.answer_received += answer_cnt;
564 TASK->ipc_info.call_received += call_cnt;
566 irq_spinlock_unlock(&TASK->lock, true);
568 return request;
571 /** Answer all calls from list with EHANGUP answer.
573 * @param box Answerbox with the list.
574 * @param lst Head of the list to be cleaned up.
576 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
578 irq_spinlock_lock(&box->lock, true);
579 while (!list_empty(lst)) {
580 call_t *call = list_get_instance(list_first(lst), call_t,
581 ab_link);
583 list_remove(&call->ab_link);
585 irq_spinlock_unlock(&box->lock, true);
587 if (lst == &box->calls)
588 SYSIPC_OP(request_process, call, box);
590 ipc_data_t old = call->data;
591 IPC_SET_RETVAL(call->data, EHANGUP);
592 answer_preprocess(call, &old);
593 _ipc_answer_free_call(call, true);
595 irq_spinlock_lock(&box->lock, true);
597 irq_spinlock_unlock(&box->lock, true);
600 /** Disconnects all phones connected to an answerbox.
602 * @param box Answerbox to disconnect phones from.
603 * @param notify_box If true, the answerbox will get a hangup message for
604 * each disconnected phone.
607 void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
609 phone_t *phone;
610 DEADLOCK_PROBE_INIT(p_phonelck);
612 call_t *call = notify_box ? ipc_call_alloc(0) : NULL;
614 /* Disconnect all phones connected to our answerbox */
615 restart_phones:
616 irq_spinlock_lock(&box->lock, true);
617 while (!list_empty(&box->connected_phones)) {
618 phone = list_get_instance(list_first(&box->connected_phones),
619 phone_t, link);
620 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
621 irq_spinlock_unlock(&box->lock, true);
622 DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
623 goto restart_phones;
626 /* Disconnect phone */
627 ASSERT(phone->state == IPC_PHONE_CONNECTED);
629 list_remove(&phone->link);
630 phone->state = IPC_PHONE_SLAMMED;
632 if (notify_box) {
633 mutex_unlock(&phone->lock);
634 irq_spinlock_unlock(&box->lock, true);
636 // FIXME: phone can become deallocated at any time now
639 * Send one message to the answerbox for each
640 * phone. Used to make sure the kbox thread
641 * wakes up after the last phone has been
642 * disconnected.
644 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
645 call->request_method = IPC_M_PHONE_HUNGUP;
646 call->flags |= IPC_CALL_DISCARD_ANSWER;
647 _ipc_call(phone, box, call);
649 /* Allocate another call in advance */
650 call = ipc_call_alloc(0);
652 /* Must start again */
653 goto restart_phones;
656 mutex_unlock(&phone->lock);
659 irq_spinlock_unlock(&box->lock, true);
661 /* Free unused call */
662 if (call)
663 ipc_call_free(call);
666 static void ipc_forget_call(call_t *call)
668 ASSERT(spinlock_locked(&TASK->active_calls_lock));
669 ASSERT(spinlock_locked(&call->forget_lock));
672 * Forget the call and donate it to the task which holds up the answer.
675 call->forget = true;
676 call->sender = NULL;
677 list_remove(&call->ta_link);
680 * The call may be freed by _ipc_answer_free_call() before we are done
681 * with it; to avoid working with a destroyed call_t structure, we
682 * must hold a reference to it.
684 ipc_call_hold(call);
686 spinlock_unlock(&call->forget_lock);
687 spinlock_unlock(&TASK->active_calls_lock);
689 atomic_dec(&call->caller_phone->active_calls);
691 SYSIPC_OP(request_forget, call);
693 ipc_call_release(call);
696 static void ipc_forget_all_active_calls(void)
698 call_t *call;
700 restart:
701 spinlock_lock(&TASK->active_calls_lock);
702 if (list_empty(&TASK->active_calls)) {
704 * We are done, there are no more active calls.
705 * Nota bene: there may still be answers waiting for pick up.
707 spinlock_unlock(&TASK->active_calls_lock);
708 return;
711 call = list_get_instance(list_first(&TASK->active_calls), call_t,
712 ta_link);
714 if (!spinlock_trylock(&call->forget_lock)) {
716 * Avoid deadlock and let async_answer() or
717 * _ipc_answer_free_call() win the race to dequeue the first
718 * call on the list.
720 spinlock_unlock(&TASK->active_calls_lock);
721 goto restart;
724 ipc_forget_call(call);
726 goto restart;
729 /** Wait for all answers to asynchronous calls to arrive. */
730 static void ipc_wait_for_all_answered_calls(void)
732 call_t *call;
733 size_t i;
735 restart:
737 * Go through all phones, until they are all free.
738 * Locking is needed as there may be connection handshakes in progress.
740 for (i = 0; i < IPC_MAX_PHONES; i++) {
741 phone_t *phone = &TASK->phones[i];
743 mutex_lock(&phone->lock);
744 if ((phone->state == IPC_PHONE_HUNGUP) &&
745 (atomic_get(&phone->active_calls) == 0)) {
746 phone->state = IPC_PHONE_FREE;
747 phone->callee = NULL;
751 * We might have had some IPC_PHONE_CONNECTING phones at the
752 * beginning of ipc_cleanup(). Depending on whether these were
753 * forgotten or answered, they will eventually enter the
754 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
755 * In the latter case, the other side may slam the open phones
756 * at any time, in which case we will get an IPC_PHONE_SLAMMED
757 * phone.
759 if ((phone->state == IPC_PHONE_CONNECTED) ||
760 (phone->state == IPC_PHONE_SLAMMED)) {
761 mutex_unlock(&phone->lock);
762 ipc_phone_hangup(phone);
764 * Now there may be one extra active call, which needs
765 * to be forgotten.
767 ipc_forget_all_active_calls();
768 goto restart;
772 * If the hangup succeeded, it has sent a HANGUP message, the
773 * IPC is now in HUNGUP state, we wait for the reply to come
775 if (phone->state != IPC_PHONE_FREE) {
776 mutex_unlock(&phone->lock);
777 break;
780 mutex_unlock(&phone->lock);
783 /* Got into cleanup */
784 if (i == IPC_MAX_PHONES)
785 return;
787 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
788 SYNCH_FLAGS_NONE);
789 ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
791 SYSIPC_OP(answer_process, call);
793 ipc_call_free(call);
794 goto restart;
797 /** Clean up all IPC communication of the current task.
799 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
800 * have to change it as well if you want to cleanup other tasks than TASK.
803 void ipc_cleanup(void)
806 * Mark the answerbox as inactive.
808 * The main purpose for doing this is to prevent any pending callback
809 * connections from getting established beyond this point.
811 irq_spinlock_lock(&TASK->answerbox.lock, true);
812 TASK->answerbox.active = false;
813 irq_spinlock_unlock(&TASK->answerbox.lock, true);
815 /* Disconnect all our phones ('ipc_phone_hangup') */
816 for (size_t i = 0; i < IPC_MAX_PHONES; i++)
817 ipc_phone_hangup(&TASK->phones[i]);
819 /* Unsubscribe from any event notifications. */
820 event_cleanup_answerbox(&TASK->answerbox);
822 /* Disconnect all connected irqs */
823 ipc_irq_cleanup(&TASK->answerbox);
825 /* Disconnect all phones connected to our regular answerbox */
826 ipc_answerbox_slam_phones(&TASK->answerbox, false);
828 #ifdef CONFIG_UDEBUG
829 /* Clean up kbox thread and communications */
830 ipc_kbox_cleanup();
831 #endif
833 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
834 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
835 ipc_cleanup_call_list(&TASK->answerbox,
836 &TASK->answerbox.dispatched_calls);
838 ipc_forget_all_active_calls();
839 ipc_wait_for_all_answered_calls();
842 /** Initilize IPC subsystem
845 void ipc_init(void)
847 ipc_call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
848 NULL, 0);
849 ipc_answerbox_slab = slab_cache_create("answerbox_t",
850 sizeof(answerbox_t), 0, NULL, NULL, 0);
854 static void ipc_print_call_list(list_t *list)
856 list_foreach(*list, ab_link, call_t, call) {
857 #ifdef __32_BITS__
858 printf("%10p ", call);
859 #endif
861 #ifdef __64_BITS__
862 printf("%18p ", call);
863 #endif
865 spinlock_lock(&call->forget_lock);
867 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
868 " %-6" PRIun " %-6" PRIun " %-7x",
869 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
870 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
871 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
872 call->flags);
874 if (call->forget) {
875 printf(" ? (call forgotten)\n");
876 } else {
877 printf(" %" PRIu64 " (%s)\n",
878 call->sender->taskid, call->sender->name);
881 spinlock_unlock(&call->forget_lock);
885 /** List answerbox contents.
887 * @param taskid Task ID.
890 void ipc_print_task(task_id_t taskid)
892 irq_spinlock_lock(&tasks_lock, true);
893 task_t *task = task_find_by_id(taskid);
895 if (!task) {
896 irq_spinlock_unlock(&tasks_lock, true);
897 return;
900 /* Hand-over-hand locking */
901 irq_spinlock_exchange(&tasks_lock, &task->lock);
903 printf("[phone id] [calls] [state\n");
905 size_t i;
906 for (i = 0; i < IPC_MAX_PHONES; i++) {
907 if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
908 printf("%-10zu (mutex busy)\n", i);
909 continue;
912 if (task->phones[i].state != IPC_PHONE_FREE) {
913 printf("%-10zu %7" PRIun " ", i,
914 atomic_get(&task->phones[i].active_calls));
916 switch (task->phones[i].state) {
917 case IPC_PHONE_CONNECTING:
918 printf("connecting");
919 break;
920 case IPC_PHONE_CONNECTED:
921 printf("connected to %" PRIu64 " (%s)",
922 task->phones[i].callee->task->taskid,
923 task->phones[i].callee->task->name);
924 break;
925 case IPC_PHONE_SLAMMED:
926 printf("slammed by %p",
927 task->phones[i].callee);
928 break;
929 case IPC_PHONE_HUNGUP:
930 printf("hung up by %p",
931 task->phones[i].callee);
932 break;
933 default:
934 break;
937 printf("\n");
940 mutex_unlock(&task->phones[i].lock);
943 irq_spinlock_lock(&task->answerbox.lock, false);
945 #ifdef __32_BITS__
946 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
947 " [flags] [sender\n");
948 #endif
950 #ifdef __64_BITS__
951 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4]"
952 " [arg5] [flags] [sender\n");
953 #endif
955 printf(" --- incomming calls ---\n");
956 ipc_print_call_list(&task->answerbox.calls);
957 printf(" --- dispatched calls ---\n");
958 ipc_print_call_list(&task->answerbox.dispatched_calls);
959 printf(" --- incoming answers ---\n");
960 ipc_print_call_list(&task->answerbox.answers);
962 irq_spinlock_unlock(&task->answerbox.lock, false);
963 irq_spinlock_unlock(&task->lock, true);
966 /** @}