Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/synch/futex.c

    r9d58539 r38dc82d  
    11/*
    22 * Copyright (c) 2006 Jakub Jermar
     3 * Copyright (c) 2012 Adam Hraska
    34 * All rights reserved.
    45 *
     
    3435 * @file
    3536 * @brief       Kernel backend for futexes.
     37 *
     38 * Kernel futex objects are stored in a global hash table futex_ht
     39 * where the physical address of the futex variable (futex_t.paddr)
     40 * is used as the lookup key. As a result multiple address spaces
     41 * may share the same futex variable.
     42 *
     43 * A kernel futex object is created the first time a task accesses
     44 * the futex (having a futex variable at a physical address not
     45 * encountered before). Futex object's lifetime is governed by
     46 * a reference count that represents the number of all the different
     47 * user space virtual addresses from all tasks that map to the
     48 * physical address of the futex variable. A futex object is freed
     49 * when the last task having accessed the futex exits.
     50 *
     51 * Each task keeps track of the futex objects it accessed in a list
     52 * of pointers (futex_ptr_t, task->futex_list) to the different futex
     53 * objects.
     54 *
     55 * To speed up translation of futex variables' virtual addresses
     56 * to their physical addresses, futex pointers accessed by the
     57 * task are furthermore stored in a concurrent hash table (CHT,
     58 * task->futexes->ht). A single lookup without locks or accesses
     59 * to the page table translates a futex variable's virtual address
     60 * into its futex kernel object.
    3661 */
    3762
     
    3964#include <synch/mutex.h>
    4065#include <synch/spinlock.h>
     66#include <synch/rcu.h>
    4167#include <mm/frame.h>
    4268#include <mm/page.h>
     
    4672#include <genarch/mm/page_pt.h>
    4773#include <genarch/mm/page_ht.h>
     74#include <adt/cht.h>
    4875#include <adt/hash_table.h>
    4976#include <adt/list.h>
     
    5279#include <panic.h>
    5380#include <errno.h>
    54 #include <print.h>
    5581
    5682#define FUTEX_HT_SIZE   1024    /* keep it a power of 2 */
    5783
    58 static void futex_initialize(futex_t *futex);
    59 
    60 static futex_t *futex_find(uintptr_t paddr);
     84/** Task specific pointer to a global kernel futex object. */
     85typedef struct futex_ptr {
     86        /** CHT link. */
     87        cht_link_t cht_link;
     88        /** List of all futex pointers used by the task. */
     89        link_t all_link;
     90        /** Kernel futex object. */
     91        futex_t *futex;
     92        /** User space virtual address of the futex variable in the task. */
     93        uintptr_t uaddr;
     94} futex_ptr_t;
     95
     96
     97static void destroy_task_cache(work_t *work);
     98
     99static void futex_initialize(futex_t *futex, uintptr_t paddr);
     100static void futex_add_ref(futex_t *futex);
     101static void futex_release_ref(futex_t *futex);
     102static void futex_release_ref_locked(futex_t *futex);
     103
     104static futex_t *get_futex(uintptr_t uaddr);
     105static futex_t *find_cached_futex(uintptr_t uaddr);
     106static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr);
     107static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *phys_addr);
     108
    61109static size_t futex_ht_hash(sysarg_t *key);
    62110static bool futex_ht_compare(sysarg_t *key, size_t keys, link_t *item);
    63111static void futex_ht_remove_callback(link_t *item);
    64112
    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.
    69  */
    70 static mutex_t futex_ht_lock;
    71 
    72 /** Futex hash table. */
     113static size_t task_fut_ht_hash(const cht_link_t *link);
     114static size_t task_fut_ht_key_hash(void *key);
     115static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2);
     116static bool task_fut_ht_key_equal(void *key, const cht_link_t *item);
     117
     118
     119/** Mutex protecting the global futex hash table.
     120 *
     121 * Acquire task specific TASK->futex_list_lock before this mutex.
     122 */
     123SPINLOCK_STATIC_INITIALIZE_NAME(futex_ht_lock, "futex-ht-lock");
     124
     125/** Global kernel futex hash table. Lock futex_ht_lock before accessing.
     126 *
     127 * Physical address of the futex variable is the lookup key.
     128 */
    73129static hash_table_t futex_ht;
    74130
    75 /** Futex hash table operations. */
     131/** Global kernel futex hash table operations. */
    76132static hash_table_operations_t futex_ht_ops = {
    77133        .hash = futex_ht_hash,
     
    80136};
    81137
     138/** Task futex cache CHT operations. */
     139static cht_ops_t task_futex_ht_ops = {
     140        .hash = task_fut_ht_hash,
     141        .key_hash = task_fut_ht_key_hash,
     142        .equal = task_fut_ht_equal,
     143        .key_equal = task_fut_ht_key_equal,
     144        .remove_callback = NULL
     145};
     146
    82147/** Initialize futex subsystem. */
    83148void futex_init(void)
    84149{
    85         mutex_initialize(&futex_ht_lock, MUTEX_PASSIVE);
    86150        hash_table_create(&futex_ht, FUTEX_HT_SIZE, 1, &futex_ht_ops);
    87151}
    88152
    89 /** Initialize kernel futex structure.
    90  *
    91  * @param futex         Kernel futex structure.
    92  */
    93 void futex_initialize(futex_t *futex)
     153/** Initializes the futex structures for the new task. */
     154void futex_task_init(struct task *task)
     155{
     156        task->futexes = malloc(sizeof(struct futex_cache), 0);
     157       
     158        cht_create(&task->futexes->ht, 0, 0, 0, true, &task_futex_ht_ops);
     159       
     160        list_initialize(&task->futexes->list);
     161        spinlock_initialize(&task->futexes->list_lock, "futex-list-lock");
     162}
     163
     164/** Destroys the futex structures for the dying task. */
     165void futex_task_deinit(task_t *task)
     166{
     167        /* Interrupts are disabled so we must not block (cannot run cht_destroy). */
     168        if (interrupts_disabled()) {
     169                /* Invoke the blocking cht_destroy in the background. */
     170                workq_global_enqueue_noblock(&task->futexes->destroy_work,
     171                        destroy_task_cache);
     172        } else {
     173                /* We can block. Invoke cht_destroy in this thread. */
     174                destroy_task_cache(&task->futexes->destroy_work);
     175        }
     176}
     177
     178/** Deallocates a task's CHT futex cache (must already be empty). */
     179static void destroy_task_cache(work_t *work)
     180{
     181        struct futex_cache *cache =
     182                member_to_inst(work, struct futex_cache, destroy_work);
     183       
     184        /*
     185         * Destroy the cache before manually freeing items of the cache in case
     186         * table resize is in progress.
     187         */
     188        cht_destroy_unsafe(&cache->ht);
     189       
     190        /* Manually free futex_ptr cache items. */
     191        list_foreach_safe(cache->list, cur_link, next_link) {
     192                futex_ptr_t *fut_ptr = member_to_inst(cur_link, futex_ptr_t, all_link);
     193
     194                list_remove(cur_link);
     195                free(fut_ptr);
     196        }
     197       
     198        free(cache);
     199}
     200
     201/** Remove references from futexes known to the current task. */
     202void futex_task_cleanup(void)
     203{
     204        struct futex_cache *futexes = TASK->futexes;
     205       
     206        /* All threads of this task have terminated. This is the last thread. */
     207        spinlock_lock(&futexes->list_lock);
     208       
     209        list_foreach_safe(futexes->list, cur_link, next_link) {
     210                futex_ptr_t *fut_ptr = member_to_inst(cur_link, futex_ptr_t, all_link);
     211
     212                /*
     213                 * The function is free to free the futex. All other threads of this
     214                 * task have already terminated, so they have also definitely
     215                 * exited their CHT futex cache protecting rcu reader sections.
     216                 * Moreover release_ref() only frees the futex if this is the
     217                 * last task referencing the futex. Therefore, only threads
     218                 * of this task may have referenced the futex if it is to be freed.
     219                 */
     220                futex_release_ref_locked(fut_ptr->futex);
     221        }
     222       
     223        spinlock_unlock(&futexes->list_lock);
     224}
     225
     226
     227/** Initialize the kernel futex structure.
     228 *
     229 * @param futex Kernel futex structure.
     230 * @param paddr Physical address of the futex variable.
     231 */
     232static void futex_initialize(futex_t *futex, uintptr_t paddr)
    94233{
    95234        waitq_initialize(&futex->wq);
    96235        link_initialize(&futex->ht_link);
    97         futex->paddr = 0;
     236        futex->paddr = paddr;
    98237        futex->refcount = 1;
     238}
     239
     240/** Increments the counter of tasks referencing the futex. */
     241static void futex_add_ref(futex_t *futex)
     242{
     243        ASSERT(spinlock_locked(&futex_ht_lock));
     244        ASSERT(0 < futex->refcount);
     245        ++futex->refcount;
     246}
     247
     248/** Decrements the counter of tasks referencing the futex. May free the futex.*/
     249static void futex_release_ref(futex_t *futex)
     250{
     251        ASSERT(spinlock_locked(&futex_ht_lock));
     252        ASSERT(0 < futex->refcount);
     253       
     254        --futex->refcount;
     255       
     256        if (0 == futex->refcount) {
     257                hash_table_remove(&futex_ht, &futex->paddr, 1);
     258        }
     259}
     260
     261/** Decrements the counter of tasks referencing the futex. May free the futex.*/
     262static void futex_release_ref_locked(futex_t *futex)
     263{
     264        spinlock_lock(&futex_ht_lock);
     265        futex_release_ref(futex);
     266        spinlock_unlock(&futex_ht_lock);
     267}
     268
     269/** Returns a futex for the virtual address @a uaddr (or creates one). */
     270static futex_t *get_futex(uintptr_t uaddr)
     271{
     272        futex_t *futex = find_cached_futex(uaddr);
     273       
     274        if (futex)
     275                return futex;
     276
     277        uintptr_t paddr;
     278
     279        if (!find_futex_paddr(uaddr, &paddr)) {
     280                return 0;
     281        }
     282
     283        return get_and_cache_futex(paddr, uaddr);
     284}
     285
     286
     287/** Finds the physical address of the futex variable. */
     288static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *paddr)
     289{
     290        page_table_lock(AS, false);
     291        spinlock_lock(&futex_ht_lock);
     292
     293        bool success = false;
     294
     295        pte_t t;
     296        bool found;
     297
     298        found = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), true, &t);
     299        if (found && PTE_VALID(&t) && PTE_PRESENT(&t)) {
     300                success = true;
     301                *paddr = PTE_GET_FRAME(&t) +
     302                    (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
     303        }
     304       
     305        spinlock_unlock(&futex_ht_lock);
     306        page_table_unlock(AS, false);
     307       
     308        return success;
     309}
     310
     311/** Returns the futex cached in this task with the virtual address uaddr. */
     312static futex_t *find_cached_futex(uintptr_t uaddr)
     313{
     314        cht_read_lock();
     315       
     316        futex_t *futex;
     317        cht_link_t *futex_ptr_link = cht_find_lazy(&TASK->futexes->ht, &uaddr);
     318
     319        if (futex_ptr_link) {
     320                futex_ptr_t *futex_ptr
     321                        = member_to_inst(futex_ptr_link, futex_ptr_t, cht_link);
     322               
     323                futex = futex_ptr->futex;
     324        } else {
     325                futex = NULL;
     326        }
     327       
     328        cht_read_unlock();
     329       
     330        return futex;
     331}
     332
     333
     334/**
     335 * Returns a kernel futex for the physical address @a phys_addr and caches
     336 * it in this task under the virtual address @a uaddr (if not already cached).
     337 */
     338static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr)
     339{
     340        futex_t *futex = malloc(sizeof(futex_t), 0);
     341       
     342        /*
     343         * Find the futex object in the global futex table (or insert it
     344         * if it is not present).
     345         */
     346        spinlock_lock(&futex_ht_lock);
     347       
     348        link_t *fut_link = hash_table_find(&futex_ht, &phys_addr);
     349       
     350        if (fut_link) {
     351                free(futex);
     352                futex = member_to_inst(fut_link, futex_t, ht_link);
     353                futex_add_ref(futex);
     354        } else {
     355                futex_initialize(futex, phys_addr);
     356                hash_table_insert(&futex_ht, &phys_addr, &futex->ht_link);
     357        }
     358       
     359        spinlock_unlock(&futex_ht_lock);
     360       
     361        /*
     362         * Cache the link to the futex object for this task.
     363         */
     364        futex_ptr_t *fut_ptr = malloc(sizeof(futex_ptr_t), 0);
     365        cht_link_t *dup_link;
     366       
     367        fut_ptr->futex = futex;
     368        fut_ptr->uaddr = uaddr;
     369       
     370        cht_read_lock();
     371       
     372        /* Cache the mapping from the virtual address to the futex for this task. */
     373        if (cht_insert_unique(&TASK->futexes->ht, &fut_ptr->cht_link, &dup_link)) {
     374                spinlock_lock(&TASK->futexes->list_lock);
     375                list_append(&fut_ptr->all_link, &TASK->futexes->list);
     376                spinlock_unlock(&TASK->futexes->list_lock);
     377        } else {
     378                /* Another thread of this task beat us to it. Use that mapping instead.*/
     379                free(fut_ptr);
     380                futex_release_ref_locked(futex);
     381               
     382                futex_ptr_t *dup = member_to_inst(dup_link, futex_ptr_t, cht_link);
     383                futex = dup->futex;             
     384        }
     385
     386        cht_read_unlock();
     387       
     388        return futex;
    99389}
    100390
     
    109399sysarg_t sys_futex_sleep(uintptr_t uaddr)
    110400{
    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);
     401        futex_t *futex = get_futex(uaddr);
     402       
     403        if (!futex)
    123404                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);
    129405
    130406#ifdef CONFIG_UDEBUG
    131407        udebug_stoppable_begin();
    132408#endif
    133         rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
     409
     410        int rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
     411
    134412#ifdef CONFIG_UDEBUG
    135413        udebug_stoppable_end();
    136414#endif
     415
    137416        return (sysarg_t) rc;
    138417}
     
    146425sysarg_t sys_futex_wakeup(uintptr_t uaddr)
    147426{
    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);
     427        futex_t *futex = get_futex(uaddr);
     428       
     429        if (futex) {
     430                waitq_wakeup(&futex->wq, WAKEUP_FIRST);
     431                return 0;
     432        } else {
    159433                return (sysarg_t) ENOENT;
    160434        }
    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 }
     435}
     436
    228437
    229438/** Compute hash index into futex hash table.
     
    268477}
    269478
    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);
     479/*
     480 * Operations of a task's CHT that caches mappings of futex user space
     481 * virtual addresses to kernel futex objects.
     482 */
     483
     484static size_t task_fut_ht_hash(const cht_link_t *link)
     485{
     486        const futex_ptr_t *fut_ptr = member_to_inst(link, futex_ptr_t, cht_link);
     487        return fut_ptr->uaddr;
     488}
     489
     490static size_t task_fut_ht_key_hash(void *key)
     491{
     492        return *(uintptr_t*)key;
     493}
     494
     495static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2)
     496{
     497        const futex_ptr_t *fut_ptr1 = member_to_inst(item1, futex_ptr_t, cht_link);
     498        const futex_ptr_t *fut_ptr2 = member_to_inst(item2, futex_ptr_t, cht_link);
     499       
     500        return fut_ptr1->uaddr == fut_ptr2->uaddr;
     501}
     502
     503static bool task_fut_ht_key_equal(void *key, const cht_link_t *item)
     504{
     505        const futex_ptr_t *fut_ptr = member_to_inst(item, futex_ptr_t, cht_link);
     506        uintptr_t uaddr = *(uintptr_t*)key;
     507       
     508        return fut_ptr->uaddr == uaddr;
    293509}
    294510
Note: See TracChangeset for help on using the changeset viewer.