source: mainline/generic/src/ipc/ipc.c@ 738ad2e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 738ad2e was 51a7dc1, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

Small fixes.

  • Property mode set to 100644
File size: 13.1 KB
RevLine 
[6d9c49a]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/* Lock ordering
30 *
31 * First the answerbox, then the phone
32 */
33
[2ba7810]34#include <synch/spinlock.h>
35#include <synch/waitq.h>
[6d9c49a]36#include <ipc/ipc.h>
37#include <errno.h>
38#include <mm/slab.h>
39#include <arch.h>
40#include <proc/task.h>
41#include <memstr.h>
42#include <debug.h>
43
44#include <print.h>
45#include <proc/thread.h>
[5626277]46#include <arch/interrupt.h>
[6d9c49a]47
[e74cb73]48/* Open channel that is assigned automatically to new tasks */
49answerbox_t *ipc_phone_0 = NULL;
[6d9c49a]50
51static slab_cache_t *ipc_call_slab;
52
[5626277]53typedef struct {
54 SPINLOCK_DECLARE(lock);
55 answerbox_t *box;
56} ipc_irq_t;
57
58static ipc_irq_t *irq_conns = NULL;
59static int irq_conns_size;
60
[ba81cab]61/* Initialize new call */
62static void _ipc_call_init(call_t *call)
63{
64 memsetb((__address)call, sizeof(*call), 0);
65 call->callerbox = &TASK->answerbox;
66 call->sender = TASK;
67}
68
[6d9c49a]69/** Allocate & initialize call structure
70 *
71 * The call is initialized, so that the reply will be directed
72 * to TASK->answerbox
[5626277]73 *
74 * @param flags Parameters for slab_alloc (ATOMIC, etc.)
[6d9c49a]75 */
[5626277]76call_t * ipc_call_alloc(int flags)
[6d9c49a]77{
78 call_t *call;
79
[5626277]80 call = slab_alloc(ipc_call_slab, flags);
[ba81cab]81 _ipc_call_init(call);
[6d9c49a]82
83 return call;
84}
85
[e74cb73]86/** Initialize allocated call */
[ba81cab]87void ipc_call_static_init(call_t *call)
[e74cb73]88{
[ba81cab]89 _ipc_call_init(call);
90 call->flags |= IPC_CALL_STATIC_ALLOC;
[e74cb73]91}
92
[6d9c49a]93/** Deallocate call stracuture */
94void ipc_call_free(call_t *call)
95{
96 slab_free(ipc_call_slab, call);
97}
98
99/** Initialize answerbox structure
100 */
101void ipc_answerbox_init(answerbox_t *box)
102{
[2ba7810]103 spinlock_initialize(&box->lock, "ipc_box_lock");
[5626277]104 spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
[2ba7810]105 waitq_initialize(&box->wq);
[6d9c49a]106 list_initialize(&box->connected_phones);
107 list_initialize(&box->calls);
108 list_initialize(&box->dispatched_calls);
109 list_initialize(&box->answers);
[5626277]110 list_initialize(&box->irq_notifs);
[2d5a54f3]111 box->task = TASK;
[6d9c49a]112}
113
[2ba7810]114/** Connect phone to answerbox */
115void ipc_phone_connect(phone_t *phone, answerbox_t *box)
[6d9c49a]116{
[ba81cab]117 spinlock_lock(&phone->lock);
118
[2ba7810]119 ASSERT(!phone->callee);
[9f22213]120 phone->busy = IPC_BUSY_CONNECTED;
[6d9c49a]121 phone->callee = box;
[e74cb73]122
[2ba7810]123 spinlock_lock(&box->lock);
[6d9c49a]124 list_append(&phone->list, &box->connected_phones);
[2ba7810]125 spinlock_unlock(&box->lock);
[ba81cab]126
127 spinlock_unlock(&phone->lock);
[2ba7810]128}
129
[6ecc8bce]130/** Initialize phone structure and connect phone to answerbox
[2ba7810]131 */
132void ipc_phone_init(phone_t *phone)
133{
134 spinlock_initialize(&phone->lock, "phone_lock");
135 phone->callee = NULL;
[9f22213]136 phone->busy = IPC_BUSY_FREE;
137 atomic_set(&phone->active_calls, 0);
[6d9c49a]138}
139
[631ca4d]140/** Helper function to facilitate synchronous calls */
141void ipc_call_sync(phone_t *phone, call_t *request)
142{
143 answerbox_t sync_box;
144
145 ipc_answerbox_init(&sync_box);
146
147 /* We will receive data on special box */
148 request->callerbox = &sync_box;
149
150 ipc_call(phone, request);
151 ipc_wait_for_call(&sync_box, 0);
152}
[6d9c49a]153
[ba81cab]154/** Answer message that was not dispatched and is not entered in
155 * any queue
156 */
157static void _ipc_answer_free_call(call_t *call)
158{
159 answerbox_t *callerbox = call->callerbox;
160
161 call->flags |= IPC_CALL_ANSWERED;
162
163 spinlock_lock(&callerbox->lock);
164 list_append(&call->list, &callerbox->answers);
165 spinlock_unlock(&callerbox->lock);
166 waitq_wakeup(&callerbox->wq, 0);
167}
168
169/** Answer message, that is in callee queue
170 *
171 * @param box Answerbox that is answering the message
172 * @param call Modified request that is being sent back
173 */
174void ipc_answer(answerbox_t *box, call_t *call)
175{
176 /* Remove from active box */
177 spinlock_lock(&box->lock);
178 list_remove(&call->list);
179 spinlock_unlock(&box->lock);
180 /* Send back answer */
181 _ipc_answer_free_call(call);
182}
183
[7c7aae16]184/** Simulate sending back a message
185 *
186 * Most errors are better handled by forming a normal backward
187 * message and sending it as a normal answer.
188 */
189void ipc_backsend_err(phone_t *phone, call_t *call, __native err)
190{
191 call->data.phone = phone;
192 atomic_inc(&phone->active_calls);
193 if (phone->busy == IPC_BUSY_CONNECTED)
194 IPC_SET_RETVAL(call->data, EHANGUP);
195 else
196 IPC_SET_RETVAL(call->data, ENOENT);
197
198 _ipc_answer_free_call(call);
199}
200
[fbcfd458]201/* Unsafe unchecking ipc_call */
202static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
203{
[9f22213]204 if (! (call->flags & IPC_CALL_FORWARDED)) {
205 atomic_inc(&phone->active_calls);
206 call->data.phone = phone;
207 }
[fbcfd458]208
209 spinlock_lock(&box->lock);
210 list_append(&call->list, &box->calls);
211 spinlock_unlock(&box->lock);
212 waitq_wakeup(&box->wq, 0);
213}
214
[631ca4d]215/** Send a asynchronous request using phone to answerbox
[6d9c49a]216 *
217 * @param phone Phone connected to answerbox
218 * @param request Request to be sent
219 */
[9f22213]220int ipc_call(phone_t *phone, call_t *call)
[6d9c49a]221{
[ba81cab]222 answerbox_t *box;
[6d9c49a]223
[ba81cab]224 spinlock_lock(&phone->lock);
[fbcfd458]225
[ba81cab]226 box = phone->callee;
227 if (!box) {
228 /* Trying to send over disconnected phone */
[fbcfd458]229 spinlock_unlock(&phone->lock);
[9f22213]230 if (call->flags & IPC_CALL_FORWARDED) {
231 IPC_SET_RETVAL(call->data, EFORWARD);
[7c7aae16]232 _ipc_answer_free_call(call);
233 } else { /* Simulate sending back a message */
[9f22213]234 if (phone->busy == IPC_BUSY_CONNECTED)
[7c7aae16]235 ipc_backsend_err(phone, call, EHANGUP);
[9f22213]236 else
[7c7aae16]237 ipc_backsend_err(phone, call, ENOENT);
[9f22213]238 }
[fbcfd458]239
[9f22213]240 return ENOENT;
[ba81cab]241 }
[fbcfd458]242 _ipc_call(phone, box, call);
243
244 spinlock_unlock(&phone->lock);
[9f22213]245 return 0;
[fbcfd458]246}
247
248/** Disconnect phone from answerbox
249 *
250 * It is allowed to call disconnect on already disconnected phone
251 *
252 * @return 0 - phone disconnected, -1 - the phone was already disconnected
253 */
254int ipc_phone_hangup(phone_t *phone)
255{
256 answerbox_t *box;
257 call_t *call;
258
259 spinlock_lock(&phone->lock);
260 box = phone->callee;
261 if (!box) {
[9f22213]262 if (phone->busy == IPC_BUSY_CONNECTING) {
263 spinlock_unlock(&phone->lock);
264 return -1;
265 }
266 /* Already disconnected phone */
267 phone->busy = IPC_BUSY_FREE;
[fbcfd458]268 spinlock_unlock(&phone->lock);
[9f22213]269 return 0;
[fbcfd458]270 }
[6d9c49a]271
[2ba7810]272 spinlock_lock(&box->lock);
[fbcfd458]273 list_remove(&phone->list);
274 phone->callee = NULL;
[2ba7810]275 spinlock_unlock(&box->lock);
[fbcfd458]276
[5626277]277 call = ipc_call_alloc(0);
[fbcfd458]278 IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
279 call->flags |= IPC_CALL_DISCARD_ANSWER;
280 _ipc_call(phone, box, call);
281
[9f22213]282 phone->busy = IPC_BUSY_FREE;
[fbcfd458]283
[ba81cab]284 spinlock_unlock(&phone->lock);
[fbcfd458]285
286 return 0;
[2ba7810]287}
288
289/** Forwards call from one answerbox to a new one
290 *
291 * @param request Request to be forwarded
292 * @param newbox Target answerbox
293 * @param oldbox Old answerbox
[9f22213]294 * @return 0 on forward ok, error code, if there was error
295 *
296 * - the return value serves only as an information for the forwarder,
297 * the original caller is notified automatically with EFORWARD
[2ba7810]298 */
[9f22213]299int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
[2ba7810]300{
301 spinlock_lock(&oldbox->lock);
302 list_remove(&call->list);
303 spinlock_unlock(&oldbox->lock);
304
[9f22213]305 return ipc_call(newphone, call);
[6d9c49a]306}
307
308
309/** Wait for phone call
310 *
311 * @return Recived message address
312 * - to distinguish between call and answer, look at call->flags
313 */
314call_t * ipc_wait_for_call(answerbox_t *box, int flags)
315{
316 call_t *request;
[5626277]317 ipl_t ipl;
[6d9c49a]318
[fbcfd458]319restart:
320 if (flags & IPC_WAIT_NONBLOCKING) {
321 if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK)
322 return NULL;
323 } else
324 waitq_sleep(&box->wq);
325
[2ba7810]326 spinlock_lock(&box->lock);
[5626277]327 if (!list_empty(&box->irq_notifs)) {
328 ipl = interrupts_disable();
329 spinlock_lock(&box->irq_lock);
330
331 request = list_get_instance(box->answers.next, call_t, list);
332 list_remove(&request->list);
333
334 spinlock_unlock(&box->irq_lock);
335 interrupts_restore(ipl);
336 } else if (!list_empty(&box->answers)) {
[fbcfd458]337 /* Handle asynchronous answers */
338 request = list_get_instance(box->answers.next, call_t, list);
339 list_remove(&request->list);
340 atomic_dec(&request->data.phone->active_calls);
341 } else if (!list_empty(&box->calls)) {
342 /* Handle requests */
343 request = list_get_instance(box->calls.next, call_t, list);
344 list_remove(&request->list);
345 /* Append request to dispatch queue */
346 list_append(&request->list, &box->dispatched_calls);
347 } else {
[ca687ad]348 /* This can happen regularly after ipc_cleanup, remove
349 * the warning in the future when the IPC is
350 * more debugged */
[fbcfd458]351 printf("WARNING: Spurious IPC wakeup.\n");
352 spinlock_unlock(&box->lock);
353 goto restart;
[6d9c49a]354 }
[2ba7810]355 spinlock_unlock(&box->lock);
[6d9c49a]356 return request;
357}
358
[ca687ad]359/** Answer all calls from list with EHANGUP msg */
360static void ipc_cleanup_call_list(link_t *lst)
361{
362 call_t *call;
363
364 while (!list_empty(lst)) {
365 call = list_get_instance(lst->next, call_t, list);
366 list_remove(&call->list);
367
368 IPC_SET_RETVAL(call->data, EHANGUP);
369 _ipc_answer_free_call(call);
370 }
371}
372
[5626277]373/** Disconnect all irq's notifications
374 *
375 * TODO: It may be better to do some linked list, so that
376 * we wouldn't need to go through whole array every cleanup
377 */
378static void ipc_irq_cleanup(answerbox_t *box)
379{
380 int i;
381 ipl_t ipl;
382
383 for (i=0; i < irq_conns_size; i++) {
384 ipl = interrupts_disable();
385 spinlock_lock(&irq_conns[i].lock);
386 if (irq_conns[i].box == box)
387 irq_conns[i].box = NULL;
388 spinlock_unlock(&irq_conns[i].lock);
389 interrupts_restore(ipl);
390 }
391}
392
[4e49572]393/** Cleans up all IPC communication of the given task
394 *
395 *
396 */
397void ipc_cleanup(task_t *task)
398{
[9f22213]399 int i;
[ca687ad]400 call_t *call;
401 phone_t *phone;
402
[9f22213]403 /* Disconnect all our phones ('ipc_phone_hangup') */
404 for (i=0;i < IPC_MAX_PHONES; i++)
405 ipc_phone_hangup(&task->phones[i]);
406
[5626277]407 /* Disconnect all connected irqs */
408 ipc_irq_cleanup(&task->answerbox);
409
[ca687ad]410 /* Disconnect all phones connected to our answerbox */
411restart_phones:
412 spinlock_lock(&task->answerbox.lock);
413 while (!list_empty(&task->answerbox.connected_phones)) {
414 phone = list_get_instance(task->answerbox.connected_phones.next,
415 phone_t,
416 list);
417 if (! spinlock_trylock(&phone->lock)) {
418 spinlock_unlock(&task->answerbox.lock);
419 goto restart_phones;
420 }
421
422 /* Disconnect phone */
423 phone->callee = NULL;
424 list_remove(&phone->list);
425
426 spinlock_unlock(&phone->lock);
427 }
[9f22213]428
429 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
[ca687ad]430 spinlock_lock(&task->answerbox.lock);
431 ipc_cleanup_call_list(&task->answerbox.dispatched_calls);
432 ipc_cleanup_call_list(&task->answerbox.calls);
433 spinlock_unlock(&task->answerbox.lock);
[4e49572]434
[9f22213]435 /* Wait for all async answers to arrive */
[ca687ad]436 while (atomic_get(&task->active_calls)) {
437 call = ipc_wait_for_call(&task->answerbox, 0);
[51a7dc1]438 ASSERT((call->flags & IPC_CALL_ANSWERED) || (call->flags & IPC_CALL_NOTIF));
[ca687ad]439 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
440
441 atomic_dec(&task->active_calls);
442 ipc_call_free(call);
443 }
[4e49572]444}
[5626277]445
446/** Initialize table of interrupt handlers */
447static void ipc_irq_make_table(int irqcount)
448{
449 int i;
450
451 irq_conns_size = irqcount;
452 irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
453 for (i=0; i < irqcount; i++) {
454 spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
455 irq_conns[i].box = NULL;
456 }
457}
458
459void ipc_irq_unregister(answerbox_t *box, int irq)
460{
461 ipl_t ipl;
462
463 ipl = interrupts_disable();
464 spinlock_lock(&irq_conns[irq].lock);
465 if (irq_conns[irq].box == box)
466 irq_conns[irq].box = NULL;
467
468 spinlock_unlock(&irq_conns[irq].lock);
469 interrupts_restore(ipl);
470}
471
472/** Register an answerbox as a receiving end of interrupts notifications */
473int ipc_irq_register(answerbox_t *box, int irq)
474{
475 ipl_t ipl;
476
477 ASSERT(irq_conns);
478
479 ipl = interrupts_disable();
480 spinlock_lock(&irq_conns[irq].lock);
481
482 if (irq_conns[irq].box) {
483 spinlock_unlock(&irq_conns[irq].lock);
484 interrupts_restore(ipl);
485 return EEXISTS;
486 }
487 irq_conns[irq].box = box;
488 spinlock_unlock(&irq_conns[irq].lock);
489 interrupts_restore(ipl);
490
491 return 0;
492}
493
494/** Notify process that an irq had happend
495 *
496 * We expect interrupts to be disabled
497 */
498void ipc_irq_send_notif(int irq)
499{
500 call_t *call;
501
502 ASSERT(irq_conns);
503 spinlock_lock(&irq_conns[irq].lock);
504
505 if (irq_conns[irq].box) {
506 call = ipc_call_alloc(FRAME_ATOMIC);
507 call->flags |= IPC_CALL_NOTIF;
508 IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
509 IPC_SET_ARG1(call->data, irq);
510
511 spinlock_lock(&irq_conns[irq].box->irq_lock);
512 list_append(&call->list, &irq_conns[irq].box->irq_notifs);
513 spinlock_unlock(&irq_conns[irq].box->irq_lock);
514
515 waitq_wakeup(&irq_conns[irq].box->wq, 0);
516 }
517
518 spinlock_unlock(&irq_conns[irq].lock);
519}
520
521/** Initilize ipc subsystem */
522void ipc_init(void)
523{
524 ipc_call_slab = slab_cache_create("ipc_call",
525 sizeof(call_t),
526 0,
527 NULL, NULL, 0);
528 ipc_irq_make_table(IRQ_COUNT);
529}
530
Note: See TracBrowser for help on using the repository browser.