xref: /illumos-gate/usr/src/test/nvme-tests/tests/ioctl/multi-reader-lock.c (revision 7a87437fd36d10a97e4d5952dba549b6f0fadbfc)
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