source: mainline/kernel/generic/src/ipc/ipc.c@ 02667d9

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

Introduce reference-counted kobjects

Capabilities are thus reduced to task-local names for references to kobjects.

  • 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/** Open channel that is assigned automatically to new tasks */
66answerbox_t *ipc_phone_0 = NULL;
67
68static slab_cache_t *call_slab;
69static slab_cache_t *answerbox_slab;
70
71slab_cache_t *phone_slab = 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
89void ipc_call_hold(call_t *call)
90{
91 atomic_inc(&call->refcnt);
92}
93
94void ipc_call_release(call_t *call)
95{
96 if (atomic_predec(&call->refcnt) == 0) {
97 if (call->buffer)
98 free(call->buffer);
99 slab_free(call_slab, call);
100 }
101}
102
103/** Allocate and initialize a call structure.
104 *
105 * The call is initialized, so that the reply will be directed to
106 * TASK->answerbox.
107 *
108 * @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
109 *
110 * @return If flags permit it, return NULL, or initialized kernel
111 * call structure with one reference.
112 *
113 */
114call_t *ipc_call_alloc(unsigned int flags)
115{
116 call_t *call = slab_alloc(call_slab, flags);
117 if (call) {
118 _ipc_call_init(call);
119 ipc_call_hold(call);
120 }
121
122 return call;
123}
124
125/** Deallocate a call structure.
126 *
127 * @param call Call structure to be freed.
128 *
129 */
130void ipc_call_free(call_t *call)
131{
132 ipc_call_release(call);
133}
134
135/** Initialize an answerbox structure.
136 *
137 * @param box Answerbox structure to be initialized.
138 * @param task Task to which the answerbox belongs.
139 *
140 */
141void ipc_answerbox_init(answerbox_t *box, task_t *task)
142{
143 irq_spinlock_initialize(&box->lock, "ipc.box.lock");
144 irq_spinlock_initialize(&box->irq_lock, "ipc.box.irqlock");
145 waitq_initialize(&box->wq);
146 list_initialize(&box->connected_phones);
147 list_initialize(&box->calls);
148 list_initialize(&box->dispatched_calls);
149 list_initialize(&box->answers);
150 list_initialize(&box->irq_notifs);
151 box->task = task;
152}
153
154/** Connect a phone to an answerbox.
155 *
156 * This function must be passed a reference to phone->kobject.
157 *
158 * @param phone Initialized phone structure.
159 * @param box Initialized answerbox structure.
160 * @return True if the phone was connected, false otherwise.
161 */
162bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
163{
164 bool active;
165
166 mutex_lock(&phone->lock);
167 irq_spinlock_lock(&box->lock, true);
168
169 active = box->active;
170 if (active) {
171 phone->state = IPC_PHONE_CONNECTED;
172 phone->callee = box;
173 /* Pass phone->kobject reference to box->connected_phones */
174 list_append(&phone->link, &box->connected_phones);
175 }
176
177 irq_spinlock_unlock(&box->lock, true);
178 mutex_unlock(&phone->lock);
179
180 if (!active) {
181 /* We still have phone->kobject's reference; drop it */
182 kobject_put(phone->kobject);
183 }
184
185 return active;
186}
187
188/** Initialize a phone structure.
189 *
190 * @param phone Phone structure to be initialized.
191 * @param caller Owning task.
192 *
193 */
194void ipc_phone_init(phone_t *phone, task_t *caller)
195{
196 mutex_initialize(&phone->lock, MUTEX_PASSIVE);
197 phone->caller = caller;
198 phone->callee = NULL;
199 phone->state = IPC_PHONE_FREE;
200 atomic_set(&phone->active_calls, 0);
201 phone->kobject = NULL;
202}
203
204/** Helper function to facilitate synchronous calls.
205 *
206 * @param phone Destination kernel phone structure.
207 * @param request Call structure with request.
208 *
209 * @return EOK on success or a negative error code.
210 *
211 */
212int ipc_call_sync(phone_t *phone, call_t *request)
213{
214 answerbox_t *mybox = slab_alloc(answerbox_slab, 0);
215 ipc_answerbox_init(mybox, TASK);
216
217 /* We will receive data in a special box. */
218 request->callerbox = mybox;
219
220 int rc = ipc_call(phone, request);
221 if (rc != EOK) {
222 slab_free(answerbox_slab, mybox);
223 return rc;
224 }
225
226 call_t *answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
227 SYNCH_FLAGS_INTERRUPTIBLE);
228 if (!answer) {
229
230 /*
231 * The sleep was interrupted.
232 *
233 * There are two possibilities now:
234 * 1) the call gets answered before we manage to forget it
235 * 2) we manage to forget the call before it gets answered
236 */
237
238 spinlock_lock(&request->forget_lock);
239 spinlock_lock(&TASK->active_calls_lock);
240
241 assert(!request->forget);
242
243 bool answered = !request->active;
244 if (!answered) {
245 /*
246 * The call is not yet answered and we won the race to
247 * forget it.
248 */
249 ipc_forget_call(request); /* releases locks */
250 rc = EINTR;
251
252 } else {
253 spinlock_unlock(&TASK->active_calls_lock);
254 spinlock_unlock(&request->forget_lock);
255 }
256
257 if (answered) {
258 /*
259 * The other side won the race to answer the call.
260 * It is safe to wait for the answer uninterruptibly
261 * now.
262 */
263 answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
264 SYNCH_FLAGS_NONE);
265 }
266 }
267 assert(!answer || request == answer);
268
269 slab_free(answerbox_slab, mybox);
270 return rc;
271}
272
273/** Answer a message which was not dispatched and is not listed in any queue.
274 *
275 * @param call Call structure to be answered.
276 * @param selflocked If true, then TASK->answebox is locked.
277 *
278 */
279void _ipc_answer_free_call(call_t *call, bool selflocked)
280{
281 /* Count sent answer */
282 irq_spinlock_lock(&TASK->lock, true);
283 TASK->ipc_info.answer_sent++;
284 irq_spinlock_unlock(&TASK->lock, true);
285
286 spinlock_lock(&call->forget_lock);
287 if (call->forget) {
288 /* This is a forgotten call and call->sender is not valid. */
289 spinlock_unlock(&call->forget_lock);
290 ipc_call_free(call);
291 return;
292 } else {
293 /*
294 * If the call is still active, i.e. it was answered
295 * in a non-standard way, remove the call from the
296 * sender's active call list.
297 */
298 if (call->active) {
299 spinlock_lock(&call->sender->active_calls_lock);
300 list_remove(&call->ta_link);
301 spinlock_unlock(&call->sender->active_calls_lock);
302 }
303 }
304 spinlock_unlock(&call->forget_lock);
305
306 answerbox_t *callerbox = call->callerbox ? call->callerbox :
307 &call->sender->answerbox;
308 bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
309
310 call->flags |= IPC_CALL_ANSWERED;
311
312 call->data.task_id = TASK->taskid;
313
314 if (do_lock)
315 irq_spinlock_lock(&callerbox->lock, true);
316
317 list_append(&call->ab_link, &callerbox->answers);
318
319 if (do_lock)
320 irq_spinlock_unlock(&callerbox->lock, true);
321
322 waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
323}
324
325/** Answer a message which is in a callee queue.
326 *
327 * @param box Answerbox that is answering the message.
328 * @param call Modified request that is being sent back.
329 *
330 */
331void ipc_answer(answerbox_t *box, call_t *call)
332{
333 /* Remove from active box */
334 irq_spinlock_lock(&box->lock, true);
335 list_remove(&call->ab_link);
336 irq_spinlock_unlock(&box->lock, true);
337
338 /* Send back answer */
339 _ipc_answer_free_call(call, false);
340}
341
342static void _ipc_call_actions_internal(phone_t *phone, call_t *call,
343 bool preforget)
344{
345 task_t *caller = phone->caller;
346
347 call->caller_phone = phone;
348
349 if (preforget) {
350 call->forget = true;
351 } else {
352 atomic_inc(&phone->active_calls);
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, sysarg_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 */
418int 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 0 if the phone is disconnected.
448 * @return -1 if the phone was already disconnected.
449 *
450 */
451int 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 -1;
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 0;
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 */
498int 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 int rc;
539
540restart:
541 rc = waitq_sleep_timeout(&box->wq, usec, flags);
542 if (SYNCH_FAILED(rc))
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 } else if (!list_empty(&box->calls)) {
567 /* Count received call */
568 call_cnt++;
569
570 /* Handle requests */
571 request = list_get_instance(list_first(&box->calls),
572 call_t, ab_link);
573 list_remove(&request->ab_link);
574
575 /* Append request to dispatch queue */
576 list_append(&request->ab_link, &box->dispatched_calls);
577 } else {
578 /* This can happen regularly after ipc_cleanup */
579 irq_spinlock_unlock(&box->lock, true);
580 goto restart;
581 }
582
583 irq_spinlock_pass(&box->lock, &TASK->lock);
584
585 TASK->ipc_info.irq_notif_received += irq_cnt;
586 TASK->ipc_info.answer_received += answer_cnt;
587 TASK->ipc_info.call_received += call_cnt;
588
589 irq_spinlock_unlock(&TASK->lock, true);
590
591 return request;
592}
593
594/** Answer all calls from list with EHANGUP answer.
595 *
596 * @param box Answerbox with the list.
597 * @param lst Head of the list to be cleaned up.
598 */
599void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
600{
601 irq_spinlock_lock(&box->lock, true);
602 while (!list_empty(lst)) {
603 call_t *call = list_get_instance(list_first(lst), call_t,
604 ab_link);
605
606 list_remove(&call->ab_link);
607
608 irq_spinlock_unlock(&box->lock, true);
609
610 if (lst == &box->calls)
611 SYSIPC_OP(request_process, call, box);
612
613 ipc_data_t old = call->data;
614 IPC_SET_RETVAL(call->data, EHANGUP);
615 answer_preprocess(call, &old);
616 _ipc_answer_free_call(call, true);
617
618 irq_spinlock_lock(&box->lock, true);
619 }
620 irq_spinlock_unlock(&box->lock, true);
621}
622
623/** Disconnects all phones connected to an answerbox.
624 *
625 * @param box Answerbox to disconnect phones from.
626 * @param notify_box If true, the answerbox will get a hangup message for
627 * each disconnected phone.
628 *
629 */
630void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
631{
632 phone_t *phone;
633 DEADLOCK_PROBE_INIT(p_phonelck);
634
635 /* Disconnect all phones connected to our answerbox */
636restart_phones:
637 irq_spinlock_lock(&box->lock, true);
638 while (!list_empty(&box->connected_phones)) {
639 phone = list_get_instance(list_first(&box->connected_phones),
640 phone_t, link);
641 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
642 irq_spinlock_unlock(&box->lock, true);
643 DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
644 goto restart_phones;
645 }
646
647 /* Disconnect phone */
648 assert(phone->state == IPC_PHONE_CONNECTED);
649
650 list_remove(&phone->link);
651 phone->state = IPC_PHONE_SLAMMED;
652
653 if (notify_box) {
654 task_hold(phone->caller);
655 mutex_unlock(&phone->lock);
656 irq_spinlock_unlock(&box->lock, true);
657
658 /*
659 * Send one call to the answerbox for each phone.
660 * Used to make sure the kbox thread wakes up after
661 * the last phone has been disconnected. The call is
662 * forgotten upon sending, so the "caller" may cease
663 * to exist as soon as we release it.
664 */
665 call_t *call = ipc_call_alloc(0);
666 IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
667 call->request_method = IPC_M_PHONE_HUNGUP;
668 call->flags |= IPC_CALL_DISCARD_ANSWER;
669 _ipc_call(phone, box, call, true);
670
671 task_release(phone->caller);
672
673 kobject_put(phone->kobject);
674
675 /* Must start again */
676 goto restart_phones;
677 }
678
679 mutex_unlock(&phone->lock);
680 kobject_put(phone->kobject);
681 }
682
683 irq_spinlock_unlock(&box->lock, true);
684}
685
686static void ipc_forget_call(call_t *call)
687{
688 assert(spinlock_locked(&TASK->active_calls_lock));
689 assert(spinlock_locked(&call->forget_lock));
690
691 /*
692 * Forget the call and donate it to the task which holds up the answer.
693 */
694
695 call->forget = true;
696 call->sender = NULL;
697 list_remove(&call->ta_link);
698
699 /*
700 * The call may be freed by _ipc_answer_free_call() before we are done
701 * with it; to avoid working with a destroyed call_t structure, we
702 * must hold a reference to it.
703 */
704 ipc_call_hold(call);
705
706 spinlock_unlock(&call->forget_lock);
707 spinlock_unlock(&TASK->active_calls_lock);
708
709 atomic_dec(&call->caller_phone->active_calls);
710
711 SYSIPC_OP(request_forget, call);
712
713 ipc_call_release(call);
714}
715
716static void ipc_forget_all_active_calls(void)
717{
718 call_t *call;
719
720restart:
721 spinlock_lock(&TASK->active_calls_lock);
722 if (list_empty(&TASK->active_calls)) {
723 /*
724 * We are done, there are no more active calls.
725 * Nota bene: there may still be answers waiting for pick up.
726 */
727 spinlock_unlock(&TASK->active_calls_lock);
728 return;
729 }
730
731 call = list_get_instance(list_first(&TASK->active_calls), call_t,
732 ta_link);
733
734 if (!spinlock_trylock(&call->forget_lock)) {
735 /*
736 * Avoid deadlock and let async_answer() or
737 * _ipc_answer_free_call() win the race to dequeue the first
738 * call on the list.
739 */
740 spinlock_unlock(&TASK->active_calls_lock);
741 goto restart;
742 }
743
744 ipc_forget_call(call);
745
746 goto restart;
747}
748
749static bool phone_cap_wait_cb(cap_t *cap, void *arg)
750{
751 phone_t *phone = cap->kobject->phone;
752 bool *restart = (bool *) arg;
753
754 mutex_lock(&phone->lock);
755 if ((phone->state == IPC_PHONE_HUNGUP) &&
756 (atomic_get(&phone->active_calls) == 0)) {
757 phone->state = IPC_PHONE_FREE;
758 phone->callee = NULL;
759 }
760
761 /*
762 * We might have had some IPC_PHONE_CONNECTING phones at the beginning
763 * of ipc_cleanup(). Depending on whether these were forgotten or
764 * answered, they will eventually enter the IPC_PHONE_FREE or
765 * IPC_PHONE_CONNECTED states, respectively. In the latter case, the
766 * other side may slam the open phones at any time, in which case we
767 * will get an IPC_PHONE_SLAMMED phone.
768 */
769 if ((phone->state == IPC_PHONE_CONNECTED) ||
770 (phone->state == IPC_PHONE_SLAMMED)) {
771 mutex_unlock(&phone->lock);
772 ipc_phone_hangup(phone);
773 /*
774 * Now there may be one extra active call, which needs to be
775 * forgotten.
776 */
777 ipc_forget_all_active_calls();
778 *restart = true;
779 return false;
780 }
781
782 /*
783 * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
784 * in HUNGUP state, we wait for the reply to come
785 */
786 if (phone->state != IPC_PHONE_FREE) {
787 mutex_unlock(&phone->lock);
788 return false;
789 }
790
791 mutex_unlock(&phone->lock);
792 return true;
793}
794
795/** Wait for all answers to asynchronous calls to arrive. */
796static void ipc_wait_for_all_answered_calls(void)
797{
798 call_t *call;
799 bool restart;
800
801restart:
802 /*
803 * Go through all phones, until they are all free.
804 * Locking is needed as there may be connection handshakes in progress.
805 */
806 restart = false;
807 if (caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
808 phone_cap_wait_cb, &restart)) {
809 /* Got into cleanup */
810 return;
811 }
812 if (restart)
813 goto restart;
814
815 call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
816 SYNCH_FLAGS_NONE);
817 assert(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
818
819 SYSIPC_OP(answer_process, call);
820
821 ipc_call_free(call);
822 goto restart;
823}
824
825static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
826{
827 ipc_phone_hangup(cap->kobject->phone);
828 return true;
829}
830
831static bool irq_cap_cleanup_cb(cap_t *cap, void *arg)
832{
833 ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
834 return true;
835}
836
837/** Clean up all IPC communication of the current task.
838 *
839 * Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
840 * have to change it as well if you want to cleanup other tasks than TASK.
841 *
842 */
843void ipc_cleanup(void)
844{
845 /*
846 * Mark the answerbox as inactive.
847 *
848 * The main purpose for doing this is to prevent any pending callback
849 * connections from getting established beyond this point.
850 */
851 irq_spinlock_lock(&TASK->answerbox.lock, true);
852 TASK->answerbox.active = false;
853 irq_spinlock_unlock(&TASK->answerbox.lock, true);
854
855 /* Disconnect all our phones ('ipc_phone_hangup') */
856 caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
857 phone_cap_cleanup_cb, NULL);
858
859 /* Unsubscribe from any event notifications. */
860 event_cleanup_answerbox(&TASK->answerbox);
861
862 /* Disconnect all connected IRQs */
863 caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_IRQ, irq_cap_cleanup_cb,
864 NULL);
865
866 /* Disconnect all phones connected to our regular answerbox */
867 ipc_answerbox_slam_phones(&TASK->answerbox, false);
868
869#ifdef CONFIG_UDEBUG
870 /* Clean up kbox thread and communications */
871 ipc_kbox_cleanup();
872#endif
873
874 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
875 ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
876 ipc_cleanup_call_list(&TASK->answerbox,
877 &TASK->answerbox.dispatched_calls);
878
879 ipc_forget_all_active_calls();
880 ipc_wait_for_all_answered_calls();
881}
882
883/** Initilize IPC subsystem
884 *
885 */
886void ipc_init(void)
887{
888 call_slab = slab_cache_create("call_t", sizeof(call_t), 0, NULL,
889 NULL, 0);
890 phone_slab = slab_cache_create("phone_t", sizeof(phone_t), 0, NULL,
891 NULL, 0);
892 answerbox_slab = slab_cache_create("answerbox_t", sizeof(answerbox_t),
893 0, NULL, NULL, 0);
894}
895
896
897static void ipc_print_call_list(list_t *list)
898{
899 list_foreach(*list, ab_link, call_t, call) {
900#ifdef __32_BITS__
901 printf("%10p ", call);
902#endif
903
904#ifdef __64_BITS__
905 printf("%18p ", call);
906#endif
907
908 spinlock_lock(&call->forget_lock);
909
910 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
911 " %-6" PRIun " %-6" PRIun " %-7x",
912 IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
913 IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
914 IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
915 call->flags);
916
917 if (call->forget) {
918 printf(" ? (call forgotten)\n");
919 } else {
920 printf(" %" PRIu64 " (%s)\n",
921 call->sender->taskid, call->sender->name);
922 }
923
924 spinlock_unlock(&call->forget_lock);
925 }
926}
927
928static bool print_task_phone_cb(cap_t *cap, void *arg)
929{
930 phone_t *phone = cap->kobject->phone;
931
932 mutex_lock(&phone->lock);
933 if (phone->state != IPC_PHONE_FREE) {
934 printf("%-11d %7" PRIun " ", cap->handle,
935 atomic_get(&phone->active_calls));
936
937 switch (phone->state) {
938 case IPC_PHONE_CONNECTING:
939 printf("connecting");
940 break;
941 case IPC_PHONE_CONNECTED:
942 printf("connected to %" PRIu64 " (%s)",
943 phone->callee->task->taskid,
944 phone->callee->task->name);
945 break;
946 case IPC_PHONE_SLAMMED:
947 printf("slammed by %p", phone->callee);
948 break;
949 case IPC_PHONE_HUNGUP:
950 printf("hung up by %p", phone->callee);
951 break;
952 default:
953 break;
954 }
955
956 printf("\n");
957 }
958 mutex_unlock(&phone->lock);
959
960 return true;
961}
962
963/** List answerbox contents.
964 *
965 * @param taskid Task ID.
966 *
967 */
968void ipc_print_task(task_id_t taskid)
969{
970 irq_spinlock_lock(&tasks_lock, true);
971 task_t *task = task_find_by_id(taskid);
972 if (!task) {
973 irq_spinlock_unlock(&tasks_lock, true);
974 return;
975 }
976 task_hold(task);
977 irq_spinlock_unlock(&tasks_lock, true);
978
979 printf("[phone cap] [calls] [state\n");
980
981 caps_apply_to_kobject_type(task, KOBJECT_TYPE_PHONE,
982 print_task_phone_cb, NULL);
983
984 irq_spinlock_lock(&task->lock, true);
985 irq_spinlock_lock(&task->answerbox.lock, false);
986
987#ifdef __32_BITS__
988 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
989 " [flags] [sender\n");
990#endif
991
992#ifdef __64_BITS__
993 printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4]"
994 " [arg5] [flags] [sender\n");
995#endif
996
997 printf(" --- incomming calls ---\n");
998 ipc_print_call_list(&task->answerbox.calls);
999 printf(" --- dispatched calls ---\n");
1000 ipc_print_call_list(&task->answerbox.dispatched_calls);
1001 printf(" --- incoming answers ---\n");
1002 ipc_print_call_list(&task->answerbox.answers);
1003
1004 irq_spinlock_unlock(&task->answerbox.lock, false);
1005 irq_spinlock_unlock(&task->lock, true);
1006
1007 task_release(task);
1008}
1009
1010/** @}
1011 */
Note: See TracBrowser for help on using the repository browser.