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
basic_lock_test(const basic_lock_test_t * test,int fd)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
basic_lock_test_no_ns_ctrl(const basic_lock_test_t * test,int fd)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
main(void)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