source: mainline/kernel/generic/src/ipc/ipc.c@ 853802e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 853802e was 63e27ef, checked in by Jiri Svoboda <jiri@…>, 8 years ago

ASSERT → assert

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