From e3719aa78f97e0538ac307a89efd149071929727 Mon Sep 17 00:00:00 2001 From: kamilaboba123 Date: Thu, 28 Aug 2025 11:00:00 +0200 Subject: [PATCH 1/2] proc: Implement futexes JIRA: RTOS-1074 --- include/syscalls.h | 5 +- proc/Makefile | 2 +- proc/futex.c | 216 +++++++++++++++++++++++++++++++++++++++++++++ proc/futex.h | 51 +++++++++++ proc/proc.h | 1 + proc/process.c | 10 +++ proc/process.h | 4 + proc/threads.c | 25 ++++++ proc/threads.h | 3 + syscalls.c | 40 +++++++++ 10 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 proc/futex.c create mode 100644 proc/futex.h diff --git a/include/syscalls.h b/include/syscalls.h index 174e25cb6..0bd6a2e44 100644 --- a/include/syscalls.h +++ b/include/syscalls.h @@ -124,5 +124,8 @@ ID(sys_mprotect) \ \ ID(sys_statvfs) \ - ID(sys_uname) + ID(sys_uname) \ + \ + ID(phFutexWait) \ + ID(phFutexWakeup) /* clang-format on */ diff --git a/proc/Makefile b/proc/Makefile index 1d5b6689f..cb97b8c0c 100644 --- a/proc/Makefile +++ b/proc/Makefile @@ -5,7 +5,7 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)proc/, proc.o threads.o process.o name.o resource.o mutex.o cond.o userintr.o ports.o) +OBJS += $(addprefix $(PREFIX_O)proc/, proc.o threads.o process.o name.o resource.o mutex.o cond.o userintr.o ports.o futex.o) ifneq (, $(findstring NOMMU, $(CPPFLAGS))) OBJS += $(PREFIX_O)proc/msg-nommu.o diff --git a/proc/futex.c b/proc/futex.c new file mode 100644 index 000000000..9a4a766ec --- /dev/null +++ b/proc/futex.c @@ -0,0 +1,216 @@ +/* + * + * Operating system kernel + * + * Futex + * + * Copyright 2025 Phoenix Systems + * Author: Kamil Kowalczyk + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include "hal/hal.h" +#include "lib/lib.h" +#include "include/errno.h" +#include "include/time.h" +#include "process.h" +#include "threads.h" +#include "futex.h" + + +static u32 _proc_futexTableHash(addr_t address) +{ + u32 key; + + // hash the address + key = address >> 3; + key ^= key >> FUTEX_SLEEPQUEUES_BITS; + return key & FUTEX_SLEEPQUEUES_MASK; +} + + +static futex_sleepqueue_t *_proc_allocFutexSleepQueue(process_t *process, addr_t address) +{ + u32 idx, i; + + idx = _proc_futexTableHash(address); + + /* Find a free slot using linear probing */ + i = idx; + do { + if (process->futexSleepQueues[i].address == 0) { + process->futexSleepQueues[i].address = address; + return &process->futexSleepQueues[i]; + } + i = (i + 1) % FUTEX_SLEEPQUEUES_SIZE; + } while (i != idx); + + return NULL; +} + + +futex_sleepqueue_t *_proc_getFutexSleepQueue(process_t *process, addr_t address) +{ + u32 idx, i; + + idx = _proc_futexTableHash(address); + + /* Find a taken slot with the same address using linear probing */ + i = idx; + do { + if (process->futexSleepQueues[i].address == address) { + return &process->futexSleepQueues[i]; + } + else if (process->futexSleepQueues[i].address == 0) { + return NULL; + } + i = (i + 1) % FUTEX_SLEEPQUEUES_SIZE; + } while (i != idx); + + return NULL; +} + + +static bool proc_futexUnwait(futex_sleepqueue_t *sq, futex_waitctx_t *wc) +{ + bool r; + spinlock_ctx_t sc; + + hal_spinlockSet(&sq->spinlock, &sc); + r = atomic_load(&wc->thread) != NULL; + if (r) { + LIST_REMOVE(&sq->waitctxs, wc); + } + hal_spinlockClear(&sq->spinlock, &sc); + return r; +} + + +int proc_futexWait(_Atomic(u32) *address, u32 value, time_t timeout, int clockType) +{ + spinlock_ctx_t sc, sqSc; + futex_sleepqueue_t *sq; + futex_waitctx_t wc; + thread_t *current; + int err = EOK; + time_t waitTime = 0, offs; + + + if (timeout != 0) { + switch (clockType) { + case PH_CLOCK_REALTIME: + proc_gettime(&waitTime, &offs); + if (waitTime + offs > timeout) { + return -ETIME; + } + waitTime = timeout - offs; + break; + case PH_CLOCK_MONOTONIC: + proc_gettime(&waitTime, NULL); + if (waitTime > timeout) { + return -ETIME; + } + waitTime = timeout; + break; + case PH_CLOCK_RELATIVE: + proc_gettime(&waitTime, NULL); + waitTime += timeout; + break; + default: + return -EINVAL; + } + } + + current = proc_current(); + + hal_spinlockSet(¤t->process->futexSqSpinlock, &sqSc); + sq = _proc_getFutexSleepQueue(current->process, (addr_t)address); + if (sq == NULL) { + sq = _proc_allocFutexSleepQueue(current->process, (addr_t)address); + } + hal_spinlockClear(¤t->process->futexSqSpinlock, &sqSc); + if (sq == NULL) { + return -ENOMEM; + } + atomic_init(&wc.thread, NULL); + + atomic_store(&wc.thread, current); + hal_spinlockSet(&sq->spinlock, &sc); + LIST_ADD(&sq->waitctxs, &wc); + hal_spinlockClear(&sq->spinlock, &sc); + + if (atomic_load(address) != value) { + err = -EAGAIN; + proc_futexUnwait(sq, &wc); + } + else { + + if (atomic_load(&wc.thread) != NULL) { + hal_spinlockSet(&sq->spinlock, &sc); + err = proc_threadWaitInterruptible(&sq->threads, &sq->spinlock, waitTime, &sc); + hal_spinlockClear(&sq->spinlock, &sc); + } + + if (err != EOK || atomic_load(&wc.thread) != NULL) { + if (proc_futexUnwait(sq, &wc) == 0) { + err = EOK; + } + } + } + return err; +} + + +int proc_futexWakeup(process_t *process, _Atomic(u32) *address, u32 wakeCount) +{ + futex_sleepqueue_t *sq; + futex_waitctx_t *wc = NULL, *wakeupList = NULL, *tmpwc; + int i = 0, woken = 0; + spinlock_ctx_t sc, sqSc; + thread_t *tmp = NULL; + + if (wakeCount == 0) { + return 0; + } + + hal_spinlockSet(&process->futexSqSpinlock, &sqSc); + sq = _proc_getFutexSleepQueue(process, (addr_t)address); + hal_spinlockClear(&process->futexSqSpinlock, &sqSc); + if (sq == NULL) { + return 0; + } + + hal_spinlockSet(&sq->spinlock, &sc); + while ((wc = sq->waitctxs) != NULL) { + LIST_REMOVE(&sq->waitctxs, wc); + LIST_ADD(&wakeupList, wc); + i++; + if (i == wakeCount && wakeCount != FUTEX_WAKEUP_ALL) { + break; + } + } + hal_spinlockClear(&sq->spinlock, &sc); + + if (wakeupList != NULL) { + wc = wakeupList; + do { + LIB_ASSERT(wc != NULL, "wc == NULL"); + tmpwc = wc; + tmp = atomic_load(&tmpwc->thread); + wc = wc->next; + atomic_store(&tmpwc->thread, NULL); + hal_spinlockSet(&sq->spinlock, &sc); + if (proc_threadWakeupOne(tmp)) { + woken++; + } + hal_spinlockClear(&sq->spinlock, &sc); + } while (wc != NULL && wc != wakeupList); + } + + return woken; +} diff --git a/proc/futex.h b/proc/futex.h new file mode 100644 index 000000000..6e61854c3 --- /dev/null +++ b/proc/futex.h @@ -0,0 +1,51 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Futex + * + * Copyright 2025 Phoenix Systems + * Author: Kamil Kowalczyk + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _PROC_FUTEX_H_ +#define _PROC_FUTEX_H_ + +#include "include/types.h" + + +/* Implementation inspired by: https://github.com/openbsd/src/blob/master/sys/kern/sys_futex.c */ + + +#define FUTEX_SLEEPQUEUES_BITS 6 +#define FUTEX_SLEEPQUEUES_SIZE (1U << FUTEX_SLEEPQUEUES_BITS) +#define FUTEX_SLEEPQUEUES_MASK (FUTEX_SLEEPQUEUES_SIZE - 1) +#define FUTEX_WAKEUP_ALL ((u32) - 1) + + +typedef struct _futex_waitctx_t { + struct _futex_waitctx_t *prev, *next; + _Atomic(struct _thread_t *) thread; +} futex_waitctx_t; + + +typedef struct { + struct _thread_t *threads; + spinlock_t spinlock; + addr_t address; + futex_waitctx_t *waitctxs; +} futex_sleepqueue_t; + + +int proc_futexWait(_Atomic(u32) *address, u32 value, time_t timeout, int clockType); + + +int proc_futexWakeup(struct _process_t *process, _Atomic(u32) *address, u32 wakeCount); + + +#endif diff --git a/proc/proc.h b/proc/proc.h index 98ed1adfc..04006bd72 100644 --- a/proc/proc.h +++ b/proc/proc.h @@ -28,6 +28,7 @@ #include "cond.h" #include "userintr.h" #include "ports.h" +#include "futex.h" extern int _proc_init(vm_map_t *kmap, vm_object_t *kernel); diff --git a/proc/process.c b/proc/process.c index 02d2ee150..52d2205b4 100644 --- a/proc/process.c +++ b/proc/process.c @@ -93,6 +93,10 @@ static void process_destroy(process_t *p) if (imapp != NULL) { vm_mapDestroy(p, imapp); } + for (int i = 0; i < FUTEX_SLEEPQUEUES_SIZE; i++) { + hal_spinlockDestroy(&p->futexSleepQueues[i].spinlock); + } + hal_spinlockDestroy(&p->futexSqSpinlock); proc_resourcesDestroy(p); proc_portsDestroy(p); @@ -216,6 +220,12 @@ int proc_start(void (*initthr)(void *), void *arg, const char *path) proc_changeMap(process, NULL, NULL, NULL); + hal_memset(&process->futexSleepQueues, 0, sizeof(process->futexSleepQueues)); + hal_spinlockCreate(&process->futexSqSpinlock, "futex_sq.spinlock"); + for (int i = 0; i < FUTEX_SLEEPQUEUES_SIZE; i++) { + hal_spinlockCreate(&process->futexSleepQueues[i].spinlock, "futex_sleepqueue.spinlock"); + } + /* Initialize resources tree for mutex and cond handles */ _resource_init(process); process_alloc(process); diff --git a/proc/process.h b/proc/process.h index c0c881ae3..4414beb1b 100644 --- a/proc/process.h +++ b/proc/process.h @@ -23,6 +23,7 @@ #include "vm/amap.h" #include "syspage.h" #include "lib/lib.h" +#include "futex.h" #define MAX_PID MAX_ID @@ -70,6 +71,9 @@ typedef struct _process_t { void *got; hal_tls_t tls; + + spinlock_t futexSqSpinlock; + futex_sleepqueue_t futexSleepQueues[FUTEX_SLEEPQUEUES_SIZE]; } process_t; diff --git a/proc/threads.c b/proc/threads.c index 1798f268f..411670f17 100644 --- a/proc/threads.c +++ b/proc/threads.c @@ -1197,6 +1197,19 @@ static int _proc_threadWakeup(thread_t **queue) } +static int _proc_threadWakeupOne(thread_t *thread) +{ + int ret = 1; + if (thread != NULL && thread != wakeupPending) { + _proc_threadDequeue(thread); + } + else { + ret = 0; + } + return ret; +} + + int proc_threadWakeup(thread_t **queue) { int ret = 0; @@ -1209,6 +1222,18 @@ int proc_threadWakeup(thread_t **queue) } +int proc_threadWakeupOne(thread_t *thread) +{ + int ret = 0; + spinlock_ctx_t sc; + + hal_spinlockSet(&threads_common.spinlock, &sc); + ret = _proc_threadWakeupOne(thread); + hal_spinlockClear(&threads_common.spinlock, &sc); + return ret; +} + + static int _proc_threadBroadcast(thread_t **queue) { int ret = 0; diff --git a/proc/threads.h b/proc/threads.h index cf830ea25..f776de9d2 100644 --- a/proc/threads.h +++ b/proc/threads.h @@ -217,4 +217,7 @@ extern int threads_sigsuspend(unsigned int mask); extern void threads_setupUserReturn(void *retval, cpu_context_t *ctx); +extern int proc_threadWakeupOne(thread_t *thread); + + #endif diff --git a/syscalls.c b/syscalls.c index 691c41662..572a82d8e 100644 --- a/syscalls.c +++ b/syscalls.c @@ -35,6 +35,46 @@ */ +int syscalls_phFutexWait(void *ustack) +{ + process_t *process; + _Atomic(u32) *addr; + u32 value; + time_t timeout; + int clockType; + + GETFROMSTACK(ustack, _Atomic(u32) *, addr, 0); + GETFROMSTACK(ustack, u32, value, 1); + GETFROMSTACK(ustack, time_t, timeout, 2); + GETFROMSTACK(ustack, int, clockType, 3); + + process = proc_current()->process; + + if (vm_mapBelongs(process, addr, sizeof(*addr)) < 0) { + return -EFAULT; + } + + return proc_futexWait(addr, value, timeout, clockType); +} + +int syscalls_phFutexWakeup(void *ustack) +{ + process_t *process; + _Atomic(u32) *addr; + u32 n_threads; + + GETFROMSTACK(ustack, _Atomic(u32) *, addr, 0); + GETFROMSTACK(ustack, u32, n_threads, 1); + + process = proc_current()->process; + + if (vm_mapBelongs(process, addr, sizeof(*addr)) < 0) { + return -EFAULT; + } + + return proc_futexWakeup(process, addr, n_threads); +} + void syscalls_debug(void *ustack) { const char *s; From ac4f6a1d3e34c827698cf54d5f52c0a79ccab045 Mon Sep 17 00:00:00 2001 From: kamilaboba123 Date: Thu, 28 Aug 2025 11:32:30 +0200 Subject: [PATCH 2/2] proc: Move to futex-based synchronization TODO: - Remove condition variables and mutexes from resources and their implementations (proc/mutex.c, proc/cond.c) - Remove syscall implementations for phMutexCreate, phMutexLock, mutexTry, mutexUnlock, phCondCreate, phCondWait, condSignal, condBroadcast from syscalls.c JIRA: RTOS-1074 --- include/syscalls.h | 12 ++---------- proc/userintr.c | 36 +++++++++++------------------------- proc/userintr.h | 5 +++-- syscalls.c | 14 +++++++++----- 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/include/syscalls.h b/include/syscalls.h index 0bd6a2e44..4c13230ad 100644 --- a/include/syscalls.h +++ b/include/syscalls.h @@ -32,16 +32,8 @@ ID(beginthreadex) \ ID(endthread) \ ID(nsleep) \ - ID(phMutexCreate) \ - ID(phMutexLock) \ - ID(mutexTry) \ - ID(mutexUnlock) \ - ID(phCondCreate) \ - ID(phCondWait) \ - ID(condSignal) \ - ID(condBroadcast) \ - ID(resourceDestroy) \ - ID(interrupt) \ + ID(phResourceDestroy) \ + ID(phInterrupt) \ ID(portCreate) \ ID(portDestroy) \ ID(portRegister) \ diff --git a/proc/userintr.c b/proc/userintr.c index 090b00325..955f2de2d 100644 --- a/proc/userintr.c +++ b/proc/userintr.c @@ -13,11 +13,16 @@ * %LICENSE% */ +#include #include "lib/lib.h" #include "resource.h" #include "userintr.h" #include "cond.h" #include "proc.h" +#include "futex.h" + + +#define COND_BROADCAST (((u32) - 1) / 2) struct { @@ -39,10 +44,6 @@ void userintr_put(userintr_t *ui) if (rem <= 0) { hal_interruptsDeleteHandler(&ui->handler); - if (ui->cond != NULL) { - cond_put(ui->cond); - } - vm_kfree(ui); } } @@ -64,9 +65,11 @@ static int userintr_dispatch(unsigned int n, cpu_context_t *ctx, void *arg) ret = ui->f(ui->handler.n, ui->arg); userintr_common.active = NULL; - if (ret >= 0 && ui->cond != NULL) { + if (ret >= 0 && ui->condFutex != NULL) { reschedule = 1; - proc_threadBroadcast(&ui->cond->queue); + /* Enqueue signal */ + atomic_store(ui->condFutex, COND_BROADCAST); + proc_futexWakeup(ui->process, ui->condFutex, FUTEX_WAKEUP_ALL); } /* Restore process address space */ @@ -77,25 +80,14 @@ static int userintr_dispatch(unsigned int n, cpu_context_t *ctx, void *arg) } -int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *arg, handle_t c) +int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *arg, _Atomic(u32) *condFutex) { process_t *process = proc_current()->process; userintr_t *ui; - cond_t *cond = NULL; int id, res; - if (c > 0) { - cond = cond_get(c); - if (cond == NULL) { - return -EINVAL; - } - } - ui = vm_kmalloc(sizeof(*ui)); if (ui == NULL) { - if (cond != NULL) { - cond_put(cond); - } return -ENOMEM; } @@ -111,7 +103,7 @@ int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *ar ui->f = f; ui->arg = arg; ui->process = process; - ui->cond = cond; + ui->condFutex = condFutex; #ifdef __TARGET_RISCV64 /* Clear PGHD_USER attribute in interrupt handler code page (RISC-V specification forbids user code execution in kernel mode). @@ -124,9 +116,6 @@ int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *ar res = hal_interruptsSetHandler(&ui->handler); if (res != EOK) { - if (cond != NULL) { - cond_put(cond); - } vm_kfree(ui); return res; } @@ -134,9 +123,6 @@ int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *ar id = resource_alloc(process, &ui->resource); if (id < 0) { hal_interruptsDeleteHandler(&ui->handler); - if (cond != NULL) { - cond_put(cond); - } vm_kfree(ui); return -ENOMEM; } diff --git a/proc/userintr.h b/proc/userintr.h index 46fe99dad..5c15aea1f 100644 --- a/proc/userintr.h +++ b/proc/userintr.h @@ -19,6 +19,7 @@ #include "hal/hal.h" #include "cond.h" #include "resource.h" +#include "include/types.h" typedef struct _userintr_t { @@ -27,14 +28,14 @@ typedef struct _userintr_t { process_t *process; int (*f)(unsigned int, void *); void *arg; - cond_t *cond; + _Atomic(u32) *condFutex; } userintr_t; extern void userintr_put(userintr_t *ui); -extern int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *arg, handle_t c); +extern int userintr_setHandler(unsigned int n, int (*f)(unsigned int, void *), void *arg, _Atomic(u32) *condFutex); extern userintr_t *userintr_active(void); diff --git a/syscalls.c b/syscalls.c index 572a82d8e..eb2da3adf 100644 --- a/syscalls.c +++ b/syscalls.c @@ -684,7 +684,7 @@ int syscalls_condBroadcast(void *ustack) */ -int syscalls_resourceDestroy(void *ustack) +int syscalls_phResourceDestroy(void *ustack) { handle_t h; @@ -698,27 +698,31 @@ int syscalls_resourceDestroy(void *ustack) */ -int syscalls_interrupt(void *ustack) +int syscalls_phInterrupt(void *ustack) { process_t *proc = proc_current()->process; unsigned int n; void *f; void *data; - handle_t cond; handle_t *handle; int res; + _Atomic(u32) *condFutex = NULL; GETFROMSTACK(ustack, unsigned int, n, 0); GETFROMSTACK(ustack, void *, f, 1); GETFROMSTACK(ustack, void *, data, 2); - GETFROMSTACK(ustack, handle_t, cond, 3); + GETFROMSTACK(ustack, _Atomic(u32) *, condFutex, 3); GETFROMSTACK(ustack, handle_t *, handle, 4); if ((handle != NULL) && (vm_mapBelongs(proc, handle, sizeof(*handle)) < 0)) { return -EFAULT; } - res = userintr_setHandler(n, f, data, cond); + if (condFutex != NULL && vm_mapBelongs(proc, condFutex, sizeof(*condFutex)) < 0) { + return -EFAULT; + } + + res = userintr_setHandler(n, f, data, condFutex); if (res < 0) { return res; }