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 test covers the following aspects of the locking behavior:
18 *
19 * o A controller write lock blocks controller read/write locks
20 * o A controller write lock blocks namespace read/write locks
21 * o A controller read lock blocks controller write locks
22 * o A controller read lock does not block namespace write locks
23 * o A namespace write lock blocks namespace read/write locks
24 * o A namespace write lock blocks controller write locks, but not read locks
25 * o A namespace read lock blocks namespace write locks
26 * o A namespace read lock blocks controller write locks
27 *
28 * The interaction of various read locks is tested in multi-reader-lock.c.
29 */
30
31 #include <err.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <stdbool.h>
35 #include <sys/sysmacros.h>
36 #include <sys/debug.h>
37
38 #include "nvme_ioctl_util.h"
39
40 typedef enum {
41 CBF_FD_CTRL,
42 CBF_FD_NS
43 } ctrl_block_fd_t;
44
45 /*
46 * This structure describes a given test case. We expect to always succeed in
47 * locking fd0 and then we will expect the return value in cbt_ret1 when we try
48 * to take lock1.
49 */
50 typedef struct {
51 const char *cbt_desc;
52 ctrl_block_fd_t cbt_fd0;
53 ctrl_block_fd_t cbt_fd1;
54 const nvme_ioctl_lock_t *cbt_lock0;
55 const nvme_ioctl_lock_t *cbt_lock1;
56 nvme_ioctl_errno_t cbt_ret1;
57 } ctrl_block_test_t;
58
59 static const ctrl_block_test_t ctrl_block_tests[] = { {
60 .cbt_desc = "controller write blocks controller read",
61 .cbt_fd0 = CBF_FD_CTRL,
62 .cbt_fd1 = CBF_FD_CTRL,
63 .cbt_lock0 = &nvme_test_ctrl_wrlock,
64 .cbt_lock1 = &nvme_test_ctrl_rdlock,
65 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
66 }, {
67 .cbt_desc = "controller write blocks controller write",
68 .cbt_fd0 = CBF_FD_CTRL,
69 .cbt_fd1 = CBF_FD_CTRL,
70 .cbt_lock0 = &nvme_test_ctrl_wrlock,
71 .cbt_lock1 = &nvme_test_ctrl_wrlock,
72 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
73 }, {
74 .cbt_desc = "controller write blocks namespace read",
75 .cbt_fd0 = CBF_FD_CTRL,
76 .cbt_fd1 = CBF_FD_NS,
77 .cbt_lock0 = &nvme_test_ctrl_wrlock,
78 .cbt_lock1 = &nvme_test_ns_rdlock,
79 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
80 }, {
81 .cbt_desc = "controller write blocks namespace write",
82 .cbt_fd0 = CBF_FD_CTRL,
83 .cbt_fd1 = CBF_FD_NS,
84 .cbt_lock0 = &nvme_test_ctrl_wrlock,
85 .cbt_lock1 = &nvme_test_ns_wrlock,
86 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
87 }, {
88 .cbt_desc = "controller read blocks controller write",
89 .cbt_fd0 = CBF_FD_CTRL,
90 .cbt_fd1 = CBF_FD_CTRL,
91 .cbt_lock0 = &nvme_test_ctrl_rdlock,
92 .cbt_lock1 = &nvme_test_ctrl_wrlock,
93 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
94 }, {
95 .cbt_desc = "controller read does not block namespace write",
96 .cbt_fd0 = CBF_FD_CTRL,
97 .cbt_fd1 = CBF_FD_NS,
98 .cbt_lock0 = &nvme_test_ctrl_rdlock,
99 .cbt_lock1 = &nvme_test_ns_wrlock,
100 .cbt_ret1 = NVME_IOCTL_E_OK
101 }, {
102 .cbt_desc = "namespace write blocks namespace read",
103 .cbt_fd0 = CBF_FD_NS,
104 .cbt_fd1 = CBF_FD_NS,
105 .cbt_lock0 = &nvme_test_ns_wrlock,
106 .cbt_lock1 = &nvme_test_ns_rdlock,
107 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
108 }, {
109 .cbt_desc = "namespace write blocks namespace read",
110 .cbt_fd0 = CBF_FD_NS,
111 .cbt_fd1 = CBF_FD_NS,
112 .cbt_lock0 = &nvme_test_ns_wrlock,
113 .cbt_lock1 = &nvme_test_ns_rdlock,
114 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
115 }, {
116 .cbt_desc = "namespace write blocks namespace write",
117 .cbt_fd0 = CBF_FD_NS,
118 .cbt_fd1 = CBF_FD_NS,
119 .cbt_lock0 = &nvme_test_ns_wrlock,
120 .cbt_lock1 = &nvme_test_ns_wrlock,
121 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
122 }, {
123 .cbt_desc = "namespace write blocks controller write",
124 .cbt_fd0 = CBF_FD_NS,
125 .cbt_fd1 = CBF_FD_CTRL,
126 .cbt_lock0 = &nvme_test_ns_wrlock,
127 .cbt_lock1 = &nvme_test_ctrl_wrlock,
128 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
129 }, {
130 .cbt_desc = "namespace write does not block controller read",
131 .cbt_fd0 = CBF_FD_NS,
132 .cbt_fd1 = CBF_FD_CTRL,
133 .cbt_lock0 = &nvme_test_ns_wrlock,
134 .cbt_lock1 = &nvme_test_ctrl_rdlock,
135 .cbt_ret1 = NVME_IOCTL_E_OK
136 }, {
137 .cbt_desc = "namespace read blocks namespace write",
138 .cbt_fd0 = CBF_FD_NS,
139 .cbt_fd1 = CBF_FD_NS,
140 .cbt_lock0 = &nvme_test_ns_rdlock,
141 .cbt_lock1 = &nvme_test_ns_wrlock,
142 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
143 }, {
144 .cbt_desc = "namespace read blocks controller write",
145 .cbt_fd0 = CBF_FD_NS,
146 .cbt_fd1 = CBF_FD_CTRL,
147 .cbt_lock0 = &nvme_test_ns_rdlock,
148 .cbt_lock1 = &nvme_test_ctrl_wrlock,
149 .cbt_ret1 = NVME_IOCTL_E_LOCK_WOULD_BLOCK
150 } };
151
152 static bool
ctrl_block_test_one(int fd0,int fd1,const ctrl_block_test_t * test)153 ctrl_block_test_one(int fd0, int fd1, const ctrl_block_test_t *test)
154 {
155 nvme_ioctl_lock_t lock0 = *test->cbt_lock0;
156 nvme_ioctl_lock_t lock1 = *test->cbt_lock1;
157
158 if (ioctl(fd0, NVME_IOC_LOCK, &lock0) != 0) {
159 warn("TEST FAILED: %s: failed to issue lock ioctl for fd0",
160 test->cbt_desc);
161 return (false);
162 } else if (lock0.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) {
163 warnx("TEST FAILED: %s: fd0 lock ioctl failed with 0x%x, "
164 "expected success", test->cbt_desc,
165 lock0.nil_common.nioc_drv_err);
166 return (false);
167 }
168
169 if (ioctl(fd1, NVME_IOC_LOCK, &lock1) != 0) {
170 warn("TEST FAILED: %s: failed to issue lock ioctl for fd1",
171 test->cbt_desc);
172 return (false);
173 } else if (lock1.nil_common.nioc_drv_err != test->cbt_ret1) {
174 warnx("TEST FAILED: %s: fd1 lock ioctl returned with 0x%x, "
175 "expected 0x%x", test->cbt_desc,
176 lock1.nil_common.nioc_drv_err, test->cbt_ret1);
177 return (false);
178 }
179
180 (void) printf("TEST PASSED: %s\n", test->cbt_desc);
181 return (true);
182 }
183
184 int
main(void)185 main(void)
186 {
187 int ret = EXIT_SUCCESS;
188
189 /*
190 * We purposefully open and close the fds every iteration of this loop
191 * so we don't have to explicitly issue conditional unlocks.
192 */
193 for (size_t i = 0; i < ARRAY_SIZE(ctrl_block_tests); i++) {
194 int fd0, fd1;
195
196 if (ctrl_block_tests[i].cbt_fd0 == CBF_FD_CTRL) {
197 fd0 = nvme_ioctl_test_get_fd(0);
198 } else {
199 fd0 = nvme_ioctl_test_get_fd(1);
200 }
201
202 if (ctrl_block_tests[i].cbt_fd1 == CBF_FD_CTRL) {
203 fd1 = nvme_ioctl_test_get_fd(0);
204 } else {
205 fd1 = nvme_ioctl_test_get_fd(1);
206 }
207
208 if (!ctrl_block_test_one(fd0, fd1, &ctrl_block_tests[i])) {
209 ret = EXIT_FAILURE;
210 }
211
212 VERIFY0(close(fd0));
213 VERIFY0(close(fd1));
214 }
215
216 return (ret);
217 }
218