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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 62ca560 was 82cbf8c6, checked in by Jakub Jermar <jakub@…>, 8 years ago

Replace the old hash table implementation in the kernel with the newer one

This replaces the original hash table implementation with the resizable one
already used in uspace. Along the way, the IRQ hash table code was streamlined
and cleaned up.

  • Property mode set to 100644
File size: 14.1 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 a wait result as defined in
398 * synch.h.
399 */
400sysarg_t sys_futex_sleep(uintptr_t uaddr)
401{
402 futex_t *futex = get_futex(uaddr);
403
404 if (!futex)
405 return (sysarg_t) ENOENT;
406
407#ifdef CONFIG_UDEBUG
408 udebug_stoppable_begin();
409#endif
410
411 int rc = waitq_sleep_timeout(&futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE);
412
413#ifdef CONFIG_UDEBUG
414 udebug_stoppable_end();
415#endif
416
417 return (sysarg_t) rc;
418}
419
420/** Wakeup one thread waiting in futex wait queue.
421 *
422 * @param uaddr Userspace address of the futex counter.
423 *
424 * @return ENOENT if there is no physical mapping for uaddr.
425 */
426sysarg_t sys_futex_wakeup(uintptr_t uaddr)
427{
428 futex_t *futex = get_futex(uaddr);
429
430 if (futex) {
431 waitq_wakeup(&futex->wq, WAKEUP_FIRST);
432 return 0;
433 } else {
434 return (sysarg_t) ENOENT;
435 }
436}
437
438
439/** Return the hash of the key stored in the item */
440size_t futex_ht_hash(const ht_link_t *item)
441{
442 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
443 return hash_mix(futex->paddr);
444}
445
446/** Return the hash of the key */
447size_t futex_ht_key_hash(void *key)
448{
449 uintptr_t *paddr = (uintptr_t *) key;
450 return hash_mix(*paddr);
451}
452
453/** Return true if the key is equal to the item's lookup key. */
454bool futex_ht_key_equal(void *key, const ht_link_t *item)
455{
456 uintptr_t *paddr = (uintptr_t *) key;
457 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
458 return *paddr == futex->paddr;
459}
460
461/** Callback for removal items from futex hash table.
462 *
463 * @param item Item removed from the hash table.
464 */
465void futex_ht_remove_callback(ht_link_t *item)
466{
467 futex_t *futex;
468
469 futex = hash_table_get_inst(item, futex_t, ht_link);
470 free(futex);
471}
472
473/*
474 * Operations of a task's CHT that caches mappings of futex user space
475 * virtual addresses to kernel futex objects.
476 */
477
478static size_t task_fut_ht_hash(const cht_link_t *link)
479{
480 const futex_ptr_t *fut_ptr = member_to_inst(link, futex_ptr_t, cht_link);
481 return fut_ptr->uaddr;
482}
483
484static size_t task_fut_ht_key_hash(void *key)
485{
486 return *(uintptr_t*)key;
487}
488
489static bool task_fut_ht_equal(const cht_link_t *item1, const cht_link_t *item2)
490{
491 const futex_ptr_t *fut_ptr1 = member_to_inst(item1, futex_ptr_t, cht_link);
492 const futex_ptr_t *fut_ptr2 = member_to_inst(item2, futex_ptr_t, cht_link);
493
494 return fut_ptr1->uaddr == fut_ptr2->uaddr;
495}
496
497static bool task_fut_ht_key_equal(void *key, const cht_link_t *item)
498{
499 const futex_ptr_t *fut_ptr = member_to_inst(item, futex_ptr_t, cht_link);
500 uintptr_t uaddr = *(uintptr_t*)key;
501
502 return fut_ptr->uaddr == uaddr;
503}
504
505/** @}
506 */
Note: See TracBrowser for help on using the repository browser.