source: mainline/kernel/generic/src/ipc/ipc.c@ 6abfd250

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

Rename caps_apply_to_all to caps_apply_to_type

  • Property mode set to 100644
File size: 25.2 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 <assert.h>
41#include <synch/spinlock.h>
42#include <synch/mutex.h>
43#include <synch/waitq.h>
44#include <ipc/ipc.h>
45#include <ipc/ipcrsc.h>
46#include <abi/ipc/methods.h>
47#include <ipc/kbox.h>
48#include <ipc/event.h>
49#include <ipc/sysipc_ops.h>
50#include <ipc/sysipc_priv.h>
51#include <errno.h>
52#include <mm/slab.h>
53#include <arch.h>
54#include <proc/task.h>
55#include <mem.h>
56#include <print.h>
57#include <console/console.h>
58#include <proc/thread.h>
59#include <arch/interrupt.h>
60#include <ipc/irq.h>
61#include <cap/cap.h>
62
63static void ipc_forget_call(call_t *);
64
65/** Open channel that is assigned automatically to new tasks */
66answerbox_t *ipc_phone_0 = NULL;
67
68static slab_cache_t *call_slab;
69static slab_cache_t *answerbox_slab;
70
71slab_cache_t *phone_slab = NULL;
72
73/** Initialize a call structure.
74 *
75 * @param call Call structure to be initialized.
76 *
77 */
78static void _ipc_call_init(call_t *call)
79{
80 memsetb(call, sizeof(*call), 0);
81 spinlock_initialize(&call->forget_lock, "forget_lock");
82 call->active = false;
83 call->forget = false;
84 call->sender = NULL;
85 call->callerbox = NULL;
86 call->buffer = NULL;
87}
88
89void ipc_call_hold(call_t *call)
90{
91 atomic_inc(&call->refcnt);
92}
93
94void ipc_call_release(call_t *call)
95{
96 if (atomic_predec(&call->refcnt) == 0) {
97 if (call->buffer)
98 free(call->buffer);
99 slab_free(call_slab, call);
100 }
101}
102
103/** Allocate and initialize a call structure.
104 *
105 * The call is initialized, so that the reply will be directed to
106 * TASK->answerbox.
107 *
108 * @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
109 *
110 * @return If flags permit it, return NULL, or initialized kernel
111 * call structure with one reference.
112 *
113 */
114call_t *ipc_call_alloc(unsigned int flags)
115{
116 call_t *call = slab_alloc(call_slab, flags);
117 if (call) {
118 _ipc_call_init(call);
119 ipc_call_hold(call);
120 }
121
122 return call;
123}
124
125/** Deallocate a call structure.
126 *
127 * @param call Call structure to be freed.
128 *
129 */
130void ipc_call_free(call_t *call)
131{
132 ipc_call_release(call);
133}
134
135/** Initialize an answerbox structure.
136 *
137 * @param box Answerbox structure to be initialized.
138 * @param task Task to which the answerbox belongs.
139 *
140 */
141void ipc_answerbox_init(answerbox_t *box, task_t *task)
142{
143 irq_spinlock_initialize(&box->lock, "ipc.box.lock");
144 irq_spinlock_initialize(&box->irq_lock, "ipc.box.irqlock");
145 waitq_initialize(&box->wq);
146 list_initialize(&box->connected_phones);
147 list_initialize(&box->calls);
148 list_initialize(&box->dispatched_calls);
149 list_initialize(&box->answers);
150 list_initialize(&box->irq_notifs);
151 box->task = task;
152}
153
154/** Connect a phone to an answerbox.
155 *
156 * @param phone Initialized phone structure.
157 * @param box Initialized answerbox structure.
158 * @return True if the phone was connected, false otherwise.
159 */
160bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
161{
162 bool active;
163
164 mutex_lock(&phone->lock);
165 irq_spinlock_lock(&box->lock, true);
166
167 active = box->active;
168 if (active) {
169 phone->state = IPC_PHONE_CONNECTED;
170 phone->callee = box;
171 list_append(&phone->link, &box->connected_phones);
172 }
173
174 irq_spinlock_unlock(&box->lock, true);
175 mutex_unlock(&phone->lock);
176
177 return active;
178}
179
180/** Initialize a phone structure.
181 *
182 * @param phone Phone structure to be initialized.
183 * @param caller Owning task.
184 *
185 */
186void ipc_phone_init(phone_t *phone, task_t *caller)
187{
188 mutex_initialize(&phone->lock, MUTEX_PASSIVE);
189 phone->caller = caller;
190 phone->callee = NULL;
191 phone->state = IPC_PHONE_FREE;
192 atomic_set(&phone->active_calls, 0);
193}
194
195/** Helper function to facilitate synchronous calls.
196 *
197 * @param phone Destination kernel phone structure.
198 * @param request Call structure with request.
199 *
200 * @return EOK on success or a negative error code.
201 *
202 */
203int ipc_call_sync(phone_t *phone, call_t *request)
204{
205 answerbox_t *mybox = slab_alloc(answerbox_slab, 0);
206 ipc_answerbox_init(mybox, TASK);
207
208 /* We will receive data in a special box. */
209 request->callerbox = mybox;
210
211 int rc = ipc_call(phone, request);
212 if (rc != EOK) {
213 slab_free(answerbox_slab, mybox);
214 return rc;
215 }
216
217 call_t *answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
218 SYNCH_FLAGS_INTERRUPTIBLE);
219 if (!answer) {
220
221 /*
222 * The sleep was interrupted.
223 *
224 * There are two possibilities now:
225 * 1) the call gets answered before we manage to forget it
226 * 2) we manage to forget the call before it gets answered
227 */
228
229 spinlock_lock(&request->forget_lock);
230 spinlock_lock(&TASK->active_calls_lock);
231
232 assert(!request->forget);
233
234 bool answered = !request->active;
235 if (!answered) {
236 /*
237 * The call is not yet answered and we won the race to
238 * forget it.
239 */
240 ipc_forget_call(request); /* releases locks */
241 rc = EINTR;
242
243 } else {
244 spinlock_unlock(&TASK->active_calls_lock);
245 spinlock_unlock(&request->forget_lock);
246 }
247
248 if (answered) {
249 /*
250 * The other side won the race to answer the call.
251 * It is safe to wait for the answer uninterruptibly
252 * now.
253 */
254 answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
255 SYNCH_FLAGS_NONE);
256 }
257 }
258 assert(!answer || request == answer);
259
260 slab_free(answerbox_slab, mybox);
261 return rc;
262}
263
264/** Answer a message which was not dispatched and is not listed in any queue.
265 *
266 * @param call Call structure to be answered.
267 * @param selflocked If true, then TASK->answebox is locked.
268 *
269 */
270void _ipc_answer_free_call(call_t *call, bool selflocked)
271{
272 /* Count sent answer */
273 irq_spinlock_lock(&TASK->lock, true);
274 TASK->ipc_info.answer_sent++;
275 irq_spinlock_unlock(&TASK->lock, true);
276
277 spinlock_lock(&call->forget_lock);
278 if (call->forget) {
279 /* This is a forgotten call and call->sender is not valid. */
280 spinlock_unlock(&call->forget_lock);
281 ipc_call_free(call);
282 return;
283 } else {
284 /*
285 * If the call is still active, i.e. it was answered
286 * in a non-standard way, remove the call from the
287 * sender's active call list.
288 */
289 if (call->active) {
290 spinlock_lock(&call->sender->active_calls_lock);
291 list_remove(&call->ta_link);
292 spinlock_unlock(&call->sender->active_calls_lock);
293 }
294 }
295 spinlock_unlock(&call->forget_lock);
296
297 answerbox_t *callerbox = call->callerbox ? call->callerbox :
298 &call->sender->answerbox;
299 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
300
301 call->flags |= IPC_CALL_ANSWERED;
302
303 call->data.task_id = TASK->taskid;
304
305 if (do_lock)
306 irq_spinlock_lock(&callerbox->lock, true);
307
308 list_append(&call->ab_link, &callerbox->answers);
309
310 if (do_lock)
311 irq_spinlock_unlock(&callerbox->lock, true);
312
313 waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
314}
315
316/** Answer a message which is in a callee queue.
317 *
318 * @param box Answerbox that is answering the message.
319 * @param call Modified request that is being sent back.
320 *
321 */
322void ipc_answer(answerbox_t *box, call_t *call)
323{
324 /* Remove from active box */
325 irq_spinlock_lock(&box->lock, true);
326 list_remove(&call->ab_link);
327 irq_spinlock_unlock(&box->lock, true);
328
329 /* Send back answer */
330 _ipc_answer_free_call(call, false);
331}
332
333static void _ipc_call_actions_internal(phone_t *phone, call_t *call,
334 bool preforget)
335{
336 task_t *caller = phone->caller;
337
338 call->caller_phone = phone;
339
340 if (preforget) {
341 call->forget = true;
342 } else {
343 atomic_inc(&phone->active_calls);
344 call->sender = caller;
345 call->active = true;
346 spinlock_lock(&caller->active_calls_lock);
347 list_append(&call->ta_link, &caller->active_calls);
348 spinlock_unlock(&caller->active_calls_lock);
349 }
350
351 call->data.phone = phone;
352 call->data.task_id = caller->taskid;
353}
354
355/** Simulate sending back a message.
356 *
357 * Most errors are better handled by forming a normal backward
358 * message and sending it as a normal answer.
359 *
360 * @param phone Phone structure the call should appear to come from.
361 * @param call Call structure to be answered.
362 * @param err Return value to be used for the answer.
363 *
364 */
365void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
366{
367 _ipc_call_actions_internal(phone, call, false);
368 IPC_SET_RETVAL(call->data, err);
369 _ipc_answer_free_call(call, false);
370}
371
372/** Unsafe unchecking version of ipc_call.
373 *
374 * @param phone Phone structure the call comes from.
375 * @param box Destination answerbox structure.
376 * @param call Call structure with request.
377 * @param preforget If true, the call will be delivered already forgotten.
378 *
379 */
380static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call,
381 bool preforget)
382{
383 task_t *caller = phone->caller;
384
385 /* Count sent ipc call */
386 irq_spinlock_lock(&caller->lock, true);
387 caller->ipc_info.call_sent++;
388 irq_spinlock_unlock(&caller->lock, true);
389
390 if (!(call->flags & IPC_CALL_FORWARDED))
391 _ipc_call_actions_internal(phone, call, preforget);
392
393 irq_spinlock_lock(&box->lock, true);
394 list_append(&call->ab_link, &box->calls);
395 irq_spinlock_unlock(&box->lock, true);
396
397 waitq_wakeup(&box->wq, WAKEUP_FIRST);
398}
399
400/** Send an asynchronous request using a phone to an answerbox.
401 *
402 * @param phone Phone structure the call comes from and which is
403 * connected to the destination answerbox.
404 * @param call Call structure with request.
405 *
406 * @return Return 0 on success, ENOENT on error.
407 *
408 */
409int ipc_call(phone_t *phone, call_t *call)
410{
411 mutex_lock(&phone->lock);
412 if (phone->state != IPC_PHONE_CONNECTED) {
413 mutex_unlock(&phone->lock);
414 if (!(call->flags & IPC_CALL_FORWARDED)) {
415 if (phone->state == IPC_PHONE_HUNGUP)
416 ipc_backsend_err(phone, call, EHANGUP);
417 else
418 ipc_backsend_err(phone, call, ENOENT);
419 }
420
421 return ENOENT;
422 }
423
424 answerbox_t *box = phone->callee;
425 _ipc_call(phone, box, call, false);
426
427 mutex_unlock(&phone->lock);
428 return 0;
429}
430
431/** Disconnect phone from answerbox.
432 *
433 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
434 * lazily later.
435 *
436 * @param phone Phone structure to be hung up.
437 *
438 * @return 0 if the phone is disconnected.
439 * @return -1 if the phone was already disconnected.
440 *
441 */
442int ipc_phone_hangup(phone_t *phone)
443{
444 mutex_lock(&phone->lock);
445 if (phone->state == IPC_PHONE_FREE ||
446 phone->state == IPC_PHONE_HUNGUP ||
447 phone->state == IPC_PHONE_CONNECTING) {
448 mutex_unlock(&phone->lock);
449 return -1;
450 }
451
452 answerbox_t *box = phone->callee;
453 if (phone->state != IPC_PHONE_SLAMMED) {
454 /* Remove myself from answerbox */
455 irq_spinlock_lock(&box->lock, true);
456 list_remove(&phone->link);
457 irq_spinlock_unlock(&box->lock, true);
458
459 call_t *call = ipc_call_alloc(0);
460 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
461 call->request_method = IPC_M_PHONE_HUNGUP;
462 call->flags |= IPC_CALL_DISCARD_ANSWER;
463 _ipc_call(phone, box, call, false);
464 }
465
466 phone->state = IPC_PHONE_HUNGUP;
467 mutex_unlock(&phone->lock);
468
469 return 0;
470}
471
472/** Forwards call from one answerbox to another one.
473 *
474 * @param call Call structure to be redirected.
475 * @param newphone Phone structure to target answerbox.
476 * @param oldbox Old answerbox structure.
477 * @param mode Flags that specify mode of the forward operation.
478 *
479 * @return 0 if forwarding succeeded or an error code if
480 * there was an error.
481 *
482 * The return value serves only as an information for the forwarder,
483 * the original caller is notified automatically with EFORWARD.
484 *
485 */
486int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox,
487 unsigned int mode)
488{
489 /* Count forwarded calls */
490 irq_spinlock_lock(&TASK->lock, true);
491 TASK->ipc_info.forwarded++;
492 irq_spinlock_pass(&TASK->lock, &oldbox->lock);
493 list_remove(&call->ab_link);
494 irq_spinlock_unlock(&oldbox->lock, true);
495
496 if (mode & IPC_FF_ROUTE_FROM_ME) {
497 call->data.phone = newphone;
498 call->data.task_id = TASK->taskid;
499 }
500
501 return ipc_call(newphone, call);
502}
503
504
505/** Wait for a phone call.
506 *
507 * @param box Answerbox expecting the call.
508 * @param usec Timeout in microseconds. See documentation for
509 * waitq_sleep_timeout() for decription of its special
510 * meaning.
511 * @param flags Select mode of sleep operation. See documentation for
512 * waitq_sleep_timeout() for description of its special
513 * meaning.
514 *
515 * @return Recived call structure or NULL.
516 *
517 * To distinguish between a call and an answer, have a look at call->flags.
518 *
519 */
520call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, unsigned int flags)
521{
522 call_t *request;
523 uint64_t irq_cnt = 0;
524 uint64_t answer_cnt = 0;
525 uint64_t call_cnt = 0;
526 int rc;
527
528restart:
529 rc = waitq_sleep_timeout(&box->wq, usec, flags);
530 if (SYNCH_FAILED(rc))
531 return NULL;
532
533 irq_spinlock_lock(&box->lock, true);
534 if (!list_empty(&box->irq_notifs)) {
535 /* Count received IRQ notification */
536 irq_cnt++;
537
538 irq_spinlock_lock(&box->irq_lock, false);
539
540 request = list_get_instance(list_first(&box->irq_notifs),
541 call_t, ab_link);
542 list_remove(&request->ab_link);
543
544 irq_spinlock_unlock(&box->irq_lock, false);
545 } else if (!list_empty(&box->answers)) {
546 /* Count received answer */
547 answer_cnt++;
548
549 /* Handle asynchronous answers */
550 request = list_get_instance(list_first(&box->answers),
551 call_t, ab_link);
552 list_remove(&request->ab_link);
553 atomic_dec(&request->caller_phone->active_calls);
554 } else if (!list_empty(&box->calls)) {
555 /* Count received call */
556 call_cnt++;
557
558 /* Handle requests */
559 request = list_get_instance(list_first(&box->calls),
560 call_t, ab_link);
561 list_remove(&request->ab_link);
562
563 /* Append request to dispatch queue */
564 list_append(&request->ab_link, &box->dispatched_calls);
565 } else {
566 /* This can happen regularly after ipc_cleanup */
567 irq_spinlock_unlock(&box->lock, true);
568 goto restart;
569 }
570
571 irq_spinlock_pass(&box->lock, &TASK->lock);
572
573 TASK->ipc_info.irq_notif_received += irq_cnt;
574 TASK->ipc_info.answer_received += answer_cnt;
575 TASK->ipc_info.call_received += call_cnt;
576
577 irq_spinlock_unlock(&TASK->lock, true);
578
579 return request;
580}
581
582/** Answer all calls from list with EHANGUP answer.
583 *
584 * @param box Answerbox with the list.
585 * @param lst Head of the list to be cleaned up.
586 */
587void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
588{
589 irq_spinlock_lock(&box->lock, true);
590 while (!list_empty(lst)) {
591 call_t *call = list_get_instance(list_first(lst), call_t,
592 ab_link);
593
594 list_remove(&call->ab_link);
595
596 irq_spinlock_unlock(&box->lock, true);
597
598 if (lst == &box->calls)
599 SYSIPC_OP(request_process, call, box);
600
601 ipc_data_t old = call->data;
602 IPC_SET_RETVAL(call->data, EHANGUP);
603 answer_preprocess(call, &old);
604 _ipc_answer_free_call(call, true);
605
606 irq_spinlock_lock(&box->lock, true);
607 }
608 irq_spinlock_unlock(&box->lock, true);
609}
610
611/** Disconnects all phones connected to an answerbox.
612 *
613 * @param box Answerbox to disconnect phones from.
614 * @param notify_box If true, the answerbox will get a hangup message for
615 * each disconnected phone.
616 *
617 */
618void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
619{
620 phone_t *phone;
621 DEADLOCK_PROBE_INIT(p_phonelck);
622
623 /* Disconnect all phones connected to our answerbox */
624restart_phones:
625 irq_spinlock_lock(&box->lock, true);
626 while (!list_empty(&box->connected_phones)) {
627 phone = list_get_instance(list_first(&box->connected_phones),
628 phone_t, link);
629 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
630 irq_spinlock_unlock(&box->lock, true);
631 DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
632 goto restart_phones;
633 }
634
635 /* Disconnect phone */
636 assert(phone->state == IPC_PHONE_CONNECTED);
637
638 list_remove(&phone->link);
639 phone->state = IPC_PHONE_SLAMMED;
640
641 if (notify_box) {
642 task_hold(phone->caller);
643 mutex_unlock(&phone->lock);
644 irq_spinlock_unlock(&box->lock, true);
645
646 /*
647 * Send one call to the answerbox for each phone.
648 * Used to make sure the kbox thread wakes up after
649 * the last phone has been disconnected. The call is
650 * forgotten upon sending, so the "caller" may cease
651 * to exist as soon as we release it.
652 */
653 call_t *call = ipc_call_alloc(0);
654 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
655 call->request_method = IPC_M_PHONE_HUNGUP;
656 call->flags |= IPC_CALL_DISCARD_ANSWER;
657 _ipc_call(phone, box, call, true);
658
659 task_release(phone->caller);
660
661 /* Must start again */
662 goto restart_phones;
663 }
664
665 mutex_unlock(&phone->lock);
666 }
667
668 irq_spinlock_unlock(&box->lock, true);
669}
670
671static void ipc_forget_call(call_t *call)
672{
673 assert(spinlock_locked(&TASK->active_calls_lock));
674 assert(spinlock_locked(&call->forget_lock));
675
676 /*
677 * Forget the call and donate it to the task which holds up the answer.
678 */
679
680 call->forget = true;
681 call->sender = NULL;
682 list_remove(&call->ta_link);
683
684 /*
685 * The call may be freed by _ipc_answer_free_call() before we are done
686 * with it; to avoid working with a destroyed call_t structure, we
687 * must hold a reference to it.
688 */
689 ipc_call_hold(call);
690
691 spinlock_unlock(&call->forget_lock);
692 spinlock_unlock(&TASK->active_calls_lock);
693
694 atomic_dec(&call->caller_phone->active_calls);
695
696 SYSIPC_OP(request_forget, call);
697
698 ipc_call_release(call);
699}
700
701static void ipc_forget_all_active_calls(void)
702{
703 call_t *call;
704
705restart:
706 spinlock_lock(&TASK->active_calls_lock);
707 if (list_empty(&TASK->active_calls)) {
708 /*
709 * We are done, there are no more active calls.
710 * Nota bene: there may still be answers waiting for pick up.
711 */
712 spinlock_unlock(&TASK->active_calls_lock);
713 return;
714 }
715
716 call = list_get_instance(list_first(&TASK->active_calls), call_t,
717 ta_link);
718
719 if (!spinlock_trylock(&call->forget_lock)) {
720 /*
721 * Avoid deadlock and let async_answer() or
722 * _ipc_answer_free_call() win the race to dequeue the first
723 * call on the list.
724 */
725 spinlock_unlock(&TASK->active_calls_lock);
726 goto restart;
727 }
728
729 ipc_forget_call(call);
730
731 goto restart;
732}
733
734static bool phone_cap_wait_cb(cap_t *cap, void *arg)
735{
736 phone_t *phone = (phone_t *) cap->kobject;
737 bool *restart = (bool *) arg;
738
739 mutex_lock(&phone->lock);
740 if ((phone->state == IPC_PHONE_HUNGUP) &&
741 (atomic_get(&phone->active_calls) == 0)) {
742 phone->state = IPC_PHONE_FREE;
743 phone->callee = NULL;
744 }
745
746 /*
747 * We might have had some IPC_PHONE_CONNECTING phones at the beginning
748 * of ipc_cleanup(). Depending on whether these were forgotten or
749 * answered, they will eventually enter the IPC_PHONE_FREE or
750 * IPC_PHONE_CONNECTED states, respectively. In the latter case, the
751 * other side may slam the open phones at any time, in which case we
752 * will get an IPC_PHONE_SLAMMED phone.
753 */
754 if ((phone->state == IPC_PHONE_CONNECTED) ||
755 (phone->state == IPC_PHONE_SLAMMED)) {
756 mutex_unlock(&phone->lock);
757 ipc_phone_hangup(phone);
758 /*
759 * Now there may be one extra active call, which needs to be
760 * forgotten.
761 */
762 ipc_forget_all_active_calls();
763 *restart = true;
764 return false;
765 }
766
767 /*
768 * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
769 * in HUNGUP state, we wait for the reply to come
770 */
771 if (phone->state != IPC_PHONE_FREE) {
772 mutex_unlock(&phone->lock);
773 return false;
774 }
775
776 mutex_unlock(&phone->lock);
777 return true;
778}
779
780/** Wait for all answers to asynchronous calls to arrive. */
781static void ipc_wait_for_all_answered_calls(void)
782{
783 call_t *call;
784 bool restart;
785
786restart:
787 /*
788 * Go through all phones, until they are all free.
789 * Locking is needed as there may be connection handshakes in progress.
790 */
791 restart = false;
792 if (caps_apply_to_type(TASK, CAP_TYPE_PHONE, phone_cap_wait_cb,
793 &restart)) {
794 /* Got into cleanup */
795 return;
796 }
797 if (restart)
798 goto restart;
799
800 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
801 SYNCH_FLAGS_NONE);
802 assert(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
803
804 SYSIPC_OP(answer_process, call);
805
806 ipc_call_free(call);
807 goto restart;
808}
809
810static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
811{
812 phone_t *phone = (phone_t *) cap->kobject;
813 ipc_phone_hangup(phone);
814 return true;
815}
816
817static bool irq_cap_cleanup_cb(cap_t *cap, void *arg)
818{
819 ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
820 return true;
821}
822
823/** Clean up all IPC communication of the current task.
824 *
825 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
826 * have to change it as well if you want to cleanup other tasks than TASK.
827 *
828 */
829void ipc_cleanup(void)
830{
831 /*
832 * Mark the answerbox as inactive.
833 *
834 * The main purpose for doing this is to prevent any pending callback
835 * connections from getting established beyond this point.
836 */
837 irq_spinlock_lock(&TASK->answerbox.lock, true);
838 TASK->answerbox.active = false;
839 irq_spinlock_unlock(&TASK->answerbox.lock, true);
840
841 /* Disconnect all our phones ('ipc_phone_hangup') */
842 caps_apply_to_type(TASK, CAP_TYPE_PHONE, phone_cap_cleanup_cb, NULL);
843
844 /* Unsubscribe from any event notifications. */
845 event_cleanup_answerbox(&TASK->answerbox);
846
847 /* Disconnect all connected IRQs */
848 caps_apply_to_type(TASK, CAP_TYPE_IRQ, irq_cap_cleanup_cb, NULL);
849
850 /* Disconnect all phones connected to our regular answerbox */
851 ipc_answerbox_slam_phones(&TASK->answerbox, false);
852
853#ifdef CONFIG_UDEBUG
854 /* Clean up kbox thread and communications */
855 ipc_kbox_cleanup();
856#endif
857
858 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
859 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
860 ipc_cleanup_call_list(&TASK->answerbox,
861 &TASK->answerbox.dispatched_calls);
862
863 ipc_forget_all_active_calls();
864 ipc_wait_for_all_answered_calls();
865}
866
867/** Initilize IPC subsystem
868 *
869 */
870void ipc_init(void)
871{
872 call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
873 NULL, 0);
874 phone_slab = slab_cache_create("phone_t", sizeof(phone_t), 0, NULL,
875 NULL, 0);
876 answerbox_slab = slab_cache_create("answerbox_t", sizeof(answerbox_t),
877 0, NULL, NULL, 0);
878}
879
880
881static void ipc_print_call_list(list_t *list)
882{
883 list_foreach(*list, ab_link, call_t, call) {
884#ifdef __32_BITS__
885 printf("%10p ", call);
886#endif
887
888#ifdef __64_BITS__
889 printf("%18p ", call);
890#endif
891
892 spinlock_lock(&call->forget_lock);
893
894 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
895 " %-6" PRIun " %-6" PRIun " %-7x",
896 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
897 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
898 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
899 call->flags);
900
901 if (call->forget) {
902 printf(" ? (call forgotten)\n");
903 } else {
904 printf(" %" PRIu64 " (%s)\n",
905 call->sender->taskid, call->sender->name);
906 }
907
908 spinlock_unlock(&call->forget_lock);
909 }
910}
911
912static bool print_task_phone_cb(cap_t *cap, void *arg)
913{
914 phone_t *phone = (phone_t *) cap->kobject;
915
916 mutex_lock(&phone->lock);
917 if (phone->state != IPC_PHONE_FREE) {
918 printf("%-11d %7" PRIun " ", cap->handle,
919 atomic_get(&phone->active_calls));
920
921 switch (phone->state) {
922 case IPC_PHONE_CONNECTING:
923 printf("connecting");
924 break;
925 case IPC_PHONE_CONNECTED:
926 printf("connected to %" PRIu64 " (%s)",
927 phone->callee->task->taskid,
928 phone->callee->task->name);
929 break;
930 case IPC_PHONE_SLAMMED:
931 printf("slammed by %p", phone->callee);
932 break;
933 case IPC_PHONE_HUNGUP:
934 printf("hung up by %p", phone->callee);
935 break;
936 default:
937 break;
938 }
939
940 printf("\n");
941 }
942 mutex_unlock(&phone->lock);
943
944 return true;
945}
946
947/** List answerbox contents.
948 *
949 * @param taskid Task ID.
950 *
951 */
952void ipc_print_task(task_id_t taskid)
953{
954 irq_spinlock_lock(&tasks_lock, true);
955 task_t *task = task_find_by_id(taskid);
956 if (!task) {
957 irq_spinlock_unlock(&tasks_lock, true);
958 return;
959 }
960 task_hold(task);
961 irq_spinlock_unlock(&tasks_lock, true);
962
963 printf("[phone cap] [calls] [state\n");
964
965 caps_apply_to_type(task, CAP_TYPE_PHONE, print_task_phone_cb, NULL);
966
967 irq_spinlock_lock(&task->lock, true);
968 irq_spinlock_lock(&task->answerbox.lock, false);
969
970#ifdef __32_BITS__
971 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
972 " [flags] [sender\n");
973#endif
974
975#ifdef __64_BITS__
976 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4]"
977 " [arg5] [flags] [sender\n");
978#endif
979
980 printf(" --- incomming calls ---\n");
981 ipc_print_call_list(&task->answerbox.calls);
982 printf(" --- dispatched calls ---\n");
983 ipc_print_call_list(&task->answerbox.dispatched_calls);
984 printf(" --- incoming answers ---\n");
985 ipc_print_call_list(&task->answerbox.answers);
986
987 irq_spinlock_unlock(&task->answerbox.lock, false);
988 irq_spinlock_unlock(&task->lock, true);
989
990 task_release(task);
991}
992
993/** @}
994 */
Note: See TracBrowser for help on using the repository browser.