xref: /freebsd/sbin/nvmecontrol/resv.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2019 Alexander Motin <mav@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/ioccom.h>
30 
31 #include <err.h>
32 #include <fcntl.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sysexits.h>
39 #include <unistd.h>
40 
41 #include "nvmecontrol.h"
42 
43 /* Tables for command line parsing */
44 
45 static cmd_fn_t resv;
46 static cmd_fn_t resvacquire;
47 static cmd_fn_t resvregister;
48 static cmd_fn_t resvrelease;
49 static cmd_fn_t resvreport;
50 
51 #define NONE 0xffffffffu
52 #define NONE64 0xffffffffffffffffull
53 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
54 #define OPT_END	{ NULL, 0, arg_none, NULL, NULL }
55 
56 static struct cmd resv_cmd = {
57 	.name = "resv",
58 	.fn = resv,
59 	.descr = "Reservation commands",
60 	.ctx_size = 0,
61 	.opts = NULL,
62 	.args = NULL,
63 };
64 
65 CMD_COMMAND(resv_cmd);
66 
67 static struct acquire_options {
68 	uint64_t	crkey;
69 	uint64_t	prkey;
70 	uint8_t		rtype;
71 	uint8_t		racqa;
72 	const char	*dev;
73 } acquire_opt = {
74 	.crkey = 0,
75 	.prkey = 0,
76 	.rtype = 0,
77 	.racqa = 0,
78 	.dev = NULL,
79 };
80 
81 static const struct opts acquire_opts[] = {
82 	OPT("crkey", 'c', arg_uint64, acquire_opt, crkey,
83 	    "Current Reservation Key"),
84 	OPT("prkey", 'p', arg_uint64, acquire_opt, prkey,
85 	    "Preempt Reservation Key"),
86 	OPT("rtype", 't', arg_uint8, acquire_opt, rtype,
87 	    "Reservation Type"),
88 	OPT("racqa", 'a', arg_uint8, acquire_opt, racqa,
89 	    "Acquire Action (0=acq, 1=pre, 2=pre+ab)"),
90 	{ NULL, 0, arg_none, NULL, NULL }
91 };
92 
93 static const struct args acquire_args[] = {
94 	{ arg_string, &acquire_opt.dev, "namespace-id" },
95 	{ arg_none, NULL, NULL },
96 };
97 
98 static struct cmd acquire_cmd = {
99 	.name = "acquire",
100 	.fn = resvacquire,
101 	.descr = "Acquire/preempt reservation",
102 	.ctx_size = sizeof(acquire_opt),
103 	.opts = acquire_opts,
104 	.args = acquire_args,
105 };
106 
107 CMD_SUBCOMMAND(resv_cmd, acquire_cmd);
108 
109 static struct register_options {
110 	uint64_t	crkey;
111 	uint64_t	nrkey;
112 	uint8_t		rrega;
113 	bool		iekey;
114 	uint8_t		cptpl;
115 	const char	*dev;
116 } register_opt = {
117 	.crkey = 0,
118 	.nrkey = 0,
119 	.rrega = 0,
120 	.iekey = false,
121 	.cptpl = 0,
122 	.dev = NULL,
123 };
124 
125 static const struct opts register_opts[] = {
126 	OPT("crkey", 'c', arg_uint64, register_opt, crkey,
127 	    "Current Reservation Key"),
128 	OPT("nrkey", 'k', arg_uint64, register_opt, nrkey,
129 	    "New Reservation Key"),
130 	OPT("rrega", 'r', arg_uint8, register_opt, rrega,
131 	    "Register Action (0=reg, 1=unreg, 2=replace)"),
132 	OPT("iekey", 'i', arg_none, register_opt, iekey,
133 	    "Ignore Existing Key"),
134 	OPT("cptpl", 'p', arg_uint8, register_opt, cptpl,
135 	    "Change Persist Through Power Loss State"),
136 	{ NULL, 0, arg_none, NULL, NULL }
137 };
138 
139 static const struct args register_args[] = {
140 	{ arg_string, &register_opt.dev, "namespace-id" },
141 	{ arg_none, NULL, NULL },
142 };
143 
144 static struct cmd register_cmd = {
145 	.name = "register",
146 	.fn = resvregister,
147 	.descr = "Register/unregister reservation",
148 	.ctx_size = sizeof(register_opt),
149 	.opts = register_opts,
150 	.args = register_args,
151 };
152 
153 CMD_SUBCOMMAND(resv_cmd, register_cmd);
154 
155 static struct release_options {
156 	uint64_t	crkey;
157 	uint8_t		rtype;
158 	uint8_t		rrela;
159 	const char	*dev;
160 } release_opt = {
161 	.crkey = 0,
162 	.rtype = 0,
163 	.rrela = 0,
164 	.dev = NULL,
165 };
166 
167 static const struct opts release_opts[] = {
168 	OPT("crkey", 'c', arg_uint64, release_opt, crkey,
169 	    "Current Reservation Key"),
170 	OPT("rtype", 't', arg_uint8, release_opt, rtype,
171 	    "Reservation Type"),
172 	OPT("rrela", 'a', arg_uint8, release_opt, rrela,
173 	    "Release Action (0=release, 1=clear)"),
174 	{ NULL, 0, arg_none, NULL, NULL }
175 };
176 
177 static const struct args release_args[] = {
178 	{ arg_string, &release_opt.dev, "namespace-id" },
179 	{ arg_none, NULL, NULL },
180 };
181 
182 static struct cmd release_cmd = {
183 	.name = "release",
184 	.fn = resvrelease,
185 	.descr = "Release/clear reservation",
186 	.ctx_size = sizeof(release_opt),
187 	.opts = release_opts,
188 	.args = release_args,
189 };
190 
191 CMD_SUBCOMMAND(resv_cmd, release_cmd);
192 
193 static struct report_options {
194 	bool		hex;
195 	bool		verbose;
196 	bool		eds;
197 	const char	*dev;
198 } report_opt = {
199 	.hex = false,
200 	.verbose = false,
201 	.eds = false,
202 	.dev = NULL,
203 };
204 
205 static const struct opts report_opts[] = {
206 	OPT("hex", 'x', arg_none, report_opt, hex,
207 	    "Print reservation status in hex"),
208 	OPT("verbose", 'v', arg_none, report_opt, verbose,
209 	    "More verbosity"),
210 	OPT("eds", 'e', arg_none, report_opt, eds,
211 	    "Extended Data Structure"),
212 	{ NULL, 0, arg_none, NULL, NULL }
213 };
214 
215 static const struct args report_args[] = {
216 	{ arg_string, &report_opt.dev, "namespace-id" },
217 	{ arg_none, NULL, NULL },
218 };
219 
220 static struct cmd report_cmd = {
221 	.name = "report",
222 	.fn = resvreport,
223 	.descr = "Print reservation status",
224 	.ctx_size = sizeof(report_opt),
225 	.opts = report_opts,
226 	.args = report_args,
227 };
228 
229 CMD_SUBCOMMAND(resv_cmd, report_cmd);
230 
231 /* handles NVME_OPC_RESERVATION_* NVM commands */
232 
233 static void
resvacquire(const struct cmd * f,int argc,char * argv[])234 resvacquire(const struct cmd *f, int argc, char *argv[])
235 {
236 	struct nvme_pt_command	pt;
237 	uint64_t	data[2];
238 	int		fd;
239 	uint32_t	nsid;
240 
241 	if (arg_parse(argc, argv, f))
242 		return;
243 	open_dev(acquire_opt.dev, &fd, 0, 1);
244 	get_nsid(fd, NULL, &nsid);
245 	if (nsid == 0) {
246 		fprintf(stderr, "This command require namespace-id\n");
247 		arg_help(argc, argv, f);
248 	}
249 
250 	data[0] = htole64(acquire_opt.crkey);
251 	data[1] = htole64(acquire_opt.prkey);
252 
253 	memset(&pt, 0, sizeof(pt));
254 	pt.cmd.opc = NVME_OPC_RESERVATION_ACQUIRE;
255 	pt.cmd.nsid = htole32(nsid);
256 	pt.cmd.cdw10 = htole32((acquire_opt.racqa & 7) |
257 	    (acquire_opt.rtype << 8));
258 	pt.buf = &data;
259 	pt.len = sizeof(data);
260 	pt.is_read = 0;
261 
262 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
263 		err(EX_IOERR, "acquire request failed");
264 
265 	if (nvme_completion_is_error(&pt.cpl))
266 		errx(EX_IOERR, "acquire request returned error");
267 
268 	close(fd);
269 	exit(0);
270 }
271 
272 static void
resvregister(const struct cmd * f,int argc,char * argv[])273 resvregister(const struct cmd *f, int argc, char *argv[])
274 {
275 	struct nvme_pt_command	pt;
276 	uint64_t	data[2];
277 	int		fd;
278 	uint32_t	nsid;
279 
280 	if (arg_parse(argc, argv, f))
281 		return;
282 	open_dev(register_opt.dev, &fd, 0, 1);
283 	get_nsid(fd, NULL, &nsid);
284 	if (nsid == 0) {
285 		fprintf(stderr, "This command require namespace-id\n");
286 		arg_help(argc, argv, f);
287 	}
288 
289 	data[0] = htole64(register_opt.crkey);
290 	data[1] = htole64(register_opt.nrkey);
291 
292 	memset(&pt, 0, sizeof(pt));
293 	pt.cmd.opc = NVME_OPC_RESERVATION_REGISTER;
294 	pt.cmd.nsid = htole32(nsid);
295 	pt.cmd.cdw10 = htole32((register_opt.rrega & 7) |
296 	    (register_opt.iekey << 3) | (register_opt.cptpl << 30));
297 	pt.buf = &data;
298 	pt.len = sizeof(data);
299 	pt.is_read = 0;
300 
301 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
302 		err(EX_IOERR, "register request failed");
303 
304 	if (nvme_completion_is_error(&pt.cpl))
305 		errx(EX_IOERR, "register request returned error");
306 
307 	close(fd);
308 	exit(0);
309 }
310 
311 static void
resvrelease(const struct cmd * f,int argc,char * argv[])312 resvrelease(const struct cmd *f, int argc, char *argv[])
313 {
314 	struct nvme_pt_command	pt;
315 	uint64_t	data[1];
316 	int		fd;
317 	uint32_t	nsid;
318 
319 	if (arg_parse(argc, argv, f))
320 		return;
321 	open_dev(release_opt.dev, &fd, 0, 1);
322 	get_nsid(fd, NULL, &nsid);
323 	if (nsid == 0) {
324 		fprintf(stderr, "This command require namespace-id\n");
325 		arg_help(argc, argv, f);
326 	}
327 
328 	data[0] = htole64(release_opt.crkey);
329 
330 	memset(&pt, 0, sizeof(pt));
331 	pt.cmd.opc = NVME_OPC_RESERVATION_RELEASE;
332 	pt.cmd.nsid = htole32(nsid);
333 	pt.cmd.cdw10 = htole32((release_opt.rrela & 7) |
334 	    (release_opt.rtype << 8));
335 	pt.buf = &data;
336 	pt.len = sizeof(data);
337 	pt.is_read = 0;
338 
339 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
340 		err(EX_IOERR, "release request failed");
341 
342 	if (nvme_completion_is_error(&pt.cpl))
343 		errx(EX_IOERR, "release request returned error");
344 
345 	close(fd);
346 	exit(0);
347 }
348 
349 static void
resvreport(const struct cmd * f,int argc,char * argv[])350 resvreport(const struct cmd *f, int argc, char *argv[])
351 {
352 	struct nvme_pt_command	pt;
353 	struct nvme_resv_status	*s;
354 	struct nvme_resv_status_ext *e;
355 	uint8_t		data[4096] __aligned(4);
356 	int		fd;
357 	u_int		i, n;
358 	uint32_t	nsid;
359 
360 	if (arg_parse(argc, argv, f))
361 		return;
362 	open_dev(report_opt.dev, &fd, 0, 1);
363 	get_nsid(fd, NULL, &nsid);
364 	if (nsid == 0) {
365 		fprintf(stderr, "This command require namespace-id\n");
366 		arg_help(argc, argv, f);
367 	}
368 
369 	bzero(data, sizeof(data));
370 	memset(&pt, 0, sizeof(pt));
371 	pt.cmd.opc = NVME_OPC_RESERVATION_REPORT;
372 	pt.cmd.nsid = htole32(nsid);
373 	pt.cmd.cdw10 = htole32(sizeof(data) / 4 - 1);
374 	pt.cmd.cdw11 = htole32(report_opt.eds);	/* EDS */
375 	pt.buf = &data;
376 	pt.len = sizeof(data);
377 	pt.is_read = 1;
378 
379 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
380 		err(EX_IOERR, "report request failed");
381 
382 	if (nvme_completion_is_error(&pt.cpl))
383 		errx(EX_IOERR, "report request returned error");
384 
385 	close(fd);
386 
387 	if (report_opt.eds)
388 		nvme_resv_status_ext_swapbytes((void *)data, sizeof(data));
389 	else
390 		nvme_resv_status_swapbytes((void *)data, sizeof(data));
391 
392 	if (report_opt.hex) {
393 		i = sizeof(data);
394 		if (!report_opt.verbose) {
395 			for (; i > 64; i--) {
396 				if (data[i - 1] != 0)
397 					break;
398 			}
399 		}
400 		print_hex(&data, i);
401 		exit(0);
402 	}
403 
404 	s = (struct nvme_resv_status *)data;
405 	n = (s->regctl[1] << 8) | s->regctl[0];
406 	printf("Generation:                       %u\n", s->gen);
407 	printf("Reservation Type:                 %u\n", s->rtype);
408 	printf("Number of Registered Controllers: %u\n", n);
409 	printf("Persist Through Power Loss State: %u\n", s->ptpls);
410 	if (report_opt.eds) {
411 		e = (struct nvme_resv_status_ext *)data;
412 		n = MIN(n, (sizeof(data) - sizeof(e)) / sizeof(e->ctrlr[0]));
413 		for (i = 0; i < n; i++) {
414 			printf("Controller ID:                    0x%04x\n",
415 			    e->ctrlr[i].ctrlr_id);
416 			printf("  Reservation Status:             %u\n",
417 			    e->ctrlr[i].rcsts);
418 			printf("  Reservation Key:                0x%08jx\n",
419 			    e->ctrlr[i].rkey);
420 			printf("  Host Identifier:                0x%08jx%08jx\n",
421 			    e->ctrlr[i].hostid[0], e->ctrlr[i].hostid[1]);
422 		}
423 	} else {
424 		n = MIN(n, (sizeof(data) - sizeof(s)) / sizeof(s->ctrlr[0]));
425 		for (i = 0; i < n; i++) {
426 			printf("Controller ID:                    0x%04x\n",
427 			    s->ctrlr[i].ctrlr_id);
428 			printf("  Reservation Status:             %u\n",
429 			    s->ctrlr[i].rcsts);
430 			printf("  Host Identifier:                0x%08jx\n",
431 			    s->ctrlr[i].hostid);
432 			printf("  Reservation Key:                0x%08jx\n",
433 			    s->ctrlr[i].rkey);
434 		}
435 	}
436 	exit(0);
437 }
438 
439 static void
resv(const struct cmd * nf __unused,int argc,char * argv[])440 resv(const struct cmd *nf __unused, int argc, char *argv[])
441 {
442 
443 	cmd_dispatch(argc, argv, &resv_cmd);
444 }
445