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 * This test covers the following aspects of the locking behavior: 18 * 19 * o A controller write lock blocks controller read/write locks 20 * o A controller write lock blocks namespace read/write locks 21 * o A controller read lock blocks controller write locks 22 * o A controller read lock does not block namespace write locks 23 * o A namespace write lock blocks namespace read/write locks 24 * o A namespace write lock blocks controller write locks, but not read locks 25 * o A namespace read lock blocks namespace write locks 26 * o A namespace read lock blocks controller write locks 27 * 28 * The interaction of various read locks is tested in multi-reader-lock.c. 29 */ 30 31 #include <err.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <stdbool.h> 35 #include <sys/sysmacros.h> 36 #include <sys/debug.h> 37 38 #include "nvme_ioctl_util.h" 39 40 typedef enum { 41 CBF_FD_CTRL, 42 CBF_FD_NS 43 } ctrl_block_fd_t; 44 45 /* 46 * This structure describes a given test case. We expect to always succeed in 47 * locking fd0 and then we will expect the return value in cbt_ret1 when we try 48 * to take lock1. 49 */ 50 typedef struct { 51 const char *cbt_desc; 52 ctrl_block_fd_t cbt_fd0; 53 ctrl_block_fd_t cbt_fd1; 54 const nvme_ioctl_lock_t *cbt_lock0; 55 const nvme_ioctl_lock_t *cbt_lock1; 56 nvme_ioctl_errno_t cbt_ret1; 57 } ctrl_block_test_t; 58 59 static const ctrl_block_test_t ctrl_block_tests[] = { { 60 .cbt_desc = "controller write blocks controller read", 61 .cbt_fd0 = CBF_FD_CTRL, 62 .cbt_fd1 = CBF_FD_CTRL, 63 .cbt_lock0 = &nvme_test_ctrl_wrlock, 64 .cbt_lock1 = &nvme_test_ctrl_rdlock, 65 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 66 }, { 67 .cbt_desc = "controller write blocks controller write", 68 .cbt_fd0 = CBF_FD_CTRL, 69 .cbt_fd1 = CBF_FD_CTRL, 70 .cbt_lock0 = &nvme_test_ctrl_wrlock, 71 .cbt_lock1 = &nvme_test_ctrl_wrlock, 72 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 73 }, { 74 .cbt_desc = "controller write blocks namespace read", 75 .cbt_fd0 = CBF_FD_CTRL, 76 .cbt_fd1 = CBF_FD_NS, 77 .cbt_lock0 = &nvme_test_ctrl_wrlock, 78 .cbt_lock1 = &nvme_test_ns_rdlock, 79 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 80 }, { 81 .cbt_desc = "controller write blocks namespace write", 82 .cbt_fd0 = CBF_FD_CTRL, 83 .cbt_fd1 = CBF_FD_NS, 84 .cbt_lock0 = &nvme_test_ctrl_wrlock, 85 .cbt_lock1 = &nvme_test_ns_wrlock, 86 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 87 }, { 88 .cbt_desc = "controller read blocks controller write", 89 .cbt_fd0 = CBF_FD_CTRL, 90 .cbt_fd1 = CBF_FD_CTRL, 91 .cbt_lock0 = &nvme_test_ctrl_rdlock, 92 .cbt_lock1 = &nvme_test_ctrl_wrlock, 93 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 94 }, { 95 .cbt_desc = "controller read does not block namespace write", 96 .cbt_fd0 = CBF_FD_CTRL, 97 .cbt_fd1 = CBF_FD_NS, 98 .cbt_lock0 = &nvme_test_ctrl_rdlock, 99 .cbt_lock1 = &nvme_test_ns_wrlock, 100 .cbt_ret1 = NVME_IOCTL_E_OK 101 }, { 102 .cbt_desc = "namespace write blocks namespace read", 103 .cbt_fd0 = CBF_FD_NS, 104 .cbt_fd1 = CBF_FD_NS, 105 .cbt_lock0 = &nvme_test_ns_wrlock, 106 .cbt_lock1 = &nvme_test_ns_rdlock, 107 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 108 }, { 109 .cbt_desc = "namespace write blocks namespace read", 110 .cbt_fd0 = CBF_FD_NS, 111 .cbt_fd1 = CBF_FD_NS, 112 .cbt_lock0 = &nvme_test_ns_wrlock, 113 .cbt_lock1 = &nvme_test_ns_rdlock, 114 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 115 }, { 116 .cbt_desc = "namespace write blocks namespace write", 117 .cbt_fd0 = CBF_FD_NS, 118 .cbt_fd1 = CBF_FD_NS, 119 .cbt_lock0 = &nvme_test_ns_wrlock, 120 .cbt_lock1 = &nvme_test_ns_wrlock, 121 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 122 }, { 123 .cbt_desc = "namespace write blocks controller write", 124 .cbt_fd0 = CBF_FD_NS, 125 .cbt_fd1 = CBF_FD_CTRL, 126 .cbt_lock0 = &nvme_test_ns_wrlock, 127 .cbt_lock1 = &nvme_test_ctrl_wrlock, 128 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 129 }, { 130 .cbt_desc = "namespace write does not block controller read", 131 .cbt_fd0 = CBF_FD_NS, 132 .cbt_fd1 = CBF_FD_CTRL, 133 .cbt_lock0 = &nvme_test_ns_wrlock, 134 .cbt_lock1 = &nvme_test_ctrl_rdlock, 135 .cbt_ret1 = NVME_IOCTL_E_OK 136 }, { 137 .cbt_desc = "namespace read blocks namespace write", 138 .cbt_fd0 = CBF_FD_NS, 139 .cbt_fd1 = CBF_FD_NS, 140 .cbt_lock0 = &nvme_test_ns_rdlock, 141 .cbt_lock1 = &nvme_test_ns_wrlock, 142 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 143 }, { 144 .cbt_desc = "namespace read blocks controller write", 145 .cbt_fd0 = CBF_FD_NS, 146 .cbt_fd1 = CBF_FD_CTRL, 147 .cbt_lock0 = &nvme_test_ns_rdlock, 148 .cbt_lock1 = &nvme_test_ctrl_wrlock, 149 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK 150 } }; 151 152 static bool 153 ctrl_block_test_one(int fd0, int fd1, const ctrl_block_test_t *test) 154 { 155 nvme_ioctl_lock_t lock0 = *test->cbt_lock0; 156 nvme_ioctl_lock_t lock1 = *test->cbt_lock1; 157 158 if (ioctl(fd0, NVME_IOC_LOCK, &lock0) != 0) { 159 warn("TEST FAILED: %s: failed to issue lock ioctl for fd0", 160 test->cbt_desc); 161 return (false); 162 } else if (lock0.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { 163 warnx("TEST FAILED: %s: fd0 lock ioctl failed with 0x%x, " 164 "expected success", test->cbt_desc, 165 lock0.nil_common.nioc_drv_err); 166 return (false); 167 } 168 169 if (ioctl(fd1, NVME_IOC_LOCK, &lock1) != 0) { 170 warn("TEST FAILED: %s: failed to issue lock ioctl for fd1", 171 test->cbt_desc); 172 return (false); 173 } else if (lock1.nil_common.nioc_drv_err != test->cbt_ret1) { 174 warnx("TEST FAILED: %s: fd1 lock ioctl returned with 0x%x, " 175 "expected 0x%x", test->cbt_desc, 176 lock1.nil_common.nioc_drv_err, test->cbt_ret1); 177 return (false); 178 } 179 180 (void) printf("TEST PASSED: %s\n", test->cbt_desc); 181 return (true); 182 } 183 184 int 185 main(void) 186 { 187 int ret = EXIT_SUCCESS; 188 189 /* 190 * We purposefully open and close the fds every iteration of this loop 191 * so we don't have to explicitly issue conditional unlocks. 192 */ 193 for (size_t i = 0; i < ARRAY_SIZE(ctrl_block_tests); i++) { 194 int fd0, fd1; 195 196 if (ctrl_block_tests[i].cbt_fd0 == CBF_FD_CTRL) { 197 fd0 = nvme_ioctl_test_get_fd(0); 198 } else { 199 fd0 = nvme_ioctl_test_get_fd(1); 200 } 201 202 if (ctrl_block_tests[i].cbt_fd1 == CBF_FD_CTRL) { 203 fd1 = nvme_ioctl_test_get_fd(0); 204 } else { 205 fd1 = nvme_ioctl_test_get_fd(1); 206 } 207 208 if (!ctrl_block_test_one(fd0, fd1, &ctrl_block_tests[i])) { 209 ret = EXIT_FAILURE; 210 } 211 212 VERIFY0(close(fd0)); 213 VERIFY0(close(fd1)); 214 } 215 216 return (ret); 217 } 218