Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/ipc.c

    rcde999a r706b4de  
    11/*
    22 * Copyright (c) 2006 Ondrej Palkovsky
    3  * Copyright (c) 2017 Jakub Jermar
    43 * All rights reserved.
    54 *
     
    4342#include <ipc/ipc.h>
    4443#include <libc.h>
    45 #include <stdlib.h>
     44#include <malloc.h>
    4645#include <errno.h>
    4746#include <adt/list.h>
     
    5150
    5251/**
    53  * Structures of this type are used for keeping track of sent asynchronous calls.
    54  */
    55 typedef struct async_call {
     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       
    5658        ipc_async_callback_t callback;
    5759        void *private;
    5860       
    59         struct {
    60                 ipc_call_t data;
    61         } msg;
     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;
    6271} 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}
    6397
    6498/** Prologue for ipc_call_async_*() functions.
     
    90124/** Epilogue for ipc_call_async_*() functions.
    91125 *
    92  * @param rc       Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
    93  * @param call     Structure returned by ipc_prepare_async().
    94  */
    95 static inline void ipc_finish_async(int rc, async_call_t *call)
     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)
    96132{
    97133        if (!call) {
    98134                /* Nothing to do regardless if failed or not */
     135                futex_unlock(&ipc_futex);
    99136                return;
    100137        }
    101138       
    102         if (rc != EOK) {
     139        if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
     140                futex_unlock(&ipc_futex);
     141               
    103142                /* Call asynchronous handler with error code */
    104143                if (call->callback)
     
    108147                return;
    109148        }
     149       
     150        call->u.callid = callid;
     151       
     152        /* Add call to the list of dispatched calls */
     153        list_append(&call->list, &dispatched_calls);
     154        futex_unlock(&ipc_futex);
    110155}
    111156
    112157/** Fast asynchronous call.
    113158 *
    114  * This function can only handle three arguments of payload. It is, however,
     159 * This function can only handle four arguments of payload. It is, however,
    115160 * faster than the more generic ipc_call_async_slow().
    116161 *
     
    121166 * error code. If the call cannot be temporarily made, it is queued.
    122167 *
    123  * @param phandle   Phone handle for the call.
    124  * @param imethod   Requested interface and method.
    125  * @param arg1      Service-defined payload argument.
    126  * @param arg2      Service-defined payload argument.
    127  * @param arg3      Service-defined payload argument.
    128  * @param private   Argument to be passed to the answer/error callback.
    129  * @param callback  Answer or error callback.
    130  */
    131 void ipc_call_async_fast(cap_handle_t phandle, sysarg_t imethod, sysarg_t arg1,
    132     sysarg_t arg2, sysarg_t arg3, void *private, ipc_async_callback_t callback)
     168 * @param phoneid     Phone handle for the call.
     169 * @param imethod     Requested interface and method.
     170 * @param arg1        Service-defined payload argument.
     171 * @param arg2        Service-defined payload argument.
     172 * @param arg3        Service-defined payload argument.
     173 * @param arg4        Service-defined payload argument.
     174 * @param private     Argument to be passed to the answer/error callback.
     175 * @param callback    Answer or error callback.
     176 */
     177void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
     178    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private,
     179    ipc_async_callback_t callback)
     180{
     181        async_call_t *call = NULL;
     182       
     183        if (callback) {
     184                call = ipc_prepare_async(private, callback);
     185                if (!call)
     186                        return;
     187        }
     188       
     189        /*
     190         * We need to make sure that we get callid
     191         * before another thread accesses the queue again.
     192         */
     193       
     194        futex_lock(&ipc_futex);
     195        ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
     196            imethod, arg1, arg2, arg3, arg4);
     197       
     198        ipc_finish_async(callid, phoneid, call);
     199}
     200
     201/** Asynchronous call transmitting the entire payload.
     202 *
     203 * Note that this function is a void function.
     204 *
     205 * During normal operation, answering this call will trigger the callback.
     206 * In case of fatal error, the callback handler is called with the proper
     207 * error code. If the call cannot be temporarily made, it is queued.
     208 *
     209 * @param phoneid     Phone handle for the call.
     210 * @param imethod     Requested interface and method.
     211 * @param arg1        Service-defined payload argument.
     212 * @param arg2        Service-defined payload argument.
     213 * @param arg3        Service-defined payload argument.
     214 * @param arg4        Service-defined payload argument.
     215 * @param arg5        Service-defined payload argument.
     216 * @param private     Argument to be passed to the answer/error callback.
     217 * @param callback    Answer or error callback.
     218 */
     219void ipc_call_async_slow(int phoneid, sysarg_t imethod, sysarg_t arg1,
     220    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private,
     221    ipc_async_callback_t callback)
    133222{
    134223        async_call_t *call = ipc_prepare_async(private, callback);
     
    136225                return;
    137226       
    138         int rc = (int) __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phandle, imethod, arg1,
    139             arg2, arg3, (sysarg_t) call);
    140        
    141         ipc_finish_async(rc, call);
    142 }
    143 
    144 /** Asynchronous call transmitting the entire payload.
    145  *
    146  * Note that this function is a void function.
    147  *
    148  * During normal operation, answering this call will trigger the callback.
    149  * In case of fatal error, the callback handler is called with the proper
    150  * error code. If the call cannot be temporarily made, it is queued.
    151  *
    152  * @param phandle   Phone handle for the call.
    153  * @param imethod   Requested interface and method.
    154  * @param arg1      Service-defined payload argument.
    155  * @param arg2      Service-defined payload argument.
    156  * @param arg3      Service-defined payload argument.
    157  * @param arg4      Service-defined payload argument.
    158  * @param arg5      Service-defined payload argument.
    159  * @param private   Argument to be passed to the answer/error callback.
    160  * @param callback  Answer or error callback.
    161  */
    162 void ipc_call_async_slow(int phandle, sysarg_t imethod, sysarg_t arg1,
    163     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private,
    164     ipc_async_callback_t callback)
    165 {
    166         async_call_t *call = ipc_prepare_async(private, callback);
    167         if (!call)
    168                 return;
    169        
    170         IPC_SET_IMETHOD(call->msg.data, imethod);
    171         IPC_SET_ARG1(call->msg.data, arg1);
    172         IPC_SET_ARG2(call->msg.data, arg2);
    173         IPC_SET_ARG3(call->msg.data, arg3);
    174         IPC_SET_ARG4(call->msg.data, arg4);
    175         IPC_SET_ARG5(call->msg.data, arg5);
    176        
    177         int rc = (int) __SYSCALL3(SYS_IPC_CALL_ASYNC_SLOW, phandle,
    178             (sysarg_t) &call->msg.data, (sysarg_t) call);
    179        
    180         ipc_finish_async(rc, call);
     227        IPC_SET_IMETHOD(call->u.msg.data, imethod);
     228        IPC_SET_ARG1(call->u.msg.data, arg1);
     229        IPC_SET_ARG2(call->u.msg.data, arg2);
     230        IPC_SET_ARG3(call->u.msg.data, arg3);
     231        IPC_SET_ARG4(call->u.msg.data, arg4);
     232        IPC_SET_ARG5(call->u.msg.data, arg5);
     233       
     234        /*
     235         * We need to make sure that we get callid
     236         * before another threadaccesses the queue again.
     237         */
     238       
     239        futex_lock(&ipc_futex);
     240        ipc_callid_t callid =
     241            ipc_call_async_internal(phoneid, &call->u.msg.data);
     242       
     243        ipc_finish_async(callid, phoneid, call);
    181244}
    182245
     
    186249 * registers. If you need to return more, use the ipc_answer_slow() instead.
    187250 *
    188  * @param chandle  Handle of the call being answered.
    189  * @param retval   Return value.
    190  * @param arg1     First return argument.
    191  * @param arg2     Second return argument.
    192  * @param arg3     Third return argument.
    193  * @param arg4     Fourth return argument.
     251 * @param callid Hash of the call being answered.
     252 * @param retval Return value.
     253 * @param arg1   First return argument.
     254 * @param arg2   Second return argument.
     255 * @param arg3   Third return argument.
     256 * @param arg4   Fourth return argument.
    194257 *
    195258 * @return Zero on success.
     
    197260 *
    198261 */
    199 int ipc_answer_fast(cap_handle_t chandle, int retval, sysarg_t arg1,
     262sysarg_t ipc_answer_fast(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
    200263    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
    201264{
    202         return (int) __SYSCALL6(SYS_IPC_ANSWER_FAST, chandle, (sysarg_t) retval, arg1, arg2,
    203             arg3, arg4);
     265        return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3,
     266            arg4);
    204267}
    205268
    206269/** Answer received call (entire payload).
    207270 *
    208  * @param chandle  Handle of the call being answered.
    209  * @param retval   Return value.
    210  * @param arg1     First return argument.
    211  * @param arg2     Second return argument.
    212  * @param arg3     Third return argument.
    213  * @param arg4     Fourth return argument.
    214  * @param arg5     Fifth return argument.
     271 * @param callid Hash of the call being answered.
     272 * @param retval Return value.
     273 * @param arg1   First return argument.
     274 * @param arg2   Second return argument.
     275 * @param arg3   Third return argument.
     276 * @param arg4   Fourth return argument.
     277 * @param arg5   Fifth return argument.
    215278 *
    216279 * @return Zero on success.
     
    218281 *
    219282 */
    220 int ipc_answer_slow(cap_handle_t chandle, int retval, sysarg_t arg1,
     283sysarg_t ipc_answer_slow(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
    221284    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
    222285{
     
    230293        IPC_SET_ARG5(data, arg5);
    231294       
    232         return (int) __SYSCALL2(SYS_IPC_ANSWER_SLOW, chandle, (sysarg_t) &data);
     295        return __SYSCALL2(SYS_IPC_ANSWER_SLOW, callid, (sysarg_t) &data);
     296}
     297
     298/** Try to dispatch queued calls from the async queue.
     299 *
     300 */
     301static void dispatch_queued_calls(void)
     302{
     303        /** @todo
     304         * Integrate intelligently ipc_futex so that it is locked during
     305         * ipc_call_async_*() until it is added to dispatched_calls.
     306         */
     307       
     308        futex_down(&async_futex);
     309       
     310        while (!list_empty(&queued_calls)) {
     311                async_call_t *call =
     312                    list_get_instance(list_first(&queued_calls), async_call_t, list);
     313                ipc_callid_t callid =
     314                    ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data);
     315               
     316                list_remove(&call->list);
     317               
     318                futex_up(&async_futex);
     319               
     320                assert(call->fid);
     321                fibril_add_ready(call->fid);
     322               
     323                if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
     324                        if (call->callback)
     325                                call->callback(call->private, ENOENT, NULL);
     326                       
     327                        free(call);
     328                } else {
     329                        call->u.callid = callid;
     330                       
     331                        futex_lock(&ipc_futex);
     332                        list_append(&call->list, &dispatched_calls);
     333                        futex_unlock(&ipc_futex);
     334                }
     335               
     336                futex_down(&async_futex);
     337        }
     338       
     339        futex_up(&async_futex);
    233340}
    234341
    235342/** Handle received answer.
    236343 *
    237  * @param data  Call data of the answer.
    238  */
    239 static void handle_answer(ipc_call_t *data)
    240 {
    241         async_call_t *call = data->label;
    242 
    243         if (!call)
    244                 return;
    245 
    246         if (call->callback)
    247                 call->callback(call->private, IPC_GET_RETVAL(*data), data);
    248         free(call);
     344 * Find the hash of the answer and call the answer callback.
     345 *
     346 * The answer has the same hash as the request OR'ed with
     347 * the IPC_CALLID_ANSWERED bit.
     348 *
     349 * @todo Use hash table.
     350 *
     351 * @param callid Hash of the received answer.
     352 * @param data   Call data of the answer.
     353 *
     354 */
     355static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
     356{
     357        callid &= ~IPC_CALLID_ANSWERED;
     358       
     359        futex_lock(&ipc_futex);
     360       
     361        link_t *item;
     362        for (item = dispatched_calls.head.next; item != &dispatched_calls.head;
     363            item = item->next) {
     364                async_call_t *call =
     365                    list_get_instance(item, async_call_t, list);
     366               
     367                if (call->u.callid == callid) {
     368                        list_remove(&call->list);
     369                       
     370                        futex_unlock(&ipc_futex);
     371                       
     372                        if (call->callback)
     373                                call->callback(call->private,
     374                                    IPC_GET_RETVAL(*data), data);
     375                       
     376                        free(call);
     377                        return;
     378                }
     379        }
     380       
     381        futex_unlock(&ipc_futex);
    249382}
    250383
    251384/** Wait for first IPC call to come.
    252  *
    253  * @param call   Incoming call storage.
    254  * @param usec   Timeout in microseconds
    255  * @param flags  Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
    256  * @param[out] out_handle  Call handle.
    257  *
    258  * @return  Error code.
    259  */
    260 int ipc_wait_cycle(ipc_call_t *call, sysarg_t usec, unsigned int flags)
    261 {
    262         int rc = (int) __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
    263        
    264         /* Handle received answers */
    265         if ((rc == EOK) && (call->cap_handle == CAP_NIL) &&
    266             (call->flags & IPC_CALL_ANSWERED)) {
    267                 handle_answer(call);
    268         }
    269        
    270         return rc;
    271 }
    272 
    273 /** Interrupt one thread of this task from waiting for IPC.
    274  *
    275  */
    276 void ipc_poke(void)
    277 {
    278         __SYSCALL0(SYS_IPC_POKE);
    279 }
    280 
    281 /** Wait for first IPC call to come.
    282  *
    283  * Only requests are returned, answers are processed internally.
    284385 *
    285386 * @param call  Incoming call storage.
    286387 * @param usec  Timeout in microseconds
    287  *
    288  * @return  Error code.
    289  *
    290  */
    291 int ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
    292 {
    293         int rc;
     388 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
     389 *
     390 * @return Hash of the call. Note that certain bits have special
     391 *         meaning: IPC_CALLID_ANSWERED is set in an answer
     392 *         and IPC_CALLID_NOTIFICATION is used for notifications.
     393 *
     394 */
     395ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
     396    unsigned int flags)
     397{
     398        ipc_callid_t callid =
     399            __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
     400       
     401        /* Handle received answers */
     402        if (callid & IPC_CALLID_ANSWERED) {
     403                handle_answer(callid, call);
     404                dispatch_queued_calls();
     405        }
     406       
     407        return callid;
     408}
     409
     410/** Interrupt one thread of this task from waiting for IPC.
     411 *
     412 */
     413void ipc_poke(void)
     414{
     415        __SYSCALL0(SYS_IPC_POKE);
     416}
     417
     418/** Wait for first IPC call to come.
     419 *
     420 * Only requests are returned, answers are processed internally.
     421 *
     422 * @param call Incoming call storage.
     423 * @param usec Timeout in microseconds
     424 *
     425 * @return Hash of the call.
     426 *
     427 */
     428ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
     429{
     430        ipc_callid_t callid;
    294431       
    295432        do {
    296                 rc = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
    297         } while ((rc == EOK) && (call->cap_handle == CAP_NIL) && (call->flags & IPC_CALL_ANSWERED));
    298        
    299         return rc;
     433                callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
     434        } while (callid & IPC_CALLID_ANSWERED);
     435       
     436        return callid;
    300437}
    301438
     
    304441 * Only requests are returned, answers are processed internally.
    305442 *
    306  * @param call  Incoming call storage.
    307  *
    308  * @return  Error code.
    309  *
    310  */
    311 int ipc_trywait_for_call(ipc_call_t *call)
    312 {
    313         int rc;
     443 * @param call Incoming call storage.
     444 *
     445 * @return Hash of the call.
     446 *
     447 */
     448ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
     449{
     450        ipc_callid_t callid;
    314451       
    315452        do {
    316                 rc = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
     453                callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
    317454                    SYNCH_FLAGS_NON_BLOCKING);
    318         } while ((rc == EOK) && (call->cap_handle == CAP_NIL) && (call->flags & IPC_CALL_ANSWERED));
    319        
    320         return rc;
     455        } while (callid & IPC_CALLID_ANSWERED);
     456       
     457        return callid;
    321458}
    322459
    323460/** Hang up a phone.
    324461 *
    325  * @param phandle Handle of the phone to be hung up.
    326  *
    327  * @return  Zero on success or an error code.
    328  *
    329  */
    330 int ipc_hangup(cap_handle_t phandle)
    331 {
    332         return (int) __SYSCALL1(SYS_IPC_HANGUP, phandle);
     462 * @param phoneid Handle of the phone to be hung up.
     463 *
     464 * @return Zero on success or a negative error code.
     465 *
     466 */
     467int ipc_hangup(int phoneid)
     468{
     469        return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
    333470}
    334471
    335472/** Forward a received call to another destination.
    336473 *
    337  * For non-system methods, the old method, arg1 and arg2 are rewritten by the
    338  * new values. For system methods, the new method, arg1 and arg2 are written to
    339  * the old arg1, arg2 and arg3, respectivelly. Calls with immutable methods are
    340  * forwarded verbatim.
    341  *
    342  * @param chandle  Handle of the call to forward.
    343  * @param phandle Phone handle to use for forwarding.
    344  * @param imethod  New interface and method for the forwarded call.
    345  * @param arg1     New value of the first argument for the forwarded call.
    346  * @param arg2     New value of the second argument for the forwarded call.
    347  * @param mode     Flags specifying mode of the forward operation.
    348  *
    349  * @return  Zero on success or an error code.
    350  *
    351  */
    352 int ipc_forward_fast(cap_handle_t chandle, cap_handle_t phandle,
    353     sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
    354 {
    355         return (int) __SYSCALL6(SYS_IPC_FORWARD_FAST, chandle, phandle, imethod, arg1,
     474 * For non-system methods, the old method, arg1 and arg2 are rewritten
     475 * by the new values. For system methods, the new method, arg1 and arg2
     476 * are written to the old arg1, arg2 and arg3, respectivelly. Calls with
     477 * immutable methods are forwarded verbatim.
     478 *
     479 * @param callid  Hash of the call to forward.
     480 * @param phoneid Phone handle to use for forwarding.
     481 * @param imethod New interface and method for the forwarded call.
     482 * @param arg1    New value of the first argument for the forwarded call.
     483 * @param arg2    New value of the second argument for the forwarded call.
     484 * @param mode    Flags specifying mode of the forward operation.
     485 *
     486 * @return Zero on success or an error code.
     487 *
     488 */
     489int ipc_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod,
     490    sysarg_t arg1, sysarg_t arg2, unsigned int mode)
     491{
     492        return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, imethod, arg1,
    356493            arg2, mode);
    357494}
    358495
    359 int ipc_forward_slow(cap_handle_t chandle, cap_handle_t phandle,
    360     sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
    361     sysarg_t arg4, sysarg_t arg5, unsigned int mode)
     496int ipc_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod,
     497    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
     498    unsigned int mode)
    362499{
    363500        ipc_call_t data;
     
    370507        IPC_SET_ARG5(data, arg5);
    371508       
    372         return (int) __SYSCALL4(SYS_IPC_FORWARD_SLOW, chandle, phandle,
    373             (sysarg_t) &data, mode);
     509        return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data,
     510            mode);
    374511}
    375512
     
    377514 *
    378515 */
    379 int ipc_connect_kbox(task_id_t id, cap_handle_t *phone)
    380 {
    381         return (int) __SYSCALL2(SYS_IPC_CONNECT_KBOX, (sysarg_t) &id, (sysarg_t) phone);
     516int ipc_connect_kbox(task_id_t id)
     517{
     518#ifdef __32_BITS__
     519        sysarg64_t arg = (sysarg64_t) id;
     520        return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg);
     521#endif
     522       
     523#ifdef __64_BITS__
     524        return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) id);
     525#endif
    382526}
    383527
Note: See TracChangeset for help on using the changeset viewer.