1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2019 Xiao Yang <ice_yangxiao@163.com> 4 * 5 * Description: 6 * Testcase to check the basic functionality of futex(FUTEX_CMP_REQUEUE). 7 * futex(FUTEX_CMP_REQUEUE) can wake up the number of waiters specified 8 * by val argument and then requeue the number of waiters limited by val2 9 * argument(i.e. move some remaining waiters from uaddr to uaddr2 address). 10 */ 11 12#include <errno.h> 13#include <sys/wait.h> 14#include <stdlib.h> 15#include <sys/time.h> 16 17#include "tst_test.h" 18#include "futextest.h" 19#include "lapi/futex.h" 20 21struct shared_data { 22 futex_t futexes[2]; 23 int spurious; 24 int test_done; 25}; 26 27static struct shared_data *sd; 28static int max_sleep_ms; 29 30static struct tcase { 31 int num_waiters; 32 int set_wakes; 33 int set_requeues; 34} tcases[] = { 35 {10, 3, 7}, 36 {10, 0, 10}, 37 {10, 2, 6}, 38 {100, 50, 50}, 39 {100, 0, 70}, 40 {1000, 100, 900}, 41 {1000, 300, 500}, 42}; 43 44static struct futex_test_variants variants[] = { 45#if (__NR_futex != __LTP__NR_INVALID_SYSCALL) 46 { .fntype = FUTEX_FN_FUTEX, .tstype = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"}, 47#endif 48 49#if (__NR_futex_time64 != __LTP__NR_INVALID_SYSCALL) 50 { .fntype = FUTEX_FN_FUTEX64, .tstype = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"}, 51#endif 52}; 53 54static void do_child(void) 55{ 56 struct futex_test_variants *tv = &variants[tst_variant]; 57 struct tst_ts usec = tst_ts_from_ms(tv->tstype, max_sleep_ms); 58 int slept_for_ms = 0; 59 int pid = getpid(); 60 int ret = 0; 61 62 if (futex_wait(tv->fntype, &sd->futexes[0], sd->futexes[0], &usec, 0) == -1) { 63 if (errno == EAGAIN) { 64 /* spurious wakeup or signal */ 65 tst_atomic_inc(&sd->spurious); 66 } else { 67 tst_res(TFAIL | TERRNO, "process %d wasn't woken up", 68 pid); 69 ret = 1; 70 } 71 } 72 73 /* make sure TST_PROCESS_STATE_WAIT() can always succeed */ 74 while (!tst_atomic_load(&sd->test_done) 75 && (slept_for_ms < max_sleep_ms)) { 76 usleep(50000); 77 slept_for_ms += 50; 78 } 79 80 exit(ret); 81} 82 83static void verify_futex_cmp_requeue(unsigned int n) 84{ 85 struct futex_test_variants *tv = &variants[tst_variant]; 86 int num_requeues = 0, num_waits = 0, num_total = 0; 87 int i, status, spurious, woken_up; 88 struct tcase *tc = &tcases[n]; 89 int pid[tc->num_waiters]; 90 int exp_ret = tc->set_wakes + tc->set_requeues; 91 92 tst_atomic_store(0, &sd->spurious); 93 tst_atomic_store(0, &sd->test_done); 94 for (i = 0; i < tc->num_waiters; i++) { 95 pid[i] = SAFE_FORK(); 96 if (!pid[i]) 97 do_child(); 98 } 99 100 for (i = 0; i < tc->num_waiters; i++) 101 TST_PROCESS_STATE_WAIT(pid[i], 'S', 0); 102 103 tst_res(TINFO, "Test %d: waiters: %d, wakes: %d, requeues: %d", 104 n, tc->num_waiters, tc->set_wakes, tc->set_requeues); 105 106 /* 107 * change futex value, so any spurious wakeups or signals after 108 * this point get bounced back to userspace. 109 */ 110 sd->futexes[0]++; 111 sd->futexes[1]++; 112 113 /* 114 * Wakes up a maximum of tc->set_wakes waiters. tc->set_requeues 115 * specifies an upper limit on the number of waiters that are requeued. 116 * Returns the total number of waiters that were woken up or requeued. 117 */ 118 TEST(futex_cmp_requeue(tv->fntype, &sd->futexes[0], sd->futexes[0], 119 &sd->futexes[1], tc->set_wakes, tc->set_requeues, 0)); 120 121 /* Fail if more than requested wakes + requeues were returned */ 122 if (TST_RET > exp_ret) { 123 tst_res(TFAIL, "futex_cmp_requeue() returned %ld, expected <= %d", 124 TST_RET, exp_ret); 125 } else { 126 tst_res(TINFO, "futex_cmp_requeue() returned %ld", TST_RET); 127 } 128 129 num_requeues = futex_wake(tv->fntype, &sd->futexes[1], tc->num_waiters, 0); 130 num_waits = futex_wake(tv->fntype, &sd->futexes[0], tc->num_waiters, 0); 131 132 tst_atomic_store(1, &sd->test_done); 133 for (i = 0; i < tc->num_waiters; i++) { 134 SAFE_WAITPID(pid[i], &status, 0); 135 if (WIFEXITED(status) && !WEXITSTATUS(status)) 136 num_total++; 137 } 138 139 spurious = tst_atomic_load(&sd->spurious); 140 tst_res(TINFO, "children woken, futex0: %d, futex1: %d, " 141 "spurious wakeups: %d", 142 num_waits, num_requeues, spurious); 143 144 /* Fail if any waiter timed out */ 145 if (num_total != tc->num_waiters) { 146 tst_res(TFAIL, "%d waiters were not woken up normally", 147 tc->num_waiters - num_total); 148 return; 149 } 150 151 /* 152 * num_requeues should be in range: 153 * (tc->set_requeues - spurious, tc->set_requeues) 154 * 155 * Fewer processes than requested can be requeued at futex1 156 * if some woke up spuriously. Finding more processes than 157 * requested at futex1 is always a failure. 158 */ 159 if ((num_requeues < tc->set_requeues - spurious) 160 || (num_requeues > tc->set_requeues)) { 161 tst_res(TFAIL, 162 "requeued %d waiters, expected range: (%d, %d)", 163 num_requeues, tc->set_requeues - spurious, 164 tc->set_requeues); 165 return; 166 } 167 168 /* 169 * woken_up = (TST_RET - num_requeues) should be in range: 170 * (tc->set_wakes - spurious, tc->set_wakes + spurious) 171 * 172 * Fewer processes than requested can be woken up, if some of 173 * them woke up spuriously before requeue. More processes than 174 * requested may appear to be woken up, if some woke up 175 * spuriously after requeue. 176 */ 177 woken_up = TST_RET - num_requeues; 178 if ((woken_up < tc->set_wakes - spurious) 179 || (woken_up > tc->set_wakes + spurious)) { 180 tst_res(TFAIL, 181 "woken up %d, expected range (%d, %d)", 182 woken_up, tc->set_wakes - spurious, 183 tc->set_wakes + spurious); 184 return; 185 } 186 187 tst_res(TPASS, "futex_cmp_requeue()"); 188} 189 190static void setup(void) 191{ 192 struct futex_test_variants *tv = &variants[tst_variant]; 193 194 tst_res(TINFO, "Testing variant: %s", tv->desc); 195 futex_supported_by_kernel(tv->fntype); 196 197 max_sleep_ms = tst_multiply_timeout(5000); 198 199 sd = SAFE_MMAP(NULL, sizeof(*sd), PROT_READ | PROT_WRITE, 200 MAP_ANONYMOUS | MAP_SHARED, -1, 0); 201 202 sd->futexes[0] = FUTEX_INITIALIZER; 203 sd->futexes[1] = FUTEX_INITIALIZER + 1000; 204} 205 206static void cleanup(void) 207{ 208 if (sd) 209 SAFE_MUNMAP((void *)sd, sizeof(*sd)); 210} 211 212static struct tst_test test = { 213 .setup = setup, 214 .cleanup = cleanup, 215 .tcnt = ARRAY_SIZE(tcases), 216 .test = verify_futex_cmp_requeue, 217 .test_variants = ARRAY_SIZE(variants), 218 .forks_child = 1, 219}; 220