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
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 kernel_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 * tasks that reference the futex variable. A futex object is freed
48 * when the last task having accessed the futex exits.
49 *
50 * Each task keeps track of the futex objects it accessed in a list
51 * of pointers (futex_ptr_t, task->futex_list) to the different futex
52 * objects.
53 */
54
55#include <assert.h>
56#include <synch/futex.h>
57#include <synch/mutex.h>
58#include <synch/spinlock.h>
59#include <mm/frame.h>
60#include <mm/page.h>
61#include <stdlib.h>
62#include <proc/thread.h>
63#include <proc/task.h>
64#include <genarch/mm/page_pt.h>
65#include <genarch/mm/page_ht.h>
66#include <adt/hash.h>
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
74/** Task specific pointer to a global kernel futex object. */
75typedef struct futex_ptr {
76 /** Link for the list of all futex pointers used by a task. */
77 link_t task_link;
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);
89
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);
94
95/** Mutex protecting the global futex hash table.
96 *
97 * Acquire task specific TASK->futex_list_lock before this mutex.
98 */
99SPINLOCK_STATIC_INITIALIZE_NAME(futex_ht_lock, "futex-ht-lock");
100
101/** Global kernel futex hash table. Lock futex_ht_lock before accessing.
102 *
103 * Physical address of the futex variable is the lookup key.
104 */
105static hash_table_t futex_ht;
106
107/** Global kernel futex hash table operations. */
108static hash_table_ops_t futex_ht_ops = {
109 .hash = futex_ht_hash,
110 .key_hash = futex_ht_key_hash,
111 .key_equal = futex_ht_key_equal,
112 .remove_callback = futex_ht_remove_callback
113};
114
115/** Initialize futex subsystem. */
116void futex_init(void)
117{
118 hash_table_create(&futex_ht, 0, 0, &futex_ht_ops);
119}
120
121/** Initializes the futex structures for the new task. */
122void futex_task_init(struct task *task)
123{
124 list_initialize(&task->futex_list);
125 spinlock_initialize(&task->futex_list_lock, "futex-list-lock");
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. */
132 spinlock_lock(&TASK->futex_list_lock);
133
134 list_foreach_safe(TASK->futex_list, cur_link, next_link) {
135 futex_ptr_t *futex_ptr = member_to_inst(cur_link, futex_ptr_t,
136 task_link);
137
138 futex_release_ref_locked(futex_ptr->futex);
139 free(futex_ptr);
140 }
141
142 spinlock_unlock(&TASK->futex_list_lock);
143}
144
145/** Initialize the kernel futex structure.
146 *
147 * @param futex Kernel futex structure.
148 * @param paddr Physical address of the futex variable.
149 */
150static void futex_initialize(futex_t *futex, uintptr_t paddr)
151{
152 waitq_initialize(&futex->wq);
153 futex->paddr = paddr;
154 futex->refcount = 1;
155}
156
157/** Increments the counter of tasks referencing the futex. */
158static void futex_add_ref(futex_t *futex)
159{
160 assert(spinlock_locked(&futex_ht_lock));
161 assert(futex->refcount > 0);
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{
168 assert(spinlock_locked(&futex_ht_lock));
169 assert(futex->refcount > 0);
170
171 --futex->refcount;
172
173 if (futex->refcount == 0)
174 hash_table_remove(&futex_ht, &futex->paddr);
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{
180 spinlock_lock(&futex_ht_lock);
181 futex_release_ref(futex);
182 spinlock_unlock(&futex_ht_lock);
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
190 if (!find_futex_paddr(uaddr, &paddr))
191 return NULL;
192
193 futex_t *futex = malloc(sizeof(futex_t));
194 if (!futex)
195 return NULL;
196
197 futex_ptr_t *futex_ptr = malloc(sizeof(futex_ptr_t));
198 if (!futex_ptr) {
199 free(futex);
200 return NULL;
201 }
202
203 /*
204 * Find the futex object in the global futex table (or insert it
205 * if it is not present).
206 */
207 spinlock_lock(&TASK->futex_list_lock);
208 spinlock_lock(&futex_ht_lock);
209
210 ht_link_t *fut_link = hash_table_find(&futex_ht, &paddr);
211
212 if (fut_link) {
213 free(futex);
214 futex = member_to_inst(fut_link, futex_t, ht_link);
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);
235 } else {
236 futex_initialize(futex, paddr);
237 hash_table_insert(&futex_ht, &futex->ht_link);
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);
244 }
245
246 spinlock_unlock(&futex_ht_lock);
247 spinlock_unlock(&TASK->futex_list_lock);
248
249 return futex;
250}
251
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);
257
258 bool success = false;
259
260 pte_t t;
261 bool found;
262
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));
268 }
269
270 spinlock_unlock(&futex_ht_lock);
271 page_table_unlock(AS, false);
272
273 return success;
274}
275
276/** Sleep in futex wait queue with a timeout.
277 *
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.
280 *
281 * @param uaddr Userspace address of the futex counter.
282 * @param timeout Maximum number of useconds to sleep. 0 means no limit.
283 *
284 * @return If there is no physical mapping for uaddr ENOENT is
285 * returned. Otherwise returns the return value of
286 * waitq_sleep_timeout().
287 */
288sys_errno_t sys_futex_sleep(uintptr_t uaddr, uintptr_t timeout)
289{
290 futex_t *futex = get_futex(uaddr);
291
292 if (!futex)
293 return (sys_errno_t) ENOENT;
294
295#ifdef CONFIG_UDEBUG
296 udebug_stoppable_begin();
297#endif
298
299 errno_t rc = waitq_sleep_timeout(&futex->wq, timeout,
300 SYNCH_FLAGS_INTERRUPTIBLE | SYNCH_FLAGS_FUTEX, NULL);
301
302#ifdef CONFIG_UDEBUG
303 udebug_stoppable_end();
304#endif
305
306 return (sys_errno_t) rc;
307}
308
309/** Wakeup one thread waiting in futex wait queue.
310 *
311 * @param uaddr Userspace address of the futex counter.
312 *
313 * @return ENOENT if there is no physical mapping for uaddr.
314 */
315sys_errno_t sys_futex_wakeup(uintptr_t uaddr)
316{
317 futex_t *futex = get_futex(uaddr);
318
319 if (futex) {
320 waitq_wakeup(&futex->wq, WAKEUP_FIRST);
321 return EOK;
322 } else {
323 return (sys_errno_t) ENOENT;
324 }
325}
326
327/** Return the hash of the key stored in the item */
328size_t futex_ht_hash(const ht_link_t *item)
329{
330 futex_t *futex = hash_table_get_inst(item, futex_t, ht_link);
331 return hash_mix(futex->paddr);
332}
333
334/** Return the hash of the key */
335size_t futex_ht_key_hash(void *key)
336{
337 uintptr_t *paddr = (uintptr_t *) key;
338 return hash_mix(*paddr);
339}
340
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;
347}
348
349/** Callback for removal items from futex hash table.
350 *
351 * @param item Item removed from the hash table.
352 */
353void futex_ht_remove_callback(ht_link_t *item)
354{
355 futex_t *futex;
356
357 futex = hash_table_get_inst(item, futex_t, ht_link);
358 free(futex);
359}
360
361/** @}
362 */
Note: See TracBrowser for help on using the repository browser.