Changeset ba81cab in mainline


Ignore:
Timestamp:
2006-03-18T01:06:13Z (19 years ago)
Author:
Ondrej Palkovsky <ondrap@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
fbcfd458
Parents:
81c4c6da
Message:

Better IPC implementation with regard to locking and final cleanup.

Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • generic/include/ipc/ipc.h

    r81c4c6da rba81cab  
    172172extern call_t * ipc_call_alloc(void);
    173173extern void ipc_answerbox_init(answerbox_t *box);
    174 extern void ipc_call_init(call_t *call);
    175 extern void ipc_forward(call_t *call, answerbox_t *newbox,answerbox_t *oldbox);
     174extern void ipc_call_static_init(call_t *call);
    176175extern void task_print_list(void);
     176extern void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox);
    177177
    178178extern answerbox_t *ipc_phone_0;
  • generic/include/ipc/ipcrsc.h

    r81c4c6da rba81cab  
    3131
    3232call_t * get_call(__native callid);
    33 phone_t * get_phone_and_lock(__native phoneid);
    3433int phone_alloc(void);
    3534void phone_dealloc(int phoneid);
  • generic/src/ipc/ipc.c

    r81c4c6da rba81cab  
    5050static slab_cache_t *ipc_call_slab;
    5151
     52/* Initialize new call */
     53static void _ipc_call_init(call_t *call)
     54{
     55        memsetb((__address)call, sizeof(*call), 0);
     56        call->callerbox = &TASK->answerbox;
     57        call->sender = TASK;
     58}
     59
    5260/** Allocate & initialize call structure
    5361 *
     
    6068
    6169        call = slab_alloc(ipc_call_slab, 0);
    62         memsetb((__address)call, sizeof(*call), 0);
    63         call->callerbox = &TASK->answerbox;
    64         call->sender = TASK;
     70        _ipc_call_init(call);
    6571
    6672        return call;
     
    6874
    6975/** Initialize allocated call */
    70 void ipc_call_init(call_t *call)
    71 {
    72         call->callerbox = &TASK->answerbox;
    73         call->flags = IPC_CALL_STATIC_ALLOC;
    74         call->sender = TASK;
     76void ipc_call_static_init(call_t *call)
     77{
     78        _ipc_call_init(call);
     79        call->flags |= IPC_CALL_STATIC_ALLOC;
    7580}
    7681
     
    97102void ipc_phone_connect(phone_t *phone, answerbox_t *box)
    98103{
     104        spinlock_lock(&phone->lock);
     105
    99106        ASSERT(!phone->callee);
    100107        phone->busy = 1;
     
    104111        list_append(&phone->list, &box->connected_phones);
    105112        spinlock_unlock(&box->lock);
     113
     114        spinlock_unlock(&phone->lock);
    106115}
    107116
     
    115124}
    116125
    117 /** Disconnect phone from answerbox */
     126/** Disconnect phone from answerbox
     127 *
     128 * It is allowed to call disconnect on already disconnected phone\
     129 */
    118130void ipc_phone_destroy(phone_t *phone)
    119131{
     
    122134        ASSERT(box);
    123135
    124         spinlock_lock(&box->lock);
    125         list_remove(&phone->list);
    126         spinlock_unlock(&box->lock);
     136        spinlock_lock(&phone->lock);
     137        spinlock_lock(&box->lock);
     138
     139        if (phone->callee) {
     140                list_remove(&phone->list);
     141                phone->callee = NULL;
     142        }
     143
     144        spinlock_unlock(&box->lock);
     145        spinlock_unlock(&phone->lock);
    127146}
    128147
     
    141160}
    142161
     162/** Answer message that was not dispatched and is not entered in
     163 * any queue
     164 */
     165static void _ipc_answer_free_call(call_t *call)
     166{
     167        answerbox_t *callerbox = call->callerbox;
     168
     169        call->flags &= ~IPC_CALL_DISPATCHED;
     170        call->flags |= IPC_CALL_ANSWERED;
     171
     172        spinlock_lock(&callerbox->lock);
     173        list_append(&call->list, &callerbox->answers);
     174        spinlock_unlock(&callerbox->lock);
     175        waitq_wakeup(&callerbox->wq, 0);
     176}
     177
     178/** Answer message, that is in callee queue
     179 *
     180 * @param box Answerbox that is answering the message
     181 * @param call Modified request that is being sent back
     182 */
     183void ipc_answer(answerbox_t *box, call_t *call)
     184{
     185        /* Remove from active box */
     186        spinlock_lock(&box->lock);
     187        list_remove(&call->list);
     188        spinlock_unlock(&box->lock);
     189        /* Send back answer */
     190        _ipc_answer_free_call(call);
     191}
     192
    143193/** Send a asynchronous request using phone to answerbox
    144194 *
     
    148198void ipc_call(phone_t *phone, call_t *call)
    149199{
    150         answerbox_t *box = phone->callee;
    151 
    152         ASSERT(box);
     200        answerbox_t *box;
     201
     202        spinlock_lock(&phone->lock);
     203        box = phone->callee;
     204        if (!box) {
     205                /* Trying to send over disconnected phone */
     206                IPC_SET_RETVAL(call->data, ENOENT);
     207                _ipc_answer_free_call(call);
     208                return;
     209        }
    153210
    154211        spinlock_lock(&box->lock);
     
    156213        spinlock_unlock(&box->lock);
    157214        waitq_wakeup(&box->wq, 0);
     215       
     216        spinlock_unlock(&phone->lock);
    158217}
    159218
     
    164223 * @param oldbox Old answerbox
    165224 */
    166 void ipc_forward(call_t *call, answerbox_t *newbox, answerbox_t *oldbox)
     225void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox)
    167226{
    168227        spinlock_lock(&oldbox->lock);
     
    170229        spinlock_unlock(&oldbox->lock);
    171230
    172         spinlock_lock(&newbox->lock);
    173         list_append(&call->list, &newbox->calls);
    174         spinlock_lock(&newbox->lock);
    175         waitq_wakeup(&newbox->wq, 0);
    176 }
    177 
    178 /** Answer message back to phone
    179  *
    180  * @param box Answerbox that is answering the message
    181  * @param request Modified request that is being sent back
    182  */
    183 void ipc_answer(answerbox_t *box, call_t *request)
    184 {
    185         answerbox_t *callerbox = request->callerbox;
    186 
    187         request->flags &= ~IPC_CALL_DISPATCHED;
    188         request->flags |= IPC_CALL_ANSWERED;
    189 
    190         spinlock_lock(&box->lock);
    191         list_remove(&request->list);
    192         spinlock_unlock(&box->lock);
    193 
    194         spinlock_lock(&callerbox->lock);
    195         list_append(&request->list, &callerbox->answers);
    196         spinlock_unlock(&callerbox->lock);
    197         waitq_wakeup(&callerbox->wq, 0);
    198 }
     231        ipc_call(newphone, call);
     232}
     233
    199234
    200235/** Wait for phone call
  • generic/src/ipc/ipcrsc.c

    r81c4c6da rba81cab  
    3838 * - answer message to phone
    3939 *
     40 * Locking strategy
     41 *
     42 * - To use a phone, disconnect a phone etc., the phone must be
     43 *   first locked and then checked that it is connected
     44 * - To connect an allocated phone it need not be locked (assigning
     45 *   pointer is atomic on all platforms)
     46 *
     47 * - To find an empty phone slot, the TASK must be locked
     48 * - To answer a message, the answerbox must be locked
     49 * - The locking of phone and answerbox is done at the ipc_ level.
     50 *   It is perfectly correct to pass unconnected phone to these functions
     51 *   and proper reply will be generated.
     52 *
     53 * - There may be objection that a race may occur when the syscall finds
     54 *   an appropriate call and before executing ipc_send, the phone call might
     55 *   be disconnected and connected elsewhere. As there is no easy solution,
     56 *   the application will be notified by an  'PHONE_DISCONNECTED' message
     57 *   and the phone will not be allocated before the application notifies
     58 *   the kernel subsystem that it does not have any pending calls regarding
     59 *   this phone call.
     60 *
     61 * Locking order
     62 *
     63 * There are 2 possibilities
     64 * - first phone, then answerbox
     65 *   + Easy locking on calls
     66 *   - Very hard traversing list of phones when disconnecting because
     67 *     the phones may disconnect during traversal of list of connected phones.
     68 *     The only possibility is try_lock with restart of list traversal.
     69 *
     70 * - first answerbox, then phone(s)
     71 *   + Easy phone disconnect
     72 *   - Multiple checks needed when sending message
     73 *
     74 * Because the answerbox destroyal is much less frequent operation,
     75 * the first method is chosen.
     76 *
     77 * Cleanup strategy
     78 *
     79 * 1) Disconnect all phones.
     80 *    * Send message 'PHONE_DISCONNECTED' to the target application
     81 * - Once all phones are disconnected, no further calls can arrive
     82 *
     83 * 2) Answer all messages in 'calls' and 'dispatched_calls' queues with
     84 *    appropriate error code.
     85 *
     86 * 3) Wait for all async answers to arrive
     87 * Alternatively - we might try to invalidate all messages by setting some
     88 * flag, that would dispose of the message once it is answered. This
     89 * would need to link all calls in one big list, which we don't currently
     90 * do.
     91 *
    4092 *
    4193 */
     
    57109        /* TODO: locking of call, ripping it from dispatched calls etc.  */
    58110        return (call_t *) callid;
    59 }
    60 
    61 /** Return pointer to phone identified by phoneid or NULL if non-existent */
    62 phone_t * get_phone_and_lock(__native phoneid)
    63 {
    64         phone_t *phone;
    65 
    66         if (phoneid >= IPC_MAX_PHONES)
    67                 return NULL;
    68 
    69         phone = &TASK->phones[phoneid];
    70         spinlock_lock(&phone->lock);
    71         if (!phone->callee) {
    72                 spinlock_unlock(&phone->lock);
    73                 return NULL;
    74         }
    75         /* TODO... */
    76         spinlock_unlock(&phone->lock);
    77         return phone;
    78111}
    79112
     
    112145}
    113146
     147/** Connect phone to a given answerbox
     148 *
     149 * @param phoneid The slot that will be connected
     150 *
     151 * The procedure _enforces_ that the user first marks the phone
     152 * busy (e.g. via phone_alloc) and then connects the phone, otherwise
     153 * race condition may appear.
     154 */
    114155void phone_connect(int phoneid, answerbox_t *box)
    115156{
    116157        phone_t *phone = &TASK->phones[phoneid];
    117158       
     159        ASSERT(phone->busy);
    118160        ipc_phone_connect(phone, box);
    119161}
  • generic/src/ipc/sysipc.c

    r81c4c6da rba81cab  
    4747 *
    4848 */
     49
     50#define GET_CHECK_PHONE(phone,phoneid,err) { \
     51      if (phoneid > IPC_MAX_PHONES) { err; } \
     52      phone = &TASK->phones[phoneid]; \
     53}
     54
    4955
    5056/** Return true if the method is a system method */
     
    151157                return EPERM;
    152158
    153         phone = get_phone_and_lock(phoneid);
    154         if (!phone)
    155                 return ENOENT;
    156 
    157         ipc_call_init(&call);
     159        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
     160
     161        ipc_call_static_init(&call);
    158162        IPC_SET_METHOD(call.data, method);
    159163        IPC_SET_ARG1(call.data, arg1);
     
    173177        phone_t *phone;
    174178
    175         ipc_call_init(&call);
     179        ipc_call_static_init(&call);
    176180        copy_from_uspace(&call.data, question, sizeof(call.data));
    177181
     
    179183                return EPERM;
    180184       
    181         phone = get_phone_and_lock(phoneid);
    182         if (!phone)
    183                 return ENOENT;
     185        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
    184186
    185187        ipc_call_sync(phone, &call);
     
    220222                return IPC_CALLRET_TEMPORARY;
    221223
    222         phone = get_phone_and_lock(phoneid);
    223         if (!phone)
    224                 return IPC_CALLRET_FATAL;
     224        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
    225225
    226226        call = ipc_call_alloc();
     
    246246                return IPC_CALLRET_TEMPORARY;
    247247
    248         phone = get_phone_and_lock(phoneid);
    249         if (!phone)
    250                 return IPC_CALLRET_FATAL;
     248        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
    251249
    252250        call = ipc_call_alloc();
     
    280278                return ENOENT;
    281279
    282         phone = get_phone_and_lock(phoneid);
    283         if (!phone) {
     280        GET_CHECK_PHONE(phone, phoneid, {
    284281                IPC_SET_RETVAL(call->data, EFORWARD);
    285282                ipc_answer(&TASK->answerbox, call);
    286283                return ENOENT;
    287         }
     284        });             
    288285
    289286        if (!is_forwardable(IPC_GET_METHOD(call->data))) {
     
    304301        }
    305302
    306         ipc_forward(call, phone->callee, &TASK->answerbox);
     303        ipc_forward(call, phone, &TASK->answerbox);
    307304
    308305        return 0;
     
    372369        phone_t *phone;
    373370
    374         ipc_call_init(&call);
     371        ipc_call_static_init(&call);
    375372        IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
    376373        IPC_SET_ARG1(call.data, arg1);
    377374        IPC_SET_ARG2(call.data, arg2);
    378375       
    379         phone = get_phone_and_lock(phoneid);
    380         if (!phone)
    381                 return ENOENT;
     376        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
    382377
    383378        ipc_call_sync(phone, &call);
     
    402397        int newphid;
    403398
    404         phone = get_phone_and_lock(phoneid);
    405         if (!phone)
    406                 return ENOENT;
     399        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
    407400
    408401        newphid = phone_alloc();
     
    410403                return ELIMIT;
    411404
    412         ipc_call_init(&call);
     405        ipc_call_static_init(&call);
    413406        IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
    414407        IPC_SET_ARG1(call.data, arg1);
Note: See TracChangeset for help on using the changeset viewer.