Changeset 67f11a0 in mainline for kernel/generic/src/ipc/ipc.c


Ignore:
Timestamp:
2018-03-15T17:40:20Z (7 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
530f2de, e9e4068
Parents:
30f1a25 (diff), a36f442 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Jakub Jermar <jakub@…> (2018-03-15 17:25:56)
git-committer:
Jakub Jermar <jakub@…> (2018-03-15 17:40:20)
Message:

Merge branch 'noreclaimers'

This commit removes the left-over from the original IPC phone life-cycle
management in which phones were freed lazily during an attempt to
allocate a new phone when the allocator found a hung-up phone with zero
active calls. This mechanism is the reason why kernel objects had to
have the reclaim method. This commit changes the behavior in that phones
are deallocated with their last reference. At the same time it makes
sure that each active call has its own reference on the phone.

This change also makes sure that each connected phone has a capability
via which it can be hung up by the user or cleaned up in ipc_cleanup().
A special mode for phone_alloc() was introduced that allows calls such
as IPC_M_CONNECT_ME_TO and IPC_M_CONNECT_TO_ME to allocate an
unpublished capability and publish it only when the phone is
successfully connected. This fixes a nasty race condition when the user
destroys the capability before the phone is connected and then this
phone becomes essentially invisible for ipc_cleanup().

Last but not least, ipc_cleanup() was slightly streamlined in that it
now knows for how many calls it has to wait from the answerbox's active
call count.

File:
1 edited

Legend:

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

    r30f1a25 r67f11a0  
    6363static void ipc_forget_call(call_t *);
    6464
    65 /** Open channel that is assigned automatically to new tasks */
    66 answerbox_t *ipc_phone_0 = NULL;
     65/** Answerbox that new tasks are automatically connected to */
     66answerbox_t *ipc_box_0 = NULL;
    6767
    6868static slab_cache_t *call_cache;
     
    147147        list_initialize(&box->answers);
    148148        list_initialize(&box->irq_notifs);
     149        atomic_set(&box->active_calls, 0);
    149150        box->task = task;
    150151}
     
    160161bool ipc_phone_connect(phone_t *phone, answerbox_t *box)
    161162{
    162         bool active;
     163        bool connected;
    163164
    164165        mutex_lock(&phone->lock);
    165166        irq_spinlock_lock(&box->lock, true);
    166167
    167         active = box->active;
    168         if (active) {
     168        connected = box->active && (phone->state == IPC_PHONE_CONNECTING);
     169        if (connected) {
    169170                phone->state = IPC_PHONE_CONNECTED;
    170171                phone->callee = box;
     
    176177        mutex_unlock(&phone->lock);
    177178
    178         if (!active) {
     179        if (!connected) {
    179180                /* We still have phone->kobject's reference; drop it */
    180181                kobject_put(phone->kobject);
    181182        }
    182183
    183         return active;
     184        return connected;
    184185}
    185186
     
    350351        } else {
    351352                atomic_inc(&phone->active_calls);
     353                if (call->callerbox)
     354                        atomic_inc(&call->callerbox->active_calls);
     355                else
     356                        atomic_inc(&caller->answerbox.active_calls);
     357                kobject_add_ref(phone->kobject);
    352358                call->sender = caller;
    353359                call->active = true;
     
    439445/** Disconnect phone from answerbox.
    440446 *
    441  * This call leaves the phone in the HUNGUP state. The change to 'free' is done
    442  * lazily later.
     447 * This call leaves the phone in the hung-up state. The phone is destroyed when
     448 * its last active call is answered and there are no references to it.
    443449 *
    444450 * @param phone Phone structure to be hung up.
     
    563569                list_remove(&request->ab_link);
    564570                atomic_dec(&request->caller_phone->active_calls);
     571                atomic_dec(&box->active_calls);
     572                kobject_put(request->caller_phone->kobject);
    565573        } else if (!list_empty(&box->calls)) {
    566574                /* Count received call */
     
    707715
    708716        atomic_dec(&call->caller_phone->active_calls);
     717        atomic_dec(&TASK->answerbox.active_calls);
     718        kobject_put(call->caller_phone->kobject);
    709719
    710720        SYSIPC_OP(request_forget, call);
     
    746756}
    747757
    748 static bool phone_cap_wait_cb(cap_t *cap, void *arg)
    749 {
    750         phone_t *phone = cap->kobject->phone;
    751         bool *restart = (bool *) arg;
    752 
    753         mutex_lock(&phone->lock);
    754         if ((phone->state == IPC_PHONE_HUNGUP) &&
    755             (atomic_get(&phone->active_calls) == 0)) {
    756                 phone->state = IPC_PHONE_FREE;
    757                 phone->callee = NULL;
    758         }
    759 
    760         /*
    761          * We might have had some IPC_PHONE_CONNECTING phones at the beginning
    762          * of ipc_cleanup(). Depending on whether these were forgotten or
    763          * answered, they will eventually enter the IPC_PHONE_FREE or
    764          * IPC_PHONE_CONNECTED states, respectively.  In the latter case, the
    765          * other side may slam the open phones at any time, in which case we
    766          * will get an IPC_PHONE_SLAMMED phone.
    767          */
    768         if ((phone->state == IPC_PHONE_CONNECTED) ||
    769             (phone->state == IPC_PHONE_SLAMMED)) {
    770                 mutex_unlock(&phone->lock);
    771                 ipc_phone_hangup(phone);
    772                 /*
    773                  * Now there may be one extra active call, which needs to be
    774                  * forgotten.
    775                  */
    776                 ipc_forget_all_active_calls();
    777                 *restart = true;
    778                 return false;
    779         }
    780 
    781         /*
    782          * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
    783          * in HUNGUP state, we wait for the reply to come
    784          */
    785         if (phone->state != IPC_PHONE_FREE) {
    786                 mutex_unlock(&phone->lock);
    787                 return false;
    788         }
    789 
    790         mutex_unlock(&phone->lock);
    791         return true;
    792 }
    793 
    794 /** Wait for all answers to asynchronous calls to arrive. */
    795 static void ipc_wait_for_all_answered_calls(void)
    796 {
    797         call_t *call;
    798         bool restart;
    799 
    800 restart:
    801         /*
    802          * Go through all phones, until they are all free.
    803          * Locking is needed as there may be connection handshakes in progress.
    804          */
    805         restart = false;
    806         if (caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
    807             phone_cap_wait_cb, &restart)) {
    808                 /* Got into cleanup */
    809                 return;
    810         }
    811         if (restart)
    812                 goto restart;
    813 
    814         call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
    815             SYNCH_FLAGS_NONE);
    816         assert(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
    817 
    818         SYSIPC_OP(answer_process, call);
    819 
    820         kobject_put(call->kobject);
    821         goto restart;
    822 }
    823 
    824758static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
    825759{
     
    830764        cap_free(cap->task, cap->handle);
    831765        return true;
     766}
     767
     768/** Wait for all answers to asynchronous calls to arrive. */
     769static void ipc_wait_for_all_answered_calls(void)
     770{
     771        while (atomic_get(&TASK->answerbox.active_calls) != 0) {
     772                call_t *call = ipc_wait_for_call(&TASK->answerbox,
     773                    SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
     774                assert(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF));
     775
     776                SYSIPC_OP(answer_process, call);
     777
     778                kobject_put(call->kobject);
     779
     780                /*
     781                 * Now there may be some new phones and new hangup calls to
     782                 * immediately forget.
     783                 */
     784                caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
     785                    phone_cap_cleanup_cb, NULL);
     786                ipc_forget_all_active_calls();
     787        }
    832788}
    833789
     
    869825        irq_spinlock_unlock(&TASK->answerbox.lock, true);
    870826
    871         /* Disconnect all our phones ('ipc_phone_hangup') */
     827        /* Hangup all phones and destroy all phone capabilities */
    872828        caps_apply_to_kobject_type(TASK, KOBJECT_TYPE_PHONE,
    873829            phone_cap_cleanup_cb, NULL);
    874830
    875         /* Unsubscribe from any event notifications. */
     831        /* Unsubscribe from any event notifications */
    876832        event_cleanup_answerbox(&TASK->answerbox);
    877833
     
    899855        ipc_forget_all_active_calls();
    900856        ipc_wait_for_all_answered_calls();
     857
     858        assert(atomic_get(&TASK->answerbox.active_calls) == 0);
    901859}
    902860
     
    968926                        break;
    969927                case IPC_PHONE_HUNGUP:
    970                         printf("hung up by %p", phone->callee);
     928                        printf("hung up to %p", phone->callee);
    971929                        break;
    972930                default:
     
    1004962        irq_spinlock_lock(&task->lock, true);
    1005963        irq_spinlock_lock(&task->answerbox.lock, false);
     964
     965        printf("Active calls: %" PRIun "\n",
     966            atomic_get(&task->answerbox.active_calls));
    1006967
    1007968#ifdef __32_BITS__
Note: See TracChangeset for help on using the changeset viewer.