Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/futex/functional/futex_priv_hash.c
29271 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2025 Sebastian Andrzej Siewior <[email protected]>
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
14
#include <linux/prctl.h>
15
#include <sys/prctl.h>
16
17
#include "../../kselftest_harness.h"
18
19
#define MAX_THREADS 64
20
21
static pthread_barrier_t barrier_main;
22
static pthread_mutex_t global_lock;
23
static pthread_t threads[MAX_THREADS];
24
static int counter;
25
26
#ifndef PR_FUTEX_HASH
27
#define PR_FUTEX_HASH 78
28
# define PR_FUTEX_HASH_SET_SLOTS 1
29
# define PR_FUTEX_HASH_GET_SLOTS 2
30
#endif
31
32
static int futex_hash_slots_set(unsigned int slots)
33
{
34
return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, slots, 0);
35
}
36
37
static int futex_hash_slots_get(void)
38
{
39
return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS);
40
}
41
42
static void futex_hash_slots_set_verify(int slots)
43
{
44
int ret;
45
46
ret = futex_hash_slots_set(slots);
47
if (ret != 0) {
48
ksft_test_result_fail("Failed to set slots to %d: %m\n", slots);
49
ksft_finished();
50
}
51
ret = futex_hash_slots_get();
52
if (ret != slots) {
53
ksft_test_result_fail("Set %d slots but PR_FUTEX_HASH_GET_SLOTS returns: %d, %m\n",
54
slots, ret);
55
ksft_finished();
56
}
57
ksft_test_result_pass("SET and GET slots %d passed\n", slots);
58
}
59
60
static void futex_hash_slots_set_must_fail(int slots)
61
{
62
int ret;
63
64
ret = futex_hash_slots_set(slots);
65
ksft_test_result(ret < 0, "futex_hash_slots_set(%d)\n",
66
slots);
67
}
68
69
static void *thread_return_fn(void *arg)
70
{
71
return NULL;
72
}
73
74
static void *thread_lock_fn(void *arg)
75
{
76
pthread_barrier_wait(&barrier_main);
77
78
pthread_mutex_lock(&global_lock);
79
counter++;
80
usleep(20);
81
pthread_mutex_unlock(&global_lock);
82
return NULL;
83
}
84
85
static void create_max_threads(void *(*thread_fn)(void *))
86
{
87
int i, ret;
88
89
for (i = 0; i < MAX_THREADS; i++) {
90
ret = pthread_create(&threads[i], NULL, thread_fn, NULL);
91
if (ret)
92
ksft_exit_fail_msg("pthread_create failed: %m\n");
93
}
94
}
95
96
static void join_max_threads(void)
97
{
98
int i, ret;
99
100
for (i = 0; i < MAX_THREADS; i++) {
101
ret = pthread_join(threads[i], NULL);
102
if (ret)
103
ksft_exit_fail_msg("pthread_join failed for thread %d\n", i);
104
}
105
}
106
107
#define SEC_IN_NSEC 1000000000
108
#define MSEC_IN_NSEC 1000000
109
110
static void futex_dummy_op(void)
111
{
112
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
113
struct timespec timeout;
114
int ret;
115
116
pthread_mutex_lock(&lock);
117
clock_gettime(CLOCK_REALTIME, &timeout);
118
timeout.tv_nsec += 100 * MSEC_IN_NSEC;
119
if (timeout.tv_nsec >= SEC_IN_NSEC) {
120
timeout.tv_nsec -= SEC_IN_NSEC;
121
timeout.tv_sec++;
122
}
123
ret = pthread_mutex_timedlock(&lock, &timeout);
124
if (ret == 0)
125
ksft_exit_fail_msg("Successfully locked an already locked mutex.\n");
126
127
if (ret != ETIMEDOUT)
128
ksft_exit_fail_msg("pthread_mutex_timedlock() did not timeout: %d.\n", ret);
129
}
130
131
static const char *test_msg_auto_create = "Automatic hash bucket init on thread creation.\n";
132
static const char *test_msg_auto_inc = "Automatic increase with more than 16 CPUs\n";
133
134
TEST(priv_hash)
135
{
136
int futex_slots1, futex_slotsn, online_cpus;
137
pthread_mutexattr_t mutex_attr_pi;
138
int ret, retry = 20;
139
140
ret = pthread_mutexattr_init(&mutex_attr_pi);
141
ret |= pthread_mutexattr_setprotocol(&mutex_attr_pi, PTHREAD_PRIO_INHERIT);
142
ret |= pthread_mutex_init(&global_lock, &mutex_attr_pi);
143
if (ret != 0) {
144
ksft_exit_fail_msg("Failed to initialize pthread mutex.\n");
145
}
146
/* First thread, expect to be 0, not yet initialized */
147
ret = futex_hash_slots_get();
148
if (ret != 0)
149
ksft_exit_fail_msg("futex_hash_slots_get() failed: %d, %m\n", ret);
150
151
ksft_test_result_pass("Basic get slots and immutable status.\n");
152
ret = pthread_create(&threads[0], NULL, thread_return_fn, NULL);
153
if (ret != 0)
154
ksft_exit_fail_msg("pthread_create() failed: %d, %m\n", ret);
155
156
ret = pthread_join(threads[0], NULL);
157
if (ret != 0)
158
ksft_exit_fail_msg("pthread_join() failed: %d, %m\n", ret);
159
160
/* First thread, has to initialize private hash */
161
futex_slots1 = futex_hash_slots_get();
162
if (futex_slots1 <= 0) {
163
ksft_print_msg("Current hash buckets: %d\n", futex_slots1);
164
ksft_exit_fail_msg("%s", test_msg_auto_create);
165
}
166
167
ksft_test_result_pass("%s", test_msg_auto_create);
168
169
online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
170
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
171
if (ret != 0)
172
ksft_exit_fail_msg("pthread_barrier_init failed: %m.\n");
173
174
ret = pthread_mutex_lock(&global_lock);
175
if (ret != 0)
176
ksft_exit_fail_msg("pthread_mutex_lock failed: %m.\n");
177
178
counter = 0;
179
create_max_threads(thread_lock_fn);
180
pthread_barrier_wait(&barrier_main);
181
182
/*
183
* The current default size of hash buckets is 16. The auto increase
184
* works only if more than 16 CPUs are available.
185
*/
186
ksft_print_msg("Online CPUs: %d\n", online_cpus);
187
if (online_cpus > 16) {
188
retry_getslots:
189
futex_slotsn = futex_hash_slots_get();
190
if (futex_slotsn < 0 || futex_slots1 == futex_slotsn) {
191
retry--;
192
/*
193
* Auto scaling on thread creation can be slightly delayed
194
* because it waits for a RCU grace period twice. The new
195
* private hash is assigned upon the first futex operation
196
* after grace period.
197
* To cover all this for testing purposes the function
198
* below will acquire a lock and acquire it again with a
199
* 100ms timeout which must timeout. This ensures we
200
* sleep for 100ms and issue a futex operation.
201
*/
202
if (retry > 0) {
203
futex_dummy_op();
204
goto retry_getslots;
205
}
206
ksft_print_msg("Expected increase of hash buckets but got: %d -> %d\n",
207
futex_slots1, futex_slotsn);
208
ksft_exit_fail_msg("%s", test_msg_auto_inc);
209
}
210
ksft_test_result_pass("%s", test_msg_auto_inc);
211
} else {
212
ksft_test_result_skip("%s", test_msg_auto_inc);
213
}
214
ret = pthread_mutex_unlock(&global_lock);
215
216
/* Once the user changes it, it has to be what is set */
217
futex_hash_slots_set_verify(2);
218
futex_hash_slots_set_verify(4);
219
futex_hash_slots_set_verify(8);
220
futex_hash_slots_set_verify(32);
221
futex_hash_slots_set_verify(16);
222
223
ret = futex_hash_slots_set(15);
224
ksft_test_result(ret < 0, "Use 15 slots\n");
225
226
futex_hash_slots_set_verify(2);
227
join_max_threads();
228
ksft_test_result(counter == MAX_THREADS, "Created and waited for %d of %d threads\n",
229
counter, MAX_THREADS);
230
counter = 0;
231
/* Once the user set something, auto resize must be disabled */
232
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
233
234
create_max_threads(thread_lock_fn);
235
join_max_threads();
236
237
ret = futex_hash_slots_get();
238
ksft_test_result(ret == 2, "No more auto-resize after manual setting, got %d\n",
239
ret);
240
241
futex_hash_slots_set_must_fail(1 << 29);
242
futex_hash_slots_set_verify(4);
243
244
/*
245
* Once the global hash has been requested, then this requested can not
246
* be undone.
247
*/
248
ret = futex_hash_slots_set(0);
249
ksft_test_result(ret == 0, "Global hash request\n");
250
if (ret != 0)
251
return;
252
253
futex_hash_slots_set_must_fail(4);
254
futex_hash_slots_set_must_fail(8);
255
futex_hash_slots_set_must_fail(8);
256
futex_hash_slots_set_must_fail(0);
257
futex_hash_slots_set_must_fail(6);
258
259
ret = pthread_barrier_init(&barrier_main, NULL, MAX_THREADS);
260
if (ret != 0)
261
ksft_exit_fail_msg("pthread_barrier_init failed: %m\n");
262
263
create_max_threads(thread_lock_fn);
264
join_max_threads();
265
266
ret = futex_hash_slots_get();
267
ksft_test_result(ret == 0, "Continue to use global hash\n");
268
}
269
270
TEST_HARNESS_MAIN
271
272