xref: /illumos-gate/usr/src/test/nvme-tests/tests/ioctl/nvme_ioctl_util.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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 2025 Oxide Computer Company
14  */
15 
16 /*
17  * Common functions for the various ioctl tests that we're using.
18  */
19 
20 #include <err.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <libdevinfo.h>
24 #include <unistd.h>
25 #include <libproc.h>
26 
27 #include "nvme_ioctl_util.h"
28 
29 /*
30  * Cached copies of devinfo nodes to speed up subsequent lookups.
31  */
32 static di_node_t nvme_test_root;
33 static di_node_t nvme_test_dev;
34 
35 /*
36  * Lock ioctl template structures. These are all non-blocking locks because we
37  * don't want the tests to hang in error.
38  */
39 const nvme_ioctl_lock_t nvme_test_ctrl_wrlock = {
40 	.nil_ent = NVME_LOCK_E_CTRL,
41 	.nil_level = NVME_LOCK_L_WRITE,
42 	.nil_flags = NVME_LOCK_F_DONT_BLOCK
43 };
44 
45 const nvme_ioctl_lock_t nvme_test_ctrl_rdlock = {
46 	.nil_ent = NVME_LOCK_E_CTRL,
47 	.nil_level = NVME_LOCK_L_READ,
48 	.nil_flags = NVME_LOCK_F_DONT_BLOCK
49 };
50 
51 const nvme_ioctl_lock_t nvme_test_ns_wrlock = {
52 	.nil_common = { .nioc_nsid = 1 },
53 	.nil_ent = NVME_LOCK_E_NS,
54 	.nil_level = NVME_LOCK_L_WRITE,
55 	.nil_flags = NVME_LOCK_F_DONT_BLOCK
56 };
57 
58 const nvme_ioctl_lock_t nvme_test_ns_rdlock = {
59 	.nil_common = { .nioc_nsid = 1 },
60 	.nil_ent = NVME_LOCK_E_NS,
61 	.nil_level = NVME_LOCK_L_READ,
62 	.nil_flags = NVME_LOCK_F_DONT_BLOCK
63 };
64 
65 const nvme_ioctl_unlock_t nvme_test_ctrl_unlock = {
66 	.niu_ent = NVME_LOCK_E_CTRL
67 };
68 
69 const nvme_ioctl_unlock_t nvme_test_ns_unlock = {
70 	.niu_common = { .nioc_nsid = 1 },
71 	.niu_ent = NVME_LOCK_E_NS
72 };
73 
74 static int
nvme_ioctl_test_find_nsid(di_node_t di,uint32_t nsid,int oflag)75 nvme_ioctl_test_find_nsid(di_node_t di, uint32_t nsid, int oflag)
76 {
77 	int fd;
78 	const char *type;
79 	char name[128], *mpath, path[PATH_MAX];
80 	di_minor_t minor;
81 
82 	if (nsid == 0) {
83 		type = DDI_NT_NVME_NEXUS;
84 		(void) strlcpy(name, "devctl", sizeof (name));
85 	} else {
86 		type = DDI_NT_NVME_ATTACHMENT_POINT;
87 		(void) snprintf(name, sizeof (name), "%u", nsid);
88 	}
89 
90 	minor = DI_MINOR_NIL;
91 	while ((minor = di_minor_next(di, minor)) != DI_MINOR_NIL) {
92 		if (strcmp(di_minor_nodetype(minor), type) == 0 &&
93 		    strcmp(di_minor_name(minor), name) == 0) {
94 			break;
95 		}
96 	}
97 
98 	if (minor == DI_MINOR_NIL) {
99 		errx(EXIT_FAILURE, "failed to find minor for nsid %u on %s%d",
100 		    nsid, di_driver_name(di), di_instance(di));
101 	}
102 
103 	mpath = di_devfs_minor_path(minor);
104 	if (mpath == NULL) {
105 		err(EXIT_FAILURE, "failed to get minor device path for nsid %u "
106 		    "on %s%d", nsid,  di_driver_name(di), di_instance(di));
107 	}
108 
109 	if (snprintf(path, sizeof (path), "/devices%s", mpath) >=
110 	    sizeof (path)) {
111 		errx(EXIT_FAILURE, "failed to construct full /devices path for "
112 		    "%s: snprintf buffer would have overflowed", mpath);
113 	}
114 	di_devfs_path_free(mpath);
115 
116 	fd = open(path, oflag);
117 	if (fd < 0) {
118 		err(EXIT_FAILURE, "failed to open minor path %s", path);
119 	}
120 
121 	return (fd);
122 }
123 
124 /*
125  * The ioctl tests expect an NVMe device to be nominated for use to test
126  * against. Translate that device into an fd.
127  */
128 int
nvme_ioctl_test_get_fd_flags(uint32_t nsid,int oflag)129 nvme_ioctl_test_get_fd_flags(uint32_t nsid, int oflag)
130 {
131 	const char *dev, *errstr;
132 	long long ll;
133 
134 	if (nvme_test_dev != NULL) {
135 		return (nvme_ioctl_test_find_nsid(nvme_test_dev, nsid, oflag));
136 	}
137 
138 	dev = getenv(NVME_TEST_DEV_ENVVAR);
139 	if (dev == NULL) {
140 		errx(EXIT_FAILURE, "cannot run test, missing required NVMe "
141 		    "device, please set the %s environment variable",
142 		    NVME_TEST_DEV_ENVVAR);
143 	}
144 
145 	if (strncmp("nvme", dev, 4) != 0) {
146 		errx(EXIT_FAILURE, "%s environment variable device %s does "
147 		    "not begin with 'nvme'", NVME_TEST_DEV_ENVVAR, dev);
148 	}
149 
150 	ll = strtonum(dev + 4, 0, INT32_MAX, &errstr);
151 	if (errstr != NULL) {
152 		errx(EXIT_FAILURE, "failed to parse %s environment variable "
153 		    "device %s instance: value is %s", NVME_TEST_DEV_ENVVAR,
154 		    dev, errstr);
155 	}
156 
157 	if (nvme_test_root == NULL) {
158 		nvme_test_root = di_init("/", DINFOCPYALL);
159 		if (nvme_test_root == DI_NODE_NIL) {
160 			err(EXIT_FAILURE, "failed to initialize libdevinfo");
161 		}
162 	}
163 
164 	for (di_node_t di = di_drv_first_node("nvme", nvme_test_root);
165 	    di != DI_NODE_NIL; di = di_drv_next_node(di)) {
166 		if (di_instance(di) == (int)ll) {
167 			nvme_test_dev = di;
168 			return (nvme_ioctl_test_find_nsid(di, nsid, oflag));
169 		}
170 	}
171 
172 	errx(EXIT_FAILURE, "failed to find %s environment variable device %s: "
173 	    "cannot run test", NVME_TEST_DEV_ENVVAR, dev);
174 }
175 
176 int
nvme_ioctl_test_get_fd(uint32_t nsid)177 nvme_ioctl_test_get_fd(uint32_t nsid)
178 {
179 	return (nvme_ioctl_test_get_fd_flags(nsid, O_RDWR));
180 }
181 
182 /*
183  * This is a wrapper that requires we successfully lock something.
184  */
185 void
nvme_ioctl_test_lock(int fd,const nvme_ioctl_lock_t * lockp)186 nvme_ioctl_test_lock(int fd, const nvme_ioctl_lock_t *lockp)
187 {
188 	nvme_ioctl_lock_t lock = *lockp;
189 	const char *targ = lockp->nil_ent == NVME_LOCK_E_CTRL ?
190 	    "controller" : "namespace";
191 	const char *level = lockp->nil_level == NVME_LOCK_L_READ ?
192 	    "read" : "write";
193 
194 	if (ioctl(fd, NVME_IOC_LOCK, &lock) != 0) {
195 		err(EXIT_FAILURE, "TEST FAILED: cannot proceed with tests due "
196 		    "to failure to issue %s %s lock ioctl", targ, level);
197 	} else if (lock.nil_common.nioc_drv_err != NVME_IOCTL_E_OK) {
198 		errx(EXIT_FAILURE, "TEST FAILED: cannot proceed with tests due "
199 		    "to failure to obtain %s %s lock, got 0x%x", targ, level,
200 		    lock.nil_common.nioc_drv_err);
201 	}
202 }
203 
204 /*
205  * Determine if a thread is blocked in our locking ioctl. We use proc_sysname()
206  * so we can avoid encoding the system call number of the ioctl into the test
207  * directly.
208  */
209 bool
nvme_ioctl_test_thr_blocked(thread_t thr)210 nvme_ioctl_test_thr_blocked(thread_t thr)
211 {
212 	lwpstatus_t lwp;
213 	char name[SYS2STR_MAX];
214 
215 	if (proc_get_lwpstatus(getpid(), (uint_t)thr, &lwp) != 0) {
216 		err(EXIT_FAILURE, "TEST FAILED: unable to continue test "
217 		    "execution as we failed to retrieve the lwpsinfo_t data "
218 		    "for thread 0x%x", thr);
219 	}
220 
221 	if ((lwp.pr_flags & PR_ASLEEP) == 0)
222 		return (false);
223 
224 	if (proc_sysname(lwp.pr_syscall, name, sizeof (name)) == NULL)
225 		return (false);
226 
227 	return (strcmp(name, "ioctl") == 0);
228 }
229 
230 const char *
nvme_ioctl_test_cmdstr(int cmd)231 nvme_ioctl_test_cmdstr(int cmd)
232 {
233 	switch (cmd) {
234 	case NVME_IOC_CTRL_INFO:
235 		return ("NVME_IOC_CTRL_INFO");
236 	case NVME_IOC_IDENTIFY:
237 		return ("NVME_IOC_IDENTIFY");
238 	case NVME_IOC_GET_LOGPAGE:
239 		return ("NVME_IOC_GET_LOGPAGE");
240 	case NVME_IOC_GET_FEATURE:
241 		return ("NVME_IOC_GET_FEATURE");
242 	case NVME_IOC_FORMAT:
243 		return ("NVME_IOC_FORMAT");
244 	case NVME_IOC_BD_DETACH:
245 		return ("NVME_IOC_BD_DETACH");
246 	case NVME_IOC_BD_ATTACH:
247 		return ("NVME_IOC_BD_ATTACH");
248 	case NVME_IOC_FIRMWARE_DOWNLOAD:
249 		return ("NVME_IOC_FIRMWARE_DOWNLOAD");
250 	case NVME_IOC_FIRMWARE_COMMIT:
251 		return ("NVME_IOC_FIRMWARE_COMMIT");
252 	case NVME_IOC_PASSTHRU:
253 		return ("NVME_IOC_PASSTHRU");
254 	case NVME_IOC_NS_INFO:
255 		return ("NVME_IOC_NS_INFO");
256 	case NVME_IOC_LOCK:
257 		return ("NVME_IOC_LOCK");
258 	case NVME_IOC_UNLOCK:
259 		return ("NVME_IOC_UNLOCK");
260 	case NVME_IOC_CTRL_ATTACH:
261 		return ("NVME_IOC_CTRL_ATTACH");
262 	case NVME_IOC_CTRL_DETACH:
263 		return ("NVME_IOC_CTRL_DETACH");
264 	case NVME_IOC_NS_CREATE:
265 		return ("NVME_IOC_NS_CREATE");
266 	case NVME_IOC_NS_DELETE:
267 		return ("NVME_IOC_NS_DELETE");
268 	default:
269 		return ("unknown");
270 	}
271 }
272