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
Line 
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
34#include <synch/spinlock.h>
35#include <synch/waitq.h>
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>
46#include <arch/interrupt.h>
47
48/* Open channel that is assigned automatically to new tasks */
49answerbox_t *ipc_phone_0 = NULL;
50
51static slab_cache_t *ipc_call_slab;
52
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
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
69/** Allocate & initialize call structure
70 *
71 * The call is initialized, so that the reply will be directed
72 * to TASK->answerbox
73 *
74 * @param flags Parameters for slab_alloc (ATOMIC, etc.)
75 */
76call_t * ipc_call_alloc(int flags)
77{
78 call_t *call;
79
80 call = slab_alloc(ipc_call_slab, flags);
81 _ipc_call_init(call);
82
83 return call;
84}
85
86/** Initialize allocated call */
87void ipc_call_static_init(call_t *call)
88{
89 _ipc_call_init(call);
90 call->flags |= IPC_CALL_STATIC_ALLOC;
91}
92
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{
103 spinlock_initialize(&box->lock, "ipc_box_lock");
104 spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
105 waitq_initialize(&box->wq);
106 list_initialize(&box->connected_phones);
107 list_initialize(&box->calls);
108 list_initialize(&box->dispatched_calls);
109 list_initialize(&box->answers);
110 list_initialize(&box->irq_notifs);
111 box->task = TASK;
112}
113
114/** Connect phone to answerbox */
115void ipc_phone_connect(phone_t *phone, answerbox_t *box)
116{
117 spinlock_lock(&phone->lock);
118
119 ASSERT(!phone->callee);
120 phone->busy = IPC_BUSY_CONNECTED;
121 phone->callee = box;
122
123 spinlock_lock(&box->lock);
124 list_append(&phone->list, &box->connected_phones);
125 spinlock_unlock(&box->lock);
126
127 spinlock_unlock(&phone->lock);
128}
129
130/** Initialize phone structure and connect phone to answerbox
131 */
132void ipc_phone_init(phone_t *phone)
133{
134 spinlock_initialize(&phone->lock, "phone_lock");
135 phone->callee = NULL;
136 phone->busy = IPC_BUSY_FREE;
137 atomic_set(&phone->active_calls, 0);
138}
139
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}
153
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
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
201/* Unsafe unchecking ipc_call */
202static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
203{
204 if (! (call->flags & IPC_CALL_FORWARDED)) {
205 atomic_inc(&phone->active_calls);
206 call->data.phone = phone;
207 }
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
215/** Send a asynchronous request using phone to answerbox
216 *
217 * @param phone Phone connected to answerbox
218 * @param request Request to be sent
219 */
220int ipc_call(phone_t *phone, call_t *call)
221{
222 answerbox_t *box;
223
224 spinlock_lock(&phone->lock);
225
226 box = phone->callee;
227 if (!box) {
228 /* Trying to send over disconnected phone */
229 spinlock_unlock(&phone->lock);
230 if (call->flags & IPC_CALL_FORWARDED) {
231 IPC_SET_RETVAL(call->data, EFORWARD);
232 _ipc_answer_free_call(call);
233 } else { /* Simulate sending back a message */
234 if (phone->busy == IPC_BUSY_CONNECTED)
235 ipc_backsend_err(phone, call, EHANGUP);
236 else
237 ipc_backsend_err(phone, call, ENOENT);
238 }
239
240 return ENOENT;
241 }
242 _ipc_call(phone, box, call);
243
244 spinlock_unlock(&phone->lock);
245 return 0;
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) {
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;
268 spinlock_unlock(&phone->lock);
269 return 0;
270 }
271
272 spinlock_lock(&box->lock);
273 list_remove(&phone->list);
274 phone->callee = NULL;
275 spinlock_unlock(&box->lock);
276
277 call = ipc_call_alloc(0);
278 IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
279 call->flags |= IPC_CALL_DISCARD_ANSWER;
280 _ipc_call(phone, box, call);
281
282 phone->busy = IPC_BUSY_FREE;
283
284 spinlock_unlock(&phone->lock);
285
286 return 0;
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
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
298 */
299int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
300{
301 spinlock_lock(&oldbox->lock);
302 list_remove(&call->list);
303 spinlock_unlock(&oldbox->lock);
304
305 return ipc_call(newphone, call);
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;
317 ipl_t ipl;
318
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
326 spinlock_lock(&box->lock);
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)) {
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 {
348 /* This can happen regularly after ipc_cleanup, remove
349 * the warning in the future when the IPC is
350 * more debugged */
351 printf("WARNING: Spurious IPC wakeup.\n");
352 spinlock_unlock(&box->lock);
353 goto restart;
354 }
355 spinlock_unlock(&box->lock);
356 return request;
357}
358
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
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
393/** Cleans up all IPC communication of the given task
394 *
395 *
396 */
397void ipc_cleanup(task_t *task)
398{
399 int i;
400 call_t *call;
401 phone_t *phone;
402
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
407 /* Disconnect all connected irqs */
408 ipc_irq_cleanup(&task->answerbox);
409
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 }
428
429 /* Answer all messages in 'calls' and 'dispatched_calls' queues */
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);
434
435 /* Wait for all async answers to arrive */
436 while (atomic_get(&task->active_calls)) {
437 call = ipc_wait_for_call(&task->answerbox, 0);
438 ASSERT((call->flags & IPC_CALL_ANSWERED) || (call->flags & IPC_CALL_NOTIF));
439 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
440
441 atomic_dec(&task->active_calls);
442 ipc_call_free(call);
443 }
444}
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.