Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 2405bb5 in mainline


Ignore:
Timestamp:
2012-08-15T19:28:43Z (9 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master
Children:
7975433
Parents:
691d8d8
Message:

Forget active calls when a task exits.

  • Synchronization between answer and forget.
  • Forgotten calls are not yet properly cleaned-up.
  • Hold the sender task when processing its active calls.
Location:
kernel/generic
Files:
3 edited

Legend:

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

    r691d8d8 r2405bb5  
    106106
    107107typedef struct {
    108         /** Task link. */
     108        /**
     109         * Task link.
     110         * Valid only when the call is not forgotten.
     111         * Protected by the task's active_calls_lock.
     112         */
    109113        link_t ta_link;
    110114
     
    113117       
    114118        unsigned int flags;
     119
     120        /** Protects the forget member. */
     121        SPINLOCK_DECLARE(forget_lock);
     122
     123        /**
     124         * True if the caller 'forgot' this call and donated it to the callee.
     125         * Forgotten calls are discarded upon answering (the answer is not
     126         * delivered) and answered calls cannot be forgotten. Forgotten calls
     127         * also do not figure on the task's active call list.
     128         *
     129         * We keep this separate from the flags so that it is not necessary
     130         * to take a lock when accessing them.
     131         */
     132        bool forget;
    115133       
    116         /** Identification of the caller. */
     134        /**
     135         * Identification of the caller.
     136         * Valid only when the call is not forgotten.
     137         */
    117138        struct task *sender;
    118139       
  • kernel/generic/src/ipc/ipc.c

    r691d8d8 r2405bb5  
    7171{
    7272        memsetb(call, sizeof(*call), 0);
     73        spinlock_initialize(&call->forget_lock, "forget_lock");
     74        call->forget = false;
    7375        call->sender = TASK;
    7476        call->buffer = NULL;
     
    161163}
    162164
     165/** Demasquerade the caller phone. */
     166static void ipc_caller_phone_demasquerade(call_t *call)
     167{
     168        if (call->flags & IPC_CALL_FORWARDED) {
     169                if (call->caller_phone) {
     170                        call->data.phone = call->caller_phone;
     171                }
     172        }
     173}
     174
    163175/** Answer a message which was not dispatched and is not listed in any queue.
    164176 *
     
    169181static void _ipc_answer_free_call(call_t *call, bool selflocked)
    170182{
    171         answerbox_t *callerbox = &call->sender->answerbox;
    172         bool do_lock = ((!selflocked) || callerbox != (&TASK->answerbox));
    173        
    174183        /* Count sent answer */
    175184        irq_spinlock_lock(&TASK->lock, true);
    176185        TASK->ipc_info.answer_sent++;
    177186        irq_spinlock_unlock(&TASK->lock, true);
     187
     188        spinlock_lock(&call->forget_lock);
     189        if (call->forget) {
     190                /* This is a forgotten call and call->sender is not valid. */
     191                spinlock_unlock(&call->forget_lock);
     192                /* TODO: free the call and its resources */
     193                return;
     194        } else {
     195                /*
     196                 * Hold the sender task so that it does not suddenly disappear
     197                 * while we are working with it.
     198                 */
     199                task_hold(call->sender);
     200
     201                /*
     202                 * Remove the call from the sender's active call list.
     203                 * We enforce this locking order so that any potential
     204                 * concurrently executing forget operation is forced to
     205                 * release its active_calls_lock and lose the race to
     206                 * forget this soon to be answered call.
     207                 */
     208                spinlock_lock(&call->sender->active_calls_lock);
     209                list_remove(&call->ta_link);
     210                spinlock_unlock(&call->sender->active_calls_lock);
     211        }
     212        spinlock_unlock(&call->forget_lock);
     213
     214        answerbox_t *callerbox = &call->sender->answerbox;
     215        bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    178216       
    179217        call->flags |= IPC_CALL_ANSWERED;
    180218       
    181         if (call->flags & IPC_CALL_FORWARDED) {
    182                 if (call->caller_phone) {
    183                         /* Demasquerade the caller phone. */
    184                         call->data.phone = call->caller_phone;
    185                 }
    186         }
    187 
    188         /*
    189          * Remove the call from the sender's active call list.
    190          */
    191         spinlock_lock(&call->sender->active_calls_lock);
    192         list_remove(&call->ta_link);
    193         spinlock_unlock(&call->sender->active_calls_lock);
     219        ipc_caller_phone_demasquerade(call);
    194220
    195221        call->data.task_id = TASK->taskid;
     
    204230       
    205231        waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
     232
     233        task_release(call->sender);
    206234}
    207235
     
    547575}
    548576
     577static void ipc_forget_all_active_calls(void)
     578{
     579        call_t *call;
     580
     581restart:
     582        spinlock_lock(&TASK->active_calls_lock);
     583        if (list_empty(&TASK->active_calls)) {
     584                /*
     585                 * We are done, there are no more active calls.
     586                 * Nota bene: there may still be answers waiting for pick up.
     587                 */
     588                spinlock_unlock(&TASK->active_calls_lock);     
     589                return;
     590        }
     591       
     592        call = list_get_instance(list_first(&TASK->active_calls), call_t,
     593            ta_link);
     594
     595        if (!spinlock_trylock(&call->forget_lock)) {
     596                /*
     597                 * Avoid deadlock and let _ipc_answer_free_call() win the race
     598                 * to answer the first call on the list.
     599                 */
     600                spinlock_unlock(&TASK->active_calls_lock);     
     601                goto restart;
     602        }
     603
     604        /*
     605         * Forget the call and donate it to the task which holds up the answer.
     606         */
     607
     608        call->forget = true;
     609        call->sender = NULL;
     610        list_remove(&call->ta_link);
     611
     612        ipc_caller_phone_demasquerade(call);
     613        atomic_dec(&call->data.phone->active_calls);
     614
     615        spinlock_unlock(&call->forget_lock);
     616        spinlock_unlock(&TASK->active_calls_lock);
     617        goto restart;
     618}
     619
     620/** Wait for all answers to asynchronous calls to arrive. */
     621static void ipc_wait_for_all_answered_calls(void)
     622{
     623        call_t *call;
     624        size_t i;
     625
     626restart:
     627        /*
     628         * Go through all phones, until they are all FREE. Locking is not
     629         * needed, no one else should modify it when we are in cleanup
     630         */
     631        for (i = 0; i < IPC_MAX_PHONES; i++) {
     632                if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
     633                    atomic_get(&TASK->phones[i].active_calls) == 0) {
     634                        TASK->phones[i].state = IPC_PHONE_FREE;
     635                        TASK->phones[i].callee = NULL;
     636                }
     637
     638                /*
     639                 * Just for sure, we might have had some IPC_PHONE_CONNECTING
     640                 * phones
     641                 */
     642                if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
     643                        ipc_phone_hangup(&TASK->phones[i]);
     644
     645                /*
     646                 * If the hangup succeeded, it has sent a HANGUP message, the
     647                 * IPC is now in HUNGUP state, we wait for the reply to come
     648                 */
     649                if (TASK->phones[i].state != IPC_PHONE_FREE)
     650                        break;
     651        }
     652               
     653        /* Got into cleanup */
     654        if (i == IPC_MAX_PHONES)
     655                return;
     656               
     657        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
     658            SYNCH_FLAGS_NONE);
     659        ASSERT((call->flags & IPC_CALL_ANSWERED) ||
     660            (call->flags & IPC_CALL_NOTIF));
     661               
     662        ipc_call_free(call);
     663        goto restart;
     664}
     665
    549666/** Clean up all IPC communication of the current task.
    550667 *
     
    556673{
    557674        /* Disconnect all our phones ('ipc_phone_hangup') */
    558         size_t i;
    559         for (i = 0; i < IPC_MAX_PHONES; i++)
     675        for (size_t i = 0; i < IPC_MAX_PHONES; i++)
    560676                ipc_phone_hangup(&TASK->phones[i]);
    561677       
     
    579695        ipc_cleanup_call_list(&TASK->answerbox.calls);
    580696        irq_spinlock_unlock(&TASK->answerbox.lock, true);
    581        
    582         /* Wait for all answers to asynchronous calls to arrive */
    583         while (true) {
    584                 /*
    585                  * Go through all phones, until they are all FREE
    586                  * Locking is not needed, no one else should modify
    587                  * it when we are in cleanup
    588                  */
    589                 for (i = 0; i < IPC_MAX_PHONES; i++) {
    590                         if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
    591                             atomic_get(&TASK->phones[i].active_calls) == 0) {
    592                                 TASK->phones[i].state = IPC_PHONE_FREE;
    593                                 TASK->phones[i].callee = NULL;
    594                         }
    595                        
    596                         /*
    597                          * Just for sure, we might have had some
    598                          * IPC_PHONE_CONNECTING phones
    599                          */
    600                         if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
    601                                 ipc_phone_hangup(&TASK->phones[i]);
    602                        
    603                         /*
    604                          * If the hangup succeeded, it has sent a HANGUP
    605                          * message, the IPC is now in HUNGUP state, we
    606                          * wait for the reply to come
    607                          */
    608                        
    609                         if (TASK->phones[i].state != IPC_PHONE_FREE)
    610                                 break;
    611                 }
    612                
    613                 /* Got into cleanup */
    614                 if (i == IPC_MAX_PHONES)
    615                         break;
    616                
    617                 call_t *call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
    618                     SYNCH_FLAGS_NONE);
    619                 ASSERT((call->flags & IPC_CALL_ANSWERED) ||
    620                     (call->flags & IPC_CALL_NOTIF));
    621                
    622                 ipc_call_free(call);
    623         }
     697
     698        ipc_forget_all_active_calls();
     699        ipc_wait_for_all_answered_calls();
    624700}
    625701
  • kernel/generic/src/ipc/sysipc.c

    r691d8d8 r2405bb5  
    420420        int rc = EOK;
    421421
     422        spinlock_lock(&answer->forget_lock);
     423        if (answer->forget) {
     424                /*
     425                 * This is a forgotten call and answer->sender is not valid.
     426                 */
     427                spinlock_unlock(&answer->forget_lock);
     428                /* TODO: free the call and its resources */
     429                return rc;
     430        } else {
     431                /*
     432                 * Hold the sender task so that it cannot suddenly disappear
     433                 * while we are working with it.
     434                 */
     435                task_hold(answer->sender);
     436        }
     437        spinlock_unlock(&answer->forget_lock);
     438
    422439        if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
    423440                /* In case of forward, hangup the forwared phone,
     
    434451        }
    435452       
    436         if (!olddata)
     453        if (!olddata) {
     454                task_release(answer->sender);
    437455                return rc;
     456        }
    438457       
    439458        switch (IPC_GET_IMETHOD(*olddata)) {
     
    469488        }
    470489       
     490        task_release(answer->sender);
     491
    471492        return rc;
    472493}
Note: See TracChangeset for help on using the changeset viewer.