source: mainline/kernel/generic/src/synch/futex.c@ aafed15

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since aafed15 was aafed15, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Declare malloc() etc in standard <stdlib.h> rather than <mm/slab.h>

  • Property mode set to 100644
File size: 9.9 KB
RevLine 
[9aa72b4]1/*
[df4ed85]2 * Copyright (c) 2006 Jakub Jermar
[669f3d32]3 * Copyright (c) 2012 Adam Hraska
[9aa72b4]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
[e88eb48]30/** @addtogroup kernel_sync
[b45c443]31 * @{
32 */
33
[9aa72b4]34/**
[b45c443]35 * @file
[cf26ba9]36 * @brief Kernel backend for futexes.
[1b20da0]37 *
38 * Kernel futex objects are stored in a global hash table futex_ht
[ba368a6]39 * where the physical address of the futex variable (futex_t.paddr)
[1b20da0]40 * is used as the lookup key. As a result multiple address spaces
41 * may share the same futex variable.
42 *
[ba368a6]43 * A kernel futex object is created the first time a task accesses
[1b20da0]44 * the futex (having a futex variable at a physical address not
[ba368a6]45 * encountered before). Futex object's lifetime is governed by
46 * a reference count that represents the number of all the different
[ef4218f]47 * tasks that reference the futex variable. A futex object is freed
[ba368a6]48 * when the last task having accessed the futex exits.
[1b20da0]49 *
[ba368a6]50 * Each task keeps track of the futex objects it accessed in a list
[1b20da0]51 * of pointers (futex_ptr_t, task->futex_list) to the different futex
[ba368a6]52 * objects.
[9aa72b4]53 */
54
[63e27ef]55#include <assert.h>
[9aa72b4]56#include <synch/futex.h>
[ee42e43]57#include <synch/mutex.h>
[303c94c]58#include <synch/spinlock.h>
[9aa72b4]59#include <mm/frame.h>
60#include <mm/page.h>
[aafed15]61#include <stdlib.h>
[303c94c]62#include <proc/thread.h>
[4fded58]63#include <proc/task.h>
[9aa72b4]64#include <genarch/mm/page_pt.h>
65#include <genarch/mm/page_ht.h>
[82cbf8c6]66#include <adt/hash.h>
[9aa72b4]67#include <adt/hash_table.h>
68#include <adt/list.h>
69#include <arch.h>
70#include <align.h>
71#include <panic.h>
72#include <errno.h>
73
[669f3d32]74/** Task specific pointer to a global kernel futex object. */
75typedef struct futex_ptr {
[ef4218f]76 /** Link for the list of all futex pointers used by a task. */
77 link_t task_link;
[669f3d32]78 /** Kernel futex object. */
79 futex_t *futex;
80} futex_ptr_t;
81
82static void futex_initialize(futex_t *futex, uintptr_t paddr);
83static void futex_add_ref(futex_t *futex);
84static void futex_release_ref(futex_t *futex);
85static void futex_release_ref_locked(futex_t *futex);
86
87static futex_t *get_futex(uintptr_t uaddr);
88static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *phys_addr);
[9aa72b4]89
[82cbf8c6]90static size_t futex_ht_hash(const ht_link_t *item);
91static size_t futex_ht_key_hash(void *key);
92static bool futex_ht_key_equal(void *key, const ht_link_t *item);
93static void futex_ht_remove_callback(ht_link_t *item);
[9aa72b4]94
[669f3d32]95/** Mutex protecting the global futex hash table.
[1b20da0]96 *
[669f3d32]97 * Acquire task specific TASK->futex_list_lock before this mutex.
[4fded58]98 */
[207e8880]99SPINLOCK_STATIC_INITIALIZE_NAME(futex_ht_lock, "futex-ht-lock");
[9aa72b4]100
[669f3d32]101/** Global kernel futex hash table. Lock futex_ht_lock before accessing.
[1b20da0]102 *
[669f3d32]103 * Physical address of the futex variable is the lookup key.
104 */
[9aa72b4]105static hash_table_t futex_ht;
106
[669f3d32]107/** Global kernel futex hash table operations. */
[82cbf8c6]108static hash_table_ops_t futex_ht_ops = {
[9aa72b4]109 .hash = futex_ht_hash,
[82cbf8c6]110 .key_hash = futex_ht_key_hash,
111 .key_equal = futex_ht_key_equal,
[9aa72b4]112 .remove_callback = futex_ht_remove_callback
113};
114
115/** Initialize futex subsystem. */
116void futex_init(void)
117{
[82cbf8c6]118 hash_table_create(&futex_ht, 0, 0, &futex_ht_ops);
[9aa72b4]119}
120
[669f3d32]121/** Initializes the futex structures for the new task. */
122void futex_task_init(struct task *task)
123{
[947ab77e]124 list_initialize(&task->futex_list);
125 spinlock_initialize(&task->futex_list_lock, "futex-list-lock");
[669f3d32]126}
127
128/** Remove references from futexes known to the current task. */
129void futex_task_cleanup(void)
130{
131 /* All threads of this task have terminated. This is the last thread. */
[947ab77e]132 spinlock_lock(&TASK->futex_list_lock);
[a35b458]133
[947ab77e]134 list_foreach_safe(TASK->futex_list, cur_link, next_link) {
[ef4218f]135 futex_ptr_t *futex_ptr = member_to_inst(cur_link, futex_ptr_t,
136 task_link);
[3ac5086]137
[ef4218f]138 futex_release_ref_locked(futex_ptr->futex);
139 free(futex_ptr);
[669f3d32]140 }
[a35b458]141
[947ab77e]142 spinlock_unlock(&TASK->futex_list_lock);
[669f3d32]143}
144
145/** Initialize the kernel futex structure.
[9aa72b4]146 *
[669f3d32]147 * @param futex Kernel futex structure.
148 * @param paddr Physical address of the futex variable.
[9aa72b4]149 */
[669f3d32]150static void futex_initialize(futex_t *futex, uintptr_t paddr)
[9aa72b4]151{
152 waitq_initialize(&futex->wq);
[669f3d32]153 futex->paddr = paddr;
[4fded58]154 futex->refcount = 1;
[9aa72b4]155}
156
[669f3d32]157/** Increments the counter of tasks referencing the futex. */
158static void futex_add_ref(futex_t *futex)
159{
[63e27ef]160 assert(spinlock_locked(&futex_ht_lock));
[ef4218f]161 assert(futex->refcount > 0);
[669f3d32]162 ++futex->refcount;
163}
164
165/** Decrements the counter of tasks referencing the futex. May free the futex.*/
166static void futex_release_ref(futex_t *futex)
167{
[63e27ef]168 assert(spinlock_locked(&futex_ht_lock));
[ef4218f]169 assert(futex->refcount > 0);
[a35b458]170
[669f3d32]171 --futex->refcount;
[a35b458]172
[ef4218f]173 if (futex->refcount == 0)
[82cbf8c6]174 hash_table_remove(&futex_ht, &futex->paddr);
[669f3d32]175}
176
177/** Decrements the counter of tasks referencing the futex. May free the futex.*/
178static void futex_release_ref_locked(futex_t *futex)
179{
[207e8880]180 spinlock_lock(&futex_ht_lock);
[669f3d32]181 futex_release_ref(futex);
[207e8880]182 spinlock_unlock(&futex_ht_lock);
[669f3d32]183}
184
185/** Returns a futex for the virtual address @a uaddr (or creates one). */
186static futex_t *get_futex(uintptr_t uaddr)
187{
188 uintptr_t paddr;
189
[947ab77e]190 if (!find_futex_paddr(uaddr, &paddr))
191 return NULL;
[669f3d32]192
[11b285d]193 futex_t *futex = malloc(sizeof(futex_t));
[7473807]194 if (!futex)
195 return NULL;
[a35b458]196
[ef4218f]197 futex_ptr_t *futex_ptr = malloc(sizeof(futex_ptr_t));
198 if (!futex_ptr) {
199 free(futex);
200 return NULL;
201 }
202
[1b20da0]203 /*
204 * Find the futex object in the global futex table (or insert it
[669f3d32]205 * if it is not present).
206 */
[ef4218f]207 spinlock_lock(&TASK->futex_list_lock);
[207e8880]208 spinlock_lock(&futex_ht_lock);
[a35b458]209
[947ab77e]210 ht_link_t *fut_link = hash_table_find(&futex_ht, &paddr);
[a35b458]211
[669f3d32]212 if (fut_link) {
213 free(futex);
214 futex = member_to_inst(fut_link, futex_t, ht_link);
[ef4218f]215
216 /*
217 * See if the futex is already known to the TASK
218 */
219 bool found = false;
220 list_foreach(TASK->futex_list, task_link, futex_ptr_t, fp) {
221 if (fp->futex->paddr == paddr) {
222 found = true;
223 break;
224 }
225 }
226 /*
227 * If not, put it on the TASK->futex_list and bump its reference
228 * count
229 */
230 if (!found) {
231 list_append(&futex_ptr->task_link, &TASK->futex_list);
232 futex_add_ref(futex);
233 } else
234 free(futex_ptr);
[669f3d32]235 } else {
[947ab77e]236 futex_initialize(futex, paddr);
[82cbf8c6]237 hash_table_insert(&futex_ht, &futex->ht_link);
[ef4218f]238
239 /*
240 * This is a new futex, so it is not on the TASK->futex_list yet
241 */
242 futex_ptr->futex = futex;
243 list_append(&futex_ptr->task_link, &TASK->futex_list);
[669f3d32]244 }
[a35b458]245
[207e8880]246 spinlock_unlock(&futex_ht_lock);
[ef4218f]247 spinlock_unlock(&TASK->futex_list_lock);
[a35b458]248
[947ab77e]249 return futex;
250}
[a35b458]251
[947ab77e]252/** Finds the physical address of the futex variable. */
253static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *paddr)
254{
255 page_table_lock(AS, false);
256 spinlock_lock(&futex_ht_lock);
[a35b458]257
[947ab77e]258 bool success = false;
[a35b458]259
[947ab77e]260 pte_t t;
261 bool found;
[a35b458]262
[947ab77e]263 found = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), true, &t);
264 if (found && PTE_VALID(&t) && PTE_PRESENT(&t)) {
265 success = true;
266 *paddr = PTE_GET_FRAME(&t) +
267 (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
[669f3d32]268 }
269
[947ab77e]270 spinlock_unlock(&futex_ht_lock);
271 page_table_unlock(AS, false);
[a35b458]272
[947ab77e]273 return success;
[669f3d32]274}
275
[b59318e]276/** Sleep in futex wait queue with a timeout.
[ef4218f]277 *
[b59318e]278 * If the sleep times out or is interrupted, the next wakeup is ignored.
279 * The userspace portion of the call must handle this condition.
[9aa72b4]280 *
[b59318e]281 * @param uaddr Userspace address of the futex counter.
282 * @param timeout Maximum number of useconds to sleep. 0 means no limit.
[9aa72b4]283 *
[4774a32]284 * @return If there is no physical mapping for uaddr ENOENT is
[897fd8f1]285 * returned. Otherwise returns the return value of
286 * waitq_sleep_timeout().
[9aa72b4]287 */
[b59318e]288sys_errno_t sys_futex_sleep(uintptr_t uaddr, uintptr_t timeout)
[9aa72b4]289{
[669f3d32]290 futex_t *futex = get_futex(uaddr);
[a35b458]291
[1b20da0]292 if (!futex)
[b7fd2a0]293 return (sys_errno_t) ENOENT;
[741fd16]294
[496232e]295#ifdef CONFIG_UDEBUG
296 udebug_stoppable_begin();
297#endif
298
[b59318e]299 errno_t rc = waitq_sleep_timeout(&futex->wq, timeout,
300 SYNCH_FLAGS_INTERRUPTIBLE | SYNCH_FLAGS_FUTEX, NULL);
[207e8880]301
[496232e]302#ifdef CONFIG_UDEBUG
303 udebug_stoppable_end();
304#endif
305
[b7fd2a0]306 return (sys_errno_t) rc;
[9aa72b4]307}
308
309/** Wakeup one thread waiting in futex wait queue.
310 *
[4774a32]311 * @param uaddr Userspace address of the futex counter.
[9aa72b4]312 *
[4774a32]313 * @return ENOENT if there is no physical mapping for uaddr.
[9aa72b4]314 */
[b7fd2a0]315sys_errno_t sys_futex_wakeup(uintptr_t uaddr)
[9aa72b4]316{
[669f3d32]317 futex_t *futex = get_futex(uaddr);
[a35b458]318
[669f3d32]319 if (futex) {
320 waitq_wakeup(&futex->wq, WAKEUP_FIRST);
[897fd8f1]321 return EOK;
[669f3d32]322 } else {
[b7fd2a0]323 return (sys_errno_t) ENOENT;
[9aa72b4]324 }
[a9ef68b]325}
326
[82cbf8c6]327/** Return the hash of the key stored in the item */
328size_t futex_ht_hash(const ht_link_t *item)
[9aa72b4]329{
[82cbf8c6]330 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
331 return hash_mix(futex->paddr);
[9aa72b4]332}
333
[82cbf8c6]334/** Return the hash of the key */
335size_t futex_ht_key_hash(void *key)
[9aa72b4]336{
[82cbf8c6]337 uintptr_t *paddr = (uintptr_t *) key;
338 return hash_mix(*paddr);
339}
[9aa72b4]340
[82cbf8c6]341/** Return true if the key is equal to the item's lookup key. */
342bool futex_ht_key_equal(void *key, const ht_link_t *item)
343{
344 uintptr_t *paddr = (uintptr_t *) key;
345 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
346 return *paddr == futex->paddr;
[9aa72b4]347}
348
349/** Callback for removal items from futex hash table.
350 *
[4774a32]351 * @param item Item removed from the hash table.
[9aa72b4]352 */
[82cbf8c6]353void futex_ht_remove_callback(ht_link_t *item)
[9aa72b4]354{
355 futex_t *futex;
356
[82cbf8c6]357 futex = hash_table_get_inst(item, futex_t, ht_link);
[9aa72b4]358 free(futex);
359}
[e090e1bc]360
[cc73a8a1]361/** @}
[b45c443]362 */
Note: See TracBrowser for help on using the repository browser.