Changeset 669f3d32 in mainline


Ignore:
Timestamp:
2012-11-20T18:26:14Z (11 years ago)
Author:
Adam Hraska <adam.hraska+hos@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
c28413a9
Parents:
04d66804
Message:

Adapted the kernel futex subsystem to use CHT.

Location:
kernel/generic
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/include/proc/task.h

    r04d66804 r669f3d32  
    4343#include <synch/mutex.h>
    4444#include <synch/futex.h>
     45#include <synch/workqueue.h>
    4546#include <adt/avl.h>
    4647#include <adt/btree.h>
     48#include <adt/cht.h>
    4749#include <adt/list.h>
    4850#include <security/cap.h>
     
    127129        task_arch_t arch;
    128130       
    129         /**
    130          * Serializes access to the B+tree of task's futexes. This mutex is
    131          * independent on the task spinlock.
    132          */
    133         mutex_t futexes_lock;
    134         /** B+tree of futexes referenced by this task. */
    135         btree_t futexes;
     131        /** Serializes access to futex_list (independent of the task spinlock). */
     132        mutex_t futex_list_lock;
     133        /** List of all futexes accesses by this task. */
     134        list_t futex_list;
     135        /** CHT mapping virtual addresses of futex variables to futex objects. */
     136        struct futex_cache {
     137                work_t destroy_work;
     138                cht_t ht;
     139        } *futexes;
    136140       
    137141        /** Accumulated accounting. */
  • kernel/generic/include/synch/futex.h

    r04d66804 r669f3d32  
    5555extern sysarg_t sys_futex_wakeup(uintptr_t);
    5656
    57 extern void futex_cleanup(void);
     57extern void futex_task_cleanup(void);
     58extern void futex_task_init(struct task *);
     59extern void futex_task_deinit(struct task *);
    5860
    5961#endif
  • kernel/generic/src/proc/task.c

    r04d66804 r669f3d32  
    4141#include <mm/slab.h>
    4242#include <atomic.h>
     43#include <synch/futex.h>
    4344#include <synch/spinlock.h>
    4445#include <synch/waitq.h>
     
    153154       
    154155        irq_spinlock_initialize(&task->lock, "task_t_lock");
    155         mutex_initialize(&task->futexes_lock, MUTEX_PASSIVE);
    156156       
    157157        list_initialize(&task->threads);
     
    165165        spinlock_initialize(&task->active_calls_lock, "active_calls_lock");
    166166        list_initialize(&task->active_calls);
     167       
     168        mutex_initialize(&task->futex_list_lock, MUTEX_PASSIVE);
     169        list_initialize(&task->futex_list);
    167170       
    168171#ifdef CONFIG_UDEBUG
     
    221224                (void) ipc_phone_connect(&task->phones[0], ipc_phone_0);
    222225       
    223         btree_create(&task->futexes);
     226        futex_task_init(task);
    224227       
    225228        /*
     
    262265         * Free up dynamically allocated state.
    263266         */
    264         btree_destroy(&task->futexes);
     267        futex_task_deinit(task);
    265268       
    266269        /*
  • kernel/generic/src/proc/thread.c

    r04d66804 r669f3d32  
    518518                         */
    519519                        ipc_cleanup();
    520                         futex_cleanup();
     520                        futex_task_cleanup();
    521521                        LOG("Cleanup of task %" PRIu64" completed.", TASK->taskid);
    522522                }
  • kernel/generic/src/synch/futex.c

    r04d66804 r669f3d32  
    11/*
    22 * Copyright (c) 2006 Jakub Jermar
     3 * Copyright (c) 2012 Adam Hraska
    34 * All rights reserved.
    45 *
     
    3940#include <synch/mutex.h>
    4041#include <synch/spinlock.h>
     42#include <synch/rcu.h>
    4143#include <mm/frame.h>
    4244#include <mm/page.h>
     
    4648#include <genarch/mm/page_pt.h>
    4749#include <genarch/mm/page_ht.h>
     50#include <adt/cht.h>
    4851#include <adt/hash_table.h>
    4952#include <adt/list.h>
     
    5255#include <panic.h>
    5356#include <errno.h>
    54 #include <print.h>
    5557
    5658#define FUTEX_HT_SIZE   1024    /* keep it a power of 2 */
    5759
    58 static void futex_initialize(futex_t *futex);
    59 
    60 static futex_t *futex_find(uintptr_t paddr);
     60/** Task specific pointer to a global kernel futex object. */
     61typedef struct futex_ptr {
     62        /** CHT link. */
     63        cht_link_t cht_link;
     64        /** List of all futex pointers used by the task. */
     65        link_t all_link;
     66        /** Kernel futex object. */
     67        futex_t *futex;
     68        /** User space virtual address of the futex variable in the task. */
     69        uintptr_t uaddr;
     70} futex_ptr_t;
     71
     72
     73static void destroy_task_cache(work_t *work);
     74
     75static void futex_initialize(futex_t *futex, uintptr_t paddr);
     76static void futex_add_ref(futex_t *futex);
     77static void futex_release_ref(futex_t *futex);
     78static void futex_release_ref_locked(futex_t *futex);
     79
     80static futex_t *get_futex(uintptr_t uaddr);
     81static futex_t *find_cached_futex(uintptr_t uaddr);
     82static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr);
     83static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *phys_addr);
     84
    6185static size_t futex_ht_hash(sysarg_t *key);
    6286static bool futex_ht_compare(sysarg_t *key, size_t keys, link_t *item);
    6387static void futex_ht_remove_callback(link_t *item);
    6488
    65 /**
    66  * Mutex protecting global futex hash table.
    67  * It is also used to serialize access to all futex_t structures.
    68  * Must be acquired before the task futex B+tree lock.
     89static size_t task_fut_ht_hash(const cht_link_t *link);
     90static size_t task_fut_ht_key_hash(void *key);
     91static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2);
     92static bool task_fut_ht_key_equal(void *key, const cht_link_t *item);
     93
     94
     95/** Mutex protecting the global futex hash table.
     96 *
     97 * Acquire task specific TASK->futex_list_lock before this mutex.
    6998 */
    7099static mutex_t futex_ht_lock;
    71100
    72 /** Futex hash table. */
     101/** Global kernel futex hash table. Lock futex_ht_lock before accessing.
     102 *
     103 * Physical address of the futex variable is the lookup key.
     104 */
    73105static hash_table_t futex_ht;
    74106
    75 /** Futex hash table operations. */
     107/** Global kernel futex hash table operations. */
    76108static hash_table_operations_t futex_ht_ops = {
    77109        .hash = futex_ht_hash,
     
    80112};
    81113
     114/** Task futex cache CHT operations. */
     115static cht_ops_t task_futex_ht_ops = {
     116        .hash = task_fut_ht_hash,
     117        .key_hash = task_fut_ht_key_hash,
     118        .equal = task_fut_ht_equal,
     119        .key_equal = task_fut_ht_key_equal,
     120        .remove_callback = NULL
     121};
     122
    82123/** Initialize futex subsystem. */
    83124void futex_init(void)
     
    87128}
    88129
    89 /** Initialize kernel futex structure.
    90  *
    91  * @param futex         Kernel futex structure.
    92  */
    93 void futex_initialize(futex_t *futex)
     130/** Initializes the futex structures for the new task. */
     131void futex_task_init(struct task *task)
     132{
     133        task->futexes = malloc(sizeof(struct futex_cache), 0);
     134       
     135        /* todo: make it block until mem is available */
     136        bool ret = cht_create(&task->futexes->ht, 0, 0, 0, &task_futex_ht_ops);
     137        ASSERT(ret);
     138}
     139
     140/** Destroys the futex structures for the dying task. */
     141void futex_task_deinit(struct task *task)
     142{
     143        /* Interrupts are disabled so we must not block (cannot run cht_destroy). */
     144        if (interrupts_disabled()) {
     145                /* Invoke the blocking cht_destroy in the background. */
     146                workq_global_enqueue_noblock(&task->futexes->destroy_work,
     147                        destroy_task_cache);
     148        } else {
     149                /* We can block. Invoke cht_destroy in this thread. */
     150                destroy_task_cache(&task->futexes->destroy_work);
     151        }
     152}
     153
     154/** Deallocates a task's CHT futex cache (must already be empty). */
     155static void destroy_task_cache(work_t *work)
     156{
     157        struct futex_cache *cache =
     158                member_to_inst(work, struct futex_cache, destroy_work);
     159       
     160        cht_destroy(&cache->ht);
     161        free(cache);
     162}
     163
     164/** Remove references from futexes known to the current task. */
     165void futex_task_cleanup(void)
     166{
     167        /* All threads of this task have terminated. This is the last thread. */
     168        mutex_lock(&TASK->futex_list_lock);
     169       
     170        list_foreach_safe(TASK->futex_list, cur_link, next_link) {
     171                futex_ptr_t *fut_ptr = member_to_inst(cur_link, futex_ptr_t, cht_link);
     172               
     173                /*
     174                 * The function is free to free the futex. All other threads of this
     175                 * task have already terminated, so they have also definitely
     176                 * exited their CHT futex cache protecting rcu reader sections.
     177                 * Moreover release_ref() only frees the futex if this is the
     178                 * last task referencing the futex. Therefore, only threads
     179                 * of this task may have referenced the futex if it is to be freed.
     180                 */
     181                futex_release_ref_locked(fut_ptr->futex);
     182               
     183                /*
     184                 * No need to remove the futex_ptr from the CHT cache of this task
     185                 * because futex_task_deinit() will destroy the CHT shortly.
     186                 */
     187                list_remove(cur_link);
     188                free(fut_ptr);
     189        }
     190       
     191        mutex_unlock(&TASK->futex_list_lock);
     192}
     193
     194
     195/** Initialize the kernel futex structure.
     196 *
     197 * @param futex Kernel futex structure.
     198 * @param paddr Physical address of the futex variable.
     199 */
     200static void futex_initialize(futex_t *futex, uintptr_t paddr)
    94201{
    95202        waitq_initialize(&futex->wq);
    96203        link_initialize(&futex->ht_link);
    97         futex->paddr = 0;
     204        futex->paddr = paddr;
    98205        futex->refcount = 1;
     206}
     207
     208/** Increments the counter of tasks referencing the futex. */
     209static void futex_add_ref(futex_t *futex)
     210{
     211        ASSERT(mutex_locked(&futex_ht_lock));
     212        ASSERT(0 < futex->refcount);
     213        ++futex->refcount;
     214}
     215
     216/** Decrements the counter of tasks referencing the futex. May free the futex.*/
     217static void futex_release_ref(futex_t *futex)
     218{
     219        ASSERT(mutex_locked(&futex_ht_lock));
     220        ASSERT(0 < futex->refcount);
     221       
     222        --futex->refcount;
     223       
     224        if (0 == futex->refcount) {
     225                hash_table_remove(&futex_ht, &futex->paddr, 1);
     226        }
     227}
     228
     229/** Decrements the counter of tasks referencing the futex. May free the futex.*/
     230static void futex_release_ref_locked(futex_t *futex)
     231{
     232        mutex_lock(&futex_ht_lock);
     233        futex_release_ref(futex);
     234        mutex_unlock(&futex_ht_lock);
     235}
     236
     237/** Returns a futex for the virtual address @a uaddr (or creates one). */
     238static futex_t *get_futex(uintptr_t uaddr)
     239{
     240        futex_t *futex = find_cached_futex(uaddr);
     241       
     242        if (futex)
     243                return futex;
     244
     245        uintptr_t paddr;
     246
     247        if (!find_futex_paddr(uaddr, &paddr))
     248                return 0;
     249
     250        return get_and_cache_futex(paddr, uaddr);
     251}
     252
     253
     254/** Finds the physical address of the futex variable. */
     255static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *paddr)
     256{
     257        page_table_lock(AS, true);
     258
     259        bool found = false;
     260        pte_t *t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), false);
     261       
     262        if (t && PTE_VALID(t) && PTE_PRESENT(t)) {
     263                found = true;
     264                *paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
     265        }
     266       
     267        page_table_unlock(AS, true);
     268       
     269        return found;
     270}
     271
     272/** Returns the futex cached in this task with the virtual address uaddr. */
     273static futex_t *find_cached_futex(uintptr_t uaddr)
     274{
     275        cht_read_lock();
     276       
     277        futex_t *futex;
     278        cht_link_t *futex_ptr_link = cht_find(&TASK->futexes->ht, &uaddr);
     279
     280        if (futex_ptr_link) {
     281                futex_ptr_t *futex_ptr
     282                        = member_to_inst(futex_ptr_link, futex_ptr_t, cht_link);
     283               
     284                futex = futex_ptr->futex;
     285        } else {
     286                futex = NULL;
     287        }
     288       
     289        cht_read_unlock();
     290       
     291        return futex;
     292}
     293
     294
     295/**
     296 * Returns a kernel futex for the physical address @a phys_addr and caches
     297 * it in this task under the virtual address @a uaddr (if not already cached).
     298 */
     299static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr)
     300{
     301        futex_t *futex = malloc(sizeof(futex_t), 0);
     302       
     303        /*
     304         * Find the futex object in the global futex table (or insert it
     305         * if it is not present).
     306         */
     307        mutex_lock(&futex_ht_lock);
     308       
     309        link_t *fut_link = hash_table_find(&futex_ht, &phys_addr);
     310       
     311        if (fut_link) {
     312                free(futex);
     313                futex = member_to_inst(fut_link, futex_t, ht_link);
     314                futex_add_ref(futex);
     315        } else {
     316                futex_initialize(futex, phys_addr);
     317                hash_table_insert(&futex_ht, &phys_addr, &futex->ht_link);
     318        }
     319       
     320        mutex_unlock(&futex_ht_lock);
     321       
     322        /*
     323         * Cache the link to the futex object for this task.
     324         */
     325        futex_ptr_t *fut_ptr = malloc(sizeof(futex_ptr_t), 0);
     326        cht_link_t *dup_link;
     327       
     328        fut_ptr->futex = futex;
     329        fut_ptr->uaddr = uaddr;
     330       
     331        cht_read_lock();
     332       
     333        /* Cache the mapping from the virtual address to the futex for this task. */
     334        if (cht_insert_unique(&TASK->futexes->ht, &fut_ptr->cht_link, &dup_link)) {
     335                mutex_lock(&TASK->futex_list_lock);
     336                list_append(&fut_ptr->all_link, &TASK->futex_list);
     337                mutex_unlock(&TASK->futex_list_lock);
     338        } else {
     339                /* Another thread of this task beat us to it. Use that mapping instead.*/
     340                free(fut_ptr);
     341                futex_release_ref_locked(futex);
     342               
     343                futex_ptr_t *dup = member_to_inst(dup_link, futex_ptr_t, cht_link);
     344                futex = dup->futex;             
     345        }
     346
     347        cht_read_unlock();
     348       
     349        return futex;
    99350}
    100351
     
    109360sysarg_t sys_futex_sleep(uintptr_t uaddr)
    110361{
    111         futex_t *futex;
    112         uintptr_t paddr;
    113         pte_t *t;
    114         int rc;
    115        
    116         /*
    117          * Find physical address of futex counter.
    118          */
    119         page_table_lock(AS, true);
    120         t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), false);
    121         if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
    122                 page_table_unlock(AS, true);
     362        futex_t *futex = get_futex(uaddr);
     363       
     364        if (!futex)
    123365                return (sysarg_t) ENOENT;
    124         }
    125         paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
    126         page_table_unlock(AS, true);
    127        
    128         futex = futex_find(paddr);
    129366
    130367#ifdef CONFIG_UDEBUG
    131368        udebug_stoppable_begin();
    132369#endif
    133         rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
     370        int rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
    134371#ifdef CONFIG_UDEBUG
    135372        udebug_stoppable_end();
     
    146383sysarg_t sys_futex_wakeup(uintptr_t uaddr)
    147384{
    148         futex_t *futex;
    149         uintptr_t paddr;
    150         pte_t *t;
    151        
    152         /*
    153          * Find physical address of futex counter.
    154          */
    155         page_table_lock(AS, true);
    156         t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), false);
    157         if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
    158                 page_table_unlock(AS, true);
     385        futex_t *futex = get_futex(uaddr);
     386       
     387        if (futex) {
     388                waitq_wakeup(&futex->wq, WAKEUP_FIRST);
     389                return 0;
     390        } else {
    159391                return (sysarg_t) ENOENT;
    160392        }
    161         paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
    162         page_table_unlock(AS, true);
    163        
    164         futex = futex_find(paddr);
    165                
    166         waitq_wakeup(&futex->wq, WAKEUP_FIRST);
    167        
    168         return 0;
    169 }
    170 
    171 /** Find kernel address of the futex structure corresponding to paddr.
    172  *
    173  * If the structure does not exist already, a new one is created.
    174  *
    175  * @param paddr         Physical address of the userspace futex counter.
    176  *
    177  * @return              Address of the kernel futex structure.
    178  */
    179 futex_t *futex_find(uintptr_t paddr)
    180 {
    181         link_t *item;
    182         futex_t *futex;
    183         btree_node_t *leaf;
    184        
    185         /*
    186          * Find the respective futex structure
    187          * or allocate new one if it does not exist already.
    188          */
    189         mutex_lock(&futex_ht_lock);
    190         item = hash_table_find(&futex_ht, &paddr);
    191         if (item) {
    192                 futex = hash_table_get_instance(item, futex_t, ht_link);
    193 
    194                 /*
    195                  * See if the current task knows this futex.
    196                  */
    197                 mutex_lock(&TASK->futexes_lock);
    198                 if (!btree_search(&TASK->futexes, paddr, &leaf)) {
    199                         /*
    200                          * The futex is new to the current task.
    201                          * Upgrade its reference count and put it to the
    202                          * current task's B+tree of known futexes.
    203                          */
    204                         futex->refcount++;
    205                         btree_insert(&TASK->futexes, paddr, futex, leaf);
    206                 }
    207                 mutex_unlock(&TASK->futexes_lock);
    208         } else {
    209                 futex = (futex_t *) malloc(sizeof(futex_t), 0);
    210                 futex_initialize(futex);
    211                 futex->paddr = paddr;
    212                 hash_table_insert(&futex_ht, &paddr, &futex->ht_link);
    213                        
    214                 /*
    215                  * This is the first task referencing the futex.
    216                  * It can be directly inserted into its
    217                  * B+tree of known futexes.
    218                  */
    219                 mutex_lock(&TASK->futexes_lock);
    220                 btree_insert(&TASK->futexes, paddr, futex, NULL);
    221                 mutex_unlock(&TASK->futexes_lock);
    222                
    223         }
    224         mutex_unlock(&futex_ht_lock);
    225        
    226         return futex;
    227 }
     393}
     394
    228395
    229396/** Compute hash index into futex hash table.
     
    268435}
    269436
    270 /** Remove references from futexes known to the current task. */
    271 void futex_cleanup(void)
    272 {
    273         mutex_lock(&futex_ht_lock);
    274         mutex_lock(&TASK->futexes_lock);
    275 
    276         list_foreach(TASK->futexes.leaf_list, cur) {
    277                 btree_node_t *node;
    278                 unsigned int i;
    279                
    280                 node = list_get_instance(cur, btree_node_t, leaf_link);
    281                 for (i = 0; i < node->keys; i++) {
    282                         futex_t *ftx;
    283                         uintptr_t paddr = node->key[i];
    284                        
    285                         ftx = (futex_t *) node->value[i];
    286                         if (--ftx->refcount == 0)
    287                                 hash_table_remove(&futex_ht, &paddr, 1);
    288                 }
    289         }
    290        
    291         mutex_unlock(&TASK->futexes_lock);
    292         mutex_unlock(&futex_ht_lock);
    293 }
     437/*
     438 * Operations of a task's CHT that caches mappings of futex user space
     439 * virtual addresses to kernel futex objects.
     440 */
     441
     442static size_t task_fut_ht_hash(const cht_link_t *link)
     443{
     444        const futex_ptr_t *fut_ptr = member_to_inst(link, futex_ptr_t, cht_link);
     445        return fut_ptr->uaddr;
     446}
     447
     448static size_t task_fut_ht_key_hash(void *key)
     449{
     450        return *(uintptr_t*)key;
     451}
     452
     453static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2)
     454{
     455        const futex_ptr_t *fut_ptr1 = member_to_inst(item1, futex_ptr_t, cht_link);
     456        const futex_ptr_t *fut_ptr2 = member_to_inst(item2, futex_ptr_t, cht_link);
     457       
     458        return fut_ptr1->uaddr == fut_ptr2->uaddr;
     459}
     460
     461static bool task_fut_ht_key_equal(void *key, const cht_link_t *item)
     462{
     463        const futex_ptr_t *fut_ptr = member_to_inst(item, futex_ptr_t, cht_link);
     464        uintptr_t uaddr = *(uintptr_t*)key;
     465       
     466        return fut_ptr->uaddr == uaddr;
     467}
     468
    294469
    295470/** @}
Note: See TracChangeset for help on using the changeset viewer.