~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to kernel/generic/src/ipc/ipc.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 Ondrej Palkovsky
 
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
 
 
29
/** @addtogroup genericipc
 
30
 * @{
 
31
 */
 
32
/** @file
 
33
 */
 
34
 
 
35
/* Lock ordering
 
36
 *
 
37
 * First the answerbox, then the phone.
 
38
 */
 
39
 
 
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>
 
45
#include <ipc/ipc.h>
 
46
#include <ipc/kbox.h>
 
47
#include <ipc/event.h>
 
48
#include <errno.h>
 
49
#include <mm/slab.h>
 
50
#include <arch.h>
 
51
#include <proc/task.h>
 
52
#include <memstr.h>
 
53
#include <debug.h>
 
54
#include <print.h>
 
55
#include <console/console.h>
 
56
#include <proc/thread.h>
 
57
#include <arch/interrupt.h>
 
58
#include <ipc/irq.h>
 
59
 
 
60
/** Open channel that is assigned automatically to new tasks */
 
61
answerbox_t *ipc_phone_0 = NULL;
 
62
 
 
63
static slab_cache_t *ipc_call_slab;
 
64
 
 
65
/** Initialize a call structure.
 
66
 *
 
67
 * @param call          Call structure to be initialized.
 
68
 */
 
69
static void _ipc_call_init(call_t *call)
 
70
{
 
71
        memsetb(call, sizeof(*call), 0);
 
72
        call->callerbox = &TASK->answerbox;
 
73
        call->sender = TASK;
 
74
        call->buffer = NULL;
 
75
}
 
76
 
 
77
/** Allocate and initialize a call structure.
 
78
 * 
 
79
 * The call is initialized, so that the reply will be directed to
 
80
 * TASK->answerbox.
 
81
 *
 
82
 * @param flags         Parameters for slab_alloc (e.g FRAME_ATOMIC).
 
83
 *
 
84
 * @return              If flags permit it, return NULL, or initialized kernel
 
85
 *                      call structure.
 
86
 */
 
87
call_t *ipc_call_alloc(int flags)
 
88
{
 
89
        call_t *call;
 
90
 
 
91
        call = slab_alloc(ipc_call_slab, flags);
 
92
        if (call)
 
93
                _ipc_call_init(call);
 
94
 
 
95
        return call;
 
96
}
 
97
 
 
98
/** Initialize a statically allocated call structure.
 
99
 *
 
100
 * @param call          Statically allocated kernel call structure to be
 
101
 *                      initialized.
 
102
 */
 
103
void ipc_call_static_init(call_t *call)
 
104
{
 
105
        _ipc_call_init(call);
 
106
        call->flags |= IPC_CALL_STATIC_ALLOC;
 
107
}
 
108
 
 
109
/** Deallocate a call structure.
 
110
 *
 
111
 * @param call          Call structure to be freed.
 
112
 */
 
113
void ipc_call_free(call_t *call)
 
114
{
 
115
        ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
 
116
        /* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
 
117
        if (call->buffer)
 
118
                free(call->buffer);
 
119
        slab_free(ipc_call_slab, call);
 
120
}
 
121
 
 
122
/** Initialize an answerbox structure.
 
123
 *
 
124
 * @param box           Answerbox structure to be initialized.
 
125
 * @param task          Task to which the answerbox belongs.
 
126
 */
 
127
void ipc_answerbox_init(answerbox_t *box, task_t *task)
 
128
{
 
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);
 
138
        box->task = task;
 
139
}
 
140
 
 
141
/** Connect a phone to an answerbox.
 
142
 *
 
143
 * @param phone         Initialized phone structure.
 
144
 * @param box           Initialized answerbox structure.
 
145
 */
 
146
void ipc_phone_connect(phone_t *phone, answerbox_t *box)
 
147
{
 
148
        mutex_lock(&phone->lock);
 
149
 
 
150
        phone->state = IPC_PHONE_CONNECTED;
 
151
        phone->callee = box;
 
152
 
 
153
        spinlock_lock(&box->lock);
 
154
        list_append(&phone->link, &box->connected_phones);
 
155
        spinlock_unlock(&box->lock);
 
156
 
 
157
        mutex_unlock(&phone->lock);
 
158
}
 
159
 
 
160
/** Initialize a phone structure.
 
161
 *
 
162
 * @param phone         Phone structure to be initialized.
 
163
 */
 
164
void ipc_phone_init(phone_t *phone)
 
165
{
 
166
        mutex_initialize(&phone->lock, MUTEX_PASSIVE);
 
167
        phone->callee = NULL;
 
168
        phone->state = IPC_PHONE_FREE;
 
169
        atomic_set(&phone->active_calls, 0);
 
170
}
 
171
 
 
172
/** Helper function to facilitate synchronous calls.
 
173
 *
 
174
 * @param phone         Destination kernel phone structure.
 
175
 * @param request       Call structure with request.
 
176
 *
 
177
 * @return              EOK on success or EINTR if the sleep was interrupted.
 
178
 */
 
179
int ipc_call_sync(phone_t *phone, call_t *request)
 
180
{
 
181
        answerbox_t sync_box; 
 
182
 
 
183
        ipc_answerbox_init(&sync_box, TASK);
 
184
 
 
185
        /* We will receive data in a special box. */
 
186
        request->callerbox = &sync_box;
 
187
 
 
188
        ipc_call(phone, request);
 
189
        if (!ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT,
 
190
            SYNCH_FLAGS_INTERRUPTIBLE))
 
191
                return EINTR;
 
192
        return EOK;
 
193
}
 
194
 
 
195
/** Answer a message which was not dispatched and is not listed in any queue.
 
196
 *
 
197
 * @param call          Call structure to be answered.
 
198
 */
 
199
static void _ipc_answer_free_call(call_t *call)
 
200
{
 
201
        answerbox_t *callerbox = call->callerbox;
 
202
 
 
203
        call->flags |= IPC_CALL_ANSWERED;
 
204
 
 
205
        if (call->flags & IPC_CALL_FORWARDED) {
 
206
                if (call->caller_phone) {
 
207
                        /* Demasquerade the caller phone. */
 
208
                        call->data.phone = call->caller_phone;
 
209
                }
 
210
        }
 
211
 
 
212
        spinlock_lock(&callerbox->lock);
 
213
        list_append(&call->link, &callerbox->answers);
 
214
        spinlock_unlock(&callerbox->lock);
 
215
        waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
 
216
}
 
217
 
 
218
/** Answer a message which is in a callee queue.
 
219
 *
 
220
 * @param box           Answerbox that is answering the message.
 
221
 * @param call          Modified request that is being sent back.
 
222
 */
 
223
void ipc_answer(answerbox_t *box, call_t *call)
 
224
{
 
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);
 
231
}
 
232
 
 
233
/** Simulate sending back a message.
 
234
 *
 
235
 * Most errors are better handled by forming a normal backward
 
236
 * message and sending it as a normal answer.
 
237
 *
 
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.
 
241
 */
 
242
void ipc_backsend_err(phone_t *phone, call_t *call, unative_t err)
 
243
{
 
244
        call->data.phone = phone;
 
245
        atomic_inc(&phone->active_calls);
 
246
        IPC_SET_RETVAL(call->data, err);
 
247
        _ipc_answer_free_call(call);
 
248
}
 
249
 
 
250
/** Unsafe unchecking version of ipc_call.
 
251
 *
 
252
 * @param phone         Phone structure the call comes from.
 
253
 * @param box           Destination answerbox structure.
 
254
 * @param call          Call structure with request.
 
255
 */
 
256
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
 
257
{
 
258
        if (!(call->flags & IPC_CALL_FORWARDED)) {
 
259
                atomic_inc(&phone->active_calls);
 
260
                call->data.phone = phone;
 
261
        }
 
262
 
 
263
        spinlock_lock(&box->lock);
 
264
        list_append(&call->link, &box->calls);
 
265
        spinlock_unlock(&box->lock);
 
266
        waitq_wakeup(&box->wq, WAKEUP_FIRST);
 
267
}
 
268
 
 
269
/** Send an asynchronous request using a phone to an answerbox.
 
270
 *
 
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.
 
274
 *
 
275
 * @return              Return 0 on success, ENOENT on error.
 
276
 */
 
277
int ipc_call(phone_t *phone, call_t *call)
 
278
{
 
279
        answerbox_t *box;
 
280
 
 
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);
 
287
                } else {
 
288
                        if (phone->state == IPC_PHONE_HUNGUP)
 
289
                                ipc_backsend_err(phone, call, EHANGUP);
 
290
                        else
 
291
                                ipc_backsend_err(phone, call, ENOENT);
 
292
                }
 
293
                return ENOENT;
 
294
        }
 
295
        box = phone->callee;
 
296
        _ipc_call(phone, box, call);
 
297
        
 
298
        mutex_unlock(&phone->lock);
 
299
        return 0;
 
300
}
 
301
 
 
302
/** Disconnect phone from answerbox.
 
303
 *
 
304
 * This call leaves the phone in the HUNGUP state. The change to 'free' is done
 
305
 * lazily later.
 
306
 *
 
307
 * @param phone         Phone structure to be hung up.
 
308
 *              
 
309
 * @return              Return 0 if the phone is disconnected.
 
310
 *                      Return -1 if the phone was already disconnected.
 
311
 */
 
312
int ipc_phone_hangup(phone_t *phone)
 
313
{
 
314
        answerbox_t *box;
 
315
        call_t *call;
 
316
        
 
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);
 
322
                return -1;
 
323
        }
 
324
        box = phone->callee;
 
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);
 
330
 
 
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);
 
335
        }
 
336
 
 
337
        phone->state = IPC_PHONE_HUNGUP;
 
338
        mutex_unlock(&phone->lock);
 
339
 
 
340
        return 0;
 
341
}
 
342
 
 
343
/** Forwards call from one answerbox to another one.
 
344
 *
 
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.
 
349
 *
 
350
 * @return              Return 0 if forwarding succeeded or an error code if
 
351
 *                      there was error.
 
352
 * 
 
353
 * The return value serves only as an information for the forwarder,
 
354
 * the original caller is notified automatically with EFORWARD.
 
355
 */
 
356
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox, int mode)
 
357
{
 
358
        spinlock_lock(&oldbox->lock);
 
359
        list_remove(&call->link);
 
360
        spinlock_unlock(&oldbox->lock);
 
361
 
 
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;
 
366
        }
 
367
 
 
368
        return ipc_call(newphone, call);
 
369
}
 
370
 
 
371
 
 
372
/** Wait for a phone call.
 
373
 *
 
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
 
377
 *                      meaning.
 
378
 * @param flags         Select mode of sleep operation. See documentation for
 
379
 *                      waitq_sleep_timeout() for description of its special
 
380
 *                      meaning.
 
381
 * @return              Recived call structure or NULL.
 
382
 * 
 
383
 * To distinguish between a call and an answer, have a look at call->flags.
 
384
 */
 
385
call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, int flags)
 
386
{
 
387
        call_t *request;
 
388
        ipl_t ipl;
 
389
        int rc;
 
390
 
 
391
restart:
 
392
        rc = waitq_sleep_timeout(&box->wq, usec, flags);
 
393
        if (SYNCH_FAILED(rc))
 
394
                return NULL;
 
395
        
 
396
        spinlock_lock(&box->lock);
 
397
        if (!list_empty(&box->irq_notifs)) {
 
398
                ipl = interrupts_disable();
 
399
                spinlock_lock(&box->irq_lock);
 
400
 
 
401
                request = list_get_instance(box->irq_notifs.next, call_t, link);
 
402
                list_remove(&request->link);
 
403
 
 
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);
 
417
        } else {
 
418
                /* This can happen regularly after ipc_cleanup */
 
419
                spinlock_unlock(&box->lock);
 
420
                goto restart;
 
421
        }
 
422
        spinlock_unlock(&box->lock);
 
423
        return request;
 
424
}
 
425
 
 
426
/** Answer all calls from list with EHANGUP answer.
 
427
 *
 
428
 * @param lst           Head of the list to be cleaned up.
 
429
 */
 
430
void ipc_cleanup_call_list(link_t *lst)
 
431
{
 
432
        call_t *call;
 
433
 
 
434
        while (!list_empty(lst)) {
 
435
                call = list_get_instance(lst->next, call_t, link);
 
436
                if (call->buffer)
 
437
                        free(call->buffer);
 
438
                list_remove(&call->link);
 
439
 
 
440
                IPC_SET_RETVAL(call->data, EHANGUP);
 
441
                _ipc_answer_free_call(call);
 
442
        }
 
443
}
 
444
 
 
445
/** Disconnects all phones connected to an answerbox.
 
446
 *
 
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.
 
450
 */
 
451
void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
 
452
{
 
453
        phone_t *phone;
 
454
        DEADLOCK_PROBE_INIT(p_phonelck);
 
455
        ipl_t ipl;
 
456
        call_t *call;
 
457
 
 
458
        call = notify_box ? ipc_call_alloc(0) : NULL;
 
459
 
 
460
        /* Disconnect all phones connected to our answerbox */
 
461
restart_phones:
 
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,
 
466
                    phone_t, link);
 
467
                if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
 
468
                        spinlock_unlock(&box->lock);
 
469
                        interrupts_restore(ipl);
 
470
                        DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
 
471
                        goto restart_phones;
 
472
                }
 
473
                
 
474
                /* Disconnect phone */
 
475
                ASSERT(phone->state == IPC_PHONE_CONNECTED);
 
476
 
 
477
                list_remove(&phone->link);
 
478
                phone->state = IPC_PHONE_SLAMMED;
 
479
 
 
480
                if (notify_box) {
 
481
                        mutex_unlock(&phone->lock);
 
482
                        spinlock_unlock(&box->lock);
 
483
                        interrupts_restore(ipl);
 
484
 
 
485
                        /*
 
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
 
489
                         * disconnected.
 
490
                         */
 
491
                        IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
 
492
                        call->flags |= IPC_CALL_DISCARD_ANSWER;
 
493
                        _ipc_call(phone, box, call);
 
494
 
 
495
                        /* Allocate another call in advance */
 
496
                        call = ipc_call_alloc(0);
 
497
 
 
498
                        /* Must start again */
 
499
                        goto restart_phones;
 
500
                }
 
501
 
 
502
                mutex_unlock(&phone->lock);
 
503
        }
 
504
 
 
505
        spinlock_unlock(&box->lock);
 
506
        interrupts_restore(ipl);
 
507
 
 
508
        /* Free unused call */
 
509
        if (call)
 
510
                ipc_call_free(call);
 
511
}
 
512
 
 
513
/** Cleans up all IPC communication of the current task.
 
514
 *
 
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.
 
517
 */
 
518
void ipc_cleanup(void)
 
519
{
 
520
        int i;
 
521
        call_t *call;
 
522
 
 
523
        /* Disconnect all our phones ('ipc_phone_hangup') */
 
524
        for (i = 0; i < IPC_MAX_PHONES; i++)
 
525
                ipc_phone_hangup(&TASK->phones[i]);
 
526
 
 
527
        /* Unsubscribe from any event notifications. */
 
528
        event_cleanup_answerbox(&TASK->answerbox);
 
529
 
 
530
        /* Disconnect all connected irqs */
 
531
        ipc_irq_cleanup(&TASK->answerbox);
 
532
 
 
533
        /* Disconnect all phones connected to our regular answerbox */
 
534
        ipc_answerbox_slam_phones(&TASK->answerbox, false);
 
535
 
 
536
#ifdef CONFIG_UDEBUG
 
537
        /* Clean up kbox thread and communications */
 
538
        ipc_kbox_cleanup();
 
539
#endif
 
540
 
 
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);
 
546
        
 
547
        /* Wait for all async answers to arrive */
 
548
        while (1) {
 
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;
 
556
                        
 
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 */
 
564
                        
 
565
                        if (TASK->phones[i].state != IPC_PHONE_FREE)
 
566
                                break;
 
567
                }
 
568
                /* Voila, got into cleanup */
 
569
                if (i == IPC_MAX_PHONES)
 
570
                        break;
 
571
                
 
572
                call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
 
573
                    SYNCH_FLAGS_NONE);
 
574
                ASSERT((call->flags & IPC_CALL_ANSWERED) ||
 
575
                    (call->flags & IPC_CALL_NOTIF));
 
576
                ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
 
577
                
 
578
                /*
 
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.
 
582
                 */
 
583
                if (!(call->flags & IPC_CALL_DISCARD_ANSWER))
 
584
                        atomic_dec(&TASK->active_calls);
 
585
                ipc_call_free(call);
 
586
        }
 
587
}
 
588
 
 
589
 
 
590
/** Initilize IPC subsystem */
 
591
void ipc_init(void)
 
592
{
 
593
        ipc_call_slab = slab_cache_create("ipc_call", sizeof(call_t), 0, NULL,
 
594
            NULL, 0);
 
595
}
 
596
 
 
597
 
 
598
/** List answerbox contents.
 
599
 *
 
600
 * @param taskid        Task ID.
 
601
 */
 
602
void ipc_print_task(task_id_t taskid)
 
603
{
 
604
        task_t *task;
 
605
        int i;
 
606
        call_t *call;
 
607
        link_t *tmp;
 
608
        
 
609
        spinlock_lock(&tasks_lock);
 
610
        task = task_find_by_id(taskid);
 
611
        if (task) 
 
612
                spinlock_lock(&task->lock);
 
613
        spinlock_unlock(&tasks_lock);
 
614
        if (!task)
 
615
                return;
 
616
 
 
617
        /* Print opened phones & details */
 
618
        printf("PHONE:\n");
 
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);
 
622
                        continue;
 
623
                }
 
624
                if (task->phones[i].state != IPC_PHONE_FREE) {
 
625
                        printf("%d: ", i);
 
626
                        switch (task->phones[i].state) {
 
627
                        case IPC_PHONE_CONNECTING:
 
628
                                printf("connecting ");
 
629
                                break;
 
630
                        case IPC_PHONE_CONNECTED:
 
631
                                printf("connected to: %p ", 
 
632
                                       task->phones[i].callee);
 
633
                                break;
 
634
                        case IPC_PHONE_SLAMMED:
 
635
                                printf("slammed by: %p ", 
 
636
                                       task->phones[i].callee);
 
637
                                break;
 
638
                        case IPC_PHONE_HUNGUP:
 
639
                                printf("hung up - was: %p ", 
 
640
                                       task->phones[i].callee);
 
641
                                break;
 
642
                        default:
 
643
                                break;
 
644
                        }
 
645
                        printf("active: %ld\n",
 
646
                            atomic_get(&task->phones[i].active_calls));
 
647
                }
 
648
                mutex_unlock(&task->phones[i].lock);
 
649
        }
 
650
 
 
651
 
 
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;
 
656
            tmp = tmp->next) {
 
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),
 
665
                    call->flags);
 
666
        }
 
667
        /* Print answerbox - calls */
 
668
        printf("ABOX - DISPATCHED CALLS:\n");
 
669
        for (tmp = task->answerbox.dispatched_calls.next;
 
670
            tmp != &task->answerbox.dispatched_calls; 
 
671
            tmp = tmp->next) {
 
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),
 
680
                    call->flags);
 
681
        }
 
682
        /* Print answerbox - calls */
 
683
        printf("ABOX - ANSWERS:\n");
 
684
        for (tmp = task->answerbox.answers.next;
 
685
            tmp != &task->answerbox.answers;
 
686
            tmp = tmp->next) {
 
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),
 
693
                    call->flags);
 
694
        }
 
695
 
 
696
        spinlock_unlock(&task->answerbox.lock);
 
697
        spinlock_unlock(&task->lock);
 
698
}
 
699
 
 
700
/** @}
 
701
 */