source: mainline/uspace/lib/c/generic/ipc.c@ fcab7ef

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

Do not create a new fibril for each IRQ notification

In the absence of fibril serialization, manager fibrils can
theoretically block in async IPC or on fibril synchronization
primitives. Consequently, it is safe to execute the IRQ handler directly
from the manager fibril. The manager fibril can block while processing
the notification, but most of the times it will not block and the
handler will execute atomically.

This changeset modifies the current behaviour so that we no longer spawn
a new notification fibril for each IRQ, but rather execute the handler
directly from the manager fibril and test if the execution blocked. If
it blocked, the manager fibril had assumed the role of a notification
fibril and we destroy it afterwards - merely to avoid fibril population
explosion. Otherwise, which is the usual behavior, we keep it so that
it resumes its job of a manager fibril.

  • Property mode set to 100644
File size: 15.0 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/** @addtogroup libc
30 * @{
31 * @}
32 */
33
34/** @addtogroup libcipc IPC
35 * @brief HelenOS uspace IPC
36 * @{
37 * @ingroup libc
38 */
39/** @file
40 */
41
42#include <ipc/ipc.h>
43#include <libc.h>
44#include <malloc.h>
45#include <errno.h>
46#include <adt/list.h>
47#include <futex.h>
48#include <fibril.h>
49#include <macros.h>
50
51/**
52 * Structures of this type are used for keeping track
53 * of sent asynchronous calls and queing unsent calls.
54 */
55typedef struct {
56 link_t list;
57
58 ipc_async_callback_t callback;
59 void *private;
60
61 union {
62 ipc_callid_t callid;
63 struct {
64 ipc_call_t data;
65 int phoneid;
66 } msg;
67 } u;
68
69 /** Fibril waiting for sending this call. */
70 fid_t fid;
71} async_call_t;
72
73LIST_INITIALIZE(dispatched_calls);
74
75/** List of asynchronous calls that were not accepted by kernel.
76 *
77 * Protected by async_futex, because if the call is not accepted
78 * by the kernel, the async framework is used automatically.
79 *
80 */
81LIST_INITIALIZE(queued_calls);
82
83static futex_t ipc_futex = FUTEX_INITIALIZER;
84
85/** Send asynchronous message via syscall.
86 *
87 * @param phoneid Phone handle for the call.
88 * @param data Call data with the request.
89 *
90 * @return Hash of the call or an error code.
91 *
92 */
93static ipc_callid_t ipc_call_async_internal(int phoneid, ipc_call_t *data)
94{
95 return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data);
96}
97
98/** Prologue for ipc_call_async_*() functions.
99 *
100 * @param private Argument for the answer/error callback.
101 * @param callback Answer/error callback.
102 *
103 * @return New, partially initialized async_call structure or NULL.
104 *
105 */
106static inline async_call_t *ipc_prepare_async(void *private,
107 ipc_async_callback_t callback)
108{
109 async_call_t *call =
110 (async_call_t *) malloc(sizeof(async_call_t));
111 if (!call) {
112 if (callback)
113 callback(private, ENOMEM, NULL);
114
115 return NULL;
116 }
117
118 call->callback = callback;
119 call->private = private;
120
121 return call;
122}
123
124/** Epilogue for ipc_call_async_*() functions.
125 *
126 * @param callid Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
127 * @param phoneid Phone handle through which the call was made.
128 * @param call Structure returned by ipc_prepare_async().
129 */
130static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
131 async_call_t *call)
132{
133 if (!call) {
134 /* Nothing to do regardless if failed or not */
135 futex_unlock(&ipc_futex);
136 return;
137 }
138
139 if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
140 futex_unlock(&ipc_futex);
141
142 /* Call asynchronous handler with error code */
143 if (call->callback)
144 call->callback(call->private, ENOENT, NULL);
145
146 free(call);
147 return;
148 }
149
150 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
151 futex_unlock(&ipc_futex);
152
153 call->u.msg.phoneid = phoneid;
154
155 futex_down(&async_futex);
156 list_append(&call->list, &queued_calls);
157
158 call->fid = fibril_get_id();
159 fibril_switch(FIBRIL_TO_MANAGER);
160 /* Async futex unlocked by previous call */
161
162 return;
163 }
164
165 call->u.callid = callid;
166
167 /* Add call to the list of dispatched calls */
168 list_append(&call->list, &dispatched_calls);
169 futex_unlock(&ipc_futex);
170}
171
172/** Fast asynchronous call.
173 *
174 * This function can only handle four arguments of payload. It is, however,
175 * faster than the more generic ipc_call_async_slow().
176 *
177 * Note that this function is a void function.
178 *
179 * During normal operation, answering this call will trigger the callback.
180 * In case of fatal error, the callback handler is called with the proper
181 * error code. If the call cannot be temporarily made, it is queued.
182 *
183 * @param phoneid Phone handle for the call.
184 * @param imethod Requested interface and method.
185 * @param arg1 Service-defined payload argument.
186 * @param arg2 Service-defined payload argument.
187 * @param arg3 Service-defined payload argument.
188 * @param arg4 Service-defined payload argument.
189 * @param private Argument to be passed to the answer/error callback.
190 * @param callback Answer or error callback.
191 */
192void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
193 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private,
194 ipc_async_callback_t callback)
195{
196 async_call_t *call = NULL;
197
198 if (callback) {
199 call = ipc_prepare_async(private, callback);
200 if (!call)
201 return;
202 }
203
204 /*
205 * We need to make sure that we get callid
206 * before another thread accesses the queue again.
207 */
208
209 futex_lock(&ipc_futex);
210 ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
211 imethod, arg1, arg2, arg3, arg4);
212
213 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
214 if (!call) {
215 call = ipc_prepare_async(private, callback);
216 if (!call) {
217 futex_unlock(&ipc_futex);
218 return;
219 }
220 }
221
222 IPC_SET_IMETHOD(call->u.msg.data, imethod);
223 IPC_SET_ARG1(call->u.msg.data, arg1);
224 IPC_SET_ARG2(call->u.msg.data, arg2);
225 IPC_SET_ARG3(call->u.msg.data, arg3);
226 IPC_SET_ARG4(call->u.msg.data, arg4);
227
228 /*
229 * To achieve deterministic behavior, we always zero out the
230 * arguments that are beyond the limits of the fast version.
231 */
232
233 IPC_SET_ARG5(call->u.msg.data, 0);
234 }
235
236 ipc_finish_async(callid, phoneid, call);
237}
238
239/** Asynchronous call transmitting the entire payload.
240 *
241 * Note that this function is a void function.
242 *
243 * During normal operation, answering this call will trigger the callback.
244 * In case of fatal error, the callback handler is called with the proper
245 * error code. If the call cannot be temporarily made, it is queued.
246 *
247 * @param phoneid Phone handle for the call.
248 * @param imethod Requested interface and method.
249 * @param arg1 Service-defined payload argument.
250 * @param arg2 Service-defined payload argument.
251 * @param arg3 Service-defined payload argument.
252 * @param arg4 Service-defined payload argument.
253 * @param arg5 Service-defined payload argument.
254 * @param private Argument to be passed to the answer/error callback.
255 * @param callback Answer or error callback.
256 */
257void ipc_call_async_slow(int phoneid, sysarg_t imethod, sysarg_t arg1,
258 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private,
259 ipc_async_callback_t callback)
260{
261 async_call_t *call = ipc_prepare_async(private, callback);
262 if (!call)
263 return;
264
265 IPC_SET_IMETHOD(call->u.msg.data, imethod);
266 IPC_SET_ARG1(call->u.msg.data, arg1);
267 IPC_SET_ARG2(call->u.msg.data, arg2);
268 IPC_SET_ARG3(call->u.msg.data, arg3);
269 IPC_SET_ARG4(call->u.msg.data, arg4);
270 IPC_SET_ARG5(call->u.msg.data, arg5);
271
272 /*
273 * We need to make sure that we get callid
274 * before another threadaccesses the queue again.
275 */
276
277 futex_lock(&ipc_futex);
278 ipc_callid_t callid =
279 ipc_call_async_internal(phoneid, &call->u.msg.data);
280
281 ipc_finish_async(callid, phoneid, call);
282}
283
284/** Answer received call (fast version).
285 *
286 * The fast answer makes use of passing retval and first four arguments in
287 * registers. If you need to return more, use the ipc_answer_slow() instead.
288 *
289 * @param callid Hash of the call being answered.
290 * @param retval Return value.
291 * @param arg1 First return argument.
292 * @param arg2 Second return argument.
293 * @param arg3 Third return argument.
294 * @param arg4 Fourth return argument.
295 *
296 * @return Zero on success.
297 * @return Value from @ref errno.h on failure.
298 *
299 */
300sysarg_t ipc_answer_fast(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
301 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
302{
303 return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3,
304 arg4);
305}
306
307/** Answer received call (entire payload).
308 *
309 * @param callid Hash of the call being answered.
310 * @param retval Return value.
311 * @param arg1 First return argument.
312 * @param arg2 Second return argument.
313 * @param arg3 Third return argument.
314 * @param arg4 Fourth return argument.
315 * @param arg5 Fifth return argument.
316 *
317 * @return Zero on success.
318 * @return Value from @ref errno.h on failure.
319 *
320 */
321sysarg_t ipc_answer_slow(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
322 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
323{
324 ipc_call_t data;
325
326 IPC_SET_RETVAL(data, retval);
327 IPC_SET_ARG1(data, arg1);
328 IPC_SET_ARG2(data, arg2);
329 IPC_SET_ARG3(data, arg3);
330 IPC_SET_ARG4(data, arg4);
331 IPC_SET_ARG5(data, arg5);
332
333 return __SYSCALL2(SYS_IPC_ANSWER_SLOW, callid, (sysarg_t) &data);
334}
335
336/** Try to dispatch queued calls from the async queue.
337 *
338 */
339static void dispatch_queued_calls(void)
340{
341 /** @todo
342 * Integrate intelligently ipc_futex so that it is locked during
343 * ipc_call_async_*() until it is added to dispatched_calls.
344 */
345
346 futex_down(&async_futex);
347
348 while (!list_empty(&queued_calls)) {
349 async_call_t *call =
350 list_get_instance(list_first(&queued_calls), async_call_t, list);
351 ipc_callid_t callid =
352 ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data);
353
354 if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY)
355 break;
356
357 list_remove(&call->list);
358
359 futex_up(&async_futex);
360
361 assert(call->fid);
362 fibril_add_ready(call->fid);
363
364 if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
365 if (call->callback)
366 call->callback(call->private, ENOENT, NULL);
367
368 free(call);
369 } else {
370 call->u.callid = callid;
371
372 futex_lock(&ipc_futex);
373 list_append(&call->list, &dispatched_calls);
374 futex_unlock(&ipc_futex);
375 }
376
377 futex_down(&async_futex);
378 }
379
380 futex_up(&async_futex);
381}
382
383/** Handle received answer.
384 *
385 * Find the hash of the answer and call the answer callback.
386 *
387 * The answer has the same hash as the request OR'ed with
388 * the IPC_CALLID_ANSWERED bit.
389 *
390 * @todo Use hash table.
391 *
392 * @param callid Hash of the received answer.
393 * @param data Call data of the answer.
394 *
395 */
396static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
397{
398 callid &= ~IPC_CALLID_ANSWERED;
399
400 futex_lock(&ipc_futex);
401
402 link_t *item;
403 for (item = dispatched_calls.head.next; item != &dispatched_calls.head;
404 item = item->next) {
405 async_call_t *call =
406 list_get_instance(item, async_call_t, list);
407
408 if (call->u.callid == callid) {
409 list_remove(&call->list);
410
411 futex_unlock(&ipc_futex);
412
413 if (call->callback)
414 call->callback(call->private,
415 IPC_GET_RETVAL(*data), data);
416
417 free(call);
418 return;
419 }
420 }
421
422 futex_unlock(&ipc_futex);
423}
424
425/** Wait for first IPC call to come.
426 *
427 * @param call Incoming call storage.
428 * @param usec Timeout in microseconds
429 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
430 *
431 * @return Hash of the call. Note that certain bits have special
432 * meaning: IPC_CALLID_ANSWERED is set in an answer
433 * and IPC_CALLID_NOTIFICATION is used for notifications.
434 *
435 */
436ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
437 unsigned int flags)
438{
439 ipc_callid_t callid =
440 __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
441
442 /* Handle received answers */
443 if (callid & IPC_CALLID_ANSWERED) {
444 handle_answer(callid, call);
445 dispatch_queued_calls();
446 }
447
448 return callid;
449}
450
451/** Interrupt one thread of this task from waiting for IPC.
452 *
453 */
454void ipc_poke(void)
455{
456 __SYSCALL0(SYS_IPC_POKE);
457}
458
459/** Wait for first IPC call to come.
460 *
461 * Only requests are returned, answers are processed internally.
462 *
463 * @param call Incoming call storage.
464 * @param usec Timeout in microseconds
465 *
466 * @return Hash of the call.
467 *
468 */
469ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
470{
471 ipc_callid_t callid;
472
473 do {
474 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
475 } while (callid & IPC_CALLID_ANSWERED);
476
477 return callid;
478}
479
480/** Check if there is an IPC call waiting to be picked up.
481 *
482 * Only requests are returned, answers are processed internally.
483 *
484 * @param call Incoming call storage.
485 *
486 * @return Hash of the call.
487 *
488 */
489ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
490{
491 ipc_callid_t callid;
492
493 do {
494 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
495 SYNCH_FLAGS_NON_BLOCKING);
496 } while (callid & IPC_CALLID_ANSWERED);
497
498 return callid;
499}
500
501/** Hang up a phone.
502 *
503 * @param phoneid Handle of the phone to be hung up.
504 *
505 * @return Zero on success or a negative error code.
506 *
507 */
508int ipc_hangup(int phoneid)
509{
510 return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
511}
512
513/** Forward a received call to another destination.
514 *
515 * For non-system methods, the old method, arg1 and arg2 are rewritten
516 * by the new values. For system methods, the new method, arg1 and arg2
517 * are written to the old arg1, arg2 and arg3, respectivelly. Calls with
518 * immutable methods are forwarded verbatim.
519 *
520 * @param callid Hash of the call to forward.
521 * @param phoneid Phone handle to use for forwarding.
522 * @param imethod New interface and method for the forwarded call.
523 * @param arg1 New value of the first argument for the forwarded call.
524 * @param arg2 New value of the second argument for the forwarded call.
525 * @param mode Flags specifying mode of the forward operation.
526 *
527 * @return Zero on success or an error code.
528 *
529 */
530int ipc_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod,
531 sysarg_t arg1, sysarg_t arg2, unsigned int mode)
532{
533 return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, imethod, arg1,
534 arg2, mode);
535}
536
537int ipc_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod,
538 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
539 unsigned int mode)
540{
541 ipc_call_t data;
542
543 IPC_SET_IMETHOD(data, imethod);
544 IPC_SET_ARG1(data, arg1);
545 IPC_SET_ARG2(data, arg2);
546 IPC_SET_ARG3(data, arg3);
547 IPC_SET_ARG4(data, arg4);
548 IPC_SET_ARG5(data, arg5);
549
550 return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data,
551 mode);
552}
553
554/** Connect to a task specified by id.
555 *
556 */
557int ipc_connect_kbox(task_id_t id)
558{
559#ifdef __32_BITS__
560 sysarg64_t arg = (sysarg64_t) id;
561 return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg);
562#endif
563
564#ifdef __64_BITS__
565 return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) id);
566#endif
567}
568
569/** @}
570 */
Note: See TracBrowser for help on using the repository browser.