Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/ipc/ipc.c

    rfeeac0d r97b8ca9  
    5959#include <ipc/irq.h>
    6060
     61static void ipc_forget_call(call_t *);
     62
    6163/** Open channel that is assigned automatically to new tasks */
    6264answerbox_t *ipc_phone_0 = NULL;
     
    7779        call->forget = false;
    7880        call->sender = NULL;
     81        call->callerbox = NULL;
    7982        call->buffer = NULL;
    8083}
     
    185188        phone->state = IPC_PHONE_FREE;
    186189        atomic_set(&phone->active_calls, 0);
     190}
     191
     192/** Helper function to facilitate synchronous calls.
     193 *
     194 * @param phone   Destination kernel phone structure.
     195 * @param request Call structure with request.
     196 *
     197 * @return EOK on success or a negative error code.
     198 *
     199 */
     200int ipc_call_sync(phone_t *phone, call_t *request)
     201{
     202        answerbox_t *mybox = slab_alloc(ipc_answerbox_slab, 0);
     203        ipc_answerbox_init(mybox, TASK);
     204       
     205        /* We will receive data in a special box. */
     206        request->callerbox = mybox;
     207       
     208        int rc = ipc_call(phone, request);
     209        if (rc != EOK) {
     210                slab_free(ipc_answerbox_slab, mybox);
     211                return rc;
     212        }
     213
     214        call_t *answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
     215            SYNCH_FLAGS_INTERRUPTIBLE);
     216        if (!answer) {
     217
     218                /*
     219                 * The sleep was interrupted.
     220                 *
     221                 * There are two possibilities now:
     222                 * 1) the call gets answered before we manage to forget it
     223                 * 2) we manage to forget the call before it gets answered
     224                 */
     225
     226                spinlock_lock(&request->forget_lock);
     227                spinlock_lock(&TASK->active_calls_lock);
     228
     229                ASSERT(!request->forget);
     230
     231                bool answered = !request->active;
     232                if (!answered) {
     233                        /*
     234                         * The call is not yet answered and we won the race to
     235                         * forget it.
     236                         */
     237                        ipc_forget_call(request);       /* releases locks */
     238                        rc = EINTR;
     239                       
     240                } else {
     241                        spinlock_unlock(&TASK->active_calls_lock);
     242                        spinlock_unlock(&request->forget_lock);
     243                }
     244
     245                if (answered) {
     246                        /*
     247                         * The other side won the race to answer the call.
     248                         * It is safe to wait for the answer uninterruptibly
     249                         * now.
     250                         */
     251                        answer = ipc_wait_for_call(mybox, SYNCH_NO_TIMEOUT,
     252                            SYNCH_FLAGS_NONE);
     253                }
     254        }
     255        ASSERT(!answer || request == answer);
     256       
     257        slab_free(ipc_answerbox_slab, mybox);
     258        return rc;
    187259}
    188260
     
    220292        spinlock_unlock(&call->forget_lock);
    221293
    222         answerbox_t *callerbox = &call->sender->answerbox;
     294        answerbox_t *callerbox = call->callerbox ? call->callerbox :
     295            &call->sender->answerbox;
    223296        bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    224297       
     
    255328}
    256329
    257 static void _ipc_call_actions_internal(phone_t *phone, call_t *call)
     330static void _ipc_call_actions_internal(phone_t *phone, call_t *call,
     331    bool preforget)
    258332{
    259333        task_t *caller = phone->caller;
    260334
    261         atomic_inc(&phone->active_calls);
    262335        call->caller_phone = phone;
    263         call->sender = caller;
    264 
    265         call->active = true;
    266         spinlock_lock(&caller->active_calls_lock);
    267         list_append(&call->ta_link, &caller->active_calls);
    268         spinlock_unlock(&caller->active_calls_lock);
     336
     337        if (preforget) {
     338                call->forget = true;
     339        } else {
     340                atomic_inc(&phone->active_calls);
     341                call->sender = caller;
     342                call->active = true;
     343                spinlock_lock(&caller->active_calls_lock);
     344                list_append(&call->ta_link, &caller->active_calls);
     345                spinlock_unlock(&caller->active_calls_lock);
     346        }
    269347
    270348        call->data.phone = phone;
     
    284362void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    285363{
    286         _ipc_call_actions_internal(phone, call);
     364        _ipc_call_actions_internal(phone, call, false);
    287365        IPC_SET_RETVAL(call->data, err);
    288366        _ipc_answer_free_call(call, false);
     
    291369/** Unsafe unchecking version of ipc_call.
    292370 *
    293  * @param phone Phone structure the call comes from.
    294  * @param box   Destination answerbox structure.
    295  * @param call  Call structure with request.
    296  *
    297  */
    298 static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
     371 * @param phone     Phone structure the call comes from.
     372 * @param box       Destination answerbox structure.
     373 * @param call      Call structure with request.
     374 * @param preforget If true, the call will be delivered already forgotten.
     375 *
     376 */
     377static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call,
     378    bool preforget)
    299379{
    300380        task_t *caller = phone->caller;
     
    306386       
    307387        if (!(call->flags & IPC_CALL_FORWARDED))
    308                 _ipc_call_actions_internal(phone, call);
     388                _ipc_call_actions_internal(phone, call, preforget);
    309389       
    310390        irq_spinlock_lock(&box->lock, true);
     
    340420       
    341421        answerbox_t *box = phone->callee;
    342         _ipc_call(phone, box, call);
     422        _ipc_call(phone, box, call, false);
    343423       
    344424        mutex_unlock(&phone->lock);
     
    378458                call->request_method = IPC_M_PHONE_HUNGUP;
    379459                call->flags |= IPC_CALL_DISCARD_ANSWER;
    380                 _ipc_call(phone, box, call);
     460                _ipc_call(phone, box, call, false);
    381461        }
    382462       
     
    537617        phone_t *phone;
    538618        DEADLOCK_PROBE_INIT(p_phonelck);
    539        
    540         call_t *call = notify_box ? ipc_call_alloc(0) : NULL;
    541619       
    542620        /* Disconnect all phones connected to our answerbox */
     
    559637               
    560638                if (notify_box) {
     639                        task_hold(phone->caller);
    561640                        mutex_unlock(&phone->lock);
    562641                        irq_spinlock_unlock(&box->lock, true);
    563642
    564                         // FIXME: phone can become deallocated at any time now
    565 
    566643                        /*
    567                          * Send one message to the answerbox for each
    568                          * phone. Used to make sure the kbox thread
    569                          * wakes up after the last phone has been
    570                          * disconnected.
     644                         * Send one call to the answerbox for each phone.
     645                         * Used to make sure the kbox thread wakes up after
     646                         * the last phone has been disconnected. The call is
     647                         * forgotten upon sending, so the "caller" may cease
     648                         * to exist as soon as we release it.
    571649                         */
     650                        call_t *call = ipc_call_alloc(0);
    572651                        IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
    573652                        call->request_method = IPC_M_PHONE_HUNGUP;
    574653                        call->flags |= IPC_CALL_DISCARD_ANSWER;
    575                         _ipc_call(phone, box, call);
    576                        
    577                         /* Allocate another call in advance */
    578                         call = ipc_call_alloc(0);
     654                        _ipc_call(phone, box, call, true);
     655
     656                        task_release(phone->caller);
    579657                       
    580658                        /* Must start again */
     
    586664       
    587665        irq_spinlock_unlock(&box->lock, true);
    588        
    589         /* Free unused call */
    590         if (call)
    591                 ipc_call_free(call);
     666}
     667
     668static void ipc_forget_call(call_t *call)
     669{
     670        ASSERT(spinlock_locked(&TASK->active_calls_lock));
     671        ASSERT(spinlock_locked(&call->forget_lock));
     672
     673        /*
     674         * Forget the call and donate it to the task which holds up the answer.
     675         */
     676
     677        call->forget = true;
     678        call->sender = NULL;
     679        list_remove(&call->ta_link);
     680
     681        /*
     682         * The call may be freed by _ipc_answer_free_call() before we are done
     683         * with it; to avoid working with a destroyed call_t structure, we
     684         * must hold a reference to it.
     685         */
     686        ipc_call_hold(call);
     687
     688        spinlock_unlock(&call->forget_lock);
     689        spinlock_unlock(&TASK->active_calls_lock);
     690
     691        atomic_dec(&call->caller_phone->active_calls);
     692
     693        SYSIPC_OP(request_forget, call);
     694
     695        ipc_call_release(call);
    592696}
    593697
     
    620724        }
    621725
    622         /*
    623          * Forget the call and donate it to the task which holds up the answer.
    624          */
    625 
    626         call->forget = true;
    627         call->sender = NULL;
    628         list_remove(&call->ta_link);
    629 
    630         /*
    631          * The call may be freed by _ipc_answer_free_call() before we are done
    632          * with it; to avoid working with a destroyed call_t structure, we
    633          * must hold a reference to it.
    634          */
    635         ipc_call_hold(call);
    636 
    637         spinlock_unlock(&call->forget_lock);
    638         spinlock_unlock(&TASK->active_calls_lock);
    639 
    640         atomic_dec(&call->caller_phone->active_calls);
    641 
    642         SYSIPC_OP(request_forget, call);
    643 
    644         ipc_call_release(call);
     726        ipc_forget_call(call);
    645727
    646728        goto restart;
     
    755837        ipc_cleanup_call_list(&TASK->answerbox,
    756838            &TASK->answerbox.dispatched_calls);
    757 
     839       
    758840        ipc_forget_all_active_calls();
    759841        ipc_wait_for_all_answered_calls();
Note: See TracChangeset for help on using the changeset viewer.