source: mainline/kernel/generic/src/ipc/ipc.c@ 03a8a8e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 03a8a8e was 03a8a8e, checked in by Jakub Jermar <jakub@…>, 13 years ago

Link each phone to its containing task.

This makes it possible to set the call's sender reliably using just the
info stored in the phone used to make the call.

  • Property mode set to 100644
File size: 22.8 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup genericipc
30 * @{
31 */
32/** @file
33 */
34
35/* Lock ordering
36 *
37 * First the answerbox, then the phone.
38 */
39
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>
60
61/** Open channel that is assigned automatically to new tasks */
62answerbox_t *ipc_phone_0 = NULL;
63
64static slab_cache_t *ipc_call_slab;
65static slab_cache_t *ipc_answerbox_slab;
66
67/** Initialize a call structure.
68 *
69 * @param call Call structure to be initialized.
70 *
71 */
72static void _ipc_call_init(call_t *call)
73{
74 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;
79 call->buffer = NULL;
80}
81
82void ipc_call_hold(call_t *call)
83{
84 atomic_inc(&call->refcnt);
85}
86
87void 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}
95
96/** Allocate and initialize a call structure.
97 *
98 * The call is initialized, so that the reply will be directed to
99 * TASK->answerbox.
100 *
101 * @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
102 *
103 * @return If flags permit it, return NULL, or initialized kernel
104 * call structure with one reference.
105 *
106 */
107call_t *ipc_call_alloc(unsigned int flags)
108{
109 call_t *call = slab_alloc(ipc_call_slab, flags);
110 if (call) {
111 _ipc_call_init(call);
112 ipc_call_hold(call);
113 }
114
115 return call;
116}
117
118/** Deallocate a call structure.
119 *
120 * @param call Call structure to be freed.
121 *
122 */
123void ipc_call_free(call_t *call)
124{
125 ipc_call_release(call);
126}
127
128/** Initialize an answerbox structure.
129 *
130 * @param box Answerbox structure to be initialized.
131 * @param task Task to which the answerbox belongs.
132 *
133 */
134void ipc_answerbox_init(answerbox_t *box, task_t *task)
135{
136 irq_spinlock_initialize(&box->lock, "ipc.box.lock");
137 irq_spinlock_initialize(&box->irq_lock, "ipc.box.irqlock");
138 waitq_initialize(&box->wq);
139 list_initialize(&box->connected_phones);
140 list_initialize(&box->calls);
141 list_initialize(&box->dispatched_calls);
142 list_initialize(&box->answers);
143 list_initialize(&box->irq_notifs);
144 list_initialize(&box->irq_list);
145 box->task = task;
146}
147
148/** Connect a phone to an answerbox.
149 *
150 * @param phone Initialized phone structure.
151 * @param box Initialized answerbox structure.
152 * @return True if the phone was connected, false otherwise.
153 */
154bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
155{
156 bool active;
157
158 mutex_lock(&phone->lock);
159 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
168 irq_spinlock_unlock(&box->lock, true);
169 mutex_unlock(&phone->lock);
170
171 return active;
172}
173
174/** Initialize a phone structure.
175 *
176 * @param phone Phone structure to be initialized.
177 * @param caller Owning task.
178 *
179 */
180void ipc_phone_init(phone_t *phone, task_t *caller)
181{
182 mutex_initialize(&phone->lock, MUTEX_PASSIVE);
183 phone->caller = caller;
184 phone->callee = NULL;
185 phone->state = IPC_PHONE_FREE;
186 atomic_set(&phone->active_calls, 0);
187}
188
189/** Answer a message which was not dispatched and is not listed in any queue.
190 *
191 * @param call Call structure to be answered.
192 * @param selflocked If true, then TASK->answebox is locked.
193 *
194 */
195void _ipc_answer_free_call(call_t *call, bool selflocked)
196{
197 /* Count sent answer */
198 irq_spinlock_lock(&TASK->lock, true);
199 TASK->ipc_info.answer_sent++;
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));
224
225 call->flags |= IPC_CALL_ANSWERED;
226
227 call->data.task_id = TASK->taskid;
228
229 if (do_lock)
230 irq_spinlock_lock(&callerbox->lock, true);
231
232 list_append(&call->ab_link, &callerbox->answers);
233
234 if (do_lock)
235 irq_spinlock_unlock(&callerbox->lock, true);
236
237 waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
238}
239
240/** Answer a message which is in a callee queue.
241 *
242 * @param box Answerbox that is answering the message.
243 * @param call Modified request that is being sent back.
244 *
245 */
246void ipc_answer(answerbox_t *box, call_t *call)
247{
248 /* Remove from active box */
249 irq_spinlock_lock(&box->lock, true);
250 list_remove(&call->ab_link);
251 irq_spinlock_unlock(&box->lock, true);
252
253 /* Send back answer */
254 _ipc_answer_free_call(call, false);
255}
256
257/** Simulate sending back a message.
258 *
259 * Most errors are better handled by forming a normal backward
260 * message and sending it as a normal answer.
261 *
262 * @param phone Phone structure the call should appear to come from.
263 * @param call Call structure to be answered.
264 * @param err Return value to be used for the answer.
265 *
266 */
267void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
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
280 call->data.phone = phone;
281
282 IPC_SET_RETVAL(call->data, err);
283 _ipc_answer_free_call(call, false);
284}
285
286/** Unsafe unchecking version of ipc_call.
287 *
288 * @param phone Phone structure the call comes from.
289 * @param box Destination answerbox structure.
290 * @param call Call structure with request.
291 *
292 */
293static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
294{
295 task_t *caller = phone->caller;
296
297 /* Count sent ipc call */
298 irq_spinlock_lock(&caller->lock, true);
299 caller->ipc_info.call_sent++;
300 irq_spinlock_unlock(&caller->lock, true);
301
302 if (!(call->flags & IPC_CALL_FORWARDED)) {
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
312 call->data.phone = phone;
313 call->data.task_id = phone->caller->taskid;
314 }
315
316 irq_spinlock_lock(&box->lock, true);
317 list_append(&call->ab_link, &box->calls);
318 irq_spinlock_unlock(&box->lock, true);
319
320 waitq_wakeup(&box->wq, WAKEUP_FIRST);
321}
322
323/** Send an asynchronous request using a phone to an answerbox.
324 *
325 * @param phone Phone structure the call comes from and which is
326 * connected to the destination answerbox.
327 * @param call Call structure with request.
328 *
329 * @return Return 0 on success, ENOENT on error.
330 *
331 */
332int ipc_call(phone_t *phone, call_t *call)
333{
334 mutex_lock(&phone->lock);
335 if (phone->state != IPC_PHONE_CONNECTED) {
336 mutex_unlock(&phone->lock);
337 if (!(call->flags & IPC_CALL_FORWARDED)) {
338 if (phone->state == IPC_PHONE_HUNGUP)
339 ipc_backsend_err(phone, call, EHANGUP);
340 else
341 ipc_backsend_err(phone, call, ENOENT);
342 }
343
344 return ENOENT;
345 }
346
347 answerbox_t *box = phone->callee;
348 _ipc_call(phone, box, call);
349
350 mutex_unlock(&phone->lock);
351 return 0;
352}
353
354/** Disconnect phone from answerbox.
355 *
356 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
357 * lazily later.
358 *
359 * @param phone Phone structure to be hung up.
360 *
361 * @return 0 if the phone is disconnected.
362 * @return -1 if the phone was already disconnected.
363 *
364 */
365int ipc_phone_hangup(phone_t *phone)
366{
367 mutex_lock(&phone->lock);
368 if (phone->state == IPC_PHONE_FREE ||
369 phone->state == IPC_PHONE_HUNGUP ||
370 phone->state == IPC_PHONE_CONNECTING) {
371 mutex_unlock(&phone->lock);
372 return -1;
373 }
374
375 answerbox_t *box = phone->callee;
376 if (phone->state != IPC_PHONE_SLAMMED) {
377 /* Remove myself from answerbox */
378 irq_spinlock_lock(&box->lock, true);
379 list_remove(&phone->link);
380 irq_spinlock_unlock(&box->lock, true);
381
382 call_t *call = ipc_call_alloc(0);
383 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
384 call->request_method = IPC_M_PHONE_HUNGUP;
385 call->flags |= IPC_CALL_DISCARD_ANSWER;
386 _ipc_call(phone, box, call);
387 }
388
389 phone->state = IPC_PHONE_HUNGUP;
390 mutex_unlock(&phone->lock);
391
392 return 0;
393}
394
395/** Forwards call from one answerbox to another one.
396 *
397 * @param call Call structure to be redirected.
398 * @param newphone Phone structure to target answerbox.
399 * @param oldbox Old answerbox structure.
400 * @param mode Flags that specify mode of the forward operation.
401 *
402 * @return 0 if forwarding succeeded or an error code if
403 * there was an error.
404 *
405 * The return value serves only as an information for the forwarder,
406 * the original caller is notified automatically with EFORWARD.
407 *
408 */
409int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox,
410 unsigned int mode)
411{
412 /* Count forwarded calls */
413 irq_spinlock_lock(&TASK->lock, true);
414 TASK->ipc_info.forwarded++;
415 irq_spinlock_pass(&TASK->lock, &oldbox->lock);
416 list_remove(&call->ab_link);
417 irq_spinlock_unlock(&oldbox->lock, true);
418
419 if (mode & IPC_FF_ROUTE_FROM_ME) {
420 call->data.phone = newphone;
421 call->data.task_id = TASK->taskid;
422 }
423
424 return ipc_call(newphone, call);
425}
426
427
428/** Wait for a phone call.
429 *
430 * @param box Answerbox expecting the call.
431 * @param usec Timeout in microseconds. See documentation for
432 * waitq_sleep_timeout() for decription of its special
433 * meaning.
434 * @param flags Select mode of sleep operation. See documentation for
435 * waitq_sleep_timeout() for description of its special
436 * meaning.
437 *
438 * @return Recived call structure or NULL.
439 *
440 * To distinguish between a call and an answer, have a look at call->flags.
441 *
442 */
443call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, unsigned int flags)
444{
445 call_t *request;
446 uint64_t irq_cnt = 0;
447 uint64_t answer_cnt = 0;
448 uint64_t call_cnt = 0;
449 int rc;
450
451restart:
452 rc = waitq_sleep_timeout(&box->wq, usec, flags);
453 if (SYNCH_FAILED(rc))
454 return NULL;
455
456 irq_spinlock_lock(&box->lock, true);
457 if (!list_empty(&box->irq_notifs)) {
458 /* Count received IRQ notification */
459 irq_cnt++;
460
461 irq_spinlock_lock(&box->irq_lock, false);
462
463 request = list_get_instance(list_first(&box->irq_notifs),
464 call_t, ab_link);
465 list_remove(&request->ab_link);
466
467 irq_spinlock_unlock(&box->irq_lock, false);
468 } else if (!list_empty(&box->answers)) {
469 /* Count received answer */
470 answer_cnt++;
471
472 /* Handle asynchronous answers */
473 request = list_get_instance(list_first(&box->answers),
474 call_t, ab_link);
475 list_remove(&request->ab_link);
476 atomic_dec(&request->caller_phone->active_calls);
477 } else if (!list_empty(&box->calls)) {
478 /* Count received call */
479 call_cnt++;
480
481 /* Handle requests */
482 request = list_get_instance(list_first(&box->calls),
483 call_t, ab_link);
484 list_remove(&request->ab_link);
485
486 /* Append request to dispatch queue */
487 list_append(&request->ab_link, &box->dispatched_calls);
488 } else {
489 /* This can happen regularly after ipc_cleanup */
490 irq_spinlock_unlock(&box->lock, true);
491 goto restart;
492 }
493
494 irq_spinlock_pass(&box->lock, &TASK->lock);
495
496 TASK->ipc_info.irq_notif_received += irq_cnt;
497 TASK->ipc_info.answer_received += answer_cnt;
498 TASK->ipc_info.call_received += call_cnt;
499
500 irq_spinlock_unlock(&TASK->lock, true);
501
502 return request;
503}
504
505/** Answer all calls from list with EHANGUP answer.
506 *
507 * @param box Answerbox with the list.
508 * @param lst Head of the list to be cleaned up.
509 */
510void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
511{
512 irq_spinlock_lock(&box->lock, true);
513 while (!list_empty(lst)) {
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;
522 IPC_SET_RETVAL(call->data, EHANGUP);
523 answer_preprocess(call, &old);
524 _ipc_answer_free_call(call, true);
525
526 irq_spinlock_lock(&box->lock, true);
527 }
528 irq_spinlock_unlock(&box->lock, true);
529}
530
531/** Disconnects all phones connected to an answerbox.
532 *
533 * @param box Answerbox to disconnect phones from.
534 * @param notify_box If true, the answerbox will get a hangup message for
535 * each disconnected phone.
536 *
537 */
538void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
539{
540 phone_t *phone;
541 DEADLOCK_PROBE_INIT(p_phonelck);
542
543 call_t *call = notify_box ? ipc_call_alloc(0) : NULL;
544
545 /* Disconnect all phones connected to our answerbox */
546restart_phones:
547 irq_spinlock_lock(&box->lock, true);
548 while (!list_empty(&box->connected_phones)) {
549 phone = list_get_instance(list_first(&box->connected_phones),
550 phone_t, link);
551 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
552 irq_spinlock_unlock(&box->lock, true);
553 DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
554 goto restart_phones;
555 }
556
557 /* Disconnect phone */
558 ASSERT(phone->state == IPC_PHONE_CONNECTED);
559
560 list_remove(&phone->link);
561 phone->state = IPC_PHONE_SLAMMED;
562
563 if (notify_box) {
564 mutex_unlock(&phone->lock);
565 irq_spinlock_unlock(&box->lock, true);
566
567 // FIXME: phone can become deallocated at any time now
568
569 /*
570 * Send one message to the answerbox for each
571 * phone. Used to make sure the kbox thread
572 * wakes up after the last phone has been
573 * disconnected.
574 */
575 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
576 call->request_method = IPC_M_PHONE_HUNGUP;
577 call->flags |= IPC_CALL_DISCARD_ANSWER;
578 _ipc_call(phone, box, call);
579
580 /* Allocate another call in advance */
581 call = ipc_call_alloc(0);
582
583 /* Must start again */
584 goto restart_phones;
585 }
586
587 mutex_unlock(&phone->lock);
588 }
589
590 irq_spinlock_unlock(&box->lock, true);
591
592 /* Free unused call */
593 if (call)
594 ipc_call_free(call);
595}
596
597static void ipc_forget_all_active_calls(void)
598{
599 call_t *call;
600
601restart:
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. */
655static void ipc_wait_for_all_answered_calls(void)
656{
657 call_t *call;
658 size_t i;
659
660restart:
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
719/** Clean up all IPC communication of the current task.
720 *
721 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
722 * have to change it as well if you want to cleanup other tasks than TASK.
723 *
724 */
725void ipc_cleanup(void)
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
737 /* Disconnect all our phones ('ipc_phone_hangup') */
738 for (size_t i = 0; i < IPC_MAX_PHONES; i++)
739 ipc_phone_hangup(&TASK->phones[i]);
740
741 /* Unsubscribe from any event notifications. */
742 event_cleanup_answerbox(&TASK->answerbox);
743
744 /* Disconnect all connected irqs */
745 ipc_irq_cleanup(&TASK->answerbox);
746
747 /* Disconnect all phones connected to our regular answerbox */
748 ipc_answerbox_slam_phones(&TASK->answerbox, false);
749
750#ifdef CONFIG_UDEBUG
751 /* Clean up kbox thread and communications */
752 ipc_kbox_cleanup();
753#endif
754
755 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
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();
762}
763
764/** Initilize IPC subsystem
765 *
766 */
767void ipc_init(void)
768{
769 ipc_call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
770 NULL, 0);
771 ipc_answerbox_slab = slab_cache_create("answerbox_t",
772 sizeof(answerbox_t), 0, NULL, NULL, 0);
773}
774
775
776static 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 }
807}
808
809/** List answerbox contents.
810 *
811 * @param taskid Task ID.
812 *
813 */
814void ipc_print_task(task_id_t taskid)
815{
816 irq_spinlock_lock(&tasks_lock, true);
817 task_t *task = task_find_by_id(taskid);
818
819 if (!task) {
820 irq_spinlock_unlock(&tasks_lock, true);
821 return;
822 }
823
824 /* Hand-over-hand locking */
825 irq_spinlock_exchange(&tasks_lock, &task->lock);
826
827 printf("[phone id] [calls] [state\n");
828
829 size_t i;
830 for (i = 0; i < IPC_MAX_PHONES; i++) {
831 if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
832 printf("%-10zu (mutex busy)\n", i);
833 continue;
834 }
835
836 if (task->phones[i].state != IPC_PHONE_FREE) {
837 printf("%-10zu %7" PRIun " ", i,
838 atomic_get(&task->phones[i].active_calls));
839
840 switch (task->phones[i].state) {
841 case IPC_PHONE_CONNECTING:
842 printf("connecting");
843 break;
844 case IPC_PHONE_CONNECTED:
845 printf("connected to %" PRIu64 " (%s)",
846 task->phones[i].callee->task->taskid,
847 task->phones[i].callee->task->name);
848 break;
849 case IPC_PHONE_SLAMMED:
850 printf("slammed by %p",
851 task->phones[i].callee);
852 break;
853 case IPC_PHONE_HUNGUP:
854 printf("hung up by %p",
855 task->phones[i].callee);
856 break;
857 default:
858 break;
859 }
860
861 printf("\n");
862 }
863
864 mutex_unlock(&task->phones[i].lock);
865 }
866
867 irq_spinlock_lock(&task->answerbox.lock, false);
868
869#ifdef __32_BITS__
870 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
871 " [flags] [sender\n");
872#endif
873
874#ifdef __64_BITS__
875 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4]"
876 " [arg5] [flags] [sender\n");
877#endif
878
879 printf(" --- incomming calls ---\n");
880 ipc_print_call_list(&task->answerbox.calls);
881 printf(" --- dispatched calls ---\n");
882 ipc_print_call_list(&task->answerbox.dispatched_calls);
883 printf(" --- incoming answers ---\n");
884 ipc_print_call_list(&task->answerbox.answers);
885
886 irq_spinlock_unlock(&task->answerbox.lock, false);
887 irq_spinlock_unlock(&task->lock, true);
888}
889
890/** @}
891 */
Note: See TracBrowser for help on using the repository browser.