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

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

Introduce reference-counted kobjects

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

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