source: mainline/kernel/generic/src/ipc/ipc.c@ 719a208

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

Fix race between ipc_answerbox_slam_phones() and ipc_cleanup()

We modify _ipc_call() to support sending of pre-forgotten calls. This is
handy in ipc_answerbox_slam_phones() when after slamming a client's
phone, we may no longer be sure of the client's continued existence.

A pre-forgotten call is delivered to the callee in an already forgotten
state, so that the caller is no longer expected to wait for it nor it is
expected to be still around. The caller is held alive for the necessary
time, but can disappear as soon as we release it.

  • 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 <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 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.