Ignore:
File:
1 edited

Legend:

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

    rcd529c4 rc33f39f  
    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);
     75        spinlock_initialize(&call->forget_lock, "forget_lock");
     76        call->active = false;
     77        call->forget = false;
    7378        call->sender = TASK;
    7479        call->buffer = NULL;
     
    132137 * @param phone Initialized phone structure.
    133138 * @param box   Initialized answerbox structure.
    134  *
    135  */
    136 void ipc_phone_connect(phone_t *phone, answerbox_t *box)
    137 {
     139 * @return      True if the phone was connected, false otherwise.
     140 */
     141bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
     142{
     143        bool active;
     144
    138145        mutex_lock(&phone->lock);
    139        
    140         phone->state = IPC_PHONE_CONNECTED;
    141         phone->callee = box;
    142        
    143146        irq_spinlock_lock(&box->lock, true);
    144         list_append(&phone->link, &box->connected_phones);
     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
    145155        irq_spinlock_unlock(&box->lock, true);
    146        
    147156        mutex_unlock(&phone->lock);
     157
     158        return active;
    148159}
    149160
     
    167178 *
    168179 */
    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        
     180void _ipc_answer_free_call(call_t *call, bool selflocked)
     181{
    174182        /* Count sent answer */
    175183        irq_spinlock_lock(&TASK->lock, true);
    176184        TASK->ipc_info.answer_sent++;
    177185        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);
     203                }
     204        }
     205        spinlock_unlock(&call->forget_lock);
     206
     207        answerbox_t *callerbox = &call->sender->answerbox;
     208        bool do_lock = ((!selflocked) || (callerbox != &TASK->answerbox));
    178209       
    179210        call->flags |= IPC_CALL_ANSWERED;
    180211       
    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 
    188212        call->data.task_id = TASK->taskid;
    189213       
     
    191215                irq_spinlock_lock(&callerbox->lock, true);
    192216       
    193         list_append(&call->link, &callerbox->answers);
     217        list_append(&call->ab_link, &callerbox->answers);
    194218       
    195219        if (do_lock)
     
    209233        /* Remove from active box */
    210234        irq_spinlock_lock(&box->lock, true);
    211         list_remove(&call->link);
     235        list_remove(&call->ab_link);
    212236        irq_spinlock_unlock(&box->lock, true);
    213237       
     
    228252void ipc_backsend_err(phone_t *phone, call_t *call, sysarg_t err)
    229253{
     254        call->caller_phone = phone;
    230255        call->data.phone = phone;
    231256        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
    232262        IPC_SET_RETVAL(call->data, err);
    233263        _ipc_answer_free_call(call, false);
     
    250280        if (!(call->flags & IPC_CALL_FORWARDED)) {
    251281                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
    252290                call->data.phone = phone;
    253291                call->data.task_id = TASK->taskid;
     
    255293       
    256294        irq_spinlock_lock(&box->lock, true);
    257         list_append(&call->link, &box->calls);
     295        list_append(&call->ab_link, &box->calls);
    258296        irq_spinlock_unlock(&box->lock, true);
    259297       
     
    275313        if (phone->state != IPC_PHONE_CONNECTED) {
    276314                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 {
     315                if (!(call->flags & IPC_CALL_FORWARDED)) {
    281316                        if (phone->state == IPC_PHONE_HUNGUP)
    282317                                ipc_backsend_err(phone, call, EHANGUP);
     
    356391        TASK->ipc_info.forwarded++;
    357392        irq_spinlock_pass(&TASK->lock, &oldbox->lock);
    358         list_remove(&call->link);
     393        list_remove(&call->ab_link);
    359394        irq_spinlock_unlock(&oldbox->lock, true);
    360395       
    361396        if (mode & IPC_FF_ROUTE_FROM_ME) {
    362                 if (!call->caller_phone)
    363                         call->caller_phone = call->data.phone;
    364397                call->data.phone = newphone;
    365398                call->data.task_id = TASK->taskid;
     
    406439               
    407440                request = list_get_instance(list_first(&box->irq_notifs),
    408                     call_t, link);
    409                 list_remove(&request->link);
     441                    call_t, ab_link);
     442                list_remove(&request->ab_link);
    410443               
    411444                irq_spinlock_unlock(&box->irq_lock, false);
     
    416449                /* Handle asynchronous answers */
    417450                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);
     451                    call_t, ab_link);
     452                list_remove(&request->ab_link);
     453                atomic_dec(&request->caller_phone->active_calls);
    421454        } else if (!list_empty(&box->calls)) {
    422455                /* Count received call */
     
    425458                /* Handle requests */
    426459                request = list_get_instance(list_first(&box->calls),
    427                     call_t, link);
    428                 list_remove(&request->link);
     460                    call_t, ab_link);
     461                list_remove(&request->ab_link);
    429462               
    430463                /* Append request to dispatch queue */
    431                 list_append(&request->link, &box->dispatched_calls);
     464                list_append(&request->ab_link, &box->dispatched_calls);
    432465        } else {
    433466                /* This can happen regularly after ipc_cleanup */
     
    449482/** Answer all calls from list with EHANGUP answer.
    450483 *
     484 * @param box Answerbox with the list.
    451485 * @param lst Head of the list to be cleaned up.
    452  *
    453  */
    454 void ipc_cleanup_call_list(list_t *lst)
    455 {
     486 */
     487void ipc_cleanup_call_list(answerbox_t *box, list_t *lst)
     488{
     489        irq_spinlock_lock(&box->lock, true);
    456490        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                
     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;
    463499                IPC_SET_RETVAL(call->data, EHANGUP);
     500                answer_preprocess(call, &old);
    464501                _ipc_answer_free_call(call, true);
    465         }
     502
     503                irq_spinlock_lock(&box->lock, true);
     504        }
     505        irq_spinlock_unlock(&box->lock, true);
    466506}
    467507
     
    529569}
    530570
     571static void ipc_forget_all_active_calls(void)
     572{
     573        call_t *call;
     574
     575restart:
     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. */
     620static void ipc_wait_for_all_answered_calls(void)
     621{
     622        call_t *call;
     623        size_t i;
     624
     625restart:
     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
    531684/** Clean up all IPC communication of the current task.
    532685 *
     
    537690void ipc_cleanup(void)
    538691{
     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
    539702        /* Disconnect all our phones ('ipc_phone_hangup') */
    540         size_t i;
    541         for (i = 0; i < IPC_MAX_PHONES; i++)
     703        for (size_t i = 0; i < IPC_MAX_PHONES; i++)
    542704                ipc_phone_hangup(&TASK->phones[i]);
    543705       
     
    557719       
    558720        /* 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         }
     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();
    606727}
    607728
     
    615736        ipc_answerbox_slab = slab_cache_create("answerbox_t",
    616737            sizeof(answerbox_t), 0, NULL, NULL, 0);
     738}
     739
     740
     741static 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        }
    617772}
    618773
     
    688843       
    689844        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        
     845        ipc_print_call_list(&task->answerbox.calls);
    709846        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        
     847        ipc_print_call_list(&task->answerbox.dispatched_calls);
    729848        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         }
     849        ipc_print_call_list(&task->answerbox.answers);
    748850       
    749851        irq_spinlock_unlock(&task->answerbox.lock, false);
Note: See TracChangeset for help on using the changeset viewer.