2
* Copyright (c) 2006 Ondrej Palkovsky
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
29
/** @addtogroup genericipc
37
* First the answerbox, then the phone.
40
#include <synch/synch.h>
41
#include <synch/spinlock.h>
42
#include <synch/mutex.h>
43
#include <synch/waitq.h>
44
#include <synch/synch.h>
47
#include <ipc/event.h>
51
#include <proc/task.h>
55
#include <console/console.h>
56
#include <proc/thread.h>
57
#include <arch/interrupt.h>
60
/** Open channel that is assigned automatically to new tasks */
61
answerbox_t *ipc_phone_0 = NULL;
63
static slab_cache_t *ipc_call_slab;
65
/** Initialize a call structure.
67
* @param call Call structure to be initialized.
69
static void _ipc_call_init(call_t *call)
71
memsetb(call, sizeof(*call), 0);
72
call->callerbox = &TASK->answerbox;
77
/** Allocate and initialize a call structure.
79
* The call is initialized, so that the reply will be directed to
82
* @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
84
* @return If flags permit it, return NULL, or initialized kernel
87
call_t *ipc_call_alloc(int flags)
91
call = slab_alloc(ipc_call_slab, flags);
98
/** Initialize a statically allocated call structure.
100
* @param call Statically allocated kernel call structure to be
103
void ipc_call_static_init(call_t *call)
105
_ipc_call_init(call);
106
call->flags |= IPC_CALL_STATIC_ALLOC;
109
/** Deallocate a call structure.
111
* @param call Call structure to be freed.
113
void ipc_call_free(call_t *call)
115
ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
116
/* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
119
slab_free(ipc_call_slab, call);
122
/** Initialize an answerbox structure.
124
* @param box Answerbox structure to be initialized.
125
* @param task Task to which the answerbox belongs.
127
void ipc_answerbox_init(answerbox_t *box, task_t *task)
129
spinlock_initialize(&box->lock, "ipc_box_lock");
130
spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
131
waitq_initialize(&box->wq);
132
list_initialize(&box->connected_phones);
133
list_initialize(&box->calls);
134
list_initialize(&box->dispatched_calls);
135
list_initialize(&box->answers);
136
list_initialize(&box->irq_notifs);
137
list_initialize(&box->irq_head);
141
/** Connect a phone to an answerbox.
143
* @param phone Initialized phone structure.
144
* @param box Initialized answerbox structure.
146
void ipc_phone_connect(phone_t *phone, answerbox_t *box)
148
mutex_lock(&phone->lock);
150
phone->state = IPC_PHONE_CONNECTED;
153
spinlock_lock(&box->lock);
154
list_append(&phone->link, &box->connected_phones);
155
spinlock_unlock(&box->lock);
157
mutex_unlock(&phone->lock);
160
/** Initialize a phone structure.
162
* @param phone Phone structure to be initialized.
164
void ipc_phone_init(phone_t *phone)
166
mutex_initialize(&phone->lock, MUTEX_PASSIVE);
167
phone->callee = NULL;
168
phone->state = IPC_PHONE_FREE;
169
atomic_set(&phone->active_calls, 0);
172
/** Helper function to facilitate synchronous calls.
174
* @param phone Destination kernel phone structure.
175
* @param request Call structure with request.
177
* @return EOK on success or EINTR if the sleep was interrupted.
179
int ipc_call_sync(phone_t *phone, call_t *request)
181
answerbox_t sync_box;
183
ipc_answerbox_init(&sync_box, TASK);
185
/* We will receive data in a special box. */
186
request->callerbox = &sync_box;
188
ipc_call(phone, request);
189
if (!ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT,
190
SYNCH_FLAGS_INTERRUPTIBLE))
195
/** Answer a message which was not dispatched and is not listed in any queue.
197
* @param call Call structure to be answered.
199
static void _ipc_answer_free_call(call_t *call)
201
answerbox_t *callerbox = call->callerbox;
203
call->flags |= IPC_CALL_ANSWERED;
205
if (call->flags & IPC_CALL_FORWARDED) {
206
if (call->caller_phone) {
207
/* Demasquerade the caller phone. */
208
call->data.phone = call->caller_phone;
212
spinlock_lock(&callerbox->lock);
213
list_append(&call->link, &callerbox->answers);
214
spinlock_unlock(&callerbox->lock);
215
waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
218
/** Answer a message which is in a callee queue.
220
* @param box Answerbox that is answering the message.
221
* @param call Modified request that is being sent back.
223
void ipc_answer(answerbox_t *box, call_t *call)
225
/* Remove from active box */
226
spinlock_lock(&box->lock);
227
list_remove(&call->link);
228
spinlock_unlock(&box->lock);
229
/* Send back answer */
230
_ipc_answer_free_call(call);
233
/** Simulate sending back a message.
235
* Most errors are better handled by forming a normal backward
236
* message and sending it as a normal answer.
238
* @param phone Phone structure the call should appear to come from.
239
* @param call Call structure to be answered.
240
* @param err Return value to be used for the answer.
242
void ipc_backsend_err(phone_t *phone, call_t *call, unative_t err)
244
call->data.phone = phone;
245
atomic_inc(&phone->active_calls);
246
IPC_SET_RETVAL(call->data, err);
247
_ipc_answer_free_call(call);
250
/** Unsafe unchecking version of ipc_call.
252
* @param phone Phone structure the call comes from.
253
* @param box Destination answerbox structure.
254
* @param call Call structure with request.
256
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
258
if (!(call->flags & IPC_CALL_FORWARDED)) {
259
atomic_inc(&phone->active_calls);
260
call->data.phone = phone;
263
spinlock_lock(&box->lock);
264
list_append(&call->link, &box->calls);
265
spinlock_unlock(&box->lock);
266
waitq_wakeup(&box->wq, WAKEUP_FIRST);
269
/** Send an asynchronous request using a phone to an answerbox.
271
* @param phone Phone structure the call comes from and which is
272
* connected to the destination answerbox.
273
* @param call Call structure with request.
275
* @return Return 0 on success, ENOENT on error.
277
int ipc_call(phone_t *phone, call_t *call)
281
mutex_lock(&phone->lock);
282
if (phone->state != IPC_PHONE_CONNECTED) {
283
mutex_unlock(&phone->lock);
284
if (call->flags & IPC_CALL_FORWARDED) {
285
IPC_SET_RETVAL(call->data, EFORWARD);
286
_ipc_answer_free_call(call);
288
if (phone->state == IPC_PHONE_HUNGUP)
289
ipc_backsend_err(phone, call, EHANGUP);
291
ipc_backsend_err(phone, call, ENOENT);
296
_ipc_call(phone, box, call);
298
mutex_unlock(&phone->lock);
302
/** Disconnect phone from answerbox.
304
* This call leaves the phone in the HUNGUP state. The change to 'free' is done
307
* @param phone Phone structure to be hung up.
309
* @return Return 0 if the phone is disconnected.
310
* Return -1 if the phone was already disconnected.
312
int ipc_phone_hangup(phone_t *phone)
317
mutex_lock(&phone->lock);
318
if (phone->state == IPC_PHONE_FREE ||
319
phone->state == IPC_PHONE_HUNGUP ||
320
phone->state == IPC_PHONE_CONNECTING) {
321
mutex_unlock(&phone->lock);
325
if (phone->state != IPC_PHONE_SLAMMED) {
326
/* Remove myself from answerbox */
327
spinlock_lock(&box->lock);
328
list_remove(&phone->link);
329
spinlock_unlock(&box->lock);
331
call = ipc_call_alloc(0);
332
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
333
call->flags |= IPC_CALL_DISCARD_ANSWER;
334
_ipc_call(phone, box, call);
337
phone->state = IPC_PHONE_HUNGUP;
338
mutex_unlock(&phone->lock);
343
/** Forwards call from one answerbox to another one.
345
* @param call Call structure to be redirected.
346
* @param newphone Phone structure to target answerbox.
347
* @param oldbox Old answerbox structure.
348
* @param mode Flags that specify mode of the forward operation.
350
* @return Return 0 if forwarding succeeded or an error code if
353
* The return value serves only as an information for the forwarder,
354
* the original caller is notified automatically with EFORWARD.
356
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox, int mode)
358
spinlock_lock(&oldbox->lock);
359
list_remove(&call->link);
360
spinlock_unlock(&oldbox->lock);
362
if (mode & IPC_FF_ROUTE_FROM_ME) {
363
if (!call->caller_phone)
364
call->caller_phone = call->data.phone;
365
call->data.phone = newphone;
368
return ipc_call(newphone, call);
372
/** Wait for a phone call.
374
* @param box Answerbox expecting the call.
375
* @param usec Timeout in microseconds. See documentation for
376
* waitq_sleep_timeout() for decription of its special
378
* @param flags Select mode of sleep operation. See documentation for
379
* waitq_sleep_timeout() for description of its special
381
* @return Recived call structure or NULL.
383
* To distinguish between a call and an answer, have a look at call->flags.
385
call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, int flags)
392
rc = waitq_sleep_timeout(&box->wq, usec, flags);
393
if (SYNCH_FAILED(rc))
396
spinlock_lock(&box->lock);
397
if (!list_empty(&box->irq_notifs)) {
398
ipl = interrupts_disable();
399
spinlock_lock(&box->irq_lock);
401
request = list_get_instance(box->irq_notifs.next, call_t, link);
402
list_remove(&request->link);
404
spinlock_unlock(&box->irq_lock);
405
interrupts_restore(ipl);
406
} else if (!list_empty(&box->answers)) {
407
/* Handle asynchronous answers */
408
request = list_get_instance(box->answers.next, call_t, link);
409
list_remove(&request->link);
410
atomic_dec(&request->data.phone->active_calls);
411
} else if (!list_empty(&box->calls)) {
412
/* Handle requests */
413
request = list_get_instance(box->calls.next, call_t, link);
414
list_remove(&request->link);
415
/* Append request to dispatch queue */
416
list_append(&request->link, &box->dispatched_calls);
418
/* This can happen regularly after ipc_cleanup */
419
spinlock_unlock(&box->lock);
422
spinlock_unlock(&box->lock);
426
/** Answer all calls from list with EHANGUP answer.
428
* @param lst Head of the list to be cleaned up.
430
void ipc_cleanup_call_list(link_t *lst)
434
while (!list_empty(lst)) {
435
call = list_get_instance(lst->next, call_t, link);
438
list_remove(&call->link);
440
IPC_SET_RETVAL(call->data, EHANGUP);
441
_ipc_answer_free_call(call);
445
/** Disconnects all phones connected to an answerbox.
447
* @param box Answerbox to disconnect phones from.
448
* @param notify_box If true, the answerbox will get a hangup message for
449
* each disconnected phone.
451
void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
454
DEADLOCK_PROBE_INIT(p_phonelck);
458
call = notify_box ? ipc_call_alloc(0) : NULL;
460
/* Disconnect all phones connected to our answerbox */
462
ipl = interrupts_disable();
463
spinlock_lock(&box->lock);
464
while (!list_empty(&box->connected_phones)) {
465
phone = list_get_instance(box->connected_phones.next,
467
if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
468
spinlock_unlock(&box->lock);
469
interrupts_restore(ipl);
470
DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
474
/* Disconnect phone */
475
ASSERT(phone->state == IPC_PHONE_CONNECTED);
477
list_remove(&phone->link);
478
phone->state = IPC_PHONE_SLAMMED;
481
mutex_unlock(&phone->lock);
482
spinlock_unlock(&box->lock);
483
interrupts_restore(ipl);
486
* Send one message to the answerbox for each
487
* phone. Used to make sure the kbox thread
488
* wakes up after the last phone has been
491
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
492
call->flags |= IPC_CALL_DISCARD_ANSWER;
493
_ipc_call(phone, box, call);
495
/* Allocate another call in advance */
496
call = ipc_call_alloc(0);
498
/* Must start again */
502
mutex_unlock(&phone->lock);
505
spinlock_unlock(&box->lock);
506
interrupts_restore(ipl);
508
/* Free unused call */
513
/** Cleans up all IPC communication of the current task.
515
* Note: ipc_hangup sets returning answerbox to TASK->answerbox, you
516
* have to change it as well if you want to cleanup other tasks than TASK.
518
void ipc_cleanup(void)
523
/* Disconnect all our phones ('ipc_phone_hangup') */
524
for (i = 0; i < IPC_MAX_PHONES; i++)
525
ipc_phone_hangup(&TASK->phones[i]);
527
/* Unsubscribe from any event notifications. */
528
event_cleanup_answerbox(&TASK->answerbox);
530
/* Disconnect all connected irqs */
531
ipc_irq_cleanup(&TASK->answerbox);
533
/* Disconnect all phones connected to our regular answerbox */
534
ipc_answerbox_slam_phones(&TASK->answerbox, false);
537
/* Clean up kbox thread and communications */
541
/* Answer all messages in 'calls' and 'dispatched_calls' queues */
542
spinlock_lock(&TASK->answerbox.lock);
543
ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
544
ipc_cleanup_call_list(&TASK->answerbox.calls);
545
spinlock_unlock(&TASK->answerbox.lock);
547
/* Wait for all async answers to arrive */
549
/* Go through all phones, until all are FREE... */
550
/* Locking not needed, no one else should modify
551
* it, when we are in cleanup */
552
for (i = 0; i < IPC_MAX_PHONES; i++) {
553
if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
554
atomic_get(&TASK->phones[i].active_calls) == 0)
555
TASK->phones[i].state = IPC_PHONE_FREE;
557
/* Just for sure, we might have had some
558
* IPC_PHONE_CONNECTING phones */
559
if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
560
ipc_phone_hangup(&TASK->phones[i]);
561
/* If the hangup succeeded, it has sent a HANGUP
562
* message, the IPC is now in HUNGUP state, we
563
* wait for the reply to come */
565
if (TASK->phones[i].state != IPC_PHONE_FREE)
568
/* Voila, got into cleanup */
569
if (i == IPC_MAX_PHONES)
572
call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
574
ASSERT((call->flags & IPC_CALL_ANSWERED) ||
575
(call->flags & IPC_CALL_NOTIF));
576
ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
579
* Record the receipt of this call in the current task's counter
580
* of active calls. IPC_M_PHONE_HUNGUP calls do not contribute
581
* to this counter so do not record answers to them either.
583
if (!(call->flags & IPC_CALL_DISCARD_ANSWER))
584
atomic_dec(&TASK->active_calls);
590
/** Initilize IPC subsystem */
593
ipc_call_slab = slab_cache_create("ipc_call", sizeof(call_t), 0, NULL,
598
/** List answerbox contents.
600
* @param taskid Task ID.
602
void ipc_print_task(task_id_t taskid)
609
spinlock_lock(&tasks_lock);
610
task = task_find_by_id(taskid);
612
spinlock_lock(&task->lock);
613
spinlock_unlock(&tasks_lock);
617
/* Print opened phones & details */
619
for (i = 0; i < IPC_MAX_PHONES; i++) {
620
if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
621
printf("%d: mutex busy\n", i);
624
if (task->phones[i].state != IPC_PHONE_FREE) {
626
switch (task->phones[i].state) {
627
case IPC_PHONE_CONNECTING:
628
printf("connecting ");
630
case IPC_PHONE_CONNECTED:
631
printf("connected to: %p ",
632
task->phones[i].callee);
634
case IPC_PHONE_SLAMMED:
635
printf("slammed by: %p ",
636
task->phones[i].callee);
638
case IPC_PHONE_HUNGUP:
639
printf("hung up - was: %p ",
640
task->phones[i].callee);
645
printf("active: %ld\n",
646
atomic_get(&task->phones[i].active_calls));
648
mutex_unlock(&task->phones[i].lock);
652
/* Print answerbox - calls */
653
spinlock_lock(&task->answerbox.lock);
654
printf("ABOX - CALLS:\n");
655
for (tmp = task->answerbox.calls.next; tmp != &task->answerbox.calls;
657
call = list_get_instance(tmp, call_t, link);
658
printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun
659
" A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
660
" A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
661
call->sender->taskid,
662
IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
663
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
664
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
667
/* Print answerbox - calls */
668
printf("ABOX - DISPATCHED CALLS:\n");
669
for (tmp = task->answerbox.dispatched_calls.next;
670
tmp != &task->answerbox.dispatched_calls;
672
call = list_get_instance(tmp, call_t, link);
673
printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun
674
" A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
675
" A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
676
call->sender->taskid,
677
IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
678
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
679
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
682
/* Print answerbox - calls */
683
printf("ABOX - ANSWERS:\n");
684
for (tmp = task->answerbox.answers.next;
685
tmp != &task->answerbox.answers;
687
call = list_get_instance(tmp, call_t, link);
688
printf("Callid:%p M:%" PRIun " A1:%" PRIun " A2:%" PRIun
689
" A3:%" PRIun " A4:%" PRIun " A5:%" PRIun " Flags:%x\n",
690
call, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
691
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
692
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
696
spinlock_unlock(&task->answerbox.lock);
697
spinlock_unlock(&task->lock);