Ignore:
File:
1 edited

Legend:

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

    rc33f39f 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;
    7873        call->sender = TASK;
    7974        call->buffer = NULL;
     
    137132 * @param phone Initialized phone structure.
    138133 * @param box   Initialized answerbox structure.
    139  * @return      True if the phone was connected, false otherwise.
    140  */
    141 bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
    142 {
    143         bool active;
    144 
     134 *
     135 */
     136void ipc_phone_connect(phone_t *phone, answerbox_t *box)
     137{
    145138        mutex_lock(&phone->lock);
     139       
     140        phone->state = IPC_PHONE_CONNECTED;
     141        phone->callee = box;
     142       
    146143        irq_spinlock_lock(&box->lock, true);
    147 
    148         active = box->active;
    149         if (active) {
    150                 phone->state = IPC_PHONE_CONNECTED;
    151                 phone->callee = box;
    152                 list_append(&phone->link, &box->connected_phones);
    153         }
    154 
     144        list_append(&phone->link, &box->connected_phones);
    155145        irq_spinlock_unlock(&box->lock, true);
     146       
    156147        mutex_unlock(&phone->lock);
    157 
    158         return active;
    159148}
    160149
     
    178167 *
    179168 */
    180 void _ipc_answer_free_call(call_t *call, bool selflocked)
    181 {
     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       
    182174        /* Count sent answer */
    183175        irq_spinlock_lock(&TASK->lock, true);
    184176        TASK->ipc_info.answer_sent++;
    185177        irq_spinlock_unlock(&TASK->lock, true);
    186 
    187         spinlock_lock(&call->forget_lock);
    188         if (call->forget) {
    189                 /* This is a forgotten call and call->sender is not valid. */
    190                 spinlock_unlock(&call->forget_lock);
    191                 ipc_call_free(call);
    192                 return;
    193         } else {
    194                 /*
    195                  * If the call is still active, i.e. it was answered
    196                  * in a non-standard way, remove the call from the
    197                  * sender's active call list.
    198                  */
    199                 if (call->active) {
    200                         spinlock_lock(&call->sender->active_calls_lock);
    201                         list_remove(&call->ta_link);
    202                         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;
    203185                }
    204186        }
    205         spinlock_unlock(&call->forget_lock);
    206 
    207         answerbox_t *callerbox = &call->sender->answerbox;
    208         bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    209        
    210         call->flags |= IPC_CALL_ANSWERED;
    211        
     187
    212188        call->data.task_id = TASK->taskid;
    213189       
     
    215191                irq_spinlock_lock(&callerbox->lock, true);
    216192       
    217         list_append(&call->ab_link, &callerbox->answers);
     193        list_append(&call->link, &callerbox->answers);
    218194       
    219195        if (do_lock)
     
    233209        /* Remove from active box */
    234210        irq_spinlock_lock(&box->lock, true);
    235         list_remove(&call->ab_link);
     211        list_remove(&call->link);
    236212        irq_spinlock_unlock(&box->lock, true);
    237213       
     
    252228void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    253229{
    254         call->caller_phone = phone;
    255230        call->data.phone = phone;
    256231        atomic_inc(&phone->active_calls);
    257 
    258         spinlock_lock(&TASK->active_calls_lock);
    259         list_append(&call->ta_link, &TASK->active_calls);
    260         spinlock_unlock(&TASK->active_calls_lock);
    261 
    262232        IPC_SET_RETVAL(call->data, err);
    263233        _ipc_answer_free_call(call, false);
     
    280250        if (!(call->flags & IPC_CALL_FORWARDED)) {
    281251                atomic_inc(&phone->active_calls);
    282 
    283                 call->caller_phone = phone;
    284                 call->active = true;
    285 
    286                 spinlock_lock(&TASK->active_calls_lock);
    287                 list_append(&call->ta_link, &TASK->active_calls);
    288                 spinlock_unlock(&TASK->active_calls_lock);
    289 
    290252                call->data.phone = phone;
    291253                call->data.task_id = TASK->taskid;
     
    293255       
    294256        irq_spinlock_lock(&box->lock, true);
    295         list_append(&call->ab_link, &box->calls);
     257        list_append(&call->link, &box->calls);
    296258        irq_spinlock_unlock(&box->lock, true);
    297259       
     
    313275        if (phone->state != IPC_PHONE_CONNECTED) {
    314276                mutex_unlock(&phone->lock);
    315                 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 {
    316281                        if (phone->state == IPC_PHONE_HUNGUP)
    317282                                ipc_backsend_err(phone, call, EHANGUP);
     
    391356        TASK->ipc_info.forwarded++;
    392357        irq_spinlock_pass(&TASK->lock, &oldbox->lock);
    393         list_remove(&call->ab_link);
     358        list_remove(&call->link);
    394359        irq_spinlock_unlock(&oldbox->lock, true);
    395360       
    396361        if (mode & IPC_FF_ROUTE_FROM_ME) {
     362                if (!call->caller_phone)
     363                        call->caller_phone = call->data.phone;
    397364                call->data.phone = newphone;
    398365                call->data.task_id = TASK->taskid;
     
    439406               
    440407                request = list_get_instance(list_first(&box->irq_notifs),
    441                     call_t, ab_link);
    442                 list_remove(&request->ab_link);
     408                    call_t, link);
     409                list_remove(&request->link);
    443410               
    444411                irq_spinlock_unlock(&box->irq_lock, false);
     
    449416                /* Handle asynchronous answers */
    450417                request = list_get_instance(list_first(&box->answers),
    451                     call_t, ab_link);
    452                 list_remove(&request->ab_link);
    453                 atomic_dec(&request->caller_phone->active_calls);
     418                    call_t, link);
     419                list_remove(&request->link);
     420                atomic_dec(&request->data.phone->active_calls);
    454421        } else if (!list_empty(&box->calls)) {
    455422                /* Count received call */
     
    458425                /* Handle requests */
    459426                request = list_get_instance(list_first(&box->calls),
    460                     call_t, ab_link);
    461                 list_remove(&request->ab_link);
     427                    call_t, link);
     428                list_remove(&request->link);
    462429               
    463430                /* Append request to dispatch queue */
    464                 list_append(&request->ab_link, &box->dispatched_calls);
     431                list_append(&request->link, &box->dispatched_calls);
    465432        } else {
    466433                /* This can happen regularly after ipc_cleanup */
     
    482449/** Answer all calls from list with EHANGUP answer.
    483450 *
    484  * @param box Answerbox with the list.
    485451 * @param lst Head of the list to be cleaned up.
    486  */
    487 void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
    488 {
    489         irq_spinlock_lock(&box->lock, true);
     452 *
     453 */
     454void ipc_cleanup_call_list(list_t *lst)
     455{
    490456        while (!list_empty(lst)) {
    491                 call_t *call = list_get_instance(list_first(lst), call_t,
    492                     ab_link);
    493                
    494                 list_remove(&call->ab_link);
    495 
    496                 irq_spinlock_unlock(&box->lock, true);
    497 
    498                 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               
    499463                IPC_SET_RETVAL(call->data, EHANGUP);
    500                 answer_preprocess(call, &old);
    501464                _ipc_answer_free_call(call, true);
    502 
    503                 irq_spinlock_lock(&box->lock, true);
    504         }
    505         irq_spinlock_unlock(&box->lock, true);
     465        }
    506466}
    507467
     
    569529}
    570530
    571 static void ipc_forget_all_active_calls(void)
    572 {
    573         call_t *call;
    574 
    575 restart:
    576         spinlock_lock(&TASK->active_calls_lock);
    577         if (list_empty(&TASK->active_calls)) {
    578                 /*
    579                  * We are done, there are no more active calls.
    580                  * Nota bene: there may still be answers waiting for pick up.
    581                  */
    582                 spinlock_unlock(&TASK->active_calls_lock);     
    583                 return;
    584         }
    585        
    586         call = list_get_instance(list_first(&TASK->active_calls), call_t,
    587             ta_link);
    588 
    589         if (!spinlock_trylock(&call->forget_lock)) {
    590                 /*
    591                  * Avoid deadlock and let async_answer() or
    592                  *  _ipc_answer_free_call() win the race to dequeue the first
    593                  * call on the list.
    594                  */
    595                 spinlock_unlock(&TASK->active_calls_lock);     
    596                 goto restart;
    597         }
    598 
    599         /*
    600          * Forget the call and donate it to the task which holds up the answer.
    601          */
    602 
    603         call->forget = true;
    604         call->sender = NULL;
    605         list_remove(&call->ta_link);
    606 
    607         spinlock_unlock(&call->forget_lock);
    608         spinlock_unlock(&TASK->active_calls_lock);
    609 
    610         atomic_dec(&call->caller_phone->active_calls);
    611 
    612         sysipc_ops_t *ops = sysipc_ops_get(call->request_method);
    613         if (ops->request_forget)
    614                 ops->request_forget(call);
    615 
    616         goto restart;
    617 }
    618 
    619 /** Wait for all answers to asynchronous calls to arrive. */
    620 static void ipc_wait_for_all_answered_calls(void)
    621 {
    622         call_t *call;
    623         size_t i;
    624 
    625 restart:
    626         /*
    627          * Go through all phones, until they are all free.
    628          * Locking is needed as there may be connection handshakes in progress.
    629          */
    630         for (i = 0; i < IPC_MAX_PHONES; i++) {
    631                 phone_t *phone = &TASK->phones[i];
    632 
    633                 mutex_lock(&phone->lock);       
    634                 if ((phone->state == IPC_PHONE_HUNGUP) &&
    635                     (atomic_get(&phone->active_calls) == 0)) {
    636                         phone->state = IPC_PHONE_FREE;
    637                         phone->callee = NULL;
    638                 }
    639 
    640                 /*
    641                  * We might have had some IPC_PHONE_CONNECTING phones at the
    642                  * beginning of ipc_cleanup(). Depending on whether these were
    643                  * forgotten or answered, they will eventually enter the
    644                  * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
    645                  * In the latter case, the other side may slam the open phones
    646                  * at any time, in which case we will get an IPC_PHONE_SLAMMED
    647                  * phone.
    648                  */
    649                 if ((phone->state == IPC_PHONE_CONNECTED) ||
    650                     (phone->state == IPC_PHONE_SLAMMED)) {
    651                         mutex_unlock(&phone->lock);
    652                         ipc_phone_hangup(phone);
    653                         /*
    654                          * Now there may be one extra active call, which needs
    655                          * to be forgotten.
    656                          */
    657                         ipc_forget_all_active_calls();
    658                         goto restart;
    659                 }
    660 
    661                 /*
    662                  * If the hangup succeeded, it has sent a HANGUP message, the
    663                  * IPC is now in HUNGUP state, we wait for the reply to come
    664                  */
    665                 if (phone->state != IPC_PHONE_FREE) {
    666                         mutex_unlock(&phone->lock);
    667                         break;
    668                 }
    669 
    670                 mutex_unlock(&phone->lock);
    671         }
    672                
    673         /* Got into cleanup */
    674         if (i == IPC_MAX_PHONES)
    675                 return;
    676                
    677         call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
    678             SYNCH_FLAGS_NONE);
    679         ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
    680         ipc_call_free(call);
    681         goto restart;
    682 }
    683 
    684531/** Clean up all IPC communication of the current task.
    685532 *
     
    690537void ipc_cleanup(void)
    691538{
    692         /*
    693          * Mark the answerbox as inactive.
    694          *
    695          * The main purpose for doing this is to prevent any pending callback
    696          * connections from getting established beyond this point.
    697          */
    698         irq_spinlock_lock(&TASK->answerbox.lock, true);
    699         TASK->answerbox.active = false;
    700         irq_spinlock_unlock(&TASK->answerbox.lock, true);
    701 
    702539        /* Disconnect all our phones ('ipc_phone_hangup') */
    703         for (size_t i = 0; i < IPC_MAX_PHONES; i++)
     540        size_t i;
     541        for (i = 0; i < IPC_MAX_PHONES; i++)
    704542                ipc_phone_hangup(&TASK->phones[i]);
    705543       
     
    719557       
    720558        /* Answer all messages in 'calls' and 'dispatched_calls' queues */
    721         ipc_cleanup_call_list(&TASK->answerbox,
    722             &TASK->answerbox.dispatched_calls);
    723         ipc_cleanup_call_list(&TASK->answerbox, &TASK->answerbox.calls);
    724 
    725         ipc_forget_all_active_calls();
    726         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        }
    727606}
    728607
     
    736615        ipc_answerbox_slab = slab_cache_create("answerbox_t",
    737616            sizeof(answerbox_t), 0, NULL, NULL, 0);
    738 }
    739 
    740 
    741 static void ipc_print_call_list(list_t *list)
    742 {
    743         list_foreach(*list, cur) {
    744                 call_t *call = list_get_instance(cur, call_t, ab_link);
    745                
    746 #ifdef __32_BITS__
    747                 printf("%10p ", call);
    748 #endif
    749                
    750 #ifdef __64_BITS__
    751                 printf("%18p ", call);
    752 #endif
    753                
    754                 spinlock_lock(&call->forget_lock);
    755 
    756                 printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
    757                     " %-6" PRIun " %-6" PRIun " %-7x",
    758                     IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
    759                     IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
    760                     IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
    761                     call->flags);
    762 
    763                 if (call->forget) {
    764                         printf(" ? (call forgotten)\n");
    765                 } else {
    766                         printf(" %" PRIu64 " (%s)\n",
    767                             call->sender->taskid, call->sender->name);
    768                 }
    769 
    770                 spinlock_unlock(&call->forget_lock);
    771         }
    772617}
    773618
     
    843688       
    844689        printf(" --- incomming calls ---\n");
    845         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       
    846709        printf(" --- dispatched calls ---\n");
    847         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       
    848729        printf(" --- incoming answers ---\n");
    849         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        }
    850748       
    851749        irq_spinlock_unlock(&task->answerbox.lock, false);
Note: See TracChangeset for help on using the changeset viewer.