File Index
Files and directories
Sign in
FILE

futex_numa_mpol.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/futex/functional/futex_numa_mpol.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2025 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
  4 */
  5
  6#define _GNU_SOURCE
  7
  8#include <errno.h>
  9#include <pthread.h>
 10#include <stdio.h>
 11#include <stdlib.h>
 12#include <unistd.h>
 13#include <numa.h>
 14#include <numaif.h>
 15
 16#include <linux/futex.h>
 17#include <sys/mman.h>
 18
 19#include "futextest.h"
 20#include "futex2test.h"
 21#include "kselftest_harness.h"
 22
 23#define MAX_THREADS	64
 24
 25static pthread_barrier_t barrier_main;
 26static pthread_t threads[MAX_THREADS];
 27
 28struct thread_args {
 29	void *futex_ptr;
 30	unsigned int flags;
 31	int result;
 32};
 33
 34static struct thread_args thread_args[MAX_THREADS];
 35
 36#ifndef FUTEX_NO_NODE
 37#define FUTEX_NO_NODE (-1)
 38#endif
 39
 40#ifndef FUTEX2_MPOL
 41#define FUTEX2_MPOL	0x08
 42#endif
 43
 44static void *thread_lock_fn(void *arg)
 45{
 46	struct thread_args *args = arg;
 47	int ret;
 48
 49	pthread_barrier_wait(&barrier_main);
 50	ret = futex2_wait(args->futex_ptr, 0, args->flags, NULL, 0);
 51	args->result = ret;
 52	return NULL;
 53}
 54
 55static void create_max_threads(void *futex_ptr)
 56{
 57	int i, ret;
 58
 59	for (i = 0; i < MAX_THREADS; i++) {
 60		thread_args[i].futex_ptr = futex_ptr;
 61		thread_args[i].flags = FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA;
 62		thread_args[i].result = 0;
 63		ret = pthread_create(&threads[i], NULL, thread_lock_fn, &thread_args[i]);
 64		if (ret)
 65			ksft_exit_fail_msg("pthread_create failed\n");
 66	}
 67}
 68
 69static void join_max_threads(void)
 70{
 71	int i, ret;
 72
 73	for (i = 0; i < MAX_THREADS; i++) {
 74		ret = pthread_join(threads[i], NULL);
 75		if (ret)
 76			ksft_exit_fail_msg("pthread_join failed for thread %d\n", i);
 77	}
 78}
 79
 80static void __test_futex(void *futex_ptr, int err_value, unsigned int futex_flags)
 81{
 82	int to_wake, ret, i, need_exit = 0;
 83
 84	pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
 85	create_max_threads(futex_ptr);
 86	pthread_barrier_wait(&barrier_main);
 87	to_wake = MAX_THREADS;
 88
 89	do {
 90		ret = futex2_wake(futex_ptr, to_wake, futex_flags);
 91
 92		if (err_value) {
 93			if (ret >= 0)
 94				ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
 95						   to_wake, futex_flags);
 96
 97			if (errno != err_value)
 98				ksft_exit_fail_msg("futex2_wake(%d, 0x%x) expected error was %d, but returned %d (%s)\n",
 99						   to_wake, futex_flags, err_value, errno, strerror(errno));
100
101			break;
102		}
103		if (ret < 0) {
104			ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
105					   to_wake, futex_flags);
106		}
107		if (!ret)
108			usleep(50);
109		to_wake -= ret;
110
111	} while (to_wake);
112	join_max_threads();
113
114	for (i = 0; i < MAX_THREADS; i++) {
115		if (err_value && thread_args[i].result != -1) {
116			ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
117				       i, thread_args[i].result);
118			need_exit = 1;
119		}
120		if (!err_value && thread_args[i].result != 0) {
121			ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
122			need_exit = 1;
123		}
124	}
125	if (need_exit)
126		ksft_exit_fail_msg("Aborting due to earlier errors.\n");
127}
128
129static void test_futex(void *futex_ptr, int err_value)
130{
131	__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
132}
133
134TEST(futex_numa_mpol)
135{
136	struct futex32_numa *futex_numa;
137	void *futex_ptr;
138	int mem_size;
139
140	mem_size = sysconf(_SC_PAGE_SIZE);
141	futex_ptr = mmap(NULL, mem_size * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
142	if (futex_ptr == MAP_FAILED)
143		ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size);
144
145	/* Create an invalid memory region for the "Memory out of range" test */
146	mprotect(futex_ptr + mem_size, mem_size, PROT_NONE);
147
148	futex_numa = futex_ptr;
149
150	ksft_print_msg("Regular test\n");
151	futex_numa->futex = 0;
152	futex_numa->numa = FUTEX_NO_NODE;
153	test_futex(futex_ptr, 0);
154
155	if (futex_numa->numa == FUTEX_NO_NODE)
156		ksft_exit_fail_msg("NUMA node is left uninitialized\n");
157
158	/* FUTEX2_NUMA futex must be 8-byte aligned */
159	ksft_print_msg("Mis-aligned futex\n");
160	test_futex(futex_ptr + mem_size - 4, EINVAL);
161
162	ksft_print_msg("Memory out of range\n");
163	test_futex(futex_ptr + mem_size, EFAULT);
164
165	futex_numa->numa = FUTEX_NO_NODE;
166	mprotect(futex_ptr, mem_size, PROT_READ);
167	ksft_print_msg("Memory, RO\n");
168	test_futex(futex_ptr, EFAULT);
169
170	mprotect(futex_ptr, mem_size, PROT_NONE);
171	ksft_print_msg("Memory, no access\n");
172	test_futex(futex_ptr, EFAULT);
173
174	mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
175	ksft_print_msg("Memory back to RW\n");
176	test_futex(futex_ptr, 0);
177
178	ksft_test_result_pass("futex2 memory boundary tests passed\n");
179
180	/* MPOL test. Does not work as expected */
181#ifdef LIBNUMA_VER_SUFFICIENT
182	for (int i = 0; i < 4; i++) {
183		unsigned long nodemask;
184		int ret;
185
186		nodemask = 1 << i;
187		ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
188			    sizeof(nodemask) * 8, 0);
189		if (ret == 0) {
190			ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0);
191			if (ret != 0)
192				ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno);
193
194			ksft_print_msg("Node %d test\n", i);
195			futex_numa->futex = 0;
196			futex_numa->numa = FUTEX_NO_NODE;
197
198			ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
199			if (ret < 0)
200				ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n");
201			if (futex_numa->numa != i) {
202				ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
203						   futex_numa->numa, i);
204			}
205		}
206	}
207	ksft_test_result_pass("futex2 MPOL hints test passed\n");
208#else
209	ksft_test_result_skip("futex2 MPOL hints test requires libnuma 2.0.16+\n");
210#endif
211	munmap(futex_ptr, mem_size * 2);
212}
213
214TEST_HARNESS_MAIN
~/ Read-only file preview.