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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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