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 * The purpose of this test is to verify that multiple readers can all grab the 18 * same lock. In this case, we will use a mix of namespace and controller locks, 19 * but only one lock per fd. Specifically we want to ensure that all three of 20 * these classes can simultaneously hold the lock: 21 * 22 * o Controller fd, controller lock 23 * o Controller fd, namespace lock 24 * o Namespace fd, namespace lock 25 * 26 * This also is testing that multiple instances of the same type can hold the fd 27 * as well. In addition, We want to ensure that this happens regardless of 28 * whomever does the first lock. In particular we want to test the following 29 * orders: 30 * 31 * 1) All controller read locks, then all ctrl ns, then all ns 32 * 2) All ns fd read locks, then all ctrl fd ns locks, then all ctrl ctrl 33 * 3) All ctrl fd ns locks, then all ctrl ctrl locks, then all ns fd 34 * 35 * Then we repeat the above but swizzling with one fd from each. 36 */ 37 38 #include <err.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <stdbool.h> 43 44 #include "nvme_ioctl_util.h" 45 46 /* 47 * Number of readers of each type. 48 */ 49 #define NREADERS 10 50 51 static bool 52 multi_lock_one(int fd, const nvme_ioctl_lock_t *tmpl, const char *desc, 53 size_t iter) 54 { 55 nvme_ioctl_lock_t lock = *tmpl; 56 57 if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { 58 warn("TEST FAILED: %s %zu: failed to issue lock ioctl", 59 desc, iter); 60 return (false); 61 } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { 62 warnx("TEST FAILED: %s %zu: lock ioctl failed with driver " 63 "error 0x%x", desc, iter, lock.nil_common.nioc_drv_err); 64 return (false); 65 } else { 66 return (true); 67 } 68 } 69 70 static bool 71 multi_unlock_one(int fd, const nvme_ioctl_unlock_t *tmpl, const char *desc, 72 size_t iter) 73 { 74 nvme_ioctl_unlock_t unlock = *tmpl; 75 if (ioctl(fd, NVME_IOC_UNLOCK, &unlock) != 0) { 76 warn("TEST FAILED: %s %zu: failed to issue unlock ioctl", 77 desc, iter); 78 return (false); 79 } else if (unlock.niu_common.nioc_drv_err != NVME_IOCTL_E_OK) { 80 warnx("TEST FAILED: %s %zu: unlock ioctl failed with driver " 81 "error 0x%x", desc, iter, unlock.niu_common.nioc_drv_err); 82 return (false); 83 } else { 84 return (true); 85 } 86 } 87 88 static bool 89 multi_unlock_all(int ctrl_ctrl[NREADERS], int ctrl_ns[NREADERS], 90 int ns_ns[NREADERS]) 91 { 92 bool ret = true; 93 for (uint32_t i = 0; i < NREADERS; i++) { 94 if (!multi_unlock_one(ctrl_ctrl[i], &nvme_test_ctrl_unlock, 95 "ctrl fd ctrl lock", i)) { 96 ret = false; 97 } 98 99 if (!multi_unlock_one(ctrl_ns[i], &nvme_test_ns_unlock, 100 "ctrl fd ns lock", i)) { 101 ret = false; 102 } 103 104 if (!multi_unlock_one(ns_ns[i], &nvme_test_ns_unlock, 105 "ns fd ns lock", i)) { 106 ret = false; 107 } 108 } 109 110 return (ret); 111 } 112 113 int 114 main(void) 115 { 116 int ret = EXIT_SUCCESS; 117 int ctrl_ctrl[NREADERS]; 118 int ctrl_ns[NREADERS]; 119 int ns_ns[NREADERS]; 120 bool test; 121 122 for (size_t i = 0; i < NREADERS; i++) { 123 ctrl_ctrl[i] = nvme_ioctl_test_get_fd(0); 124 ctrl_ns[i] = nvme_ioctl_test_get_fd(0); 125 ns_ns[i] = nvme_ioctl_test_get_fd(1); 126 } 127 128 /* 129 * Order 1 130 */ 131 test = true; 132 for (size_t i = 0; i < NREADERS; i++) { 133 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 134 "ctrl fd ctrl lock", i)) { 135 test = false; 136 } 137 } 138 139 for (size_t i = 0; i < NREADERS; i++) { 140 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 141 "ctrl fd ns lock", i)) { 142 test = false; 143 } 144 } 145 146 for (size_t i = 0; i < NREADERS; i++) { 147 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 148 "ns fd ns lock", i)) { 149 test = false; 150 } 151 } 152 153 if (test) { 154 (void) printf("TEST PASSED: all read locks taken: order 1\n"); 155 } else { 156 warnx("TEST FAILED: failed to take all read locks following " 157 "order 1"); 158 ret = EXIT_FAILURE; 159 } 160 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 161 ret = EXIT_FAILURE; 162 } 163 164 /* 165 * Order 2 166 */ 167 test = true; 168 for (size_t i = 0; i < NREADERS; i++) { 169 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 170 "ns fd ns lock", i)) { 171 test = false; 172 } 173 } 174 175 for (size_t i = 0; i < NREADERS; i++) { 176 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 177 "ctrl fd ns lock", i)) { 178 test = false; 179 } 180 } 181 182 for (size_t i = 0; i < NREADERS; i++) { 183 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 184 "ctrl fd ctrl lock", i)) { 185 test = false; 186 } 187 } 188 189 if (test) { 190 (void) printf("TEST PASSED: all read locks taken: order 2\n"); 191 } else { 192 warnx("TEST FAILED: failed to take all read locks following " 193 "order 2"); 194 ret = EXIT_FAILURE; 195 } 196 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 197 ret = EXIT_FAILURE; 198 } 199 200 /* 201 * Order 3 202 */ 203 test = true; 204 for (size_t i = 0; i < NREADERS; i++) { 205 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 206 "ctrl fd ns lock", i)) { 207 test = false; 208 } 209 } 210 211 for (size_t i = 0; i < NREADERS; i++) { 212 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 213 "ns fd ns lock", i)) { 214 test = false; 215 } 216 } 217 218 for (size_t i = 0; i < NREADERS; i++) { 219 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 220 "ctrl fd ctrl lock", i)) { 221 test = false; 222 } 223 } 224 225 if (test) { 226 (void) printf("TEST PASSED: all read locks taken: order 3\n"); 227 } else { 228 warnx("TEST FAILED: failed to take all read locks following " 229 "order 3"); 230 ret = EXIT_FAILURE; 231 } 232 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 233 ret = EXIT_FAILURE; 234 } 235 /* 236 * Swizzle 1. 237 */ 238 test = true; 239 for (size_t i = 0; i < NREADERS; i++) { 240 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 241 "ctrl fd ctrl lock", i)) { 242 test = false; 243 } 244 245 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 246 "ctrl fd ns lock", i)) { 247 test = false; 248 } 249 250 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 251 "ns fd ns lock", i)) { 252 test = false; 253 } 254 } 255 256 if (test) { 257 (void) printf("TEST PASSED: all read locks taken: swizzle 1\n"); 258 } else { 259 warnx("TEST FAILED: failed to take all read locks following " 260 "swizzle 1"); 261 ret = EXIT_FAILURE; 262 } 263 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 264 ret = EXIT_FAILURE; 265 } 266 267 /* 268 * Swizzle 2. 269 */ 270 test = true; 271 for (size_t i = 0; i < NREADERS; i++) { 272 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 273 "ns fd ns lock", i)) { 274 test = false; 275 } 276 277 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 278 "ctrl fd ns lock", i)) { 279 test = false; 280 } 281 282 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 283 "ctrl fd ctrl lock", i)) { 284 test = false; 285 } 286 } 287 288 if (test) { 289 (void) printf("TEST PASSED: all read locks taken: swizzle 2\n"); 290 } else { 291 warnx("TEST FAILED: failed to take all read locks following " 292 "swizzle 2"); 293 ret = EXIT_FAILURE; 294 } 295 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 296 ret = EXIT_FAILURE; 297 } 298 299 /* 300 * Swizzle 3. 301 */ 302 test = true; 303 for (size_t i = 0; i < NREADERS; i++) { 304 if (!multi_lock_one(ctrl_ns[i], &nvme_test_ns_rdlock, 305 "ctrl fd ns lock", i)) { 306 test = false; 307 } 308 309 if (!multi_lock_one(ns_ns[i], &nvme_test_ns_rdlock, 310 "ns fd ns lock", i)) { 311 test = false; 312 } 313 314 if (!multi_lock_one(ctrl_ctrl[i], &nvme_test_ctrl_rdlock, 315 "ctrl fd ctrl lock", i)) { 316 test = false; 317 } 318 } 319 320 if (test) { 321 (void) printf("TEST PASSED: all read locks taken: swizzle 3\n"); 322 } else { 323 warnx("TEST FAILED: failed to take all read locks following " 324 "swizzle 3"); 325 ret = EXIT_FAILURE; 326 } 327 if (!multi_unlock_all(ctrl_ctrl, ctrl_ns, ns_ns)) { 328 ret = EXIT_FAILURE; 329 } 330 331 return (ret); 332 } 333