File Index
Files and directories
Sign in
FILE

futex_requeue_pi.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/futex/functional/futex_requeue_pi.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/******************************************************************************
  3 *
  4 *   Copyright © International Business Machines  Corp., 2006-2008
  5 *
  6 * DESCRIPTION
  7 *      This test excercises the futex syscall op codes needed for requeuing
  8 *      priority inheritance aware POSIX condition variables and mutexes.
  9 *
 10 * AUTHORS
 11 *      Sripathi Kodi <sripathik@in.ibm.com>
 12 *      Darren Hart <dvhart@linux.intel.com>
 13 *
 14 * HISTORY
 15 *      2008-Jan-13: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
 16 *      2009-Nov-6: futex test adaptation by Darren Hart <dvhart@linux.intel.com>
 17 *
 18 *****************************************************************************/
 19
 20#define _GNU_SOURCE
 21
 22#include <errno.h>
 23#include <limits.h>
 24#include <pthread.h>
 25#include <stdio.h>
 26#include <stdlib.h>
 27#include <signal.h>
 28#include <string.h>
 29
 30#include "atomic.h"
 31#include "futextest.h"
 32#include "kselftest_harness.h"
 33
 34#define MAX_WAKE_ITERS 1000
 35#define THREAD_MAX 10
 36#define SIGNAL_PERIOD_US 100
 37
 38atomic_t waiters_blocked = ATOMIC_INITIALIZER;
 39atomic_t waiters_woken = ATOMIC_INITIALIZER;
 40
 41futex_t f1 = FUTEX_INITIALIZER;
 42futex_t f2 = FUTEX_INITIALIZER;
 43futex_t wake_complete = FUTEX_INITIALIZER;
 44
 45struct thread_arg {
 46	long id;
 47	struct timespec *timeout;
 48	int lock;
 49	int ret;
 50};
 51#define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
 52
 53FIXTURE(args)
 54{
 55};
 56
 57FIXTURE_SETUP(args)
 58{
 59};
 60
 61FIXTURE_TEARDOWN(args)
 62{
 63};
 64
 65FIXTURE_VARIANT(args)
 66{
 67	long timeout_ns;
 68	bool broadcast;
 69	bool owner;
 70	bool locked;
 71};
 72
 73/*
 74 * For a given timeout value, this macro creates a test input with all the
 75 * possible combinations of valid arguments
 76 */
 77#define FIXTURE_VARIANT_ADD_TIMEOUT(timeout)		\
 78							\
 79FIXTURE_VARIANT_ADD(args, t_##timeout)			\
 80{							\
 81	.timeout_ns = timeout,				\
 82};							\
 83							\
 84FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast)	\
 85{							\
 86	.timeout_ns = timeout,				\
 87	.broadcast = true,				\
 88};							\
 89							\
 90FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast_locked) \
 91{							\
 92	.timeout_ns = timeout,				\
 93	.broadcast = true,				\
 94	.locked = true,					\
 95};							\
 96							\
 97FIXTURE_VARIANT_ADD(args, t_##timeout##_broadcast_owner) \
 98{							\
 99	.timeout_ns = timeout,				\
100	.broadcast = true,				\
101	.owner = true,					\
102};							\
103							\
104FIXTURE_VARIANT_ADD(args, t_##timeout##_locked)		\
105{							\
106	.timeout_ns = timeout,				\
107	.locked = true,					\
108};							\
109							\
110FIXTURE_VARIANT_ADD(args, t_##timeout##_owner)		\
111{							\
112	.timeout_ns = timeout,				\
113	.owner = true,					\
114};							\
115
116FIXTURE_VARIANT_ADD_TIMEOUT(0);
117FIXTURE_VARIANT_ADD_TIMEOUT(5000);
118FIXTURE_VARIANT_ADD_TIMEOUT(500000);
119FIXTURE_VARIANT_ADD_TIMEOUT(2000000000);
120
121int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
122		     int policy, int prio)
123{
124	int ret;
125	struct sched_param schedp;
126	pthread_attr_t attr;
127
128	pthread_attr_init(&attr);
129	memset(&schedp, 0, sizeof(schedp));
130
131	ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
132	if (ret) {
133		ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
134		return -1;
135	}
136
137	ret = pthread_attr_setschedpolicy(&attr, policy);
138	if (ret) {
139		ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
140		return -1;
141	}
142
143	schedp.sched_priority = prio;
144	ret = pthread_attr_setschedparam(&attr, &schedp);
145	if (ret) {
146		ksft_exit_fail_msg("pthread_attr_setschedparam\n");
147		return -1;
148	}
149
150	ret = pthread_create(pth, &attr, func, arg);
151	if (ret) {
152		ksft_exit_fail_msg("pthread_create\n");
153		return -1;
154	}
155	return 0;
156}
157
158
159void *waiterfn(void *arg)
160{
161	struct thread_arg *args = (struct thread_arg *)arg;
162	futex_t old_val;
163
164	ksft_print_dbg_msg("Waiter %ld: running\n", args->id);
165	/* Each thread sleeps for a different amount of time
166	 * This is to avoid races, because we don't lock the
167	 * external mutex here */
168	usleep(1000 * (long)args->id);
169
170	old_val = f1;
171	atomic_inc(&waiters_blocked);
172	ksft_print_dbg_msg("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
173	     &f1, f1, &f2);
174	args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout,
175					  FUTEX_PRIVATE_FLAG);
176
177	ksft_print_dbg_msg("waiter %ld woke with %d %s\n", args->id, args->ret,
178	     args->ret < 0 ? strerror(errno) : "");
179	atomic_inc(&waiters_woken);
180	if (args->ret < 0) {
181		if (args->timeout && errno == ETIMEDOUT)
182			args->ret = 0;
183		else {
184			ksft_exit_fail_msg("futex_wait_requeue_pi\n");
185		}
186		futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
187	}
188	futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
189
190	ksft_print_dbg_msg("Waiter %ld: exiting with %d\n", args->id, args->ret);
191	pthread_exit((void *)&args->ret);
192}
193
194void *broadcast_wakerfn(void *arg)
195{
196	struct thread_arg *args = (struct thread_arg *)arg;
197	int nr_requeue = INT_MAX;
198	int task_count = 0;
199	futex_t old_val;
200	int nr_wake = 1;
201	int i = 0;
202
203	ksft_print_dbg_msg("Waker: waiting for waiters to block\n");
204	while (waiters_blocked.val < THREAD_MAX)
205		usleep(1000);
206	usleep(1000);
207
208	ksft_print_dbg_msg("Waker: Calling broadcast\n");
209	if (args->lock) {
210		ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2);
211		futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
212	}
213 continue_requeue:
214	old_val = f1;
215	args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue,
216				   FUTEX_PRIVATE_FLAG);
217	if (args->ret < 0) {
218		ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
219	} else if (++i < MAX_WAKE_ITERS) {
220		task_count += args->ret;
221		if (task_count < THREAD_MAX - waiters_woken.val)
222			goto continue_requeue;
223	} else {
224		ksft_exit_fail_msg("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
225		       MAX_WAKE_ITERS, task_count, THREAD_MAX);
226	}
227
228	futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG);
229
230	if (args->lock)
231		futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
232
233	if (args->ret > 0)
234		args->ret = task_count;
235
236	ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret);
237	pthread_exit((void *)&args->ret);
238}
239
240void *signal_wakerfn(void *arg)
241{
242	struct thread_arg *args = (struct thread_arg *)arg;
243	unsigned int old_val;
244	int nr_requeue = 0;
245	int task_count = 0;
246	int nr_wake = 1;
247	int i = 0;
248
249	ksft_print_dbg_msg("Waker: waiting for waiters to block\n");
250	while (waiters_blocked.val < THREAD_MAX)
251		usleep(1000);
252	usleep(1000);
253
254	while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) {
255		ksft_print_dbg_msg("task_count: %d, waiters_woken: %d\n",
256		     task_count, waiters_woken.val);
257		if (args->lock) {
258			ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
259			    f2, &f2);
260			futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
261		}
262		ksft_print_dbg_msg("Waker: Calling signal\n");
263		/* cond_signal */
264		old_val = f1;
265		args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2,
266						 nr_wake, nr_requeue,
267						 FUTEX_PRIVATE_FLAG);
268		if (args->ret < 0)
269			args->ret = -errno;
270		ksft_print_dbg_msg("futex: %x\n", f2);
271		if (args->lock) {
272			ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
273			    f2, &f2);
274			futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
275		}
276		ksft_print_dbg_msg("futex: %x\n", f2);
277		if (args->ret < 0)
278			ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
279
280		task_count += args->ret;
281		usleep(SIGNAL_PERIOD_US);
282		i++;
283		/* we have to loop at least THREAD_MAX times */
284		if (i > MAX_WAKE_ITERS + THREAD_MAX) {
285			ksft_exit_fail_msg("max signaling iterations (%d) reached, giving up on pending waiters.\n",
286			      MAX_WAKE_ITERS + THREAD_MAX);
287		}
288	}
289
290	futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG);
291
292	if (args->ret >= 0)
293		args->ret = task_count;
294
295	ksft_print_dbg_msg("Waker: exiting with %d\n", args->ret);
296	ksft_print_dbg_msg("Waker: waiters_woken: %d\n", waiters_woken.val);
297	pthread_exit((void *)&args->ret);
298}
299
300void *third_party_blocker(void *arg)
301{
302	struct thread_arg *args = (struct thread_arg *)arg;
303	int ret2 = 0;
304
305	args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG);
306	if (args->ret)
307		goto out;
308	args->ret = futex_wait(&wake_complete, wake_complete, NULL,
309			       FUTEX_PRIVATE_FLAG);
310	ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
311
312 out:
313	if (args->ret || ret2)
314		ksft_exit_fail_msg("third_party_blocker() futex error");
315
316	pthread_exit((void *)&args->ret);
317}
318
319TEST_F(args, futex_requeue_pi)
320{
321	struct thread_arg blocker_arg = THREAD_ARG_INITIALIZER;
322	struct thread_arg waker_arg = THREAD_ARG_INITIALIZER;
323	pthread_t waiter[THREAD_MAX], waker, blocker;
324	void *(*wakerfn)(void *) = signal_wakerfn;
325	bool third_party_owner = variant->owner;
326	long timeout_ns = variant->timeout_ns;
327	bool broadcast = variant->broadcast;
328	struct thread_arg args[THREAD_MAX];
329	struct timespec ts, *tsp = NULL;
330	bool lock = variant->locked;
331	int *waiter_ret, i, ret = 0;
332
333	ksft_print_msg(
334		"\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
335		broadcast, lock, third_party_owner, timeout_ns);
336
337	if (timeout_ns) {
338		time_t secs;
339
340		ksft_print_dbg_msg("timeout_ns = %ld\n", timeout_ns);
341		ret = clock_gettime(CLOCK_MONOTONIC, &ts);
342		secs = (ts.tv_nsec + timeout_ns) / 1000000000;
343		ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000;
344		ts.tv_sec += secs;
345		ksft_print_dbg_msg("ts.tv_sec  = %ld\n", ts.tv_sec);
346		ksft_print_dbg_msg("ts.tv_nsec = %ld\n", ts.tv_nsec);
347		tsp = &ts;
348	}
349
350	if (broadcast)
351		wakerfn = broadcast_wakerfn;
352
353	if (third_party_owner) {
354		if (create_rt_thread(&blocker, third_party_blocker,
355				     (void *)&blocker_arg, SCHED_FIFO, 1)) {
356			ksft_exit_fail_msg("Creating third party blocker thread failed\n");
357		}
358	}
359
360	atomic_set(&waiters_woken, 0);
361	for (i = 0; i < THREAD_MAX; i++) {
362		args[i].id = i;
363		args[i].timeout = tsp;
364		ksft_print_dbg_msg("Starting thread %d\n", i);
365		if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i],
366				     SCHED_FIFO, 1)) {
367			ksft_exit_fail_msg("Creating waiting thread failed\n");
368		}
369	}
370	waker_arg.lock = lock;
371	if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg,
372			     SCHED_FIFO, 1)) {
373		ksft_exit_fail_msg("Creating waker thread failed\n");
374	}
375
376	/* Wait for threads to finish */
377	/* Store the first error or failure encountered in waiter_ret */
378	waiter_ret = &args[0].ret;
379	for (i = 0; i < THREAD_MAX; i++)
380		pthread_join(waiter[i],
381			     *waiter_ret ? NULL : (void **)&waiter_ret);
382
383	if (third_party_owner)
384		pthread_join(blocker, NULL);
385	pthread_join(waker, NULL);
386
387	if (!ret) {
388		if (*waiter_ret)
389			ret = *waiter_ret;
390		else if (waker_arg.ret < 0)
391			ret = waker_arg.ret;
392		else if (blocker_arg.ret)
393			ret = blocker_arg.ret;
394	}
395
396	if (ret)
397		ksft_test_result_fail("fail");
398}
399
400TEST_HARNESS_MAIN
~/ Read-only file preview.