Skip to content

Commit 774b56e

Browse files
authored
Merge pull request #221 from smartobjectoriented/217-implementation-of-futex-syscall
Futex syscall implementation
2 parents 37a2f1a + 8be034c commit 774b56e

File tree

10 files changed

+241
-25
lines changed

10 files changed

+241
-25
lines changed

so3/include/futex.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (C) 2014-2025 Daniel Rossier <daniel.rossier@heig-vd.ch>
3+
* Copyright (C) 2025 Jean-Pierre Miceli <jean-pierre.miceli@heig-vd.ch>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 as
7+
* published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
*/
19+
20+
#ifndef FUTEX_H
21+
#define FUTEX_H
22+
23+
#include <timer.h>
24+
#include <thread.h>
25+
#include <list.h>
26+
#include <spinlock.h>
27+
#include <syscall.h>
28+
29+
/* Commands */
30+
#define FUTEX_WAIT 0
31+
#define FUTEX_WAKE 1
32+
#define FUTEX_FD 2
33+
#define FUTEX_REQUEUE 3
34+
#define FUTEX_CMP_REQUEUE 4
35+
#define FUTEX_WAKE_OP 5
36+
#define FUTEX_LOCK_PI 6
37+
#define FUTEX_UNLOCK_PI 7
38+
#define FUTEX_TRYLOCK_PI 8
39+
#define FUTEX_WAIT_BITSET 9
40+
41+
#define FUTEX_PRIVATE_FLAG 128
42+
#define FUTEX_CLOCK_REALTIME 256
43+
#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
44+
45+
/**
46+
* list of futexes
47+
*/
48+
typedef struct futex {
49+
struct list_head list;
50+
struct list_head f_element;
51+
uintptr_t key;
52+
} futex_t;
53+
54+
SYSCALL_DECLARE(futex, u32 *uaddr, int op, u32 val, const struct timespec *utime, u32 *uaddr2, u32 val3)
55+
56+
#endif /* FUTEX_H */

so3/include/list.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ static inline int list_is_last(const struct list_head *list, const struct list_h
169169
return list->next == head;
170170
}
171171

172+
/**
173+
* list_is_head - tests whether @list is the list @head
174+
* @list: the entry to test
175+
* @head: the head of the list
176+
*/
177+
static inline int list_is_head(const struct list_head *list, const struct list_head *head)
178+
{
179+
return list == head;
180+
}
181+
172182
/**
173183
* list_empty - tests whether a list is empty
174184
* @head: the list to test.

so3/include/mutex.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,4 @@ void mutex_lock(struct mutex *lock);
5959
void mutex_unlock(struct mutex *lock);
6060
void mutex_init(struct mutex *lock);
6161

62-
SYSCALL_DECLARE(mutex_init, void);
63-
SYSCALL_DECLARE(mutex_lock, unsigned long number);
64-
SYSCALL_DECLARE(mutex_unlock, unsigned long number);
65-
6662
#endif /* MUTEX_H */

so3/include/process.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <memory.h>
3030
#include <signal.h>
3131
#include <ptrace.h>
32+
#include <spinlock.h>
33+
#include <futex.h>
3234
#include <mutex.h>
3335
#include <syscall.h>
3436

@@ -106,6 +108,10 @@ struct pcb {
106108
/* Number of pages required by this process (including binary image) */
107109
size_t page_count;
108110

111+
/* List of futexes */
112+
struct list_head futex;
113+
spinlock_t futex_lock;
114+
109115
/* List of frames (physical pages) belonging to this process */
110116
struct list_head page_list;
111117

so3/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ obj-y += main.o \
88
thread.o \
99
schedule.o \
1010
mutex.o \
11+
futex.o \
1112
spinlock.o \
1213
syscalls.o \
1314
softirq.o \

so3/kernel/futex.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (C) 2025 Jean-Pierre Miceli <jean-pierre.miceli@heig-vd.ch>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License version 2 as
6+
* published by the Free Software Foundation.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program; if not, write to the Free Software
15+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16+
*
17+
*/
18+
19+
#include <process.h>
20+
#include <heap.h>
21+
#include <errno.h>
22+
#include <futex.h>
23+
24+
/**
25+
* do_futex_wait - block on futex_w
26+
*
27+
* @param futex_w address of the futex word
28+
* @param val expected value of the futex word
29+
* @return 0 on success or error value
30+
*/
31+
static int do_futex_wait(uint32_t *futex_w, uint32_t val)
32+
{
33+
unsigned long flags;
34+
pcb_t *pcb = current()->pcb;
35+
struct list_head *pos;
36+
futex_t *futex;
37+
queue_thread_t f_element;
38+
39+
flags = spin_lock_irqsave(&pcb->futex_lock);
40+
41+
if (*futex_w != val) {
42+
spin_unlock_irqrestore(&pcb->futex_lock, flags);
43+
return -EAGAIN;
44+
}
45+
46+
/* look if a futex_w already exists */
47+
list_for_each(pos, &pcb->futex) {
48+
futex = list_entry(pos, futex_t, list);
49+
50+
if ((uintptr_t) futex_w == futex->key)
51+
break;
52+
}
53+
54+
if (list_is_head(pos, &pcb->futex)) {
55+
/* no futex on futex_w */
56+
futex = (futex_t *) calloc(1, sizeof(futex_t));
57+
if (futex == NULL)
58+
BUG();
59+
60+
futex->key = (uintptr_t) futex_w;
61+
62+
INIT_LIST_HEAD(&futex->f_element);
63+
list_add_tail(&futex->list, &pcb->futex);
64+
}
65+
66+
f_element.tcb = current();
67+
68+
list_add_tail(&f_element.list, &futex->f_element);
69+
70+
/* go to sleep. */
71+
spin_unlock(&pcb->futex_lock);
72+
waiting();
73+
74+
BUG_ON(local_irq_is_enabled());
75+
76+
spin_lock(&pcb->futex_lock);
77+
78+
if (list_empty(&futex->f_element))
79+
free(futex);
80+
81+
spin_unlock_irqrestore(&pcb->futex_lock, flags);
82+
83+
return 0;
84+
}
85+
86+
/**
87+
* do_futex_wake - wake one or more tasks blocked on uaddr
88+
*
89+
* @nr_wake wake up to this many tasks
90+
* @return the number of waiters that were woken up
91+
*/
92+
static int do_futex_wake(uint32_t *futex_w, uint32_t nr_wake)
93+
{
94+
unsigned long flags;
95+
pcb_t *pcb = current()->pcb;
96+
struct list_head *pos, *p;
97+
futex_t *futex;
98+
queue_thread_t *f_element;
99+
unsigned idx = 0;
100+
101+
flags = spin_lock_irqsave(&pcb->futex_lock);
102+
103+
/* Search for the futex element with futex_w as key */
104+
list_for_each(pos, &pcb->futex) {
105+
futex = list_entry(pos, futex_t, list);
106+
107+
if ((uintptr_t) futex_w == futex->key)
108+
break;
109+
}
110+
111+
/* Check if the wanted key was found in the list */
112+
if (list_is_head(pos, &pcb->futex)) {
113+
/* key does not exists in futex - Error */
114+
spin_unlock_irqrestore(&pcb->futex_lock, flags);
115+
return -EINVAL;
116+
}
117+
118+
/* wakes at most nr_wake of the waiters that are waiting */
119+
list_for_each_safe(pos, p, &futex->list) {
120+
f_element = list_entry(pos, queue_thread_t, list);
121+
122+
if (idx == nr_wake)
123+
break;
124+
125+
list_del(&f_element->list);
126+
ready(f_element->tcb);
127+
128+
idx++;
129+
}
130+
131+
spin_unlock_irqrestore(&pcb->futex_lock, flags);
132+
133+
return idx;
134+
}
135+
136+
SYSCALL_DEFINE6(futex, uint32_t *, uaddr, int, op, uint32_t, val, const struct timespec *, utime, uint32_t *, uaddr2, uint32_t,
137+
val3)
138+
{
139+
int cmd = op & FUTEX_CMD_MASK;
140+
141+
if (utime)
142+
printk("[futex] utime parameter is not used in current implementation\n");
143+
144+
switch (cmd) {
145+
case FUTEX_WAIT:
146+
return do_futex_wait(uaddr, val);
147+
case FUTEX_WAKE:
148+
return do_futex_wake(uaddr, val);
149+
case FUTEX_FD:
150+
case FUTEX_REQUEUE:
151+
case FUTEX_CMP_REQUEUE:
152+
case FUTEX_WAKE_OP:
153+
case FUTEX_LOCK_PI:
154+
case FUTEX_UNLOCK_PI:
155+
case FUTEX_TRYLOCK_PI:
156+
case FUTEX_WAIT_BITSET:
157+
printk("Futex cmd '%d' not supported !\n");
158+
return -EINVAL;
159+
}
160+
161+
return -ENOSYS;
162+
}

so3/kernel/mutex.c

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -146,27 +146,6 @@ void mutex_unlock(struct mutex *lock)
146146
schedule();
147147
}
148148

149-
/*
150-
* The following syscall implementation are a first attempt, mainly used for debugging kernel mutexes.
151-
*/
152-
SYSCALL_DEFINE1(mutex_lock, unsigned long, number)
153-
{
154-
if (number >= N_MUTEX) {
155-
return -EINVAL;
156-
}
157-
mutex_lock(&current()->pcb->lock[number]);
158-
return 0;
159-
}
160-
161-
SYSCALL_DEFINE1(mutex_unlock, unsigned long, number)
162-
{
163-
if (number >= N_MUTEX) {
164-
return -EINVAL;
165-
}
166-
mutex_unlock(&current()->pcb->lock[number]);
167-
return 0;
168-
}
169-
170149
void mutex_init(struct mutex *lock)
171150
{
172151
memset(lock, 0, sizeof(struct mutex));

so3/kernel/process.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ pcb_t *new_process(void)
202202
/* Init the list of pages */
203203
INIT_LIST_HEAD(&pcb->page_list);
204204

205+
/* Init the futex */
206+
INIT_LIST_HEAD(&pcb->futex);
207+
spin_lock_init(&pcb->futex_lock);
208+
205209
pcb->pid = pid_current++;
206210

207211
/* Init the list of child threads */

so3/kernel/syscalls.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <signal.h>
2929
#include <timer.h>
3030
#include <net.h>
31+
#include <futex.h>
3132
#include <syscall.h>
3233

3334
extern void __get_syscall_args_ext(uint32_t *syscall_no);

so3/syscall.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ newfstatat
3737
mmap
3838
mmap2
3939
nanosleep
40+
futex
4041
pipe IPC_PIPE
4142
pipe2 IPC_PIPE
4243
rt_sigaction IPC_SIGNAL

0 commit comments

Comments
 (0)