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
multi_lock_one(int fd,const nvme_ioctl_lock_t * tmpl,const char * desc,size_t iter)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
multi_unlock_one(int fd,const nvme_ioctl_unlock_t * tmpl,const char * desc,size_t iter)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
multi_unlock_all(int ctrl_ctrl[NREADERS],int ctrl_ns[NREADERS],int ns_ns[NREADERS])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
main(void)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