Ignore:
File:
1 edited

Legend:

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

    re9fe33b rcd529c4  
    4545#include <ipc/kbox.h>
    4646#include <ipc/event.h>
    47 #include <ipc/sysipc_ops.h>
    48 #include <ipc/sysipc_priv.h>
    4947#include <errno.h>
    5048#include <mm/slab.h>
     
    7371{
    7472        memsetb(call, sizeof(*call), 0);
    75         spinlock_initialize(&call->forget_lock, "forget_lock");
    76         call->active = false;
    77         call->forget = false;
    78         call->sender = NULL;
     73        call->sender = TASK;
    7974        call->buffer = NULL;
    80 }
    81 
    82 void ipc_call_hold(call_t *call)
    83 {
    84         atomic_inc(&call->refcnt);
    85 }
    86 
    87 void 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         }
    9475}
    9576
     
    10283 *
    10384 * @return If flags permit it, return NULL, or initialized kernel
    104  *         call structure with one reference.
     85 *         call structure.
    10586 *
    10687 */
     
    10889{
    10990        call_t *call = slab_alloc(ipc_call_slab, flags);
    110         if (call) {
     91        if (call)
    11192                _ipc_call_init(call);
    112                 ipc_call_hold(call);
    113         }
    11493       
    11594        return call;
     
    123102void ipc_call_free(call_t *call)
    124103{
    125         ipc_call_release(call);
     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);
    126108}
    127109
     
    150132 * @param phone Initialized phone structure.
    151133 * @param box   Initialized answerbox structure.
    152  * @return      True if the phone was connected, false otherwise.
    153  */
    154 bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
    155 {
    156         bool active;
    157 
     134 *
     135 */
     136void ipc_phone_connect(phone_t *phone, answerbox_t *box)
     137{
    158138        mutex_lock(&phone->lock);
     139       
     140        phone->state = IPC_PHONE_CONNECTED;
     141        phone->callee = box;
     142       
    159143        irq_spinlock_lock(&box->lock, true);
    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 
     144        list_append(&phone->link, &box->connected_phones);
    168145        irq_spinlock_unlock(&box->lock, true);
     146       
    169147        mutex_unlock(&phone->lock);
    170 
    171         return active;
    172148}
    173149
     
    175151 *
    176152 * @param phone Phone structure to be initialized.
    177  * @param caller Owning task.
    178  *
    179  */
    180 void ipc_phone_init(phone_t *phone, task_t *caller)
     153 *
     154 */
     155void ipc_phone_init(phone_t *phone)
    181156{
    182157        mutex_initialize(&phone->lock, MUTEX_PASSIVE);
    183         phone->caller = caller;
    184158        phone->callee = NULL;
    185159        phone->state = IPC_PHONE_FREE;
     
    193167 *
    194168 */
    195 void _ipc_answer_free_call(call_t *call, bool selflocked)
    196 {
     169static 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       
    197174        /* Count sent answer */
    198175        irq_spinlock_lock(&TASK->lock, true);
    199176        TASK->ipc_info.answer_sent++;
    200177        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);
     178       
     179        call->flags |= IPC_CALL_ANSWERED;
     180       
     181        if (call->flags & IPC_CALL_FORWARDED) {
     182                if (call->caller_phone) {
     183                        /* Demasquerade the caller phone. */
     184                        call->data.phone = call->caller_phone;
    218185                }
    219186        }
    220         spinlock_unlock(&call->forget_lock);
    221 
    222         answerbox_t *callerbox = &call->sender->answerbox;
    223         bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    224        
    225         call->flags |= IPC_CALL_ANSWERED;
    226        
     187
    227188        call->data.task_id = TASK->taskid;
    228189       
     
    230191                irq_spinlock_lock(&callerbox->lock, true);
    231192       
    232         list_append(&call->ab_link, &callerbox->answers);
     193        list_append(&call->link, &callerbox->answers);
    233194       
    234195        if (do_lock)
     
    248209        /* Remove from active box */
    249210        irq_spinlock_lock(&box->lock, true);
    250         list_remove(&call->ab_link);
     211        list_remove(&call->link);
    251212        irq_spinlock_unlock(&box->lock, true);
    252213       
     
    267228void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    268229{
    269         task_t *caller = phone->caller;
    270 
     230        call->data.phone = phone;
    271231        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 
    280         call->data.phone = phone;
    281 
    282232        IPC_SET_RETVAL(call->data, err);
    283233        _ipc_answer_free_call(call, false);
     
    293243static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
    294244{
    295         task_t *caller = phone->caller;
    296 
    297245        /* Count sent ipc call */
    298         irq_spinlock_lock(&caller->lock, true);
    299         caller->ipc_info.call_sent++;
    300         irq_spinlock_unlock(&caller->lock, true);
     246        irq_spinlock_lock(&TASK->lock, true);
     247        TASK->ipc_info.call_sent++;
     248        irq_spinlock_unlock(&TASK->lock, true);
    301249       
    302250        if (!(call->flags & IPC_CALL_FORWARDED)) {
    303251                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 
    312252                call->data.phone = phone;
    313                 call->data.task_id = caller->taskid;
     253                call->data.task_id = TASK->taskid;
    314254        }
    315255       
    316256        irq_spinlock_lock(&box->lock, true);
    317         list_append(&call->ab_link, &box->calls);
     257        list_append(&call->link, &box->calls);
    318258        irq_spinlock_unlock(&box->lock, true);
    319259       
     
    335275        if (phone->state != IPC_PHONE_CONNECTED) {
    336276                mutex_unlock(&phone->lock);
    337                 if (!(call->flags & IPC_CALL_FORWARDED)) {
     277                if (call->flags & IPC_CALL_FORWARDED) {
     278                        IPC_SET_RETVAL(call->data, EFORWARD);
     279                        _ipc_answer_free_call(call, false);
     280                } else {
    338281                        if (phone->state == IPC_PHONE_HUNGUP)
    339282                                ipc_backsend_err(phone, call, EHANGUP);
     
    382325                call_t *call = ipc_call_alloc(0);
    383326                IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
    384                 call->request_method = IPC_M_PHONE_HUNGUP;
    385327                call->flags |= IPC_CALL_DISCARD_ANSWER;
    386328                _ipc_call(phone, box, call);
     
    414356        TASK->ipc_info.forwarded++;
    415357        irq_spinlock_pass(&TASK->lock, &oldbox->lock);
    416         list_remove(&call->ab_link);
     358        list_remove(&call->link);
    417359        irq_spinlock_unlock(&oldbox->lock, true);
    418360       
    419361        if (mode & IPC_FF_ROUTE_FROM_ME) {
     362                if (!call->caller_phone)
     363                        call->caller_phone = call->data.phone;
    420364                call->data.phone = newphone;
    421365                call->data.task_id = TASK->taskid;
     
    462406               
    463407                request = list_get_instance(list_first(&box->irq_notifs),
    464                     call_t, ab_link);
    465                 list_remove(&request->ab_link);
     408                    call_t, link);
     409                list_remove(&request->link);
    466410               
    467411                irq_spinlock_unlock(&box->irq_lock, false);
     
    472416                /* Handle asynchronous answers */
    473417                request = list_get_instance(list_first(&box->answers),
    474                     call_t, ab_link);
    475                 list_remove(&request->ab_link);
    476                 atomic_dec(&request->caller_phone->active_calls);
     418                    call_t, link);
     419                list_remove(&request->link);
     420                atomic_dec(&request->data.phone->active_calls);
    477421        } else if (!list_empty(&box->calls)) {
    478422                /* Count received call */
     
    481425                /* Handle requests */
    482426                request = list_get_instance(list_first(&box->calls),
    483                     call_t, ab_link);
    484                 list_remove(&request->ab_link);
     427                    call_t, link);
     428                list_remove(&request->link);
    485429               
    486430                /* Append request to dispatch queue */
    487                 list_append(&request->ab_link, &box->dispatched_calls);
     431                list_append(&request->link, &box->dispatched_calls);
    488432        } else {
    489433                /* This can happen regularly after ipc_cleanup */
     
    505449/** Answer all calls from list with EHANGUP answer.
    506450 *
    507  * @param box Answerbox with the list.
    508451 * @param lst Head of the list to be cleaned up.
    509  */
    510 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
    511 {
    512         irq_spinlock_lock(&box->lock, true);
     452 *
     453 */
     454void ipc_cleanup_call_list(list_t *lst)
     455{
    513456        while (!list_empty(lst)) {
    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;
     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               
    522463                IPC_SET_RETVAL(call->data, EHANGUP);
    523                 answer_preprocess(call, &old);
    524464                _ipc_answer_free_call(call, true);
    525 
    526                 irq_spinlock_lock(&box->lock, true);
    527         }
    528         irq_spinlock_unlock(&box->lock, true);
     465        }
    529466}
    530467
     
    564501                        mutex_unlock(&phone->lock);
    565502                        irq_spinlock_unlock(&box->lock, true);
    566 
    567                         // FIXME: phone can become deallocated at any time now
    568 
     503                       
    569504                        /*
    570505                         * Send one message to the answerbox for each
     
    574509                         */
    575510                        IPC_SET_IMETHOD(call->data, IPC_M_PHONE_HUNGUP);
    576                         call->request_method = IPC_M_PHONE_HUNGUP;
    577511                        call->flags |= IPC_CALL_DISCARD_ANSWER;
    578512                        _ipc_call(phone, box, call);
     
    595529}
    596530
    597 static void ipc_forget_all_active_calls(void)
    598 {
    599         call_t *call;
    600 
    601 restart:
    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. */
    655 static void ipc_wait_for_all_answered_calls(void)
    656 {
    657         call_t *call;
    658         size_t i;
    659 
    660 restart:
    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 
    719531/** Clean up all IPC communication of the current task.
    720532 *
     
    725537void ipc_cleanup(void)
    726538{
    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 
    737539        /* Disconnect all our phones ('ipc_phone_hangup') */
    738         for (size_t i = 0; i < IPC_MAX_PHONES; i++)
     540        size_t i;
     541        for (i = 0; i < IPC_MAX_PHONES; i++)
    739542                ipc_phone_hangup(&TASK->phones[i]);
    740543       
     
    754557       
    755558        /* Answer all messages in 'calls' and 'dispatched_calls' queues */
    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();
     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        }
    762606}
    763607
     
    771615        ipc_answerbox_slab = slab_cache_create("answerbox_t",
    772616            sizeof(answerbox_t), 0, NULL, NULL, 0);
    773 }
    774 
    775 
    776 static 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         }
    807617}
    808618
     
    878688       
    879689        printf(" --- incomming calls ---\n");
    880         ipc_print_call_list(&task->answerbox.calls);
     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       
    881709        printf(" --- dispatched calls ---\n");
    882         ipc_print_call_list(&task->answerbox.dispatched_calls);
     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       
    883729        printf(" --- incoming answers ---\n");
    884         ipc_print_call_list(&task->answerbox.answers);
     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        }
    885748       
    886749        irq_spinlock_unlock(&task->answerbox.lock, false);
Note: See TracChangeset for help on using the changeset viewer.