Changeset 58775d30 in mainline for kernel/generic/src/synch/futex.c


Ignore:
Timestamp:
2015-03-16T16:07:21Z (10 years ago)
Author:
Jan Vesely <jano.vesely@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
2003739
Parents:
6069061 (diff), 795e2bf (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.
Message:

Mainline changes

File:
1 edited

Legend:

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

    r6069061 r58775d30  
    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 found = false;
     294        pte_t *t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), true);
     295       
     296        if (t && PTE_VALID(t) && PTE_PRESENT(t)) {
     297                found = true;
     298                *paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
     299        }
     300       
     301        spinlock_unlock(&futex_ht_lock);
     302        page_table_unlock(AS, false);
     303       
     304        return found;
     305}
     306
     307/** Returns the futex cached in this task with the virtual address uaddr. */
     308static futex_t *find_cached_futex(uintptr_t uaddr)
     309{
     310        cht_read_lock();
     311       
     312        futex_t *futex;
     313        cht_link_t *futex_ptr_link = cht_find_lazy(&TASK->futexes->ht, &uaddr);
     314
     315        if (futex_ptr_link) {
     316                futex_ptr_t *futex_ptr
     317                        = member_to_inst(futex_ptr_link, futex_ptr_t, cht_link);
     318               
     319                futex = futex_ptr->futex;
     320        } else {
     321                futex = NULL;
     322        }
     323       
     324        cht_read_unlock();
     325       
     326        return futex;
     327}
     328
     329
     330/**
     331 * Returns a kernel futex for the physical address @a phys_addr and caches
     332 * it in this task under the virtual address @a uaddr (if not already cached).
     333 */
     334static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr)
     335{
     336        futex_t *futex = malloc(sizeof(futex_t), 0);
     337       
     338        /*
     339         * Find the futex object in the global futex table (or insert it
     340         * if it is not present).
     341         */
     342        spinlock_lock(&futex_ht_lock);
     343       
     344        link_t *fut_link = hash_table_find(&futex_ht, &phys_addr);
     345       
     346        if (fut_link) {
     347                free(futex);
     348                futex = member_to_inst(fut_link, futex_t, ht_link);
     349                futex_add_ref(futex);
     350        } else {
     351                futex_initialize(futex, phys_addr);
     352                hash_table_insert(&futex_ht, &phys_addr, &futex->ht_link);
     353        }
     354       
     355        spinlock_unlock(&futex_ht_lock);
     356       
     357        /*
     358         * Cache the link to the futex object for this task.
     359         */
     360        futex_ptr_t *fut_ptr = malloc(sizeof(futex_ptr_t), 0);
     361        cht_link_t *dup_link;
     362       
     363        fut_ptr->futex = futex;
     364        fut_ptr->uaddr = uaddr;
     365       
     366        cht_read_lock();
     367       
     368        /* Cache the mapping from the virtual address to the futex for this task. */
     369        if (cht_insert_unique(&TASK->futexes->ht, &fut_ptr->cht_link, &dup_link)) {
     370                spinlock_lock(&TASK->futexes->list_lock);
     371                list_append(&fut_ptr->all_link, &TASK->futexes->list);
     372                spinlock_unlock(&TASK->futexes->list_lock);
     373        } else {
     374                /* Another thread of this task beat us to it. Use that mapping instead.*/
     375                free(fut_ptr);
     376                futex_release_ref_locked(futex);
     377               
     378                futex_ptr_t *dup = member_to_inst(dup_link, futex_ptr_t, cht_link);
     379                futex = dup->futex;             
     380        }
     381
     382        cht_read_unlock();
     383       
     384        return futex;
    99385}
    100386
     
    109395sysarg_t sys_futex_sleep(uintptr_t uaddr)
    110396{
    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);
     397        futex_t *futex = get_futex(uaddr);
     398       
     399        if (!futex)
    123400                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);
    129 
    130 #ifdef CONFIG_UDEBUG
    131         udebug_stoppable_begin();
    132 #endif
    133         rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
    134 #ifdef CONFIG_UDEBUG
    135         udebug_stoppable_end();
    136 #endif
     401
     402        int rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
     403
    137404        return (sysarg_t) rc;
    138405}
     
    146413sysarg_t sys_futex_wakeup(uintptr_t uaddr)
    147414{
    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);
     415        futex_t *futex = get_futex(uaddr);
     416       
     417        if (futex) {
     418                waitq_wakeup(&futex->wq, WAKEUP_FIRST);
     419                return 0;
     420        } else {
    159421                return (sysarg_t) ENOENT;
    160422        }
    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 }
     423}
     424
    228425
    229426/** Compute hash index into futex hash table.
     
    268465}
    269466
    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, leaf_link, btree_node_t, node) {
    277                 unsigned int i;
    278                
    279                 for (i = 0; i < node->keys; i++) {
    280                         futex_t *ftx;
    281                         uintptr_t paddr = node->key[i];
    282                        
    283                         ftx = (futex_t *) node->value[i];
    284                         if (--ftx->refcount == 0)
    285                                 hash_table_remove(&futex_ht, &paddr, 1);
    286                 }
    287         }
    288        
    289         mutex_unlock(&TASK->futexes_lock);
    290         mutex_unlock(&futex_ht_lock);
     467/*
     468 * Operations of a task's CHT that caches mappings of futex user space
     469 * virtual addresses to kernel futex objects.
     470 */
     471
     472static size_t task_fut_ht_hash(const cht_link_t *link)
     473{
     474        const futex_ptr_t *fut_ptr = member_to_inst(link, futex_ptr_t, cht_link);
     475        return fut_ptr->uaddr;
     476}
     477
     478static size_t task_fut_ht_key_hash(void *key)
     479{
     480        return *(uintptr_t*)key;
     481}
     482
     483static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2)
     484{
     485        const futex_ptr_t *fut_ptr1 = member_to_inst(item1, futex_ptr_t, cht_link);
     486        const futex_ptr_t *fut_ptr2 = member_to_inst(item2, futex_ptr_t, cht_link);
     487       
     488        return fut_ptr1->uaddr == fut_ptr2->uaddr;
     489}
     490
     491static bool task_fut_ht_key_equal(void *key, const cht_link_t *item)
     492{
     493        const futex_ptr_t *fut_ptr = member_to_inst(item, futex_ptr_t, cht_link);
     494        uintptr_t uaddr = *(uintptr_t*)key;
     495       
     496        return fut_ptr->uaddr == uaddr;
    291497}
    292498
Note: See TracChangeset for help on using the changeset viewer.