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