Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions so3/include/futex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2014-2025 Daniel Rossier <daniel.rossier@heig-vd.ch>
* Copyright (C) 2025 Jean-Pierre Miceli <jean-pierre.miceli@heig-vd.ch>
*
* 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 <timer.h>
#include <thread.h>
#include <list.h>
#include <spinlock.h>
#include <syscall.h>

/* 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 */
10 changes: 10 additions & 0 deletions so3/include/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 0 additions & 4 deletions so3/include/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
6 changes: 6 additions & 0 deletions so3/include/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <memory.h>
#include <signal.h>
#include <ptrace.h>
#include <spinlock.h>
#include <futex.h>
#include <mutex.h>
#include <syscall.h>

Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions so3/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ obj-y += main.o \
thread.o \
schedule.o \
mutex.o \
futex.o \
spinlock.o \
syscalls.o \
softirq.o \
Expand Down
162 changes: 162 additions & 0 deletions so3/kernel/futex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (C) 2025 Jean-Pierre Miceli <jean-pierre.miceli@heig-vd.ch>
*
* 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 <process.h>
#include <heap.h>
#include <errno.h>
#include <futex.h>

/**
* 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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also returned -EINVAL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is to indicate that calloc failed. Usually we put BUG().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense :-)


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;
}
21 changes: 0 additions & 21 deletions so3/kernel/mutex.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&current()->pcb->lock[number]);
return 0;
}

SYSCALL_DEFINE1(mutex_unlock, unsigned long, number)
{
if (number >= N_MUTEX) {
return -EINVAL;
}
mutex_unlock(&current()->pcb->lock[number]);
return 0;
}

void mutex_init(struct mutex *lock)
{
memset(lock, 0, sizeof(struct mutex));
Expand Down
4 changes: 4 additions & 0 deletions so3/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
Expand Down
1 change: 1 addition & 0 deletions so3/kernel/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <signal.h>
#include <timer.h>
#include <net.h>
#include <futex.h>
#include <syscall.h>

extern void __get_syscall_args_ext(uint32_t *syscall_no);
Expand Down
1 change: 1 addition & 0 deletions so3/syscall.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ newfstatat
mmap
mmap2
nanosleep
futex
pipe IPC_PIPE
pipe2 IPC_PIPE
rt_sigaction IPC_SIGNAL
Expand Down