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

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

Give reference to phone for each active call

This effectively removes the remains of the lazy phone recycling
mechanism used until the previous commit. Hungup phones are now
destroyed when their last active call is answered (and their reference
count goes to zero).

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