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

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

Make capability handles type-safe

Define distinct pointer types for the handles of the supported
capability types and use them instead of integer handles. This makes it
virtually impossible to pass a non-handle or a handle of different type
instead of the proper handle. Also turn cap_handle_t into an "untyped"
capability handle that can be assigned to and from the "typed" handles.

This commit also fixes a bug in msim-con driver, which wrongly used the
IRQ number instead of the IRQ capability handle to unregister the IRQ.

This commit also fixes the wrong use of the capability handle instead
of error code in libusbhost.

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