1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * semaphore-specific tests 18 */ 19 20 21 #include <err.h> 22 #include <stdlib.h> 23 #include <semaphore.h> 24 #include <sys/debug.h> 25 #include <sys/sysmacros.h> 26 #include <stdbool.h> 27 #include <errno.h> 28 #include <string.h> 29 30 #include "clock_lock.h" 31 32 static void 33 clock_sem_create(const char *desc, void **argp) 34 { 35 int ret; 36 sem_t *sem; 37 38 sem = calloc(1, sizeof (sem_t)); 39 if (sem == NULL) { 40 err(EXIT_FAILURE, "TEST FAILED: %s: failed to allocate memory " 41 "for a semaphore", desc); 42 } 43 44 if ((ret = sem_init(sem, 0, 1)) != 0) { 45 errc(EXIT_FAILURE, ret, "TEST FAILED: %s: failed to create " 46 "semaphore", desc); 47 } 48 49 *argp = sem; 50 } 51 52 static void 53 clock_sem_destroy(void *arg) 54 { 55 VERIFY0(sem_destroy(arg)); 56 free(arg); 57 } 58 59 static void 60 clock_sem_lock(void *arg) 61 { 62 VERIFY0(sem_trywait(arg)); 63 } 64 65 static void 66 clock_sem_unlock(void *arg) 67 { 68 VERIFY0(sem_post(arg)); 69 } 70 71 const lock_ops_t clock_lock_sem_ops = { 72 .lo_create = clock_sem_create, 73 .lo_destroy = clock_sem_destroy, 74 .lo_lock = clock_sem_lock, 75 .lo_unlock = clock_sem_unlock 76 }; 77 78 static bool 79 clock_test_sem_invalid_source(const clock_test_t *test, void *prim) 80 { 81 bool ret = true; 82 sem_t *sem = prim; 83 const clockid_t clocks[] = { 0x7777, INT32_MAX, 0x23, CLOCK_VIRTUAL, 84 CLOCK_THREAD_CPUTIME_ID, CLOCK_PROCESS_CPUTIME_ID }; 85 86 for (size_t i = 0; i < ARRAY_SIZE(clocks); i++) { 87 clockid_t c = clocks[i]; 88 89 if (sem_clockwait(sem, c, &clock_to_100ms) != -1 || 90 errno != EINVAL) { 91 warnx("TEST FAILED: %s: sem_clockwait with clock 0x%x " 92 "returned %s, not EINVAL", test->ct_desc, c, 93 strerrorname_np(errno)); 94 ret = false; 95 } 96 97 if (sem_relclockwait_np(sem, c, &clock_to_100ms) != 98 -1 || errno != EINVAL) { 99 warnx("TEST FAILED: %s: sem_relclockwait_np with clock " 100 "0x%x returned %s, not EINVAL", test->ct_desc, c, 101 strerrorname_np(errno)); 102 ret = false; 103 } 104 } 105 106 return (ret); 107 } 108 109 static bool 110 clock_test_sem_inv_to_ign_abs(const clock_test_t *test, void *prim) 111 { 112 bool ret = true; 113 sem_t *sem = prim; 114 115 if (sem_timedwait(sem, &clock_to_invns) != 0) { 116 warnx("TEST FAILED: %s: sem_timedwait failed with an invalid " 117 "timeout when the lock when lock was available: expected " 118 "success, found %s", test->ct_desc, strerrorname_np(errno)); 119 ret = false; 120 } else { 121 test->ct_ops->lo_unlock(sem); 122 } 123 124 if (sem_clockwait(sem, CLOCK_MONOTONIC, &clock_to_invns) != 0) { 125 warnx("TEST FAILED: %s: sem_clockwait failed with an invalid " 126 "timeout when the lock when lock was available: expected " 127 "success, found %s", test->ct_desc, strerrorname_np(errno)); 128 ret = false; 129 } else { 130 test->ct_ops->lo_unlock(sem); 131 } 132 133 return (ret); 134 } 135 136 static bool 137 clock_test_sem_inv_to_abs(const clock_test_t *test, void *prim) 138 { 139 bool ret = true; 140 sem_t *sem = prim; 141 142 if (sem_timedwait(sem, &clock_to_invns) != -1 || errno != EINVAL) { 143 warnx("TEST FAILED: %s: sem_timedwait with invalid timeout " 144 "returned %s, not EINVAL", test->ct_desc, 145 strerrorname_np(errno)); 146 ret = false; 147 } 148 149 if (sem_clockwait(sem, CLOCK_MONOTONIC, &clock_to_invns) != -1 || 150 errno != EINVAL) { 151 warnx("TEST FAILED: %s: sem_clockwait with invalid timeout " 152 "returned %s, not EINVAL", test->ct_desc, 153 strerrorname_np(errno)); 154 ret = false; 155 } 156 157 return (ret); 158 } 159 160 static bool 161 clock_test_sem_inv_to_ign_rel(const clock_test_t *test, void *prim) 162 { 163 bool ret = true; 164 sem_t *sem = prim; 165 const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs, 166 &clock_to_invnegns }; 167 const char *descs[] = { "too many nanoseconds", "negative seconds", 168 "negative nanoseconds" }; 169 170 for (size_t i = 0; i < ARRAY_SIZE(specs); i++) { 171 if (sem_reltimedwait_np(sem, specs[i]) != 0) { 172 warnx("TEST FAILED: %s: sem_reltimedwait_np " 173 "failed with invalid timeout %s when the lock when " 174 "lock was available: expected success, found %s", 175 test->ct_desc, descs[i], strerrorname_np(errno)); 176 ret = false; 177 } else { 178 test->ct_ops->lo_unlock(sem); 179 } 180 181 if (sem_relclockwait_np(sem, CLOCK_MONOTONIC, specs[i]) != 0) { 182 warnx("TEST FAILED: %s: sem_relclockwait_np " 183 "failed with invalid timeout %s when the lock when " 184 "lock was available: expected success, found %s", 185 test->ct_desc, descs[i], strerrorname_np(errno)); 186 ret = false; 187 } else { 188 test->ct_ops->lo_unlock(sem); 189 } 190 } 191 192 return (ret); 193 } 194 195 static bool 196 clock_test_sem_inv_to_rel(const clock_test_t *test, void *prim) 197 { 198 bool ret = true; 199 sem_t *sem = prim; 200 const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs, 201 &clock_to_invnegns }; 202 const char *descs[] = { "too many nanoseconds", "negative seconds", 203 "negative nanoseconds" }; 204 205 for (size_t i = 0; i < ARRAY_SIZE(specs); i++) { 206 if (sem_reltimedwait_np(sem, specs[i]) != -1 || 207 errno != EINVAL) { 208 warnx("TEST FAILED: %s: sem_reltimedwait_np " 209 "with invalid timeout %s returned %s, not EINVAL", 210 test->ct_desc, descs[i], strerrorname_np(errno)); 211 ret = false; 212 } 213 214 if (sem_relclockwait_np(sem, CLOCK_MONOTONIC, specs[i]) != -1 || 215 errno != EINVAL) { 216 warnx("TEST FAILED: %s: sem_relclockwait_np " 217 "with invalid timeout %s returned %s, not EINVAL", 218 test->ct_desc, descs[i], strerrorname_np(errno)); 219 ret = false; 220 } 221 } 222 223 return (ret); 224 } 225 226 static bool 227 clock_test_sem_to_abs(const clock_test_t *test, void *prim) 228 { 229 sem_t *sem = prim; 230 struct timespec to; 231 int p; 232 bool ret = true, elapse; 233 234 clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to); 235 p = sem_timedwait(sem, &to); 236 elapse = clock_abs_after(CLOCK_REALTIME, &to); 237 if (p != -1 && errno != ETIMEDOUT) { 238 warnx("TEST FAILED: %s sem_timedwait on locked semaphore " 239 "returned %s, not ETIMEDOUT", test->ct_desc, 240 strerrorname_np(errno)); 241 ret = false; 242 } 243 if (!elapse) { 244 warnx("TEST FAILED: %s: sem_timedwait on locked semaphore " 245 "did not block long enough!", test->ct_desc); 246 ret = false; 247 } 248 249 clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to); 250 p = sem_clockwait(sem, CLOCK_REALTIME, &to); 251 elapse = clock_abs_after(CLOCK_REALTIME, &to); 252 if (p != -1 && errno != ETIMEDOUT) { 253 warnx("TEST FAILED: %s: sem_clockwait on locked semaphore " 254 "with CLOCK_REALTIME returned %s, not ETIMEDOUT", 255 test->ct_desc, strerrorname_np(errno)); 256 ret = false; 257 } 258 if (!elapse) { 259 warnx("TEST FAILED: %s: sem_clockwait on locked semaphore " 260 "with CLOCK_REALTIME did not block long enough!", 261 test->ct_desc); 262 ret = false; 263 } 264 265 clock_rel_to_abs(CLOCK_HIGHRES, &clock_to_100ms, &to); 266 p = sem_clockwait(sem, CLOCK_HIGHRES, &to); 267 elapse = clock_abs_after(CLOCK_HIGHRES, &to); 268 if (p != -1 && errno != ETIMEDOUT) { 269 warnx("TEST FAILED: %s: sem_clockwait on locked semaphore " 270 "with CLOCK_HIGHRES returned %s, not ETIMEDOUT", 271 test->ct_desc, strerrorname_np(errno)); 272 ret = false; 273 } 274 if (!elapse) { 275 warnx("TEST FAILED: %s: sem_clockwait on locked semaphore " 276 "with CLOCK_HIGHRES did not block long enough!", 277 test->ct_desc); 278 ret = false; 279 } 280 281 return (ret); 282 } 283 284 static bool 285 clock_test_sem_to_rel(const clock_test_t *test, void *prim) 286 { 287 sem_t *sem = prim; 288 struct timespec start; 289 int p; 290 bool ret = true, elapse; 291 292 if (clock_gettime(CLOCK_REALTIME, &start) != 0) { 293 err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME); 294 } 295 p = sem_reltimedwait_np(sem, &clock_to_100ms); 296 elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms); 297 if (p != -1 && errno != ETIMEDOUT) { 298 warnx("TEST FAILED: %s: sem_reltimedwait_np on locked " 299 "sempahore returned %s, not ETIMEDOUT", test->ct_desc, 300 strerrorname_np(errno)); 301 ret = false; 302 } 303 if (!elapse) { 304 warnx("TEST FAILED: %s: sem_reltimedwait_np on locked " 305 "sempahore did not block long enough!", test->ct_desc); 306 ret = false; 307 } 308 309 if (clock_gettime(CLOCK_REALTIME, &start) != 0) { 310 err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME); 311 } 312 p = sem_relclockwait_np(sem, CLOCK_REALTIME, 313 &clock_to_100ms); 314 elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms); 315 if (p != -1 && errno != ETIMEDOUT) { 316 warnx("TEST FAILED: %s: sem_relclockwait_np on locked " 317 "semaphore with CLOCK_REALTIME returned %s, not ETIMEDOUT", 318 test->ct_desc, strerrorname_np(errno)); 319 ret = false; 320 } 321 if (!elapse) { 322 warnx("TEST FAILED: %s: sem_relclockwait_np on locked " 323 "sempahore with CLOCK_REALTIME did not block long enough!", 324 test->ct_desc); 325 ret = false; 326 } 327 328 if (clock_gettime(CLOCK_HIGHRES, &start) != 0) { 329 err(EXIT_FAILURE, "failed to read clock %d", CLOCK_HIGHRES); 330 } 331 p = sem_relclockwait_np(sem, CLOCK_HIGHRES, 332 &clock_to_100ms); 333 elapse = clock_rel_after(CLOCK_HIGHRES, &start, &clock_to_100ms); 334 if (p != -1 && errno != ETIMEDOUT) { 335 warnx("TEST FAILED: %s: sem_relclockwait_np on locked " 336 "semaphore with CLOCK_HIGHRES returned %s, not ETIMEDOUT", 337 test->ct_desc, strerrorname_np(errno)); 338 ret = false; 339 } 340 if (!elapse) { 341 warnx("TEST FAILED: %s: sem_relclockwait_np on locked " 342 "semaphore with CLOCK_HIGHRES did not block long enough!", 343 test->ct_desc); 344 ret = false; 345 } 346 347 return (ret); 348 } 349 350 const clock_test_t clock_sem_tests[] = { { 351 .ct_desc = "sem: invalid and unsupported clock sources", 352 .ct_ops = &clock_lock_sem_ops, 353 .ct_test = clock_test_sem_invalid_source 354 }, { 355 .ct_desc = "sem: invalid timeout works if lock available (absolute)", 356 .ct_ops = &clock_lock_sem_ops, 357 .ct_test = clock_test_sem_inv_to_ign_abs 358 }, { 359 .ct_desc = "sem: invalid timeout works if lock available (relative)", 360 .ct_ops = &clock_lock_sem_ops, 361 .ct_test = clock_test_sem_inv_to_ign_rel 362 }, { 363 .ct_desc = "sem: invalid timeout fails if lock taken (absolute)", 364 .ct_ops = &clock_lock_sem_ops, 365 .ct_test = clock_test_sem_inv_to_abs, 366 .ct_enter = true 367 }, { 368 .ct_desc = "sem: invalid timeout fails if lock taken (relative)", 369 .ct_ops = &clock_lock_sem_ops, 370 .ct_test = clock_test_sem_inv_to_rel, 371 .ct_enter = true 372 }, { 373 .ct_desc = "sem: timeout fires correctly (absolute)", 374 .ct_ops = &clock_lock_sem_ops, 375 .ct_test = clock_test_sem_to_abs, 376 .ct_enter = true 377 }, { 378 .ct_desc = "sem: timeout fires correctly (relative)", 379 .ct_ops = &clock_lock_sem_ops, 380 .ct_test = clock_test_sem_to_rel, 381 .ct_enter = true 382 } }; 383 384 size_t clock_sem_ntests = ARRAY_SIZE(clock_sem_tests); 385