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

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

Use recursive mutex to protect task_t::cap_info

This makes it possible to use the mutex-protected capability APIs even
inside caps_apply_to_kobject_type() callbacks. Now there is no need to
provide eg. cap_unpublish_locked() and cap_free_locked(). Likewise,
ipc_irq_unsubscribe() can be used when the task's cap_info is already
locked by the current thread inside of a callback.

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