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

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

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * Copyright (c) 2012 Adam Hraska
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
30/** @addtogroup sync
31 * @{
32 */
33
34/**
35 * @file
36 * @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.
61 */
62
63#include <assert.h>
64#include <synch/futex.h>
65#include <synch/mutex.h>
66#include <synch/spinlock.h>
67#include <synch/rcu.h>
68#include <mm/frame.h>
69#include <mm/page.h>
70#include <mm/slab.h>
71#include <proc/thread.h>
72#include <proc/task.h>
73#include <genarch/mm/page_pt.h>
74#include <genarch/mm/page_ht.h>
75#include <adt/cht.h>
76#include <adt/hash.h>
77#include <adt/hash_table.h>
78#include <adt/list.h>
79#include <arch.h>
80#include <align.h>
81#include <panic.h>
82#include <errno.h>
83
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
109static size_t futex_ht_hash(const ht_link_t *item);
110static size_t futex_ht_key_hash(void *key);
111static bool futex_ht_key_equal(void *key, const ht_link_t *item);
112static void futex_ht_remove_callback(ht_link_t *item);
113
114static size_t task_fut_ht_hash(const cht_link_t *link);
115static size_t task_fut_ht_key_hash(void *key);
116static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2);
117static bool task_fut_ht_key_equal(void *key, const cht_link_t *item);
118
119
120/** Mutex protecting the global futex hash table.
121 *
122 * Acquire task specific TASK->futex_list_lock before this mutex.
123 */
124SPINLOCK_STATIC_INITIALIZE_NAME(futex_ht_lock, "futex-ht-lock");
125
126/** Global kernel futex hash table. Lock futex_ht_lock before accessing.
127 *
128 * Physical address of the futex variable is the lookup key.
129 */
130static hash_table_t futex_ht;
131
132/** Global kernel futex hash table operations. */
133static hash_table_ops_t futex_ht_ops = {
134 .hash = futex_ht_hash,
135 .key_hash = futex_ht_key_hash,
136 .key_equal = futex_ht_key_equal,
137 .remove_callback = futex_ht_remove_callback
138};
139
140/** Task futex cache CHT operations. */
141static cht_ops_t task_futex_ht_ops = {
142 .hash = task_fut_ht_hash,
143 .key_hash = task_fut_ht_key_hash,
144 .equal = task_fut_ht_equal,
145 .key_equal = task_fut_ht_key_equal,
146 .remove_callback = NULL
147};
148
149/** Initialize futex subsystem. */
150void futex_init(void)
151{
152 hash_table_create(&futex_ht, 0, 0, &futex_ht_ops);
153}
154
155/** Initializes the futex structures for the new task. */
156void futex_task_init(struct task *task)
157{
158 task->futexes = malloc(sizeof(struct futex_cache), 0);
159
160 cht_create(&task->futexes->ht, 0, 0, 0, true, &task_futex_ht_ops);
161
162 list_initialize(&task->futexes->list);
163 spinlock_initialize(&task->futexes->list_lock, "futex-list-lock");
164}
165
166/** Destroys the futex structures for the dying task. */
167void futex_task_deinit(task_t *task)
168{
169 /* Interrupts are disabled so we must not block (cannot run cht_destroy). */
170 if (interrupts_disabled()) {
171 /* Invoke the blocking cht_destroy in the background. */
172 workq_global_enqueue_noblock(&task->futexes->destroy_work,
173 destroy_task_cache);
174 } else {
175 /* We can block. Invoke cht_destroy in this thread. */
176 destroy_task_cache(&task->futexes->destroy_work);
177 }
178}
179
180/** Deallocates a task's CHT futex cache (must already be empty). */
181static void destroy_task_cache(work_t *work)
182{
183 struct futex_cache *cache =
184 member_to_inst(work, struct futex_cache, destroy_work);
185
186 /*
187 * Destroy the cache before manually freeing items of the cache in case
188 * table resize is in progress.
189 */
190 cht_destroy_unsafe(&cache->ht);
191
192 /* Manually free futex_ptr cache items. */
193 list_foreach_safe(cache->list, cur_link, next_link) {
194 futex_ptr_t *fut_ptr = member_to_inst(cur_link, futex_ptr_t, all_link);
195
196 list_remove(cur_link);
197 free(fut_ptr);
198 }
199
200 free(cache);
201}
202
203/** Remove references from futexes known to the current task. */
204void futex_task_cleanup(void)
205{
206 struct futex_cache *futexes = TASK->futexes;
207
208 /* All threads of this task have terminated. This is the last thread. */
209 spinlock_lock(&futexes->list_lock);
210
211 list_foreach_safe(futexes->list, cur_link, next_link) {
212 futex_ptr_t *fut_ptr = member_to_inst(cur_link, futex_ptr_t, all_link);
213
214 /*
215 * The function is free to free the futex. All other threads of this
216 * task have already terminated, so they have also definitely
217 * exited their CHT futex cache protecting rcu reader sections.
218 * Moreover release_ref() only frees the futex if this is the
219 * last task referencing the futex. Therefore, only threads
220 * of this task may have referenced the futex if it is to be freed.
221 */
222 futex_release_ref_locked(fut_ptr->futex);
223 }
224
225 spinlock_unlock(&futexes->list_lock);
226}
227
228
229/** Initialize the kernel futex structure.
230 *
231 * @param futex Kernel futex structure.
232 * @param paddr Physical address of the futex variable.
233 */
234static void futex_initialize(futex_t *futex, uintptr_t paddr)
235{
236 waitq_initialize(&futex->wq);
237 futex->paddr = paddr;
238 futex->refcount = 1;
239}
240
241/** Increments the counter of tasks referencing the futex. */
242static void futex_add_ref(futex_t *futex)
243{
244 assert(spinlock_locked(&futex_ht_lock));
245 assert(0 < futex->refcount);
246 ++futex->refcount;
247}
248
249/** Decrements the counter of tasks referencing the futex. May free the futex.*/
250static void futex_release_ref(futex_t *futex)
251{
252 assert(spinlock_locked(&futex_ht_lock));
253 assert(0 < futex->refcount);
254
255 --futex->refcount;
256
257 if (0 == futex->refcount) {
258 hash_table_remove(&futex_ht, &futex->paddr);
259 }
260}
261
262/** Decrements the counter of tasks referencing the futex. May free the futex.*/
263static void futex_release_ref_locked(futex_t *futex)
264{
265 spinlock_lock(&futex_ht_lock);
266 futex_release_ref(futex);
267 spinlock_unlock(&futex_ht_lock);
268}
269
270/** Returns a futex for the virtual address @a uaddr (or creates one). */
271static futex_t *get_futex(uintptr_t uaddr)
272{
273 futex_t *futex = find_cached_futex(uaddr);
274
275 if (futex)
276 return futex;
277
278 uintptr_t paddr;
279
280 if (!find_futex_paddr(uaddr, &paddr)) {
281 return 0;
282 }
283
284 return get_and_cache_futex(paddr, uaddr);
285}
286
287
288/** Finds the physical address of the futex variable. */
289static bool find_futex_paddr(uintptr_t uaddr, uintptr_t *paddr)
290{
291 page_table_lock(AS, false);
292 spinlock_lock(&futex_ht_lock);
293
294 bool success = false;
295
296 pte_t t;
297 bool found;
298
299 found = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE), true, &t);
300 if (found && PTE_VALID(&t) && PTE_PRESENT(&t)) {
301 success = true;
302 *paddr = PTE_GET_FRAME(&t) +
303 (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
304 }
305
306 spinlock_unlock(&futex_ht_lock);
307 page_table_unlock(AS, false);
308
309 return success;
310}
311
312/** Returns the futex cached in this task with the virtual address uaddr. */
313static futex_t *find_cached_futex(uintptr_t uaddr)
314{
315 cht_read_lock();
316
317 futex_t *futex;
318 cht_link_t *futex_ptr_link = cht_find_lazy(&TASK->futexes->ht, &uaddr);
319
320 if (futex_ptr_link) {
321 futex_ptr_t *futex_ptr
322 = member_to_inst(futex_ptr_link, futex_ptr_t, cht_link);
323
324 futex = futex_ptr->futex;
325 } else {
326 futex = NULL;
327 }
328
329 cht_read_unlock();
330
331 return futex;
332}
333
334
335/**
336 * Returns a kernel futex for the physical address @a phys_addr and caches
337 * it in this task under the virtual address @a uaddr (if not already cached).
338 */
339static futex_t *get_and_cache_futex(uintptr_t phys_addr, uintptr_t uaddr)
340{
341 futex_t *futex = malloc(sizeof(futex_t), 0);
342
343 /*
344 * Find the futex object in the global futex table (or insert it
345 * if it is not present).
346 */
347 spinlock_lock(&futex_ht_lock);
348
349 ht_link_t *fut_link = hash_table_find(&futex_ht, &phys_addr);
350
351 if (fut_link) {
352 free(futex);
353 futex = member_to_inst(fut_link, futex_t, ht_link);
354 futex_add_ref(futex);
355 } else {
356 futex_initialize(futex, phys_addr);
357 hash_table_insert(&futex_ht, &futex->ht_link);
358 }
359
360 spinlock_unlock(&futex_ht_lock);
361
362 /*
363 * Cache the link to the futex object for this task.
364 */
365 futex_ptr_t *fut_ptr = malloc(sizeof(futex_ptr_t), 0);
366 cht_link_t *dup_link;
367
368 fut_ptr->futex = futex;
369 fut_ptr->uaddr = uaddr;
370
371 cht_read_lock();
372
373 /* Cache the mapping from the virtual address to the futex for this task. */
374 if (cht_insert_unique(&TASK->futexes->ht, &fut_ptr->cht_link, &dup_link)) {
375 spinlock_lock(&TASK->futexes->list_lock);
376 list_append(&fut_ptr->all_link, &TASK->futexes->list);
377 spinlock_unlock(&TASK->futexes->list_lock);
378 } else {
379 /* Another thread of this task beat us to it. Use that mapping instead.*/
380 free(fut_ptr);
381 futex_release_ref_locked(futex);
382
383 futex_ptr_t *dup = member_to_inst(dup_link, futex_ptr_t, cht_link);
384 futex = dup->futex;
385 }
386
387 cht_read_unlock();
388
389 return futex;
390}
391
392/** Sleep in futex wait queue.
393 *
394 * @param uaddr Userspace address of the futex counter.
395 *
396 * @return If there is no physical mapping for uaddr ENOENT is
397 * returned. Otherwise returns the return value of
398 * waitq_sleep_timeout().
399 */
400sys_errno_t sys_futex_sleep(uintptr_t uaddr)
401{
402 futex_t *futex = get_futex(uaddr);
403
404 if (!futex)
405 return (sys_errno_t) ENOENT;
406
407#ifdef CONFIG_UDEBUG
408 udebug_stoppable_begin();
409#endif
410
411 errno_t rc = waitq_sleep_timeout(
412 &futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE, NULL);
413
414#ifdef CONFIG_UDEBUG
415 udebug_stoppable_end();
416#endif
417
418 return (sys_errno_t) rc;
419}
420
421/** Wakeup one thread waiting in futex wait queue.
422 *
423 * @param uaddr Userspace address of the futex counter.
424 *
425 * @return ENOENT if there is no physical mapping for uaddr.
426 */
427sys_errno_t sys_futex_wakeup(uintptr_t uaddr)
428{
429 futex_t *futex = get_futex(uaddr);
430
431 if (futex) {
432 waitq_wakeup(&futex->wq, WAKEUP_FIRST);
433 return EOK;
434 } else {
435 return (sys_errno_t) ENOENT;
436 }
437}
438
439
440/** Return the hash of the key stored in the item */
441size_t futex_ht_hash(const ht_link_t *item)
442{
443 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
444 return hash_mix(futex->paddr);
445}
446
447/** Return the hash of the key */
448size_t futex_ht_key_hash(void *key)
449{
450 uintptr_t *paddr = (uintptr_t *) key;
451 return hash_mix(*paddr);
452}
453
454/** Return true if the key is equal to the item's lookup key. */
455bool futex_ht_key_equal(void *key, const ht_link_t *item)
456{
457 uintptr_t *paddr = (uintptr_t *) key;
458 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
459 return *paddr == futex->paddr;
460}
461
462/** Callback for removal items from futex hash table.
463 *
464 * @param item Item removed from the hash table.
465 */
466void futex_ht_remove_callback(ht_link_t *item)
467{
468 futex_t *futex;
469
470 futex = hash_table_get_inst(item, futex_t, ht_link);
471 free(futex);
472}
473
474/*
475 * Operations of a task's CHT that caches mappings of futex user space
476 * virtual addresses to kernel futex objects.
477 */
478
479static size_t task_fut_ht_hash(const cht_link_t *link)
480{
481 const futex_ptr_t *fut_ptr = member_to_inst(link, futex_ptr_t, cht_link);
482 return fut_ptr->uaddr;
483}
484
485static size_t task_fut_ht_key_hash(void *key)
486{
487 return *(uintptr_t*)key;
488}
489
490static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2)
491{
492 const futex_ptr_t *fut_ptr1 = member_to_inst(item1, futex_ptr_t, cht_link);
493 const futex_ptr_t *fut_ptr2 = member_to_inst(item2, futex_ptr_t, cht_link);
494
495 return fut_ptr1->uaddr == fut_ptr2->uaddr;
496}
497
498static bool task_fut_ht_key_equal(void *key, const cht_link_t *item)
499{
500 const futex_ptr_t *fut_ptr = member_to_inst(item, futex_ptr_t, cht_link);
501 uintptr_t uaddr = *(uintptr_t*)key;
502
503 return fut_ptr->uaddr == uaddr;
504}
505
506/** @}
507 */
Note: See TracBrowser for help on using the repository browser.