source: mainline/kernel/generic/src/ipc/ipc.c@ 071cb36

Last change on this file since 071cb36 was 6769005, checked in by Jakub Jermar <jakub@…>, 7 years ago

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

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