Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 9e87562 in mainline


Ignore:
Timestamp:
2017-09-18T20:52:12Z (3 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
master
Children:
6abfd250
Parents:
e5f5ce0
Message:

Make all accesses to capabilites exclusive

This commit makes sure that all accesses to the capabilities array and other
metadata are protected by a mutex. This is necessary for future resizing of the
capabilities array.

Group task's capabilities by type so that it is possible to visit all
capabilities of the given type effectively.

Provide cap_publish() and cap_unpublish() to automate steps that make the
capability visible/invisible to userspace and insert/remove the capability from
the respective type list.

Location:
kernel/generic
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/include/cap/cap.h

    re5f5ce0 r9e87562  
    3838#include <typedefs.h>
    3939#include <ipc/ipc.h>
     40#include <adt/list.h>
     41#include <synch/mutex.h>
    4042
    4143#define MAX_CAPS  64
    42 
    43 #define for_each_cap(task, cap, type) \
    44         for (int i = 0, l = 1; i < MAX_CAPS && l; i++) \
    45                 for (cap_t *(cap) = cap_get((task), i, (type)); \
    46                     (cap) && !(l = 0); (cap) = NULL, l = 1)
    47 
    48 #define for_each_cap_current(cap, type) \
    49         for_each_cap(TASK, (cap), (type))
    5044
    5145typedef enum {
     
    5347        CAP_TYPE_ALLOCATED,
    5448        CAP_TYPE_PHONE,
    55         CAP_TYPE_IRQ
     49        CAP_TYPE_IRQ,
     50        CAP_TYPE_MAX
    5651} cap_type_t;
    5752
     
    6257        bool (* can_reclaim)(struct cap *);
    6358
     59        /* Link to the task's capabilities of the same type. */
     60        link_t link;
     61
    6462        /* The underlying kernel object. */
    6563        void *kobject;
    6664} cap_t;
    6765
     66typedef struct cap_info {
     67        mutex_t lock;
     68
     69        list_t type_list[CAP_TYPE_MAX];
     70
     71        cap_t *caps;
     72} cap_info_t;
     73
    6874struct task;
    6975
    70 void caps_task_alloc(struct task *);
    71 void caps_task_free(struct task *);
    72 void caps_task_init(struct task *);
     76extern void caps_task_alloc(struct task *);
     77extern void caps_task_free(struct task *);
     78extern void caps_task_init(struct task *);
     79extern bool caps_apply_to_all(struct task *, cap_type_t,
     80    bool (*)(cap_t *, void *), void *);
     81extern void caps_lock(struct task *);
     82extern void caps_unlock(struct task *);
    7383
    7484extern void cap_initialize(cap_t *, int);
    7585extern cap_t *cap_get(struct task *, int, cap_type_t);
    76 extern cap_t *cap_get_current(int, cap_type_t);
    7786extern int cap_alloc(struct task *);
     87extern void cap_publish(struct task *, int, cap_type_t, void *);
     88extern cap_t *cap_unpublish(struct task *, int, cap_type_t);
    7889extern void cap_free(struct task *, int);
    7990
  • kernel/generic/include/proc/task.h

    re5f5ce0 r9e87562  
    6060#include <abi/sysinfo.h>
    6161#include <arch.h>
     62#include <cap/cap.h>
    6263
    6364#define TASK                 THE->task
     
    9899
    99100        /** Capabilities */
    100         struct cap *caps;
     101        cap_info_t *cap_info;
    101102       
    102103        /* IPC stuff */
  • kernel/generic/src/cap/cap.c

    re5f5ce0 r9e87562  
    3535#include <cap/cap.h>
    3636#include <proc/task.h>
    37 #include <synch/spinlock.h>
     37#include <synch/mutex.h>
    3838#include <abi/errno.h>
    3939#include <mm/slab.h>
     40#include <adt/list.h>
    4041
    4142void cap_initialize(cap_t *cap, int handle)
     
    4445        cap->handle = handle;
    4546        cap->can_reclaim = NULL;
     47        link_initialize(&cap->link);
    4648}
    4749
    4850void caps_task_alloc(task_t *task)
    4951{
    50         task->caps = malloc(sizeof(cap_t) * MAX_CAPS, 0);
     52        task->cap_info = (cap_info_t *) malloc(sizeof(cap_info_t), 0);
     53        task->cap_info->caps = malloc(sizeof(cap_t) * MAX_CAPS, 0);
    5154}
    5255
    5356void caps_task_init(task_t *task)
    5457{
     58        mutex_initialize(&task->cap_info->lock, MUTEX_PASSIVE);
     59
     60        for (int i = 0; i < CAP_TYPE_MAX; i++)
     61                list_initialize(&task->cap_info->type_list[i]);
     62
    5563        for (int i = 0; i < MAX_CAPS; i++)
    56                 cap_initialize(&task->caps[i], i);
     64                cap_initialize(&task->cap_info->caps[i], i);
    5765}
    5866
    5967void caps_task_free(task_t *task)
    6068{
    61         free(task->caps);
     69        free(task->cap_info->caps);
     70        free(task->cap_info);
     71}
     72
     73bool caps_apply_to_all(task_t *task, cap_type_t type,
     74    bool (*cb)(cap_t *, void *), void *arg)
     75{
     76        bool done = true;
     77
     78        mutex_lock(&task->cap_info->lock);
     79        list_foreach_safe(task->cap_info->type_list[type], cur, next) {
     80                cap_t *cap = list_get_instance(cur, cap_t, link);
     81                done = cb(cap, arg);
     82                if (!done)
     83                        break;
     84        }
     85        mutex_unlock(&task->cap_info->lock);
     86
     87        return done;
     88}
     89
     90void caps_lock(task_t *task)
     91{
     92        mutex_lock(&task->cap_info->lock);
     93}
     94
     95void caps_unlock(task_t *task)
     96{
     97        mutex_unlock(&task->cap_info->lock);
    6298}
    6399
    64100cap_t *cap_get(task_t *task, int handle, cap_type_t type)
    65101{
     102        assert(mutex_locked(&task->cap_info->lock));
     103
    66104        if ((handle < 0) || (handle >= MAX_CAPS))
    67105                return NULL;
    68         if (task->caps[handle].type != type)
     106        if (task->cap_info->caps[handle].type != type)
    69107                return NULL;
    70         return &task->caps[handle];
    71 }
    72 
    73 cap_t *cap_get_current(int handle, cap_type_t type)
    74 {
    75         return cap_get(TASK, handle, type);
     108        return &task->cap_info->caps[handle];
    76109}
    77110
     
    80113        int handle;
    81114
    82         irq_spinlock_lock(&task->lock, true);
     115        mutex_lock(&task->cap_info->lock);
    83116        for (handle = 0; handle < MAX_CAPS; handle++) {
    84                 cap_t *cap = &task->caps[handle];
     117                cap_t *cap = &task->cap_info->caps[handle];
    85118                if (cap->type > CAP_TYPE_ALLOCATED) {
    86119                        if (cap->can_reclaim && cap->can_reclaim(cap))
     
    89122                if (cap->type == CAP_TYPE_INVALID) {
    90123                        cap->type = CAP_TYPE_ALLOCATED;
    91                         irq_spinlock_unlock(&task->lock, true);
     124                        mutex_unlock(&task->cap_info->lock);
    92125                        return handle;
    93126                }
    94127        }
    95         irq_spinlock_unlock(&task->lock, true);
     128        mutex_unlock(&task->cap_info->lock);
    96129
    97130        return ELIMIT;
     131}
     132
     133void cap_publish(task_t *task, int handle, cap_type_t type, void *kobject)
     134{
     135        mutex_lock(&task->cap_info->lock);
     136        cap_t *cap = cap_get(task, handle, CAP_TYPE_ALLOCATED);
     137        assert(cap);
     138        cap->type = type;
     139        cap->kobject = kobject;
     140        list_append(&cap->link, &task->cap_info->type_list[type]);
     141        mutex_unlock(&task->cap_info->lock);
     142}
     143
     144cap_t *cap_unpublish(task_t *task, int handle, cap_type_t type)
     145{
     146        cap_t *cap;
     147
     148        mutex_lock(&task->cap_info->lock);
     149        cap = cap_get(task, handle, type);
     150        if (cap) {
     151                list_remove(&cap->link);
     152                cap->type = CAP_TYPE_ALLOCATED;
     153        }
     154        mutex_unlock(&task->cap_info->lock);
     155
     156        return cap;
    98157}
    99158
     
    102161        assert(handle >= 0);
    103162        assert(handle < MAX_CAPS);
    104         assert(task->caps[handle].type == CAP_TYPE_ALLOCATED);
     163        assert(task->cap_info->caps[handle].type == CAP_TYPE_ALLOCATED);
    105164
    106         irq_spinlock_lock(&task->lock, true);
    107         cap_initialize(&task->caps[handle], handle);
    108         irq_spinlock_unlock(&task->lock, true);
     165        mutex_lock(&task->cap_info->lock);
     166        cap_initialize(&task->cap_info->caps[handle], handle);
     167        mutex_unlock(&task->cap_info->lock);
    109168}
    110169
  • kernel/generic/src/ipc/ipc.c

    re5f5ce0 r9e87562  
    710710                 * Nota bene: there may still be answers waiting for pick up.
    711711                 */
    712                 spinlock_unlock(&TASK->active_calls_lock);     
     712                spinlock_unlock(&TASK->active_calls_lock);
    713713                return;
    714714        }
     
    723723                 * call on the list.
    724724                 */
    725                 spinlock_unlock(&TASK->active_calls_lock);     
     725                spinlock_unlock(&TASK->active_calls_lock);
    726726                goto restart;
    727727        }
     
    730730
    731731        goto restart;
     732}
     733
     734static bool phone_cap_wait_cb(cap_t *cap, void *arg)
     735{
     736        phone_t *phone = (phone_t *) cap->kobject;
     737        bool *restart = (bool *) arg;
     738
     739        mutex_lock(&phone->lock);
     740        if ((phone->state == IPC_PHONE_HUNGUP) &&
     741            (atomic_get(&phone->active_calls) == 0)) {
     742                phone->state = IPC_PHONE_FREE;
     743                phone->callee = NULL;
     744        }
     745
     746        /*
     747         * We might have had some IPC_PHONE_CONNECTING phones at the beginning
     748         * of ipc_cleanup(). Depending on whether these were forgotten or
     749         * answered, they will eventually enter the IPC_PHONE_FREE or
     750         * IPC_PHONE_CONNECTED states, respectively.  In the latter case, the
     751         * other side may slam the open phones at any time, in which case we
     752         * will get an IPC_PHONE_SLAMMED phone.
     753         */
     754        if ((phone->state == IPC_PHONE_CONNECTED) ||
     755            (phone->state == IPC_PHONE_SLAMMED)) {
     756                mutex_unlock(&phone->lock);
     757                ipc_phone_hangup(phone);
     758                /*
     759                 * Now there may be one extra active call, which needs to be
     760                 * forgotten.
     761                 */
     762                ipc_forget_all_active_calls();
     763                *restart = true;
     764                return false;
     765        }
     766
     767        /*
     768         * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
     769         * in HUNGUP state, we wait for the reply to come
     770         */
     771        if (phone->state != IPC_PHONE_FREE) {
     772                mutex_unlock(&phone->lock);
     773                return false;
     774        }
     775
     776        mutex_unlock(&phone->lock);
     777        return true;
    732778}
    733779
     
    736782{
    737783        call_t *call;
    738         bool all_clean;
     784        bool restart;
    739785
    740786restart:
     
    743789         * Locking is needed as there may be connection handshakes in progress.
    744790         */
    745         all_clean = true;
    746         for_each_cap_current(cap, CAP_TYPE_PHONE) {
    747                 phone_t *phone = (phone_t *) cap->kobject;
    748 
    749                 mutex_lock(&phone->lock);
    750                 if ((phone->state == IPC_PHONE_HUNGUP) &&
    751                     (atomic_get(&phone->active_calls) == 0)) {
    752                         phone->state = IPC_PHONE_FREE;
    753                         phone->callee = NULL;
    754                 }
    755 
    756                 /*
    757                  * We might have had some IPC_PHONE_CONNECTING phones at the
    758                  * beginning of ipc_cleanup(). Depending on whether these were
    759                  * forgotten or answered, they will eventually enter the
    760                  * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
    761                  * In the latter case, the other side may slam the open phones
    762                  * at any time, in which case we will get an IPC_PHONE_SLAMMED
    763                  * phone.
    764                  */
    765                 if ((phone->state == IPC_PHONE_CONNECTED) ||
    766                     (phone->state == IPC_PHONE_SLAMMED)) {
    767                         mutex_unlock(&phone->lock);
    768                         ipc_phone_hangup(phone);
    769                         /*
    770                          * Now there may be one extra active call, which needs
    771                          * to be forgotten.
    772                          */
    773                         ipc_forget_all_active_calls();
    774                         goto restart;
    775                 }
    776 
    777                 /*
    778                  * If the hangup succeeded, it has sent a HANGUP message, the
    779                  * IPC is now in HUNGUP state, we wait for the reply to come
    780                  */
    781                 if (phone->state != IPC_PHONE_FREE) {
    782                         mutex_unlock(&phone->lock);
    783                         all_clean = false;
    784                         break;
    785                 }
    786 
    787                 mutex_unlock(&phone->lock);
    788         }
    789                
    790         /* Got into cleanup */
    791         if (all_clean)
     791        restart = false;
     792        if (caps_apply_to_all(TASK, CAP_TYPE_PHONE, phone_cap_wait_cb,
     793            &restart)) {
     794                /* Got into cleanup */
    792795                return;
    793                
     796        }
     797        if (restart)
     798                goto restart;
     799       
    794800        call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
    795801            SYNCH_FLAGS_NONE);
     
    800806        ipc_call_free(call);
    801807        goto restart;
     808}
     809
     810static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
     811{
     812        phone_t *phone = (phone_t *) cap->kobject;
     813        ipc_phone_hangup(phone);
     814        return true;
     815}
     816
     817static bool irq_cap_cleanup_cb(cap_t *cap, void *arg)
     818{
     819        ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
     820        return true;
    802821}
    803822
     
    821840
    822841        /* Disconnect all our phones ('ipc_phone_hangup') */
    823         for_each_cap_current(cap, CAP_TYPE_PHONE) {
    824                 phone_t *phone = (phone_t *) cap->kobject;
    825                 ipc_phone_hangup(phone);
    826         }
     842        caps_apply_to_all(TASK, CAP_TYPE_PHONE, phone_cap_cleanup_cb, NULL);
    827843       
    828844        /* Unsubscribe from any event notifications. */
     
    830846       
    831847        /* Disconnect all connected IRQs */
    832         for_each_cap_current(cap, CAP_TYPE_IRQ) {
    833                 ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
    834         }
     848        caps_apply_to_all(TASK, CAP_TYPE_IRQ, irq_cap_cleanup_cb, NULL);
    835849       
    836850        /* Disconnect all phones connected to our regular answerbox */
     
    896910}
    897911
     912static bool print_task_phone_cb(cap_t *cap, void *arg)
     913{
     914        phone_t *phone = (phone_t *) cap->kobject;
     915
     916        mutex_lock(&phone->lock);
     917        if (phone->state != IPC_PHONE_FREE) {
     918                printf("%-11d %7" PRIun " ", cap->handle,
     919                    atomic_get(&phone->active_calls));
     920               
     921                switch (phone->state) {
     922                case IPC_PHONE_CONNECTING:
     923                        printf("connecting");
     924                        break;
     925                case IPC_PHONE_CONNECTED:
     926                        printf("connected to %" PRIu64 " (%s)",
     927                            phone->callee->task->taskid,
     928                            phone->callee->task->name);
     929                        break;
     930                case IPC_PHONE_SLAMMED:
     931                        printf("slammed by %p", phone->callee);
     932                        break;
     933                case IPC_PHONE_HUNGUP:
     934                        printf("hung up by %p", phone->callee);
     935                        break;
     936                default:
     937                        break;
     938                }
     939               
     940                printf("\n");
     941        }
     942        mutex_unlock(&phone->lock);
     943
     944        return true;
     945}
     946
    898947/** List answerbox contents.
    899948 *
     
    905954        irq_spinlock_lock(&tasks_lock, true);
    906955        task_t *task = task_find_by_id(taskid);
    907        
    908956        if (!task) {
    909957                irq_spinlock_unlock(&tasks_lock, true);
    910958                return;
    911959        }
    912        
    913         /* Hand-over-hand locking */
    914         irq_spinlock_exchange(&tasks_lock, &task->lock);
     960        task_hold(task);
     961        irq_spinlock_unlock(&tasks_lock, true);
    915962       
    916963        printf("[phone cap] [calls] [state\n");
    917964       
    918         for_each_cap(task, cap, CAP_TYPE_PHONE) {
    919                 phone_t *phone = (phone_t *) cap->kobject;
    920        
    921                 if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
    922                         printf("%-11d (mutex busy)\n", cap->handle);
    923                         continue;
    924                 }
    925                
    926                 if (phone->state != IPC_PHONE_FREE) {
    927                         printf("%-11d %7" PRIun " ", cap->handle,
    928                             atomic_get(&phone->active_calls));
    929                        
    930                         switch (phone->state) {
    931                         case IPC_PHONE_CONNECTING:
    932                                 printf("connecting");
    933                                 break;
    934                         case IPC_PHONE_CONNECTED:
    935                                 printf("connected to %" PRIu64 " (%s)",
    936                                     phone->callee->task->taskid,
    937                                     phone->callee->task->name);
    938                                 break;
    939                         case IPC_PHONE_SLAMMED:
    940                                 printf("slammed by %p", phone->callee);
    941                                 break;
    942                         case IPC_PHONE_HUNGUP:
    943                                 printf("hung up by %p", phone->callee);
    944                                 break;
    945                         default:
    946                                 break;
    947                         }
    948                        
    949                         printf("\n");
    950                 }
    951                
    952                 mutex_unlock(&phone->lock);
    953         }
    954        
     965        caps_apply_to_all(task, CAP_TYPE_PHONE, print_task_phone_cb, NULL);
     966       
     967        irq_spinlock_lock(&task->lock, true);
    955968        irq_spinlock_lock(&task->answerbox.lock, false);
    956969       
     
    974987        irq_spinlock_unlock(&task->answerbox.lock, false);
    975988        irq_spinlock_unlock(&task->lock, true);
     989
     990        task_release(task);
    976991}
    977992
  • kernel/generic/src/ipc/ipcrsc.c

    re5f5ce0 r9e87562  
    175175phone_t *phone_get(task_t *task, int handle)
    176176{
     177        phone_t *phone;
     178
     179        caps_lock(task);
    177180        cap_t *cap = cap_get(task, handle, CAP_TYPE_PHONE);
     181        phone = (phone_t *) cap->kobject;
     182        caps_unlock(task);
    178183        if (!cap)
    179184                return NULL;
    180185       
    181         return (phone_t *) cap->kobject;
     186        return phone;
    182187}
    183188
     
    217222                phone->state = IPC_PHONE_CONNECTING;
    218223               
    219                 irq_spinlock_lock(&task->lock, true);
     224                // FIXME: phase this out eventually
     225                mutex_lock(&task->cap_info->lock);
    220226                cap_t *cap = cap_get(task, handle, CAP_TYPE_ALLOCATED);
    221                 cap->type = CAP_TYPE_PHONE;
    222                 cap->kobject = (void *) phone;
    223227                cap->can_reclaim = phone_can_reclaim;
    224                 irq_spinlock_unlock(&task->lock, true);
     228                mutex_unlock(&task->cap_info->lock);
     229
     230                cap_publish(task, handle, CAP_TYPE_PHONE, phone);
    225231        }
    226232       
     
    237243void phone_dealloc(int handle)
    238244{
    239         irq_spinlock_lock(&TASK->lock, true);
    240         cap_t *cap = cap_get_current(handle, CAP_TYPE_PHONE);
     245        cap_t *cap = cap_unpublish(TASK, handle, CAP_TYPE_PHONE);
    241246        assert(cap);
    242         cap->type = CAP_TYPE_ALLOCATED;
    243         irq_spinlock_unlock(&TASK->lock, true);
    244247       
    245248        phone_t *phone = (phone_t *) cap->kobject;
  • kernel/generic/src/ipc/irq.c

    re5f5ce0 r9e87562  
    314314        }
    315315       
    316         cap_t *cap = cap_get_current(handle, CAP_TYPE_ALLOCATED);
    317         assert(cap);
    318        
    319316        irq_initialize(irq);
    320317        irq->inr = inr;
     
    327324        irq->notif_cfg.counter = 0;
    328325       
    329         cap->kobject = (void *) irq;
    330        
    331326        /*
    332          * Insert the IRQ structure into the uspace IRQ hash table and retype
    333          * the capability. By retyping the capability inside the critical
    334          * section, we make sure another thread cannot attempt to unregister the
    335          * IRQ before it is inserted into the hash table.
     327         * Insert the IRQ structure into the uspace IRQ hash table.
    336328         */
    337329        irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
    338330        irq_spinlock_lock(&irq->lock, false);
    339331       
    340         cap->type = CAP_TYPE_IRQ;
    341332        hash_table_insert(&irq_uspace_hash_table, key, &irq->link);
    342333       
    343334        irq_spinlock_unlock(&irq->lock, false);
    344335        irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
     336
     337        cap_publish(TASK, handle, CAP_TYPE_IRQ, irq);
    345338       
    346339        return handle;
     
    357350int ipc_irq_unsubscribe(answerbox_t *box, int handle)
    358351{
    359         irq_spinlock_lock(&TASK->lock, true);
    360         cap_t *cap = cap_get_current(handle, CAP_TYPE_IRQ);
    361         if (!cap) {
    362                 irq_spinlock_unlock(&TASK->lock, true);
     352        cap_t *cap = cap_unpublish(TASK, handle, CAP_TYPE_IRQ);
     353        if (!cap)
    363354                return ENOENT;
    364         }
    365         /* Make sure only one thread can win the race to unsubscribe. */
    366         cap->type = CAP_TYPE_ALLOCATED;
    367         irq_spinlock_unlock(&TASK->lock, true);
    368355       
    369356        irq_t *irq = (irq_t *) cap->kobject;
  • kernel/generic/src/proc/task.c

    re5f5ce0 r9e87562  
    616616        if (*additional)
    617617                printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
    618                     "%9" PRIua, task->taskid, ucycles, usuffix, kcycles,
     618                    "%9" PRIua "\n", task->taskid, ucycles, usuffix, kcycles,
    619619                    ksuffix, atomic_get(&task->refcount));
    620620        else
     
    622622                    task->taskid, task->name, task->container, task, task->as);
    623623#endif
    624        
    625         if (*additional) {
    626                 for_each_cap(task, cap, CAP_TYPE_PHONE) {
    627                         phone_t *phone = (phone_t *) cap->kobject;
    628                         if (phone->callee)
    629                                 printf(" %d:%p", cap->handle, phone->callee);
    630                 }
    631                 printf("\n");
    632         }
    633624       
    634625        irq_spinlock_unlock(&task->lock, false);
Note: See TracChangeset for help on using the changeset viewer.