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 * Create a thread that blocks on a lock and then once we know it is blocked, 18 * signal it. Verify that it errored out with EINTR. Once we do that, we ensure 19 * we can take all four basic locks in turn to verify that our state isn't bad. 20 */ 21 22 #include <err.h> 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <stdbool.h> 26 #include <sys/sysmacros.h> 27 #include <sys/debug.h> 28 #include <thread.h> 29 #include <synch.h> 30 #include <strings.h> 31 #include <signal.h> 32 33 #include "nvme_ioctl_util.h" 34 35 static volatile int lock_sig_ret = EXIT_SUCCESS; 36 static volatile uint32_t lock_sig_nsignals = 0; 37 static volatile thread_t lock_sig_thrid; 38 39 typedef struct { 40 const char *lss_desc; 41 const nvme_ioctl_lock_t *lss_lock; 42 } lock_sig_test_t; 43 44 static const lock_sig_test_t lock_sig_tests[] = { 45 { "controller write lock", &nvme_test_ctrl_wrlock }, 46 { "controller read lock", &nvme_test_ctrl_wrlock }, 47 { "namespace write lock", &nvme_test_ns_wrlock }, 48 { "namespace read lock", &nvme_test_ns_wrlock } 49 }; 50 51 static void 52 lock_signal_hdlr(int sig) 53 { 54 VERIFY3U(sig, ==, SIGINFO); 55 VERIFY3U(thr_self(), ==, lock_sig_thrid); 56 lock_sig_nsignals++; 57 } 58 59 static void * 60 lock_signal_thr(void *arg) 61 { 62 int fd = nvme_ioctl_test_get_fd(0); 63 const lock_sig_test_t *test = arg; 64 nvme_ioctl_lock_t lock = *test->lss_lock; 65 sigset_t set; 66 int ret; 67 68 VERIFY0(sigemptyset(&set)); 69 VERIFY0(sigaddset(&set, SIGINFO)); 70 lock_sig_thrid = thr_self(); 71 72 if ((ret = thr_sigsetmask(SIG_UNBLOCK, &set, NULL)) != 0) { 73 errc(EXIT_FAILURE, ret, "failed to unblock SIGINFO"); 74 } 75 76 77 lock.nil_flags &= ~NVME_LOCK_F_DONT_BLOCK; 78 if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { 79 err(EXIT_FAILURE, "TEST FAILED: unable to continue test " 80 "execution due to lock ioctl failure"); 81 } 82 83 if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_LOCK_WAIT_SIGNAL) { 84 warnx("TEST FAILED: %s: lock thread didn't error with " 85 "NVME_IOCTL_E_LOCK_WAIT_SIGNAL (%u), but found instead %u", 86 test->lss_desc, NVME_IOCTL_E_LOCK_WAIT_SIGNAL, 87 lock.nil_common.nioc_drv_err); 88 lock_sig_ret = EXIT_FAILURE; 89 } else { 90 (void) printf("TEST PASSED: %s: thread successfully " 91 "interrupted\n", test->lss_desc); 92 } 93 94 thr_exit(NULL); 95 } 96 97 static void 98 lock_signal_one(const lock_sig_test_t *test) 99 { 100 int fd = nvme_ioctl_test_get_fd(0); 101 int ret; 102 thread_t thr; 103 104 nvme_ioctl_test_lock(fd, &nvme_test_ctrl_wrlock); 105 ret = thr_create(NULL, 0, lock_signal_thr, (void *)test, 0, &thr); 106 if (ret != 0) { 107 errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue " 108 "because we failed to create the thread to signal", 109 test->lss_desc); 110 } 111 112 while (!nvme_ioctl_test_thr_blocked(thr)) { 113 struct timespec sleep; 114 115 sleep.tv_sec = 0; 116 sleep.tv_nsec = MSEC2NSEC(10); 117 (void) nanosleep(&sleep, NULL); 118 } 119 120 ret = thr_kill(thr, SIGINFO); 121 if (ret != 0) { 122 errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue " 123 "because we failed to send SIGINFO to tid %u", 124 test->lss_desc, thr); 125 } 126 127 ret = thr_join(thr, NULL, NULL); 128 if (ret != 0) { 129 errc(EXIT_FAILURE, ret, "TEST FAILED: %s: cannot continue " 130 "because we failed to join thread %u", test->lss_desc, thr); 131 } 132 133 VERIFY0(close(fd)); 134 fd = nvme_ioctl_test_get_fd(0); 135 nvme_ioctl_test_lock(fd, test->lss_lock); 136 (void) printf("TEST PASSED: %s: successfully grabbed follow up lock\n", 137 test->lss_desc); 138 VERIFY0(close(fd)); 139 } 140 141 int 142 main(void) 143 { 144 int ret; 145 sigset_t set; 146 struct sigaction act; 147 148 VERIFY0(sigfillset(&set)); 149 if ((ret = thr_sigsetmask(SIG_BLOCK, &set, NULL)) != 0) { 150 errc(EXIT_FAILURE, ret, "failed to block signals"); 151 } 152 153 act.sa_handler = lock_signal_hdlr; 154 VERIFY0(sigemptyset(&act.sa_mask)); 155 act.sa_flags = 0; 156 VERIFY0(sigaction(SIGINFO, &act, NULL)); 157 158 for (size_t i = 0; i < ARRAY_SIZE(lock_sig_tests); i++) { 159 lock_signal_one(&lock_sig_tests[i]); 160 } 161 162 if (lock_sig_nsignals != ARRAY_SIZE(lock_sig_tests)) { 163 lock_sig_ret = EXIT_FAILURE; 164 warnx("TEST FAILED: Didn't get %zu SIGINFO handlers, instead " 165 "got %u", ARRAY_SIZE(lock_sig_tests), lock_sig_nsignals); 166 } else { 167 (void) printf("TEST PASSED: Successfully ran SIGINFO " 168 "handlers\n"); 169 } 170 171 return (lock_sig_ret); 172 } 173