Ignore:
File:
1 edited

Legend:

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

    rcd529c4 re9fe33b  
    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       
     
    228267void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    229268{
     269        task_t *caller = phone->caller;
     270
     271        atomic_inc(&phone->active_calls);
     272        call->caller_phone = phone;
     273        call->sender = caller;
     274
     275        call->active = true;
     276        spinlock_lock(&caller->active_calls_lock);
     277        list_append(&call->ta_link, &caller->active_calls);
     278        spinlock_unlock(&caller->active_calls_lock);
     279
    230280        call->data.phone = phone;
    231         atomic_inc(&phone->active_calls);
     281
    232282        IPC_SET_RETVAL(call->data, err);
    233283        _ipc_answer_free_call(call, false);
     
    243293static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
    244294{
     295        task_t *caller = phone->caller;
     296
    245297        /* Count sent ipc call */
    246         irq_spinlock_lock(&TASK->lock, true);
    247         TASK->ipc_info.call_sent++;
    248         irq_spinlock_unlock(&TASK->lock, true);
     298        irq_spinlock_lock(&caller->lock, true);
     299        caller->ipc_info.call_sent++;
     300        irq_spinlock_unlock(&caller->lock, true);
    249301       
    250302        if (!(call->flags & IPC_CALL_FORWARDED)) {
    251303                atomic_inc(&phone->active_calls);
     304                call->caller_phone = phone;
     305                call->sender = caller;
     306
     307                call->active = true;
     308                spinlock_lock(&caller->active_calls_lock);
     309                list_append(&call->ta_link, &caller->active_calls);
     310                spinlock_unlock(&caller->active_calls_lock);
     311
    252312                call->data.phone = phone;
    253                 call->data.task_id = TASK->taskid;
     313                call->data.task_id = caller->taskid;
    254314        }
    255315       
    256316        irq_spinlock_lock(&box->lock, true);
    257         list_append(&call->link, &box->calls);
     317        list_append(&call->ab_link, &box->calls);
    258318        irq_spinlock_unlock(&box->lock, true);
    259319       
     
    275335        if (phone->state != IPC_PHONE_CONNECTED) {
    276336                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 {
     337                if (!(call->flags & IPC_CALL_FORWARDED)) {
    281338                        if (phone->state == IPC_PHONE_HUNGUP)
    282339                                ipc_backsend_err(phone, call, EHANGUP);
     
    325382                call_t *call = ipc_call_alloc(0);
    326383                IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
     384                call->request_method = IPC_M_PHONE_HUNGUP;
    327385                call->flags |= IPC_CALL_DISCARD_ANSWER;
    328386                _ipc_call(phone, box, call);
     
    356414        TASK->ipc_info.forwarded++;
    357415        irq_spinlock_pass(&TASK->lock, &oldbox->lock);
    358         list_remove(&call->link);
     416        list_remove(&call->ab_link);
    359417        irq_spinlock_unlock(&oldbox->lock, true);
    360418       
    361419        if (mode & IPC_FF_ROUTE_FROM_ME) {
    362                 if (!call->caller_phone)
    363                         call->caller_phone = call->data.phone;
    364420                call->data.phone = newphone;
    365421                call->data.task_id = TASK->taskid;
     
    406462               
    407463                request = list_get_instance(list_first(&box->irq_notifs),
    408                     call_t, link);
    409                 list_remove(&request->link);
     464                    call_t, ab_link);
     465                list_remove(&request->ab_link);
    410466               
    411467                irq_spinlock_unlock(&box->irq_lock, false);
     
    416472                /* Handle asynchronous answers */
    417473                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);
     474                    call_t, ab_link);
     475                list_remove(&request->ab_link);
     476                atomic_dec(&request->caller_phone->active_calls);
    421477        } else if (!list_empty(&box->calls)) {
    422478                /* Count received call */
     
    425481                /* Handle requests */
    426482                request = list_get_instance(list_first(&box->calls),
    427                     call_t, link);
    428                 list_remove(&request->link);
     483                    call_t, ab_link);
     484                list_remove(&request->ab_link);
    429485               
    430486                /* Append request to dispatch queue */
    431                 list_append(&request->link, &box->dispatched_calls);
     487                list_append(&request->ab_link, &box->dispatched_calls);
    432488        } else {
    433489                /* This can happen regularly after ipc_cleanup */
     
    449505/** Answer all calls from list with EHANGUP answer.
    450506 *
     507 * @param box Answerbox with the list.
    451508 * @param lst Head of the list to be cleaned up.
    452  *
    453  */
    454 void ipc_cleanup_call_list(list_t *lst)
    455 {
     509 */
     510void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
     511{
     512        irq_spinlock_lock(&box->lock, true);
    456513        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                
     514                call_t *call = list_get_instance(list_first(lst), call_t,
     515                    ab_link);
     516               
     517                list_remove(&call->ab_link);
     518
     519                irq_spinlock_unlock(&box->lock, true);
     520
     521                ipc_data_t old = call->data;
    463522                IPC_SET_RETVAL(call->data, EHANGUP);
     523                answer_preprocess(call, &old);
    464524                _ipc_answer_free_call(call, true);
    465         }
     525
     526                irq_spinlock_lock(&box->lock, true);
     527        }
     528        irq_spinlock_unlock(&box->lock, true);
    466529}
    467530
     
    501564                        mutex_unlock(&phone->lock);
    502565                        irq_spinlock_unlock(&box->lock, true);
    503                        
     566
     567                        // FIXME: phone can become deallocated at any time now
     568
    504569                        /*
    505570                         * Send one message to the answerbox for each
     
    509574                         */
    510575                        IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
     576                        call->request_method = IPC_M_PHONE_HUNGUP;
    511577                        call->flags |= IPC_CALL_DISCARD_ANSWER;
    512578                        _ipc_call(phone, box, call);
     
    529595}
    530596
     597static void ipc_forget_all_active_calls(void)
     598{
     599        call_t *call;
     600
     601restart:
     602        spinlock_lock(&TASK->active_calls_lock);
     603        if (list_empty(&TASK->active_calls)) {
     604                /*
     605                 * We are done, there are no more active calls.
     606                 * Nota bene: there may still be answers waiting for pick up.
     607                 */
     608                spinlock_unlock(&TASK->active_calls_lock);     
     609                return;
     610        }
     611       
     612        call = list_get_instance(list_first(&TASK->active_calls), call_t,
     613            ta_link);
     614
     615        if (!spinlock_trylock(&call->forget_lock)) {
     616                /*
     617                 * Avoid deadlock and let async_answer() or
     618                 *  _ipc_answer_free_call() win the race to dequeue the first
     619                 * call on the list.
     620                 */
     621                spinlock_unlock(&TASK->active_calls_lock);     
     622                goto restart;
     623        }
     624
     625        /*
     626         * Forget the call and donate it to the task which holds up the answer.
     627         */
     628
     629        call->forget = true;
     630        call->sender = NULL;
     631        list_remove(&call->ta_link);
     632
     633        /*
     634         * The call may be freed by _ipc_answer_free_call() before we are done
     635         * with it; to avoid working with a destroyed call_t structure, we
     636         * must hold a reference to it.
     637         */
     638        ipc_call_hold(call);
     639
     640        spinlock_unlock(&call->forget_lock);
     641        spinlock_unlock(&TASK->active_calls_lock);
     642
     643        atomic_dec(&call->caller_phone->active_calls);
     644
     645        sysipc_ops_t *ops = sysipc_ops_get(call->request_method);
     646        if (ops->request_forget)
     647                ops->request_forget(call);
     648
     649        ipc_call_release(call);
     650
     651        goto restart;
     652}
     653
     654/** Wait for all answers to asynchronous calls to arrive. */
     655static void ipc_wait_for_all_answered_calls(void)
     656{
     657        call_t *call;
     658        size_t i;
     659
     660restart:
     661        /*
     662         * Go through all phones, until they are all free.
     663         * Locking is needed as there may be connection handshakes in progress.
     664         */
     665        for (i = 0; i < IPC_MAX_PHONES; i++) {
     666                phone_t *phone = &TASK->phones[i];
     667
     668                mutex_lock(&phone->lock);       
     669                if ((phone->state == IPC_PHONE_HUNGUP) &&
     670                    (atomic_get(&phone->active_calls) == 0)) {
     671                        phone->state = IPC_PHONE_FREE;
     672                        phone->callee = NULL;
     673                }
     674
     675                /*
     676                 * We might have had some IPC_PHONE_CONNECTING phones at the
     677                 * beginning of ipc_cleanup(). Depending on whether these were
     678                 * forgotten or answered, they will eventually enter the
     679                 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
     680                 * In the latter case, the other side may slam the open phones
     681                 * at any time, in which case we will get an IPC_PHONE_SLAMMED
     682                 * phone.
     683                 */
     684                if ((phone->state == IPC_PHONE_CONNECTED) ||
     685                    (phone->state == IPC_PHONE_SLAMMED)) {
     686                        mutex_unlock(&phone->lock);
     687                        ipc_phone_hangup(phone);
     688                        /*
     689                         * Now there may be one extra active call, which needs
     690                         * to be forgotten.
     691                         */
     692                        ipc_forget_all_active_calls();
     693                        goto restart;
     694                }
     695
     696                /*
     697                 * If the hangup succeeded, it has sent a HANGUP message, the
     698                 * IPC is now in HUNGUP state, we wait for the reply to come
     699                 */
     700                if (phone->state != IPC_PHONE_FREE) {
     701                        mutex_unlock(&phone->lock);
     702                        break;
     703                }
     704
     705                mutex_unlock(&phone->lock);
     706        }
     707               
     708        /* Got into cleanup */
     709        if (i == IPC_MAX_PHONES)
     710                return;
     711               
     712        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
     713            SYNCH_FLAGS_NONE);
     714        ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
     715        ipc_call_free(call);
     716        goto restart;
     717}
     718
    531719/** Clean up all IPC communication of the current task.
    532720 *
     
    537725void ipc_cleanup(void)
    538726{
     727        /*
     728         * Mark the answerbox as inactive.
     729         *
     730         * The main purpose for doing this is to prevent any pending callback
     731         * connections from getting established beyond this point.
     732         */
     733        irq_spinlock_lock(&TASK->answerbox.lock, true);
     734        TASK->answerbox.active = false;
     735        irq_spinlock_unlock(&TASK->answerbox.lock, true);
     736
    539737        /* Disconnect all our phones ('ipc_phone_hangup') */
    540         size_t i;
    541         for (i = 0; i < IPC_MAX_PHONES; i++)
     738        for (size_t i = 0; i < IPC_MAX_PHONES; i++)
    542739                ipc_phone_hangup(&TASK->phones[i]);
    543740       
     
    557754       
    558755        /* 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         }
     756        ipc_cleanup_call_list(&TASK->answerbox,
     757            &TASK->answerbox.dispatched_calls);
     758        ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
     759
     760        ipc_forget_all_active_calls();
     761        ipc_wait_for_all_answered_calls();
    606762}
    607763
     
    615771        ipc_answerbox_slab = slab_cache_create("answerbox_t",
    616772            sizeof(answerbox_t), 0, NULL, NULL, 0);
     773}
     774
     775
     776static void ipc_print_call_list(list_t *list)
     777{
     778        list_foreach(*list, cur) {
     779                call_t *call = list_get_instance(cur, call_t, ab_link);
     780               
     781#ifdef __32_BITS__
     782                printf("%10p ", call);
     783#endif
     784               
     785#ifdef __64_BITS__
     786                printf("%18p ", call);
     787#endif
     788               
     789                spinlock_lock(&call->forget_lock);
     790
     791                printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
     792                    " %-6" PRIun " %-6" PRIun " %-7x",
     793                    IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
     794                    IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
     795                    IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
     796                    call->flags);
     797
     798                if (call->forget) {
     799                        printf(" ? (call forgotten)\n");
     800                } else {
     801                        printf(" %" PRIu64 " (%s)\n",
     802                            call->sender->taskid, call->sender->name);
     803                }
     804
     805                spinlock_unlock(&call->forget_lock);
     806        }
    617807}
    618808
     
    688878       
    689879        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        
     880        ipc_print_call_list(&task->answerbox.calls);
    709881        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        
     882        ipc_print_call_list(&task->answerbox.dispatched_calls);
    729883        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         }
     884        ipc_print_call_list(&task->answerbox.answers);
    748885       
    749886        irq_spinlock_unlock(&task->answerbox.lock, false);
Note: See TracChangeset for help on using the changeset viewer.