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 validates the simplest form of locking functionality: 18 * 19 * o On a controller fd we can take controller read and write locks. 20 * o On a controller fd we can take namespace read and write locks. 21 * o On a namespace fd we can take namespace read and write locks with nsid = 0 22 * to get our nsid. 23 * o On a namespace fd we can specify our nsid still. 24 * o A namespace fd cannot take a controller lock with either nsid. 25 */ 26 27 #include <err.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <stdbool.h> 32 #include <sys/sysmacros.h> 33 #include <sys/debug.h> 34 35 #include "nvme_ioctl_util.h" 36 37 /* 38 * Loop for multiple times on each lock just to make sure this isn't a one off. 39 */ 40 #define BASIC_LOCK_NITERS 3 41 42 typedef struct { 43 const char *blt_desc; 44 nvme_lock_ent_t blt_ent; 45 nvme_lock_level_t blt_level; 46 uint32_t blt_nsid; 47 } basic_lock_test_t; 48 49 static const basic_lock_test_t basic_lock_tests[] = { 50 { "ctrl fd ctrl write lock", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 0 }, 51 { "ctrl fd ctrl read lock", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 0 }, 52 { "ctrl fd ns write lock", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 1 }, 53 { "ctrl fd ns read lock", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 1 } 54 }; 55 56 static const basic_lock_test_t basic_ns_lock_tests[] = { 57 { "ns fd ns write lock (nsid=0)", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 58 0 }, 59 { "ns fd ns read lock (nsid=0)", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 0 }, 60 { "ns fd ns write lock (nsid=1)", NVME_LOCK_E_NS, NVME_LOCK_L_WRITE, 61 1 }, 62 { "ns fd ns read lock (nsid=1)", NVME_LOCK_E_NS, NVME_LOCK_L_READ, 1 } 63 }; 64 65 static const basic_lock_test_t basic_ns_ctrl_lock_tests[] = { 66 { "ns fd ctrl write lock (nsid=0)", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 67 0 }, 68 { "ns fd ctrl read lock (nsid=0)", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 69 0 }, 70 { "ns fd ctrl write lock (nsid=1)", NVME_LOCK_E_CTRL, NVME_LOCK_L_WRITE, 71 1 }, 72 { "ns fd ctrl read lock (nsid=1)", NVME_LOCK_E_CTRL, NVME_LOCK_L_READ, 73 1 } 74 }; 75 76 77 static bool 78 basic_lock_test(const basic_lock_test_t *test, int fd) 79 { 80 nvme_ioctl_lock_t lock; 81 nvme_ioctl_unlock_t unlock; 82 83 (void) memset(&lock, 0, sizeof (lock)); 84 lock.nil_common.nioc_nsid = test->blt_nsid; 85 lock.nil_ent = test->blt_ent; 86 lock.nil_level = test->blt_level; 87 lock.nil_flags = NVME_LOCK_F_DONT_BLOCK; 88 89 (void) memset(&unlock, 0, sizeof (unlock)); 90 unlock.niu_common.nioc_nsid = test->blt_nsid; 91 unlock.niu_ent = test->blt_ent; 92 93 for (uint32_t i = 0; i < BASIC_LOCK_NITERS; i++) { 94 if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { 95 warn("TEST FAILED: %s %u: failed to issue lock ioctl", 96 test->blt_desc, i); 97 return (false); 98 } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { 99 warnx("TEST FAILED: %s %u: lock ioctl failed with " 100 "driver error 0x%x", test->blt_desc, i, 101 lock.nil_common.nioc_drv_err); 102 return (false); 103 } else { 104 (void) printf("TEST PASSED: %s %u: lock acquired\n", 105 test->blt_desc, i); 106 } 107 108 if (ioctl(fd, NVME_IOC_UNLOCK, &unlock) != 0) { 109 return (false); 110 } else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) { 111 return (false); 112 } else { 113 (void) printf("TEST PASSED: %s %u: lock released\n", 114 test->blt_desc, i); 115 } 116 } 117 118 return (true); 119 } 120 121 /* 122 * Verify that attempting to grab these locks fails with 123 * NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL. We don't bother with multiple repetitions 124 * here. 125 */ 126 static bool 127 basic_lock_test_no_ns_ctrl(const basic_lock_test_t *test, int fd) 128 { 129 nvme_ioctl_lock_t lock; 130 bool ret = true; 131 132 (void) memset(&lock, 0, sizeof (lock)); 133 lock.nil_common.nioc_nsid = test->blt_nsid; 134 lock.nil_ent = test->blt_ent; 135 lock.nil_level = test->blt_level; 136 lock.nil_flags = NVME_LOCK_F_DONT_BLOCK; 137 138 if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) { 139 warn("TEST FAILED: %s: failed to issue lock ioctl", 140 test->blt_desc); 141 return (false); 142 } else if (lock.nil_common.nioc_drv_err == NVME_IOCTL_E_OK) { 143 errx(EXIT_FAILURE, "TEST FAILED: %s: lock erroneously " 144 "acquired: cannot continue test", test->blt_desc); 145 } else if (lock.nil_common.nioc_drv_err != 146 NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL) { 147 warnx("TEST FAILED: %s: lock ioctl failed with " 148 "driver error 0x%x, expected 0x%x " 149 "(NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL)", test->blt_desc, 150 lock.nil_common.nioc_drv_err, 151 NVME_IOCTL_E_NS_CANNOT_LOCK_CTRL); 152 return (false); 153 } else { 154 (void) printf("TEST PASSED: %s: lock denied\n", test->blt_desc); 155 } 156 157 return (ret); 158 } 159 160 int 161 main(void) 162 { 163 int fd = nvme_ioctl_test_get_fd(0); 164 int ret = EXIT_SUCCESS; 165 166 for (size_t i = 0; i < ARRAY_SIZE(basic_lock_tests); i++) { 167 if (!basic_lock_test(&basic_lock_tests[i], fd)) { 168 ret = EXIT_FAILURE; 169 } 170 } 171 172 VERIFY0(close(fd)); 173 174 fd = nvme_ioctl_test_get_fd(1); 175 for (size_t i = 0; i < ARRAY_SIZE(basic_ns_lock_tests); i++) { 176 if (!basic_lock_test(&basic_ns_lock_tests[i], fd)) { 177 ret = EXIT_FAILURE; 178 } 179 } 180 181 for (size_t i = 0; i < ARRAY_SIZE(basic_ns_ctrl_lock_tests); i++) { 182 if (!basic_lock_test_no_ns_ctrl(&basic_ns_ctrl_lock_tests[i], 183 fd)) { 184 ret = EXIT_FAILURE; 185 } 186 } 187 188 return (ret); 189 } 190