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