source: mainline/kernel/generic/src/ipc/ipc.c@ 6abfd250

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

Rename caps_apply_to_all to caps_apply_to_type

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