Ignore:
File:
1 edited

Legend:

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

    rcd529c4 r466e95f7  
    4545#include <ipc/kbox.h>
    4646#include <ipc/event.h>
     47#include <ipc/sysipc_ops.h>
     48#include <ipc/sysipc_priv.h>
    4749#include <errno.h>
    4850#include <mm/slab.h>
     
    7173{
    7274        memsetb(call, sizeof(*call), 0);
    73         call->sender = TASK;
     75        spinlock_initialize(&call->forget_lock, "forget_lock");
     76        call->active = false;
     77        call->forget = false;
     78        call->sender = NULL;
    7479        call->buffer = NULL;
     80}
     81
     82void ipc_call_hold(call_t *call)
     83{
     84        atomic_inc(&call->refcnt);
     85}
     86
     87void ipc_call_release(call_t *call)
     88{
     89        if (atomic_predec(&call->refcnt) == 0) {
     90                if (call->buffer)
     91                        free(call->buffer);
     92                slab_free(ipc_call_slab, call);
     93        }
    7594}
    7695
     
    83102 *
    84103 * @return If flags permit it, return NULL, or initialized kernel
    85  *         call structure.
     104 *         call structure with one reference.
    86105 *
    87106 */
     
    89108{
    90109        call_t *call = slab_alloc(ipc_call_slab, flags);
    91         if (call)
     110        if (call) {
    92111                _ipc_call_init(call);
     112                ipc_call_hold(call);
     113        }
    93114       
    94115        return call;
     
    102123void ipc_call_free(call_t *call)
    103124{
    104         /* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
    105         if (call->buffer)
    106                 free(call->buffer);
    107         slab_free(ipc_call_slab, call);
     125        ipc_call_release(call);
    108126}
    109127
     
    132150 * @param phone Initialized phone structure.
    133151 * @param box   Initialized answerbox structure.
    134  *
    135  */
    136 void ipc_phone_connect(phone_t *phone, answerbox_t *box)
    137 {
     152 * @return      True if the phone was connected, false otherwise.
     153 */
     154bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
     155{
     156        bool active;
     157
    138158        mutex_lock(&phone->lock);
    139        
    140         phone->state = IPC_PHONE_CONNECTED;
    141         phone->callee = box;
    142        
    143159        irq_spinlock_lock(&box->lock, true);
    144         list_append(&phone->link, &box->connected_phones);
     160
     161        active = box->active;
     162        if (active) {
     163                phone->state = IPC_PHONE_CONNECTED;
     164                phone->callee = box;
     165                list_append(&phone->link, &box->connected_phones);
     166        }
     167
    145168        irq_spinlock_unlock(&box->lock, true);
    146        
    147169        mutex_unlock(&phone->lock);
     170
     171        return active;
    148172}
    149173
     
    151175 *
    152176 * @param phone Phone structure to be initialized.
    153  *
    154  */
    155 void ipc_phone_init(phone_t *phone)
     177 * @param caller Owning task.
     178 *
     179 */
     180void ipc_phone_init(phone_t *phone, task_t *caller)
    156181{
    157182        mutex_initialize(&phone->lock, MUTEX_PASSIVE);
     183        phone->caller = caller;
    158184        phone->callee = NULL;
    159185        phone->state = IPC_PHONE_FREE;
     
    167193 *
    168194 */
    169 static void _ipc_answer_free_call(call_t *call, bool selflocked)
    170 {
    171         answerbox_t *callerbox = &call->sender->answerbox;
    172         bool do_lock = ((!selflocked) || callerbox != (&TASK->answerbox));
    173        
     195void _ipc_answer_free_call(call_t *call, bool selflocked)
     196{
    174197        /* Count sent answer */
    175198        irq_spinlock_lock(&TASK->lock, true);
    176199        TASK->ipc_info.answer_sent++;
    177200        irq_spinlock_unlock(&TASK->lock, true);
     201
     202        spinlock_lock(&call->forget_lock);
     203        if (call->forget) {
     204                /* This is a forgotten call and call->sender is not valid. */
     205                spinlock_unlock(&call->forget_lock);
     206                ipc_call_free(call);
     207                return;
     208        } else {
     209                /*
     210                 * If the call is still active, i.e. it was answered
     211                 * in a non-standard way, remove the call from the
     212                 * sender's active call list.
     213                 */
     214                if (call->active) {
     215                        spinlock_lock(&call->sender->active_calls_lock);
     216                        list_remove(&call->ta_link);
     217                        spinlock_unlock(&call->sender->active_calls_lock);
     218                }
     219        }
     220        spinlock_unlock(&call->forget_lock);
     221
     222        answerbox_t *callerbox = &call->sender->answerbox;
     223        bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    178224       
    179225        call->flags |= IPC_CALL_ANSWERED;
    180226       
    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 
    188227        call->data.task_id = TASK->taskid;
    189228       
     
    191230                irq_spinlock_lock(&callerbox->lock, true);
    192231       
    193         list_append(&call->link, &callerbox->answers);
     232        list_append(&call->ab_link, &callerbox->answers);
    194233       
    195234        if (do_lock)
     
    209248        /* Remove from active box */
    210249        irq_spinlock_lock(&box->lock, true);
    211         list_remove(&call->link);
     250        list_remove(&call->ab_link);
    212251        irq_spinlock_unlock(&box->lock, true);
    213252       
    214253        /* Send back answer */
    215254        _ipc_answer_free_call(call, false);
     255}
     256
     257static void _ipc_call_actions_internal(phone_t *phone, call_t *call)
     258{
     259        task_t *caller = phone->caller;
     260
     261        atomic_inc(&phone->active_calls);
     262        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);
     269
     270        call->data.phone = phone;
     271        call->data.task_id = caller->taskid;
    216272}
    217273
     
    228284void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    229285{
    230         call->data.phone = phone;
    231         atomic_inc(&phone->active_calls);
     286        _ipc_call_actions_internal(phone, call);
    232287        IPC_SET_RETVAL(call->data, err);
    233288        _ipc_answer_free_call(call, false);
     
    243298static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
    244299{
     300        task_t *caller = phone->caller;
     301
    245302        /* Count sent ipc call */
    246         irq_spinlock_lock(&TASK->lock, true);
    247         TASK->ipc_info.call_sent++;
    248         irq_spinlock_unlock(&TASK->lock, true);
    249        
    250         if (!(call->flags & IPC_CALL_FORWARDED)) {
    251                 atomic_inc(&phone->active_calls);
    252                 call->data.phone = phone;
    253                 call->data.task_id = TASK->taskid;
    254         }
     303        irq_spinlock_lock(&caller->lock, true);
     304        caller->ipc_info.call_sent++;
     305        irq_spinlock_unlock(&caller->lock, true);
     306       
     307        if (!(call->flags & IPC_CALL_FORWARDED))
     308                _ipc_call_actions_internal(phone, call);
    255309       
    256310        irq_spinlock_lock(&box->lock, true);
    257         list_append(&call->link, &box->calls);
     311        list_append(&call->ab_link, &box->calls);
    258312        irq_spinlock_unlock(&box->lock, true);
    259313       
     
    275329        if (phone->state != IPC_PHONE_CONNECTED) {
    276330                mutex_unlock(&phone->lock);
    277                 if (call->flags & IPC_CALL_FORWARDED) {
    278                         IPC_SET_RETVAL(call->data, EFORWARD);
    279                         _ipc_answer_free_call(call, false);
    280                 } else {
     331                if (!(call->flags & IPC_CALL_FORWARDED)) {
    281332                        if (phone->state == IPC_PHONE_HUNGUP)
    282333                                ipc_backsend_err(phone, call, EHANGUP);
     
    325376                call_t *call = ipc_call_alloc(0);
    326377                IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
     378                call->request_method = IPC_M_PHONE_HUNGUP;
    327379                call->flags |= IPC_CALL_DISCARD_ANSWER;
    328380                _ipc_call(phone, box, call);
     
    356408        TASK->ipc_info.forwarded++;
    357409        irq_spinlock_pass(&TASK->lock, &oldbox->lock);
    358         list_remove(&call->link);
     410        list_remove(&call->ab_link);
    359411        irq_spinlock_unlock(&oldbox->lock, true);
    360412       
    361413        if (mode & IPC_FF_ROUTE_FROM_ME) {
    362                 if (!call->caller_phone)
    363                         call->caller_phone = call->data.phone;
    364414                call->data.phone = newphone;
    365415                call->data.task_id = TASK->taskid;
     
    406456               
    407457                request = list_get_instance(list_first(&box->irq_notifs),
    408                     call_t, link);
    409                 list_remove(&request->link);
     458                    call_t, ab_link);
     459                list_remove(&request->ab_link);
    410460               
    411461                irq_spinlock_unlock(&box->irq_lock, false);
     
    416466                /* Handle asynchronous answers */
    417467                request = list_get_instance(list_first(&box->answers),
    418                     call_t, link);
    419                 list_remove(&request->link);
    420                 atomic_dec(&request->data.phone->active_calls);
     468                    call_t, ab_link);
     469                list_remove(&request->ab_link);
     470                atomic_dec(&request->caller_phone->active_calls);
    421471        } else if (!list_empty(&box->calls)) {
    422472                /* Count received call */
     
    425475                /* Handle requests */
    426476                request = list_get_instance(list_first(&box->calls),
    427                     call_t, link);
    428                 list_remove(&request->link);
     477                    call_t, ab_link);
     478                list_remove(&request->ab_link);
    429479               
    430480                /* Append request to dispatch queue */
    431                 list_append(&request->link, &box->dispatched_calls);
     481                list_append(&request->ab_link, &box->dispatched_calls);
    432482        } else {
    433483                /* This can happen regularly after ipc_cleanup */
     
    449499/** Answer all calls from list with EHANGUP answer.
    450500 *
     501 * @param box Answerbox with the list.
    451502 * @param lst Head of the list to be cleaned up.
    452  *
    453  */
    454 void ipc_cleanup_call_list(list_t *lst)
    455 {
     503 */
     504void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
     505{
     506        irq_spinlock_lock(&box->lock, true);
    456507        while (!list_empty(lst)) {
    457                 call_t *call = list_get_instance(list_first(lst), call_t, link);
    458                 if (call->buffer)
    459                         free(call->buffer);
    460                
    461                 list_remove(&call->link);
    462                
     508                call_t *call = list_get_instance(list_first(lst), call_t,
     509                    ab_link);
     510               
     511                list_remove(&call->ab_link);
     512
     513                irq_spinlock_unlock(&box->lock, true);
     514
     515                if (lst == &box->calls)
     516                        SYSIPC_OP(request_process, call, box);
     517
     518                ipc_data_t old = call->data;
    463519                IPC_SET_RETVAL(call->data, EHANGUP);
     520                answer_preprocess(call, &old);
    464521                _ipc_answer_free_call(call, true);
    465         }
     522
     523                irq_spinlock_lock(&box->lock, true);
     524        }
     525        irq_spinlock_unlock(&box->lock, true);
    466526}
    467527
     
    501561                        mutex_unlock(&phone->lock);
    502562                        irq_spinlock_unlock(&box->lock, true);
    503                        
     563
     564                        // FIXME: phone can become deallocated at any time now
     565
    504566                        /*
    505567                         * Send one message to the answerbox for each
     
    509571                         */
    510572                        IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
     573                        call->request_method = IPC_M_PHONE_HUNGUP;
    511574                        call->flags |= IPC_CALL_DISCARD_ANSWER;
    512575                        _ipc_call(phone, box, call);
     
    529592}
    530593
     594static void ipc_forget_all_active_calls(void)
     595{
     596        call_t *call;
     597
     598restart:
     599        spinlock_lock(&TASK->active_calls_lock);
     600        if (list_empty(&TASK->active_calls)) {
     601                /*
     602                 * We are done, there are no more active calls.
     603                 * Nota bene: there may still be answers waiting for pick up.
     604                 */
     605                spinlock_unlock(&TASK->active_calls_lock);     
     606                return;
     607        }
     608       
     609        call = list_get_instance(list_first(&TASK->active_calls), call_t,
     610            ta_link);
     611
     612        if (!spinlock_trylock(&call->forget_lock)) {
     613                /*
     614                 * Avoid deadlock and let async_answer() or
     615                 *  _ipc_answer_free_call() win the race to dequeue the first
     616                 * call on the list.
     617                 */
     618                spinlock_unlock(&TASK->active_calls_lock);     
     619                goto restart;
     620        }
     621
     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);
     645
     646        goto restart;
     647}
     648
     649/** Wait for all answers to asynchronous calls to arrive. */
     650static void ipc_wait_for_all_answered_calls(void)
     651{
     652        call_t *call;
     653        size_t i;
     654
     655restart:
     656        /*
     657         * Go through all phones, until they are all free.
     658         * Locking is needed as there may be connection handshakes in progress.
     659         */
     660        for (i = 0; i < IPC_MAX_PHONES; i++) {
     661                phone_t *phone = &TASK->phones[i];
     662
     663                mutex_lock(&phone->lock);       
     664                if ((phone->state == IPC_PHONE_HUNGUP) &&
     665                    (atomic_get(&phone->active_calls) == 0)) {
     666                        phone->state = IPC_PHONE_FREE;
     667                        phone->callee = NULL;
     668                }
     669
     670                /*
     671                 * We might have had some IPC_PHONE_CONNECTING phones at the
     672                 * beginning of ipc_cleanup(). Depending on whether these were
     673                 * forgotten or answered, they will eventually enter the
     674                 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
     675                 * In the latter case, the other side may slam the open phones
     676                 * at any time, in which case we will get an IPC_PHONE_SLAMMED
     677                 * phone.
     678                 */
     679                if ((phone->state == IPC_PHONE_CONNECTED) ||
     680                    (phone->state == IPC_PHONE_SLAMMED)) {
     681                        mutex_unlock(&phone->lock);
     682                        ipc_phone_hangup(phone);
     683                        /*
     684                         * Now there may be one extra active call, which needs
     685                         * to be forgotten.
     686                         */
     687                        ipc_forget_all_active_calls();
     688                        goto restart;
     689                }
     690
     691                /*
     692                 * If the hangup succeeded, it has sent a HANGUP message, the
     693                 * IPC is now in HUNGUP state, we wait for the reply to come
     694                 */
     695                if (phone->state != IPC_PHONE_FREE) {
     696                        mutex_unlock(&phone->lock);
     697                        break;
     698                }
     699
     700                mutex_unlock(&phone->lock);
     701        }
     702               
     703        /* Got into cleanup */
     704        if (i == IPC_MAX_PHONES)
     705                return;
     706               
     707        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
     708            SYNCH_FLAGS_NONE);
     709        ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
     710
     711        SYSIPC_OP(answer_process, call);
     712
     713        ipc_call_free(call);
     714        goto restart;
     715}
     716
    531717/** Clean up all IPC communication of the current task.
    532718 *
     
    537723void ipc_cleanup(void)
    538724{
     725        /*
     726         * Mark the answerbox as inactive.
     727         *
     728         * The main purpose for doing this is to prevent any pending callback
     729         * connections from getting established beyond this point.
     730         */
     731        irq_spinlock_lock(&TASK->answerbox.lock, true);
     732        TASK->answerbox.active = false;
     733        irq_spinlock_unlock(&TASK->answerbox.lock, true);
     734
    539735        /* Disconnect all our phones ('ipc_phone_hangup') */
    540         size_t i;
    541         for (i = 0; i < IPC_MAX_PHONES; i++)
     736        for (size_t i = 0; i < IPC_MAX_PHONES; i++)
    542737                ipc_phone_hangup(&TASK->phones[i]);
    543738       
     
    557752       
    558753        /* Answer all messages in 'calls' and 'dispatched_calls' queues */
    559         irq_spinlock_lock(&TASK->answerbox.lock, true);
    560         ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
    561         ipc_cleanup_call_list(&TASK->answerbox.calls);
    562         irq_spinlock_unlock(&TASK->answerbox.lock, true);
    563        
    564         /* Wait for all answers to asynchronous calls to arrive */
    565         while (true) {
    566                 /*
    567                  * Go through all phones, until they are all FREE
    568                  * Locking is not needed, no one else should modify
    569                  * it when we are in cleanup
    570                  */
    571                 for (i = 0; i < IPC_MAX_PHONES; i++) {
    572                         if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
    573                             atomic_get(&TASK->phones[i].active_calls) == 0) {
    574                                 TASK->phones[i].state = IPC_PHONE_FREE;
    575                                 TASK->phones[i].callee = NULL;
    576                         }
    577                        
    578                         /*
    579                          * Just for sure, we might have had some
    580                          * IPC_PHONE_CONNECTING phones
    581                          */
    582                         if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
    583                                 ipc_phone_hangup(&TASK->phones[i]);
    584                        
    585                         /*
    586                          * If the hangup succeeded, it has sent a HANGUP
    587                          * message, the IPC is now in HUNGUP state, we
    588                          * wait for the reply to come
    589                          */
    590                        
    591                         if (TASK->phones[i].state != IPC_PHONE_FREE)
    592                                 break;
    593                 }
    594                
    595                 /* Got into cleanup */
    596                 if (i == IPC_MAX_PHONES)
    597                         break;
    598                
    599                 call_t *call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
    600                     SYNCH_FLAGS_NONE);
    601                 ASSERT((call->flags & IPC_CALL_ANSWERED) ||
    602                     (call->flags & IPC_CALL_NOTIF));
    603                
    604                 ipc_call_free(call);
    605         }
     754        ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
     755        ipc_cleanup_call_list(&TASK->answerbox,
     756            &TASK->answerbox.dispatched_calls);
     757
     758        ipc_forget_all_active_calls();
     759        ipc_wait_for_all_answered_calls();
    606760}
    607761
     
    615769        ipc_answerbox_slab = slab_cache_create("answerbox_t",
    616770            sizeof(answerbox_t), 0, NULL, NULL, 0);
     771}
     772
     773
     774static void ipc_print_call_list(list_t *list)
     775{
     776        list_foreach(*list, cur) {
     777                call_t *call = list_get_instance(cur, call_t, ab_link);
     778               
     779#ifdef __32_BITS__
     780                printf("%10p ", call);
     781#endif
     782               
     783#ifdef __64_BITS__
     784                printf("%18p ", call);
     785#endif
     786               
     787                spinlock_lock(&call->forget_lock);
     788
     789                printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
     790                    " %-6" PRIun " %-6" PRIun " %-7x",
     791                    IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
     792                    IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
     793                    IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
     794                    call->flags);
     795
     796                if (call->forget) {
     797                        printf(" ? (call forgotten)\n");
     798                } else {
     799                        printf(" %" PRIu64 " (%s)\n",
     800                            call->sender->taskid, call->sender->name);
     801                }
     802
     803                spinlock_unlock(&call->forget_lock);
     804        }
    617805}
    618806
     
    688876       
    689877        printf(" --- incomming calls ---\n");
    690         list_foreach(task->answerbox.calls, cur) {
    691                 call_t *call = list_get_instance(cur, call_t, link);
    692                
    693 #ifdef __32_BITS__
    694                 printf("%10p ", call);
    695 #endif
    696                
    697 #ifdef __64_BITS__
    698                 printf("%18p ", call);
    699 #endif
    700                
    701                 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
    702                     " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
    703                     IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
    704                     IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
    705                     IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
    706                     call->flags, call->sender->taskid, call->sender->name);
    707         }
    708        
     878        ipc_print_call_list(&task->answerbox.calls);
    709879        printf(" --- dispatched calls ---\n");
    710         list_foreach(task->answerbox.dispatched_calls, cur) {
    711                 call_t *call = list_get_instance(cur, call_t, link);
    712                
    713 #ifdef __32_BITS__
    714                 printf("%10p ", call);
    715 #endif
    716                
    717 #ifdef __64_BITS__
    718                 printf("%18p ", call);
    719 #endif
    720                
    721                 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
    722                     " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
    723                     IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
    724                     IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
    725                     IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
    726                     call->flags, call->sender->taskid, call->sender->name);
    727         }
    728        
     880        ipc_print_call_list(&task->answerbox.dispatched_calls);
    729881        printf(" --- incoming answers ---\n");
    730         list_foreach(task->answerbox.answers, cur) {
    731                 call_t *call = list_get_instance(cur, call_t, link);
    732                
    733 #ifdef __32_BITS__
    734                 printf("%10p ", call);
    735 #endif
    736                
    737 #ifdef __64_BITS__
    738                 printf("%18p ", call);
    739 #endif
    740                
    741                 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
    742                     " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
    743                     IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
    744                     IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
    745                     IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
    746                     call->flags, call->sender->taskid, call->sender->name);
    747         }
     882        ipc_print_call_list(&task->answerbox.answers);
    748883       
    749884        irq_spinlock_unlock(&task->answerbox.lock, false);
Note: See TracChangeset for help on using the changeset viewer.