source: mainline/kernel/generic/src/ipc/ipc.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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