diff --git a/so3/include/futex.h b/so3/include/futex.h new file mode 100644 index 000000000..688bc56f9 --- /dev/null +++ b/so3/include/futex.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014-2025 Daniel Rossier + * Copyright (C) 2025 Jean-Pierre Miceli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FUTEX_H +#define FUTEX_H + +#include +#include +#include +#include +#include + +/* Commands */ +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 +#define FUTEX_FD 2 +#define FUTEX_REQUEUE 3 +#define FUTEX_CMP_REQUEUE 4 +#define FUTEX_WAKE_OP 5 +#define FUTEX_LOCK_PI 6 +#define FUTEX_UNLOCK_PI 7 +#define FUTEX_TRYLOCK_PI 8 +#define FUTEX_WAIT_BITSET 9 + +#define FUTEX_PRIVATE_FLAG 128 +#define FUTEX_CLOCK_REALTIME 256 +#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) + +/** + * list of futexes + */ +typedef struct futex { + struct list_head list; + struct list_head f_element; + uintptr_t key; +} futex_t; + +SYSCALL_DECLARE(futex, u32 *uaddr, int op, u32 val, const struct timespec *utime, u32 *uaddr2, u32 val3) + +#endif /* FUTEX_H */ diff --git a/so3/include/list.h b/so3/include/list.h index c6790da98..164a9ea20 100644 --- a/so3/include/list.h +++ b/so3/include/list.h @@ -169,6 +169,16 @@ static inline int list_is_last(const struct list_head *list, const struct list_h return list->next == head; } +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + /** * list_empty - tests whether a list is empty * @head: the list to test. diff --git a/so3/include/mutex.h b/so3/include/mutex.h index 49f3847c9..2e8f2d63a 100644 --- a/so3/include/mutex.h +++ b/so3/include/mutex.h @@ -59,8 +59,4 @@ void mutex_lock(struct mutex *lock); void mutex_unlock(struct mutex *lock); void mutex_init(struct mutex *lock); -SYSCALL_DECLARE(mutex_init, void); -SYSCALL_DECLARE(mutex_lock, unsigned long number); -SYSCALL_DECLARE(mutex_unlock, unsigned long number); - #endif /* MUTEX_H */ diff --git a/so3/include/process.h b/so3/include/process.h index 5b444deab..1fdb04a95 100644 --- a/so3/include/process.h +++ b/so3/include/process.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -82,6 +84,10 @@ struct pcb { /* Number of pages required by this process (including binary image) */ size_t page_count; + /* List of futexes */ + struct list_head futex; + spinlock_t futex_lock; + /* List of frames (physical pages) belonging to this process */ struct list_head page_list; diff --git a/so3/kernel/Makefile b/so3/kernel/Makefile index 8b9900a3d..71f68a103 100644 --- a/so3/kernel/Makefile +++ b/so3/kernel/Makefile @@ -8,6 +8,7 @@ obj-y += main.o \ thread.o \ schedule.o \ mutex.o \ + futex.o \ spinlock.o \ syscalls.o \ softirq.o \ diff --git a/so3/kernel/futex.c b/so3/kernel/futex.c new file mode 100644 index 000000000..0672ef76e --- /dev/null +++ b/so3/kernel/futex.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2025 Jean-Pierre Miceli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +/** + * do_futex_wait - block on futex_w + * + * @param futex_w address of the futex word + * @param val expected value of the futex word + * @return 0 on success or error value + */ +static int do_futex_wait(uint32_t *futex_w, uint32_t val) +{ + unsigned long flags; + pcb_t *pcb = current()->pcb; + struct list_head *pos; + futex_t *futex; + queue_thread_t f_element; + + flags = spin_lock_irqsave(&pcb->futex_lock); + + if (*futex_w != val) { + spin_unlock_irqrestore(&pcb->futex_lock, flags); + return -EAGAIN; + } + + /* look if a futex_w already exists */ + list_for_each(pos, &pcb->futex) { + futex = list_entry(pos, futex_t, list); + + if ((uintptr_t) futex_w == futex->key) + break; + } + + if (list_is_head(pos, &pcb->futex)) { + /* no futex on futex_w */ + futex = (futex_t *) calloc(1, sizeof(futex_t)); + if (futex == NULL) + BUG(); + + futex->key = (uintptr_t) futex_w; + + INIT_LIST_HEAD(&futex->f_element); + list_add_tail(&futex->list, &pcb->futex); + } + + f_element.tcb = current(); + + list_add_tail(&f_element.list, &futex->f_element); + + /* go to sleep. */ + spin_unlock(&pcb->futex_lock); + waiting(); + + BUG_ON(local_irq_is_enabled()); + + spin_lock(&pcb->futex_lock); + + if (list_empty(&futex->f_element)) + free(futex); + + spin_unlock_irqrestore(&pcb->futex_lock, flags); + + return 0; +} + +/** + * do_futex_wake - wake one or more tasks blocked on uaddr + * + * @nr_wake wake up to this many tasks + * @return the number of waiters that were woken up + */ +static int do_futex_wake(uint32_t *futex_w, uint32_t nr_wake) +{ + unsigned long flags; + pcb_t *pcb = current()->pcb; + struct list_head *pos, *p; + futex_t *futex; + queue_thread_t *f_element; + unsigned idx = 0; + + flags = spin_lock_irqsave(&pcb->futex_lock); + + /* Search for the futex element with futex_w as key */ + list_for_each(pos, &pcb->futex) { + futex = list_entry(pos, futex_t, list); + + if ((uintptr_t) futex_w == futex->key) + break; + } + + /* Check if the wanted key was found in the list */ + if (list_is_head(pos, &pcb->futex)) { + /* key does not exists in futex - Error */ + spin_unlock_irqrestore(&pcb->futex_lock, flags); + return -EINVAL; + } + + /* wakes at most nr_wake of the waiters that are waiting */ + list_for_each_safe(pos, p, &futex->list) { + f_element = list_entry(pos, queue_thread_t, list); + + if (idx == nr_wake) + break; + + list_del(&f_element->list); + ready(f_element->tcb); + + idx++; + } + + spin_unlock_irqrestore(&pcb->futex_lock, flags); + + return idx; +} + +SYSCALL_DEFINE6(futex, uint32_t *, uaddr, int, op, uint32_t, val, const struct timespec *, utime, uint32_t *, uaddr2, uint32_t, + val3) +{ + int cmd = op & FUTEX_CMD_MASK; + + if (utime) + printk("[futex] utime parameter is not used in current implementation\n"); + + switch (cmd) { + case FUTEX_WAIT: + return do_futex_wait(uaddr, val); + case FUTEX_WAKE: + return do_futex_wake(uaddr, val); + case FUTEX_FD: + case FUTEX_REQUEUE: + case FUTEX_CMP_REQUEUE: + case FUTEX_WAKE_OP: + case FUTEX_LOCK_PI: + case FUTEX_UNLOCK_PI: + case FUTEX_TRYLOCK_PI: + case FUTEX_WAIT_BITSET: + printk("Futex cmd '%d' not supported !\n"); + return -EINVAL; + } + + return -ENOSYS; +} diff --git a/so3/kernel/mutex.c b/so3/kernel/mutex.c index 7915cef27..4714292e9 100644 --- a/so3/kernel/mutex.c +++ b/so3/kernel/mutex.c @@ -146,27 +146,6 @@ void mutex_unlock(struct mutex *lock) schedule(); } -/* - * The following syscall implementation are a first attempt, mainly used for debugging kernel mutexes. - */ -SYSCALL_DEFINE1(mutex_lock, unsigned long, number) -{ - if (number >= N_MUTEX) { - return -EINVAL; - } - mutex_lock(¤t()->pcb->lock[number]); - return 0; -} - -SYSCALL_DEFINE1(mutex_unlock, unsigned long, number) -{ - if (number >= N_MUTEX) { - return -EINVAL; - } - mutex_unlock(¤t()->pcb->lock[number]); - return 0; -} - void mutex_init(struct mutex *lock) { memset(lock, 0, sizeof(struct mutex)); diff --git a/so3/kernel/process.c b/so3/kernel/process.c index 13ec54092..e9ade4316 100644 --- a/so3/kernel/process.c +++ b/so3/kernel/process.c @@ -205,6 +205,10 @@ pcb_t *new_process(void) /* Init the list of pages */ INIT_LIST_HEAD(&pcb->page_list); + /* Init the futex */ + INIT_LIST_HEAD(&pcb->futex); + spin_lock_init(&pcb->futex_lock); + pcb->pid = pid_current++; for (i = 0; i < PROC_MAX_THREADS; i++) diff --git a/so3/kernel/syscalls.c b/so3/kernel/syscalls.c index b87cc486d..251dc1430 100644 --- a/so3/kernel/syscalls.c +++ b/so3/kernel/syscalls.c @@ -28,6 +28,7 @@ #include #include #include +#include #include extern void __get_syscall_args_ext(uint32_t *syscall_no); diff --git a/so3/syscall.tbl b/so3/syscall.tbl index 1a3921119..a5b247c27 100644 --- a/so3/syscall.tbl +++ b/so3/syscall.tbl @@ -37,6 +37,7 @@ newfstatat mmap mmap2 nanosleep +futex pipe IPC_PIPE pipe2 IPC_PIPE rt_sigaction IPC_SIGNAL