source: mainline/kernel/generic/src/ipc/ipc.c@ 0b00599

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

Use correct answerbox to answer IPC_M_PHONE_HUNGUP in kbox thread

When ipc_kbox_cleanup() slams the phones connected to the kbox, it
requests answerbox notification. To that end,
ipc_answerbox_slam_phones() allocates a new IPC_M_PHONE_HUNGUP call per
each slammed phone and sends it directly to its own kbox. The allocation
of the call structure happens in the context of the debugee, not the
debugger, so call→callerbox is wrong because it is initialized with
&TASK→answerbox. This causes confusion in _ipc_answer_free_call()
invoked from kbox_proc_phone_hungup(), which picks a wrong answerbox for
the answer. The debugger, in turn, never sees the call answered and
lingers in its own ipc_cleanup() forever.

We correct this by allowing call→callerbox to be NULL, in which case
_ipc_answer_free_call() will use call→sender→answerbox instead.

  • Property mode set to 100644
File size: 24.5 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
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{
332 task_t *caller = phone->caller;
333
334 atomic_inc(&phone->active_calls);
335 call->caller_phone = phone;
336 call->sender = caller;
337
338 call->active = true;
339 spinlock_lock(&caller->active_calls_lock);
340 list_append(&call->ta_link, &caller->active_calls);
341 spinlock_unlock(&caller->active_calls_lock);
342
343 call->data.phone = phone;
344 call->data.task_id = caller->taskid;
345}
346
347/** Simulate sending back a message.
348 *
349 * Most errors are better handled by forming a normal backward
350 * message and sending it as a normal answer.
351 *
352 * @param phone Phone structure the call should appear to come from.
353 * @param call Call structure to be answered.
354 * @param err Return value to be used for the answer.
355 *
356 */
357void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
358{
359 _ipc_call_actions_internal(phone, call);
360 IPC_SET_RETVAL(call->data, err);
361 _ipc_answer_free_call(call, false);
362}
363
364/** Unsafe unchecking version of ipc_call.
365 *
366 * @param phone Phone structure the call comes from.
367 * @param box Destination answerbox structure.
368 * @param call Call structure with request.
369 *
370 */
371static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
372{
373 task_t *caller = phone->caller;
374
375 /* Count sent ipc call */
376 irq_spinlock_lock(&caller->lock, true);
377 caller->ipc_info.call_sent++;
378 irq_spinlock_unlock(&caller->lock, true);
379
380 if (!(call->flags & IPC_CALL_FORWARDED))
381 _ipc_call_actions_internal(phone, call);
382
383 irq_spinlock_lock(&box->lock, true);
384 list_append(&call->ab_link, &box->calls);
385 irq_spinlock_unlock(&box->lock, true);
386
387 waitq_wakeup(&box->wq, WAKEUP_FIRST);
388}
389
390/** Send an asynchronous request using a phone to an answerbox.
391 *
392 * @param phone Phone structure the call comes from and which is
393 * connected to the destination answerbox.
394 * @param call Call structure with request.
395 *
396 * @return Return 0 on success, ENOENT on error.
397 *
398 */
399int ipc_call(phone_t *phone, call_t *call)
400{
401 mutex_lock(&phone->lock);
402 if (phone->state != IPC_PHONE_CONNECTED) {
403 mutex_unlock(&phone->lock);
404 if (!(call->flags & IPC_CALL_FORWARDED)) {
405 if (phone->state == IPC_PHONE_HUNGUP)
406 ipc_backsend_err(phone, call, EHANGUP);
407 else
408 ipc_backsend_err(phone, call, ENOENT);
409 }
410
411 return ENOENT;
412 }
413
414 answerbox_t *box = phone->callee;
415 _ipc_call(phone, box, call);
416
417 mutex_unlock(&phone->lock);
418 return 0;
419}
420
421/** Disconnect phone from answerbox.
422 *
423 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
424 * lazily later.
425 *
426 * @param phone Phone structure to be hung up.
427 *
428 * @return 0 if the phone is disconnected.
429 * @return -1 if the phone was already disconnected.
430 *
431 */
432int ipc_phone_hangup(phone_t *phone)
433{
434 mutex_lock(&phone->lock);
435 if (phone->state == IPC_PHONE_FREE ||
436 phone->state == IPC_PHONE_HUNGUP ||
437 phone->state == IPC_PHONE_CONNECTING) {
438 mutex_unlock(&phone->lock);
439 return -1;
440 }
441
442 answerbox_t *box = phone->callee;
443 if (phone->state != IPC_PHONE_SLAMMED) {
444 /* Remove myself from answerbox */
445 irq_spinlock_lock(&box->lock, true);
446 list_remove(&phone->link);
447 irq_spinlock_unlock(&box->lock, true);
448
449 call_t *call = ipc_call_alloc(0);
450 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
451 call->request_method = IPC_M_PHONE_HUNGUP;
452 call->flags |= IPC_CALL_DISCARD_ANSWER;
453 _ipc_call(phone, box, call);
454 }
455
456 phone->state = IPC_PHONE_HUNGUP;
457 mutex_unlock(&phone->lock);
458
459 return 0;
460}
461
462/** Forwards call from one answerbox to another one.
463 *
464 * @param call Call structure to be redirected.
465 * @param newphone Phone structure to target answerbox.
466 * @param oldbox Old answerbox structure.
467 * @param mode Flags that specify mode of the forward operation.
468 *
469 * @return 0 if forwarding succeeded or an error code if
470 * there was an error.
471 *
472 * The return value serves only as an information for the forwarder,
473 * the original caller is notified automatically with EFORWARD.
474 *
475 */
476int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox,
477 unsigned int mode)
478{
479 /* Count forwarded calls */
480 irq_spinlock_lock(&TASK->lock, true);
481 TASK->ipc_info.forwarded++;
482 irq_spinlock_pass(&TASK->lock, &oldbox->lock);
483 list_remove(&call->ab_link);
484 irq_spinlock_unlock(&oldbox->lock, true);
485
486 if (mode & IPC_FF_ROUTE_FROM_ME) {
487 call->data.phone = newphone;
488 call->data.task_id = TASK->taskid;
489 }
490
491 return ipc_call(newphone, call);
492}
493
494
495/** Wait for a phone call.
496 *
497 * @param box Answerbox expecting the call.
498 * @param usec Timeout in microseconds. See documentation for
499 * waitq_sleep_timeout() for decription of its special
500 * meaning.
501 * @param flags Select mode of sleep operation. See documentation for
502 * waitq_sleep_timeout() for description of its special
503 * meaning.
504 *
505 * @return Recived call structure or NULL.
506 *
507 * To distinguish between a call and an answer, have a look at call->flags.
508 *
509 */
510call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, unsigned int flags)
511{
512 call_t *request;
513 uint64_t irq_cnt = 0;
514 uint64_t answer_cnt = 0;
515 uint64_t call_cnt = 0;
516 int rc;
517
518restart:
519 rc = waitq_sleep_timeout(&box->wq, usec, flags);
520 if (SYNCH_FAILED(rc))
521 return NULL;
522
523 irq_spinlock_lock(&box->lock, true);
524 if (!list_empty(&box->irq_notifs)) {
525 /* Count received IRQ notification */
526 irq_cnt++;
527
528 irq_spinlock_lock(&box->irq_lock, false);
529
530 request = list_get_instance(list_first(&box->irq_notifs),
531 call_t, ab_link);
532 list_remove(&request->ab_link);
533
534 irq_spinlock_unlock(&box->irq_lock, false);
535 } else if (!list_empty(&box->answers)) {
536 /* Count received answer */
537 answer_cnt++;
538
539 /* Handle asynchronous answers */
540 request = list_get_instance(list_first(&box->answers),
541 call_t, ab_link);
542 list_remove(&request->ab_link);
543 atomic_dec(&request->caller_phone->active_calls);
544 } else if (!list_empty(&box->calls)) {
545 /* Count received call */
546 call_cnt++;
547
548 /* Handle requests */
549 request = list_get_instance(list_first(&box->calls),
550 call_t, ab_link);
551 list_remove(&request->ab_link);
552
553 /* Append request to dispatch queue */
554 list_append(&request->ab_link, &box->dispatched_calls);
555 } else {
556 /* This can happen regularly after ipc_cleanup */
557 irq_spinlock_unlock(&box->lock, true);
558 goto restart;
559 }
560
561 irq_spinlock_pass(&box->lock, &TASK->lock);
562
563 TASK->ipc_info.irq_notif_received += irq_cnt;
564 TASK->ipc_info.answer_received += answer_cnt;
565 TASK->ipc_info.call_received += call_cnt;
566
567 irq_spinlock_unlock(&TASK->lock, true);
568
569 return request;
570}
571
572/** Answer all calls from list with EHANGUP answer.
573 *
574 * @param box Answerbox with the list.
575 * @param lst Head of the list to be cleaned up.
576 */
577void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
578{
579 irq_spinlock_lock(&box->lock, true);
580 while (!list_empty(lst)) {
581 call_t *call = list_get_instance(list_first(lst), call_t,
582 ab_link);
583
584 list_remove(&call->ab_link);
585
586 irq_spinlock_unlock(&box->lock, true);
587
588 if (lst == &box->calls)
589 SYSIPC_OP(request_process, call, box);
590
591 ipc_data_t old = call->data;
592 IPC_SET_RETVAL(call->data, EHANGUP);
593 answer_preprocess(call, &old);
594 _ipc_answer_free_call(call, true);
595
596 irq_spinlock_lock(&box->lock, true);
597 }
598 irq_spinlock_unlock(&box->lock, true);
599}
600
601/** Disconnects all phones connected to an answerbox.
602 *
603 * @param box Answerbox to disconnect phones from.
604 * @param notify_box If true, the answerbox will get a hangup message for
605 * each disconnected phone.
606 *
607 */
608void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
609{
610 phone_t *phone;
611 DEADLOCK_PROBE_INIT(p_phonelck);
612
613 call_t *call = notify_box ? ipc_call_alloc(0) : NULL;
614
615 /* Disconnect all phones connected to our answerbox */
616restart_phones:
617 irq_spinlock_lock(&box->lock, true);
618 while (!list_empty(&box->connected_phones)) {
619 phone = list_get_instance(list_first(&box->connected_phones),
620 phone_t, link);
621 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
622 irq_spinlock_unlock(&box->lock, true);
623 DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
624 goto restart_phones;
625 }
626
627 /* Disconnect phone */
628 ASSERT(phone->state == IPC_PHONE_CONNECTED);
629
630 list_remove(&phone->link);
631 phone->state = IPC_PHONE_SLAMMED;
632
633 if (notify_box) {
634 mutex_unlock(&phone->lock);
635 irq_spinlock_unlock(&box->lock, true);
636
637 // FIXME: phone can become deallocated at any time now
638
639 /*
640 * Send one message to the answerbox for each
641 * phone. Used to make sure the kbox thread
642 * wakes up after the last phone has been
643 * disconnected.
644 */
645 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
646 call->request_method = IPC_M_PHONE_HUNGUP;
647 call->flags |= IPC_CALL_DISCARD_ANSWER;
648 _ipc_call(phone, box, call);
649
650 /* Allocate another call in advance */
651 call = ipc_call_alloc(0);
652
653 /* Must start again */
654 goto restart_phones;
655 }
656
657 mutex_unlock(&phone->lock);
658 }
659
660 irq_spinlock_unlock(&box->lock, true);
661
662 /* Free unused call */
663 if (call)
664 ipc_call_free(call);
665}
666
667static void ipc_forget_call(call_t *call)
668{
669 ASSERT(spinlock_locked(&TASK->active_calls_lock));
670 ASSERT(spinlock_locked(&call->forget_lock));
671
672 /*
673 * Forget the call and donate it to the task which holds up the answer.
674 */
675
676 call->forget = true;
677 call->sender = NULL;
678 list_remove(&call->ta_link);
679
680 /*
681 * The call may be freed by _ipc_answer_free_call() before we are done
682 * with it; to avoid working with a destroyed call_t structure, we
683 * must hold a reference to it.
684 */
685 ipc_call_hold(call);
686
687 spinlock_unlock(&call->forget_lock);
688 spinlock_unlock(&TASK->active_calls_lock);
689
690 atomic_dec(&call->caller_phone->active_calls);
691
692 SYSIPC_OP(request_forget, call);
693
694 ipc_call_release(call);
695}
696
697static void ipc_forget_all_active_calls(void)
698{
699 call_t *call;
700
701restart:
702 spinlock_lock(&TASK->active_calls_lock);
703 if (list_empty(&TASK->active_calls)) {
704 /*
705 * We are done, there are no more active calls.
706 * Nota bene: there may still be answers waiting for pick up.
707 */
708 spinlock_unlock(&TASK->active_calls_lock);
709 return;
710 }
711
712 call = list_get_instance(list_first(&TASK->active_calls), call_t,
713 ta_link);
714
715 if (!spinlock_trylock(&call->forget_lock)) {
716 /*
717 * Avoid deadlock and let async_answer() or
718 * _ipc_answer_free_call() win the race to dequeue the first
719 * call on the list.
720 */
721 spinlock_unlock(&TASK->active_calls_lock);
722 goto restart;
723 }
724
725 ipc_forget_call(call);
726
727 goto restart;
728}
729
730/** Wait for all answers to asynchronous calls to arrive. */
731static void ipc_wait_for_all_answered_calls(void)
732{
733 call_t *call;
734 size_t i;
735
736restart:
737 /*
738 * Go through all phones, until they are all free.
739 * Locking is needed as there may be connection handshakes in progress.
740 */
741 for (i = 0; i < IPC_MAX_PHONES; i++) {
742 phone_t *phone = &TASK->phones[i];
743
744 mutex_lock(&phone->lock);
745 if ((phone->state == IPC_PHONE_HUNGUP) &&
746 (atomic_get(&phone->active_calls) == 0)) {
747 phone->state = IPC_PHONE_FREE;
748 phone->callee = NULL;
749 }
750
751 /*
752 * We might have had some IPC_PHONE_CONNECTING phones at the
753 * beginning of ipc_cleanup(). Depending on whether these were
754 * forgotten or answered, they will eventually enter the
755 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
756 * In the latter case, the other side may slam the open phones
757 * at any time, in which case we will get an IPC_PHONE_SLAMMED
758 * phone.
759 */
760 if ((phone->state == IPC_PHONE_CONNECTED) ||
761 (phone->state == IPC_PHONE_SLAMMED)) {
762 mutex_unlock(&phone->lock);
763 ipc_phone_hangup(phone);
764 /*
765 * Now there may be one extra active call, which needs
766 * to be forgotten.
767 */
768 ipc_forget_all_active_calls();
769 goto restart;
770 }
771
772 /*
773 * If the hangup succeeded, it has sent a HANGUP message, the
774 * IPC is now in HUNGUP state, we wait for the reply to come
775 */
776 if (phone->state != IPC_PHONE_FREE) {
777 mutex_unlock(&phone->lock);
778 break;
779 }
780
781 mutex_unlock(&phone->lock);
782 }
783
784 /* Got into cleanup */
785 if (i == IPC_MAX_PHONES)
786 return;
787
788 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
789 SYNCH_FLAGS_NONE);
790 ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
791
792 SYSIPC_OP(answer_process, call);
793
794 ipc_call_free(call);
795 goto restart;
796}
797
798/** Clean up all IPC communication of the current task.
799 *
800 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
801 * have to change it as well if you want to cleanup other tasks than TASK.
802 *
803 */
804void ipc_cleanup(void)
805{
806 /*
807 * Mark the answerbox as inactive.
808 *
809 * The main purpose for doing this is to prevent any pending callback
810 * connections from getting established beyond this point.
811 */
812 irq_spinlock_lock(&TASK->answerbox.lock, true);
813 TASK->answerbox.active = false;
814 irq_spinlock_unlock(&TASK->answerbox.lock, true);
815
816 /* Disconnect all our phones ('ipc_phone_hangup') */
817 for (size_t i = 0; i < IPC_MAX_PHONES; i++)
818 ipc_phone_hangup(&TASK->phones[i]);
819
820 /* Unsubscribe from any event notifications. */
821 event_cleanup_answerbox(&TASK->answerbox);
822
823 /* Disconnect all connected irqs */
824 ipc_irq_cleanup(&TASK->answerbox);
825
826 /* Disconnect all phones connected to our regular answerbox */
827 ipc_answerbox_slam_phones(&TASK->answerbox, false);
828
829#ifdef CONFIG_UDEBUG
830 /* Clean up kbox thread and communications */
831 ipc_kbox_cleanup();
832#endif
833
834 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
835 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
836 ipc_cleanup_call_list(&TASK->answerbox,
837 &TASK->answerbox.dispatched_calls);
838
839 ipc_forget_all_active_calls();
840 ipc_wait_for_all_answered_calls();
841}
842
843/** Initilize IPC subsystem
844 *
845 */
846void ipc_init(void)
847{
848 ipc_call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
849 NULL, 0);
850 ipc_answerbox_slab = slab_cache_create("answerbox_t",
851 sizeof(answerbox_t), 0, NULL, NULL, 0);
852}
853
854
855static void ipc_print_call_list(list_t *list)
856{
857 list_foreach(*list, ab_link, call_t, call) {
858#ifdef __32_BITS__
859 printf("%10p ", call);
860#endif
861
862#ifdef __64_BITS__
863 printf("%18p ", call);
864#endif
865
866 spinlock_lock(&call->forget_lock);
867
868 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
869 " %-6" PRIun " %-6" PRIun " %-7x",
870 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
871 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
872 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
873 call->flags);
874
875 if (call->forget) {
876 printf(" ? (call forgotten)\n");
877 } else {
878 printf(" %" PRIu64 " (%s)\n",
879 call->sender->taskid, call->sender->name);
880 }
881
882 spinlock_unlock(&call->forget_lock);
883 }
884}
885
886/** List answerbox contents.
887 *
888 * @param taskid Task ID.
889 *
890 */
891void ipc_print_task(task_id_t taskid)
892{
893 irq_spinlock_lock(&tasks_lock, true);
894 task_t *task = task_find_by_id(taskid);
895
896 if (!task) {
897 irq_spinlock_unlock(&tasks_lock, true);
898 return;
899 }
900
901 /* Hand-over-hand locking */
902 irq_spinlock_exchange(&tasks_lock, &task->lock);
903
904 printf("[phone id] [calls] [state\n");
905
906 size_t i;
907 for (i = 0; i < IPC_MAX_PHONES; i++) {
908 if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
909 printf("%-10zu (mutex busy)\n", i);
910 continue;
911 }
912
913 if (task->phones[i].state != IPC_PHONE_FREE) {
914 printf("%-10zu %7" PRIun " ", i,
915 atomic_get(&task->phones[i].active_calls));
916
917 switch (task->phones[i].state) {
918 case IPC_PHONE_CONNECTING:
919 printf("connecting");
920 break;
921 case IPC_PHONE_CONNECTED:
922 printf("connected to %" PRIu64 " (%s)",
923 task->phones[i].callee->task->taskid,
924 task->phones[i].callee->task->name);
925 break;
926 case IPC_PHONE_SLAMMED:
927 printf("slammed by %p",
928 task->phones[i].callee);
929 break;
930 case IPC_PHONE_HUNGUP:
931 printf("hung up by %p",
932 task->phones[i].callee);
933 break;
934 default:
935 break;
936 }
937
938 printf("\n");
939 }
940
941 mutex_unlock(&task->phones[i].lock);
942 }
943
944 irq_spinlock_lock(&task->answerbox.lock, false);
945
946#ifdef __32_BITS__
947 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
948 " [flags] [sender\n");
949#endif
950
951#ifdef __64_BITS__
952 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4]"
953 " [arg5] [flags] [sender\n");
954#endif
955
956 printf(" --- incomming calls ---\n");
957 ipc_print_call_list(&task->answerbox.calls);
958 printf(" --- dispatched calls ---\n");
959 ipc_print_call_list(&task->answerbox.dispatched_calls);
960 printf(" --- incoming answers ---\n");
961 ipc_print_call_list(&task->answerbox.answers);
962
963 irq_spinlock_unlock(&task->answerbox.lock, false);
964 irq_spinlock_unlock(&task->lock, true);
965}
966
967/** @}
968 */
Note: See TracBrowser for help on using the repository browser.