mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
selftests/futex: Add futex_numa_mpol
Test the basic functionality for the NUMA and MPOL flags: - FUTEX2_NUMA should take the NUMA node which is after the uaddr and use it. - Only update the node if FUTEX_NO_NODE was set by the user - FUTEX2_MPOL should use the memory based on the policy. I attempted to set the node with mbind() and then use this with MPOL but this fails and futex falls back to the default node for the current CPU. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20250416162921.513656-22-bigeasy@linutronix.de
This commit is contained in:
parent
cda95faef7
commit
3163369407
5 changed files with 290 additions and 1 deletions
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
futex_numa_mpol
|
||||
futex_priv_hash
|
||||
futex_requeue
|
||||
futex_requeue_pi
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
INCLUDES := -I../include -I../../ $(KHDR_INCLUDES)
|
||||
CFLAGS := $(CFLAGS) -g -O2 -Wall -pthread $(INCLUDES) $(KHDR_INCLUDES)
|
||||
LDLIBS := -lpthread -lrt
|
||||
LDLIBS := -lpthread -lrt -lnuma
|
||||
|
||||
LOCAL_HDRS := \
|
||||
../include/futextest.h \
|
||||
|
@ -18,6 +18,7 @@ TEST_GEN_PROGS := \
|
|||
futex_wait \
|
||||
futex_requeue \
|
||||
futex_priv_hash \
|
||||
futex_numa_mpol \
|
||||
futex_waitv
|
||||
|
||||
TEST_PROGS := run.sh
|
||||
|
|
232
tools/testing/selftests/futex/functional/futex_numa_mpol.c
Normal file
232
tools/testing/selftests/futex/functional/futex_numa_mpol.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2025 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
|
||||
#include <linux/futex.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "futextest.h"
|
||||
#include "futex2test.h"
|
||||
|
||||
#define MAX_THREADS 64
|
||||
|
||||
static pthread_barrier_t barrier_main;
|
||||
static pthread_t threads[MAX_THREADS];
|
||||
|
||||
struct thread_args {
|
||||
void *futex_ptr;
|
||||
unsigned int flags;
|
||||
int result;
|
||||
};
|
||||
|
||||
static struct thread_args thread_args[MAX_THREADS];
|
||||
|
||||
#ifndef FUTEX_NO_NODE
|
||||
#define FUTEX_NO_NODE (-1)
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_MPOL
|
||||
#define FUTEX2_MPOL 0x08
|
||||
#endif
|
||||
|
||||
static void *thread_lock_fn(void *arg)
|
||||
{
|
||||
struct thread_args *args = arg;
|
||||
int ret;
|
||||
|
||||
pthread_barrier_wait(&barrier_main);
|
||||
ret = futex2_wait(args->futex_ptr, 0, args->flags, NULL, 0);
|
||||
args->result = ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void create_max_threads(void *futex_ptr)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < MAX_THREADS; i++) {
|
||||
thread_args[i].futex_ptr = futex_ptr;
|
||||
thread_args[i].flags = FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA;
|
||||
thread_args[i].result = 0;
|
||||
ret = pthread_create(&threads[i], NULL, thread_lock_fn, &thread_args[i]);
|
||||
if (ret) {
|
||||
error("pthread_create failed\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void join_max_threads(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < MAX_THREADS; i++) {
|
||||
ret = pthread_join(threads[i], NULL);
|
||||
if (ret) {
|
||||
error("pthread_join failed for thread %d\n", errno, i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flags)
|
||||
{
|
||||
int to_wake, ret, i, need_exit = 0;
|
||||
|
||||
pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
|
||||
create_max_threads(futex_ptr);
|
||||
pthread_barrier_wait(&barrier_main);
|
||||
to_wake = MAX_THREADS;
|
||||
|
||||
do {
|
||||
ret = futex2_wake(futex_ptr, to_wake, futex_flags);
|
||||
if (must_fail) {
|
||||
if (ret < 0)
|
||||
break;
|
||||
fail("Should fail, but didn't\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ret < 0) {
|
||||
error("Failed futex2_wake(%d)\n", errno, to_wake);
|
||||
exit(1);
|
||||
}
|
||||
if (!ret)
|
||||
usleep(50);
|
||||
to_wake -= ret;
|
||||
|
||||
} while (to_wake);
|
||||
join_max_threads();
|
||||
|
||||
for (i = 0; i < MAX_THREADS; i++) {
|
||||
if (must_fail && thread_args[i].result != -1) {
|
||||
fail("Thread %d should fail but succeeded (%d)\n", i, thread_args[i].result);
|
||||
need_exit = 1;
|
||||
}
|
||||
if (!must_fail && thread_args[i].result != 0) {
|
||||
fail("Thread %d failed (%d)\n", i, thread_args[i].result);
|
||||
need_exit = 1;
|
||||
}
|
||||
}
|
||||
if (need_exit)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void test_futex(void *futex_ptr, int must_fail)
|
||||
{
|
||||
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
|
||||
}
|
||||
|
||||
static void test_futex_mpol(void *futex_ptr, int must_fail)
|
||||
{
|
||||
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
|
||||
}
|
||||
|
||||
static void usage(char *prog)
|
||||
{
|
||||
printf("Usage: %s\n", prog);
|
||||
printf(" -c Use color\n");
|
||||
printf(" -h Display this help message\n");
|
||||
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
|
||||
VQUIET, VCRITICAL, VINFO);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct futex32_numa *futex_numa;
|
||||
int mem_size, i;
|
||||
void *futex_ptr;
|
||||
char c;
|
||||
|
||||
while ((c = getopt(argc, argv, "chv:")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
log_color(1);
|
||||
break;
|
||||
case 'h':
|
||||
usage(basename(argv[0]));
|
||||
exit(0);
|
||||
break;
|
||||
case 'v':
|
||||
log_verbosity(atoi(optarg));
|
||||
break;
|
||||
default:
|
||||
usage(basename(argv[0]));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
mem_size = sysconf(_SC_PAGE_SIZE);
|
||||
futex_ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
if (futex_ptr == MAP_FAILED) {
|
||||
error("mmap() for %d bytes failed\n", errno, mem_size);
|
||||
return 1;
|
||||
}
|
||||
futex_numa = futex_ptr;
|
||||
|
||||
info("Regular test\n");
|
||||
futex_numa->futex = 0;
|
||||
futex_numa->numa = FUTEX_NO_NODE;
|
||||
test_futex(futex_ptr, 0);
|
||||
|
||||
if (futex_numa->numa == FUTEX_NO_NODE) {
|
||||
fail("NUMA node is left unitiliazed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
info("Memory too small\n");
|
||||
test_futex(futex_ptr + mem_size - 4, 1);
|
||||
|
||||
info("Memory out of range\n");
|
||||
test_futex(futex_ptr + mem_size, 1);
|
||||
|
||||
futex_numa->numa = FUTEX_NO_NODE;
|
||||
mprotect(futex_ptr, mem_size, PROT_READ);
|
||||
info("Memory, RO\n");
|
||||
test_futex(futex_ptr, 1);
|
||||
|
||||
mprotect(futex_ptr, mem_size, PROT_NONE);
|
||||
info("Memory, no access\n");
|
||||
test_futex(futex_ptr, 1);
|
||||
|
||||
mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
|
||||
info("Memory back to RW\n");
|
||||
test_futex(futex_ptr, 0);
|
||||
|
||||
/* MPOL test. Does not work as expected */
|
||||
for (i = 0; i < 4; i++) {
|
||||
unsigned long nodemask;
|
||||
int ret;
|
||||
|
||||
nodemask = 1 << i;
|
||||
ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
|
||||
sizeof(nodemask) * 8, 0);
|
||||
if (ret == 0) {
|
||||
info("Node %d test\n", i);
|
||||
futex_numa->futex = 0;
|
||||
futex_numa->numa = FUTEX_NO_NODE;
|
||||
|
||||
ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
|
||||
if (ret < 0)
|
||||
error("Failed to wake 0 with MPOL.\n", errno);
|
||||
if (0)
|
||||
test_futex_mpol(futex_numa, 0);
|
||||
if (futex_numa->numa != i) {
|
||||
fail("Returned NUMA node is %d expected %d\n",
|
||||
futex_numa->numa, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -86,3 +86,6 @@ echo
|
|||
echo
|
||||
./futex_priv_hash $COLOR
|
||||
./futex_priv_hash -g $COLOR
|
||||
|
||||
echo
|
||||
./futex_numa_mpol $COLOR
|
||||
|
|
|
@ -18,14 +18,43 @@ struct futex_waitv {
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifndef __NR_futex_wake
|
||||
#define __NR_futex_wake 454
|
||||
#endif
|
||||
|
||||
#ifndef __NR_futex_wait
|
||||
#define __NR_futex_wait 455
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_SIZE_U32
|
||||
#define FUTEX2_SIZE_U32 0x02
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_NUMA
|
||||
#define FUTEX2_NUMA 0x04
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_MPOL
|
||||
#define FUTEX2_MPOL 0x08
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_PRIVATE
|
||||
#define FUTEX2_PRIVATE FUTEX_PRIVATE_FLAG
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX2_NO_NODE
|
||||
#define FUTEX_NO_NODE (-1)
|
||||
#endif
|
||||
|
||||
#ifndef FUTEX_32
|
||||
#define FUTEX_32 FUTEX2_SIZE_U32
|
||||
#endif
|
||||
|
||||
struct futex32_numa {
|
||||
futex_t futex;
|
||||
futex_t numa;
|
||||
};
|
||||
|
||||
/**
|
||||
* futex_waitv - Wait at multiple futexes, wake on any
|
||||
* @waiters: Array of waiters
|
||||
|
@ -38,3 +67,26 @@ static inline int futex_waitv(volatile struct futex_waitv *waiters, unsigned lon
|
|||
{
|
||||
return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid);
|
||||
}
|
||||
|
||||
/*
|
||||
* futex_wait() - block on uaddr with optional timeout
|
||||
* @val: Expected value
|
||||
* @flags: FUTEX2 flags
|
||||
* @timeout: Relative timeout
|
||||
* @clockid: Clock id for the timeout
|
||||
*/
|
||||
static inline int futex2_wait(void *uaddr, long val, unsigned int flags,
|
||||
struct timespec *timeout, clockid_t clockid)
|
||||
{
|
||||
return syscall(__NR_futex_wait, uaddr, val, ~0U, flags, timeout, clockid);
|
||||
}
|
||||
|
||||
/*
|
||||
* futex2_wake() - Wake a number of futexes
|
||||
* @nr: Number of threads to wake at most
|
||||
* @flags: FUTEX2 flags
|
||||
*/
|
||||
static inline int futex2_wake(void *uaddr, int nr, unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_futex_wake, uaddr, ~0U, nr, flags);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue