2
* Copyright (c) 2006 Jakub Jermar
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* - Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* - Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* - The name of the author may not be used to endorse or promote products
15
* derived from this software without specific prior written permission.
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
* @brief Kernel backend for futexes.
38
#include <synch/futex.h>
39
#include <synch/rwlock.h>
40
#include <synch/spinlock.h>
41
#include <synch/synch.h>
45
#include <proc/thread.h>
46
#include <proc/task.h>
47
#include <genarch/mm/page_pt.h>
48
#include <genarch/mm/page_ht.h>
49
#include <adt/hash_table.h>
57
#define FUTEX_HT_SIZE 1024 /* keep it a power of 2 */
59
static void futex_initialize(futex_t *futex);
61
static futex_t *futex_find(uintptr_t paddr);
62
static size_t futex_ht_hash(unative_t *key);
63
static bool futex_ht_compare(unative_t *key, size_t keys, link_t *item);
64
static void futex_ht_remove_callback(link_t *item);
67
* Read-write lock protecting global futex hash table.
68
* It is also used to serialize access to all futex_t structures.
69
* Must be acquired before the task futex B+tree lock.
71
static rwlock_t futex_ht_lock;
73
/** Futex hash table. */
74
static hash_table_t futex_ht;
76
/** Futex hash table operations. */
77
static hash_table_operations_t futex_ht_ops = {
78
.hash = futex_ht_hash,
79
.compare = futex_ht_compare,
80
.remove_callback = futex_ht_remove_callback
83
/** Initialize futex subsystem. */
86
rwlock_initialize(&futex_ht_lock);
87
hash_table_create(&futex_ht, FUTEX_HT_SIZE, 1, &futex_ht_ops);
90
/** Initialize kernel futex structure.
92
* @param futex Kernel futex structure.
94
void futex_initialize(futex_t *futex)
96
waitq_initialize(&futex->wq);
97
link_initialize(&futex->ht_link);
102
/** Sleep in futex wait queue.
104
* @param uaddr Userspace address of the futex counter.
105
* @param usec If non-zero, number of microseconds this thread is willing to
107
* @param flags Select mode of operation.
109
* @return One of ESYNCH_TIMEOUT, ESYNCH_OK_ATOMIC and ESYNCH_OK_BLOCKED. See
110
* synch.h. If there is no physical mapping for uaddr ENOENT is returned.
112
unative_t sys_futex_sleep_timeout(uintptr_t uaddr, uint32_t usec, int flags)
120
ipl = interrupts_disable();
123
* Find physical address of futex counter.
125
page_table_lock(AS, true);
126
t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
127
if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
128
page_table_unlock(AS, true);
129
interrupts_restore(ipl);
130
return (unative_t) ENOENT;
132
paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
133
page_table_unlock(AS, true);
135
interrupts_restore(ipl);
137
futex = futex_find(paddr);
140
udebug_stoppable_begin();
142
rc = waitq_sleep_timeout(&futex->wq, usec, flags |
143
SYNCH_FLAGS_INTERRUPTIBLE);
146
udebug_stoppable_end();
148
return (unative_t) rc;
151
/** Wakeup one thread waiting in futex wait queue.
153
* @param uaddr Userspace address of the futex counter.
155
* @return ENOENT if there is no physical mapping for uaddr.
157
unative_t sys_futex_wakeup(uintptr_t uaddr)
164
ipl = interrupts_disable();
167
* Find physical address of futex counter.
169
page_table_lock(AS, true);
170
t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
171
if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
172
page_table_unlock(AS, true);
173
interrupts_restore(ipl);
174
return (unative_t) ENOENT;
176
paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
177
page_table_unlock(AS, true);
179
interrupts_restore(ipl);
181
futex = futex_find(paddr);
183
waitq_wakeup(&futex->wq, WAKEUP_FIRST);
188
/** Find kernel address of the futex structure corresponding to paddr.
190
* If the structure does not exist already, a new one is created.
192
* @param paddr Physical address of the userspace futex counter.
194
* @return Address of the kernel futex structure.
196
futex_t *futex_find(uintptr_t paddr)
203
* Find the respective futex structure
204
* or allocate new one if it does not exist already.
206
rwlock_read_lock(&futex_ht_lock);
207
item = hash_table_find(&futex_ht, &paddr);
209
futex = hash_table_get_instance(item, futex_t, ht_link);
212
* See if the current task knows this futex.
214
mutex_lock(&TASK->futexes_lock);
215
if (!btree_search(&TASK->futexes, paddr, &leaf)) {
217
* The futex is new to the current task.
218
* However, we only have read access.
219
* Gain write access and try again.
221
mutex_unlock(&TASK->futexes_lock);
222
goto gain_write_access;
224
mutex_unlock(&TASK->futexes_lock);
226
rwlock_read_unlock(&futex_ht_lock);
230
* Upgrade to writer is not currently supported,
231
* therefore, it is necessary to release the read lock
232
* and reacquire it as a writer.
234
rwlock_read_unlock(&futex_ht_lock);
236
rwlock_write_lock(&futex_ht_lock);
238
* Avoid possible race condition by searching
239
* the hash table once again with write access.
241
item = hash_table_find(&futex_ht, &paddr);
243
futex = hash_table_get_instance(item, futex_t, ht_link);
246
* See if this futex is known to the current task.
248
mutex_lock(&TASK->futexes_lock);
249
if (!btree_search(&TASK->futexes, paddr, &leaf)) {
251
* The futex is new to the current task.
252
* Upgrade its reference count and put it to the
253
* current task's B+tree of known futexes.
256
btree_insert(&TASK->futexes, paddr, futex,
259
mutex_unlock(&TASK->futexes_lock);
261
rwlock_write_unlock(&futex_ht_lock);
263
futex = (futex_t *) malloc(sizeof(futex_t), 0);
264
futex_initialize(futex);
265
futex->paddr = paddr;
266
hash_table_insert(&futex_ht, &paddr, &futex->ht_link);
269
* This is the first task referencing the futex.
270
* It can be directly inserted into its
271
* B+tree of known futexes.
273
mutex_lock(&TASK->futexes_lock);
274
btree_insert(&TASK->futexes, paddr, futex, NULL);
275
mutex_unlock(&TASK->futexes_lock);
277
rwlock_write_unlock(&futex_ht_lock);
284
/** Compute hash index into futex hash table.
286
* @param key Address where the key (i.e. physical address of futex counter) is
289
* @return Index into futex hash table.
291
size_t futex_ht_hash(unative_t *key)
293
return (*key & (FUTEX_HT_SIZE - 1));
296
/** Compare futex hash table item with a key.
298
* @param key Address where the key (i.e. physical address of futex counter) is
301
* @return True if the item matches the key. False otherwise.
303
bool futex_ht_compare(unative_t *key, size_t keys, link_t *item)
309
futex = hash_table_get_instance(item, futex_t, ht_link);
310
return *key == futex->paddr;
313
/** Callback for removal items from futex hash table.
315
* @param item Item removed from the hash table.
317
void futex_ht_remove_callback(link_t *item)
321
futex = hash_table_get_instance(item, futex_t, ht_link);
325
/** Remove references from futexes known to the current task. */
326
void futex_cleanup(void)
330
rwlock_write_lock(&futex_ht_lock);
331
mutex_lock(&TASK->futexes_lock);
333
for (cur = TASK->futexes.leaf_head.next;
334
cur != &TASK->futexes.leaf_head; cur = cur->next) {
338
node = list_get_instance(cur, btree_node_t, leaf_link);
339
for (i = 0; i < node->keys; i++) {
341
uintptr_t paddr = node->key[i];
343
ftx = (futex_t *) node->value[i];
344
if (--ftx->refcount == 0)
345
hash_table_remove(&futex_ht, &paddr, 1);
349
mutex_unlock(&TASK->futexes_lock);
350
rwlock_write_unlock(&futex_ht_lock);