xref: /linux/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c (revision c574fb2ed7c96f87fc0e5295e910e646a7ee4dfa)
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 exercises the futex_wait_requeue_pi() signal handling both
8  *      before and after the requeue. The first should be restarted by the
9  *      kernel. The latter should return EWOULDBLOCK to the waiter.
10  *
11  * AUTHORS
12  *      Darren Hart <dvhart@linux.intel.com>
13  *
14  * HISTORY
15  *      2008-May-5: Initial version by Darren Hart <dvhart@linux.intel.com>
16  *
17  *****************************************************************************/
18 
19 #include <errno.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <pthread.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "atomic.h"
29 #include "futextest.h"
30 #include "../../kselftest_harness.h"
31 
32 #define DELAY_US 100
33 
34 futex_t f1 = FUTEX_INITIALIZER;
35 futex_t f2 = FUTEX_INITIALIZER;
36 atomic_t requeued = ATOMIC_INITIALIZER;
37 
38 int waiter_ret = 0;
39 
create_rt_thread(pthread_t * pth,void * (* func)(void *),void * arg,int policy,int prio)40 int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg,
41 		     int policy, int prio)
42 {
43 	struct sched_param schedp;
44 	pthread_attr_t attr;
45 	int ret;
46 
47 	pthread_attr_init(&attr);
48 	memset(&schedp, 0, sizeof(schedp));
49 
50 	ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
51 	if (ret)
52 		ksft_exit_fail_msg("pthread_attr_setinheritsched\n");
53 
54 	ret = pthread_attr_setschedpolicy(&attr, policy);
55 	if (ret)
56 		ksft_exit_fail_msg("pthread_attr_setschedpolicy\n");
57 
58 	schedp.sched_priority = prio;
59 	ret = pthread_attr_setschedparam(&attr, &schedp);
60 	if (ret)
61 		ksft_exit_fail_msg("pthread_attr_setschedparam\n");
62 
63 	ret = pthread_create(pth, &attr, func, arg);
64 	if (ret)
65 		ksft_exit_fail_msg("pthread_create\n");
66 
67 	return 0;
68 }
69 
handle_signal(int signo)70 void handle_signal(int signo)
71 {
72 	ksft_print_dbg_msg("signal received %s requeue\n",
73 	     requeued.val ? "after" : "prior to");
74 }
75 
waiterfn(void * arg)76 void *waiterfn(void *arg)
77 {
78 	unsigned int old_val;
79 	int res;
80 
81 	ksft_print_dbg_msg("Waiter running\n");
82 	ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
83 	old_val = f1;
84 	res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL,
85 				    FUTEX_PRIVATE_FLAG);
86 	if (!requeued.val || errno != EWOULDBLOCK) {
87 		ksft_test_result_fail("unexpected return from futex_wait_requeue_pi: %d (%s)\n",
88 		     res, strerror(errno));
89 		ksft_print_dbg_msg("w2:futex: %x\n", f2);
90 		if (!res)
91 			futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
92 	}
93 
94 	pthread_exit(NULL);
95 }
96 
97 
TEST(futex_requeue_pi_signal_restart)98 TEST(futex_requeue_pi_signal_restart)
99 {
100 	unsigned int old_val;
101 	struct sigaction sa;
102 	pthread_t waiter;
103 	int res;
104 
105 	sa.sa_handler = handle_signal;
106 	sigemptyset(&sa.sa_mask);
107 	sa.sa_flags = 0;
108 	if (sigaction(SIGUSR1, &sa, NULL))
109 		ksft_exit_fail_msg("sigaction\n");
110 
111 	ksft_print_dbg_msg("m1:f2: %x\n", f2);
112 	ksft_print_dbg_msg("Creating waiter\n");
113 	res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1);
114 	if (res)
115 		ksft_exit_fail_msg("Creating waiting thread failed");
116 
117 	ksft_print_dbg_msg("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2);
118 	ksft_print_dbg_msg("m2:f2: %x\n", f2);
119 	futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG);
120 	ksft_print_dbg_msg("m3:f2: %x\n", f2);
121 
122 	while (1) {
123 		/*
124 		 * signal the waiter before requeue, waiter should automatically
125 		 * restart futex_wait_requeue_pi() in the kernel. Wait for the
126 		 * waiter to block on f1 again.
127 		 */
128 		ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n");
129 		pthread_kill(waiter, SIGUSR1);
130 		usleep(DELAY_US);
131 
132 		ksft_print_dbg_msg("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n");
133 		old_val = f1;
134 		res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0,
135 					   FUTEX_PRIVATE_FLAG);
136 		/*
137 		 * If res is non-zero, we either requeued the waiter or hit an
138 		 * error, break out and handle it. If it is zero, then the
139 		 * signal may have hit before the waiter was blocked on f1.
140 		 * Try again.
141 		 */
142 		if (res > 0) {
143 			atomic_set(&requeued, 1);
144 			break;
145 		} else if (res < 0) {
146 			ksft_exit_fail_msg("FUTEX_CMP_REQUEUE_PI failed\n");
147 		}
148 	}
149 	ksft_print_dbg_msg("m4:f2: %x\n", f2);
150 
151 	/*
152 	 * Signal the waiter after requeue, waiter should return from
153 	 * futex_wait_requeue_pi() with EWOULDBLOCK. Join the thread here so the
154 	 * futex_unlock_pi() can't happen before the signal wakeup is detected
155 	 * in the kernel.
156 	 */
157 	ksft_print_dbg_msg("Issuing SIGUSR1 to waiter\n");
158 	pthread_kill(waiter, SIGUSR1);
159 	ksft_print_dbg_msg("Waiting for waiter to return\n");
160 	pthread_join(waiter, NULL);
161 
162 	ksft_print_dbg_msg("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2);
163 	futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG);
164 	ksft_print_dbg_msg("m5:f2: %x\n", f2);
165 }
166 
167 TEST_HARNESS_MAIN
168