Ignore:
File:
1 edited

Legend:

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

    r706b4de rcde999a  
    11/*
    22 * Copyright (c) 2006 Ondrej Palkovsky
     3 * Copyright (c) 2017 Jakub Jermar
    34 * All rights reserved.
    45 *
     
    4243#include <ipc/ipc.h>
    4344#include <libc.h>
    44 #include <malloc.h>
     45#include <stdlib.h>
    4546#include <errno.h>
    4647#include <adt/list.h>
     
    5051
    5152/**
    52  * Structures of this type are used for keeping track
    53  * of sent asynchronous calls and queing unsent calls.
    54  */
    55 typedef struct {
    56         link_t list;
    57        
     53 * Structures of this type are used for keeping track of sent asynchronous calls.
     54 */
     55typedef struct async_call {
    5856        ipc_async_callback_t callback;
    5957        void *private;
    6058       
    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;
     59        struct {
     60                ipc_call_t data;
     61        } msg;
    7162} async_call_t;
    72 
    73 LIST_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  */
    81 LIST_INITIALIZE(queued_calls);
    82 
    83 static 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  */
    93 static 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 }
    9763
    9864/** Prologue for ipc_call_async_*() functions.
     
    12490/** Epilogue for ipc_call_async_*() functions.
    12591 *
    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  */
    130 static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
    131     async_call_t *call)
     92 * @param rc       Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
     93 * @param call     Structure returned by ipc_prepare_async().
     94 */
     95static inline void ipc_finish_async(int rc, async_call_t *call)
    13296{
    13397        if (!call) {
    13498                /* Nothing to do regardless if failed or not */
    135                 futex_unlock(&ipc_futex);
    13699                return;
    137100        }
    138101       
    139         if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
    140                 futex_unlock(&ipc_futex);
    141                
     102        if (rc != EOK) {
    142103                /* Call asynchronous handler with error code */
    143104                if (call->callback)
     
    147108                return;
    148109        }
    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);
    155110}
    156111
    157112/** Fast asynchronous call.
    158113 *
    159  * This function can only handle four arguments of payload. It is, however,
     114 * This function can only handle three arguments of payload. It is, however,
    160115 * faster than the more generic ipc_call_async_slow().
    161116 *
     
    166121 * error code. If the call cannot be temporarily made, it is queued.
    167122 *
    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  */
    177 void 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);
     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 */
     131void 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)
     133{
     134        async_call_t *call = ipc_prepare_async(private, callback);
     135        if (!call)
     136                return;
     137       
     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);
    199142}
    200143
     
    207150 * error code. If the call cannot be temporarily made, it is queued.
    208151 *
    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  */
    219 void ipc_call_async_slow(int phoneid, sysarg_t imethod, sysarg_t arg1,
     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 */
     162void ipc_call_async_slow(int phandle, sysarg_t imethod, sysarg_t arg1,
    220163    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, void *private,
    221164    ipc_async_callback_t callback)
     
    225168                return;
    226169       
    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);
     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);
    244181}
    245182
     
    249186 * registers. If you need to return more, use the ipc_answer_slow() instead.
    250187 *
    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.
     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.
    257194 *
    258195 * @return Zero on success.
     
    260197 *
    261198 */
    262 sysarg_t ipc_answer_fast(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
     199int ipc_answer_fast(cap_handle_t chandle, int retval, sysarg_t arg1,
    263200    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
    264201{
    265         return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3,
    266             arg4);
     202        return (int) __SYSCALL6(SYS_IPC_ANSWER_FAST, chandle, (sysarg_t) retval, arg1, arg2,
     203            arg3, arg4);
    267204}
    268205
    269206/** Answer received call (entire payload).
    270207 *
    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.
     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.
    278215 *
    279216 * @return Zero on success.
     
    281218 *
    282219 */
    283 sysarg_t ipc_answer_slow(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
     220int ipc_answer_slow(cap_handle_t chandle, int retval, sysarg_t arg1,
    284221    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
    285222{
     
    293230        IPC_SET_ARG5(data, arg5);
    294231       
    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  */
    301 static 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);
     232        return (int) __SYSCALL2(SYS_IPC_ANSWER_SLOW, chandle, (sysarg_t) &data);
     233}
     234
     235/** Handle received answer.
     236 *
     237 * @param data  Call data of the answer.
     238 */
     239static 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);
     249}
     250
     251/** 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 */
     260int 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);
    337268        }
    338269       
    339         futex_up(&async_futex);
    340 }
    341 
    342 /** Handle received answer.
    343  *
    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  */
    355 static 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);
     270        return rc;
     271}
     272
     273/** Interrupt one thread of this task from waiting for IPC.
     274 *
     275 */
     276void ipc_poke(void)
     277{
     278        __SYSCALL0(SYS_IPC_POKE);
    382279}
    383280
    384281/** Wait for first IPC call to come.
     282 *
     283 * Only requests are returned, answers are processed internally.
    385284 *
    386285 * @param call  Incoming call storage.
    387286 * @param usec  Timeout in microseconds
    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  */
    395 ipc_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  */
    413 void ipc_poke(void)
    414 {
    415         __SYSCALL0(SYS_IPC_POKE);
    416 }
    417 
    418 /** Wait for first IPC call to come.
     287 *
     288 * @return  Error code.
     289 *
     290 */
     291int ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
     292{
     293        int rc;
     294       
     295        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;
     300}
     301
     302/** Check if there is an IPC call waiting to be picked up.
    419303 *
    420304 * Only requests are returned, answers are processed internally.
    421305 *
    422  * @param call Incoming call storage.
    423  * @param usec Timeout in microseconds
    424  *
    425  * @return Hash of the call.
    426  *
    427  */
    428 ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, sysarg_t usec)
    429 {
    430         ipc_callid_t callid;
     306 * @param call  Incoming call storage.
     307 *
     308 * @return  Error code.
     309 *
     310 */
     311int ipc_trywait_for_call(ipc_call_t *call)
     312{
     313        int rc;
    431314       
    432315        do {
    433                 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
    434         } while (callid & IPC_CALLID_ANSWERED);
    435        
    436         return callid;
    437 }
    438 
    439 /** Check if there is an IPC call waiting to be picked up.
    440  *
    441  * Only requests are returned, answers are processed internally.
    442  *
    443  * @param call Incoming call storage.
    444  *
    445  * @return Hash of the call.
    446  *
    447  */
    448 ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
    449 {
    450         ipc_callid_t callid;
    451        
    452         do {
    453                 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
     316                rc = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
    454317                    SYNCH_FLAGS_NON_BLOCKING);
    455         } while (callid & IPC_CALLID_ANSWERED);
    456        
    457         return callid;
     318        } while ((rc == EOK) && (call->cap_handle == CAP_NIL) && (call->flags & IPC_CALL_ANSWERED));
     319       
     320        return rc;
    458321}
    459322
    460323/** Hang up a phone.
    461324 *
    462  * @param phoneid Handle of the phone to be hung up.
    463  *
    464  * @return Zero on success or a negative error code.
    465  *
    466  */
    467 int ipc_hangup(int phoneid)
    468 {
    469         return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
     325 * @param phandle Handle of the phone to be hung up.
     326 *
     327 * @return  Zero on success or an error code.
     328 *
     329 */
     330int ipc_hangup(cap_handle_t phandle)
     331{
     332        return (int) __SYSCALL1(SYS_IPC_HANGUP, phandle);
    470333}
    471334
    472335/** Forward a received call to another destination.
    473336 *
    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  */
    489 int 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,
     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 */
     352int 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,
    493356            arg2, mode);
    494357}
    495358
    496 int 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)
     359int 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)
    499362{
    500363        ipc_call_t data;
     
    507370        IPC_SET_ARG5(data, arg5);
    508371       
    509         return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data,
    510             mode);
     372        return (int) __SYSCALL4(SYS_IPC_FORWARD_SLOW, chandle, phandle,
     373            (sysarg_t) &data, mode);
    511374}
    512375
     
    514377 *
    515378 */
    516 int 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
     379int 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);
    526382}
    527383
Note: See TracChangeset for help on using the changeset viewer.