xref: /freebsd/sbin/nvmecontrol/resv.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/ioccom.h>
33 
34 #include <err.h>
35 #include <fcntl.h>
36 #include <stdbool.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "nvmecontrol.h"
44 
45 /* Tables for command line parsing */
46 
47 static cmd_fn_t resv;
48 static cmd_fn_t resvacquire;
49 static cmd_fn_t resvregister;
50 static cmd_fn_t resvrelease;
51 static cmd_fn_t resvreport;
52 
53 #define NONE 0xffffffffu
54 #define NONE64 0xffffffffffffffffull
55 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
56 #define OPT_END	{ NULL, 0, arg_none, NULL, NULL }
57 
58 static struct cmd resv_cmd = {
59 	.name = "resv",
60 	.fn = resv,
61 	.descr = "Reservation commands",
62 	.ctx_size = 0,
63 	.opts = NULL,
64 	.args = NULL,
65 };
66 
67 CMD_COMMAND(resv_cmd);
68 
69 static struct acquire_options {
70 	uint64_t	crkey;
71 	uint64_t	prkey;
72 	uint8_t		rtype;
73 	uint8_t		racqa;
74 	const char	*dev;
75 } acquire_opt = {
76 	.crkey = 0,
77 	.prkey = 0,
78 	.rtype = 0,
79 	.racqa = 0,
80 	.dev = NULL,
81 };
82 
83 static const struct opts acquire_opts[] = {
84 	OPT("crkey", 'c', arg_uint64, acquire_opt, crkey,
85 	    "Current Reservation Key"),
86 	OPT("prkey", 'p', arg_uint64, acquire_opt, prkey,
87 	    "Preempt Reservation Key"),
88 	OPT("rtype", 't', arg_uint8, acquire_opt, rtype,
89 	    "Reservation Type"),
90 	OPT("racqa", 'a', arg_uint8, acquire_opt, racqa,
91 	    "Acquire Action (0=acq, 1=pre, 2=pre+ab)"),
92 	{ NULL, 0, arg_none, NULL, NULL }
93 };
94 
95 static const struct args acquire_args[] = {
96 	{ arg_string, &acquire_opt.dev, "namespace-id" },
97 	{ arg_none, NULL, NULL },
98 };
99 
100 static struct cmd acquire_cmd = {
101 	.name = "acquire",
102 	.fn = resvacquire,
103 	.descr = "Acquire/preempt reservation",
104 	.ctx_size = sizeof(acquire_opt),
105 	.opts = acquire_opts,
106 	.args = acquire_args,
107 };
108 
109 CMD_SUBCOMMAND(resv_cmd, acquire_cmd);
110 
111 static struct register_options {
112 	uint64_t	crkey;
113 	uint64_t	nrkey;
114 	uint8_t		rrega;
115 	bool		iekey;
116 	uint8_t		cptpl;
117 	const char	*dev;
118 } register_opt = {
119 	.crkey = 0,
120 	.nrkey = 0,
121 	.rrega = 0,
122 	.iekey = false,
123 	.cptpl = 0,
124 	.dev = NULL,
125 };
126 
127 static const struct opts register_opts[] = {
128 	OPT("crkey", 'c', arg_uint64, register_opt, crkey,
129 	    "Current Reservation Key"),
130 	OPT("nrkey", 'k', arg_uint64, register_opt, nrkey,
131 	    "New Reservation Key"),
132 	OPT("rrega", 'r', arg_uint8, register_opt, rrega,
133 	    "Register Action (0=reg, 1=unreg, 2=replace)"),
134 	OPT("iekey", 'i', arg_none, register_opt, iekey,
135 	    "Ignore Existing Key"),
136 	OPT("cptpl", 'p', arg_uint8, register_opt, cptpl,
137 	    "Change Persist Through Power Loss State"),
138 	{ NULL, 0, arg_none, NULL, NULL }
139 };
140 
141 static const struct args register_args[] = {
142 	{ arg_string, &register_opt.dev, "namespace-id" },
143 	{ arg_none, NULL, NULL },
144 };
145 
146 static struct cmd register_cmd = {
147 	.name = "register",
148 	.fn = resvregister,
149 	.descr = "Register/unregister reservation",
150 	.ctx_size = sizeof(register_opt),
151 	.opts = register_opts,
152 	.args = register_args,
153 };
154 
155 CMD_SUBCOMMAND(resv_cmd, register_cmd);
156 
157 static struct release_options {
158 	uint64_t	crkey;
159 	uint8_t		rtype;
160 	uint8_t		rrela;
161 	const char	*dev;
162 } release_opt = {
163 	.crkey = 0,
164 	.rtype = 0,
165 	.rrela = 0,
166 	.dev = NULL,
167 };
168 
169 static const struct opts release_opts[] = {
170 	OPT("crkey", 'c', arg_uint64, release_opt, crkey,
171 	    "Current Reservation Key"),
172 	OPT("rtype", 't', arg_uint8, release_opt, rtype,
173 	    "Reservation Type"),
174 	OPT("rrela", 'a', arg_uint8, release_opt, rrela,
175 	    "Release Action (0=release, 1=clear)"),
176 	{ NULL, 0, arg_none, NULL, NULL }
177 };
178 
179 static const struct args release_args[] = {
180 	{ arg_string, &release_opt.dev, "namespace-id" },
181 	{ arg_none, NULL, NULL },
182 };
183 
184 static struct cmd release_cmd = {
185 	.name = "release",
186 	.fn = resvrelease,
187 	.descr = "Release/clear reservation",
188 	.ctx_size = sizeof(release_opt),
189 	.opts = release_opts,
190 	.args = release_args,
191 };
192 
193 CMD_SUBCOMMAND(resv_cmd, release_cmd);
194 
195 static struct report_options {
196 	bool		hex;
197 	bool		verbose;
198 	bool		eds;
199 	const char	*dev;
200 } report_opt = {
201 	.hex = false,
202 	.verbose = false,
203 	.eds = false,
204 	.dev = NULL,
205 };
206 
207 static const struct opts report_opts[] = {
208 	OPT("hex", 'x', arg_none, report_opt, hex,
209 	    "Print reservation status in hex"),
210 	OPT("verbose", 'v', arg_none, report_opt, verbose,
211 	    "More verbosity"),
212 	OPT("eds", 'e', arg_none, report_opt, eds,
213 	    "Extended Data Structure"),
214 	{ NULL, 0, arg_none, NULL, NULL }
215 };
216 
217 static const struct args report_args[] = {
218 	{ arg_string, &report_opt.dev, "namespace-id" },
219 	{ arg_none, NULL, NULL },
220 };
221 
222 static struct cmd report_cmd = {
223 	.name = "report",
224 	.fn = resvreport,
225 	.descr = "Print reservation status",
226 	.ctx_size = sizeof(report_opt),
227 	.opts = report_opts,
228 	.args = report_args,
229 };
230 
231 CMD_SUBCOMMAND(resv_cmd, report_cmd);
232 
233 /* handles NVME_OPC_RESERVATION_* NVM commands */
234 
235 static void
236 resvacquire(const struct cmd *f, int argc, char *argv[])
237 {
238 	struct nvme_pt_command	pt;
239 	uint64_t	data[2];
240 	int		fd;
241 	uint32_t	nsid;
242 
243 	if (arg_parse(argc, argv, f))
244 		return;
245 	open_dev(acquire_opt.dev, &fd, 1, 1);
246 	get_nsid(fd, NULL, &nsid);
247 	if (nsid == 0) {
248 		fprintf(stderr, "This command require namespace-id\n");
249 		arg_help(argc, argv, f);
250 	}
251 
252 	data[0] = htole64(acquire_opt.crkey);
253 	data[1] = htole64(acquire_opt.prkey);
254 
255 	memset(&pt, 0, sizeof(pt));
256 	pt.cmd.opc = NVME_OPC_RESERVATION_ACQUIRE;
257 	pt.cmd.cdw10 = htole32((acquire_opt.racqa & 7) |
258 	    (acquire_opt.rtype << 8));
259 	pt.buf = &data;
260 	pt.len = sizeof(data);
261 	pt.is_read = 0;
262 
263 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
264 		err(1, "acquire request failed");
265 
266 	if (nvme_completion_is_error(&pt.cpl))
267 		errx(1, "acquire request returned error");
268 
269 	close(fd);
270 	exit(0);
271 }
272 
273 static void
274 resvregister(const struct cmd *f, int argc, char *argv[])
275 {
276 	struct nvme_pt_command	pt;
277 	uint64_t	data[2];
278 	int		fd;
279 	uint32_t	nsid;
280 
281 	if (arg_parse(argc, argv, f))
282 		return;
283 	open_dev(register_opt.dev, &fd, 1, 1);
284 	get_nsid(fd, NULL, &nsid);
285 	if (nsid == 0) {
286 		fprintf(stderr, "This command require namespace-id\n");
287 		arg_help(argc, argv, f);
288 	}
289 
290 	data[0] = htole64(register_opt.crkey);
291 	data[1] = htole64(register_opt.nrkey);
292 
293 	memset(&pt, 0, sizeof(pt));
294 	pt.cmd.opc = NVME_OPC_RESERVATION_REGISTER;
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(1, "register request failed");
303 
304 	if (nvme_completion_is_error(&pt.cpl))
305 		errx(1, "register request returned error");
306 
307 	close(fd);
308 	exit(0);
309 }
310 
311 static void
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, 1, 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.cdw10 = htole32((release_opt.rrela & 7) |
333 	    (release_opt.rtype << 8));
334 	pt.buf = &data;
335 	pt.len = sizeof(data);
336 	pt.is_read = 0;
337 
338 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
339 		err(1, "release request failed");
340 
341 	if (nvme_completion_is_error(&pt.cpl))
342 		errx(1, "release request returned error");
343 
344 	close(fd);
345 	exit(0);
346 }
347 
348 static void
349 resvreport(const struct cmd *f, int argc, char *argv[])
350 {
351 	struct nvme_pt_command	pt;
352 	struct nvme_resv_status	*s;
353 	struct nvme_resv_status_ext *e;
354 	uint8_t		data[4096] __aligned(4);
355 	int		fd;
356 	u_int		i, n;
357 	uint32_t	nsid;
358 
359 	if (arg_parse(argc, argv, f))
360 		return;
361 	open_dev(report_opt.dev, &fd, 1, 1);
362 	get_nsid(fd, NULL, &nsid);
363 	if (nsid == 0) {
364 		fprintf(stderr, "This command require namespace-id\n");
365 		arg_help(argc, argv, f);
366 	}
367 
368 	bzero(data, sizeof(data));
369 	memset(&pt, 0, sizeof(pt));
370 	pt.cmd.opc = NVME_OPC_RESERVATION_REPORT;
371 	pt.cmd.cdw10 = htole32(sizeof(data) / 4 - 1);
372 	pt.cmd.cdw11 = htole32(report_opt.eds);	/* EDS */
373 	pt.buf = &data;
374 	pt.len = sizeof(data);
375 	pt.is_read = 1;
376 
377 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
378 		err(1, "report request failed");
379 
380 	if (nvme_completion_is_error(&pt.cpl))
381 		errx(1, "report request returned error");
382 
383 	close(fd);
384 
385 	if (report_opt.eds)
386 		nvme_resv_status_ext_swapbytes((void *)data, sizeof(data));
387 	else
388 		nvme_resv_status_swapbytes((void *)data, sizeof(data));
389 
390 	if (report_opt.hex) {
391 		i = sizeof(data);
392 		if (!report_opt.verbose) {
393 			for (; i > 64; i--) {
394 				if (data[i - 1] != 0)
395 					break;
396 			}
397 		}
398 		print_hex(&data, i);
399 		exit(0);
400 	}
401 
402 	s = (struct nvme_resv_status *)data;
403 	n = (s->regctl[1] << 8) | s->regctl[0];
404 	printf("Generation:                       %u\n", s->gen);
405 	printf("Reservation Type:                 %u\n", s->rtype);
406 	printf("Number of Registered Controllers: %u\n", n);
407 	printf("Persist Through Power Loss State: %u\n", s->ptpls);
408 	if (report_opt.eds) {
409 		e = (struct nvme_resv_status_ext *)data;
410 		n = MIN(n, (sizeof(data) - sizeof(e)) / sizeof(e->ctrlr[0]));
411 		for (i = 0; i < n; i++) {
412 			printf("Controller ID:                    0x%04x\n",
413 			    e->ctrlr[i].ctrlr_id);
414 			printf("  Reservation Status:             %u\n",
415 			    e->ctrlr[i].rcsts);
416 			printf("  Reservation Key:                0x%08jx\n",
417 			    e->ctrlr[i].rkey);
418 			printf("  Host Identifier:                0x%08jx%08jx\n",
419 			    e->ctrlr[i].hostid[0], e->ctrlr[i].hostid[1]);
420 		}
421 	} else {
422 		n = MIN(n, (sizeof(data) - sizeof(s)) / sizeof(s->ctrlr[0]));
423 		for (i = 0; i < n; i++) {
424 			printf("Controller ID:                    0x%04x\n",
425 			    s->ctrlr[i].ctrlr_id);
426 			printf("  Reservation Status:             %u\n",
427 			    s->ctrlr[i].rcsts);
428 			printf("  Host Identifier:                0x%08jx\n",
429 			    s->ctrlr[i].hostid);
430 			printf("  Reservation Key:                0x%08jx\n",
431 			    s->ctrlr[i].rkey);
432 		}
433 	}
434 	exit(0);
435 }
436 
437 static void
438 resv(const struct cmd *nf __unused, int argc, char *argv[])
439 {
440 
441 	cmd_dispatch(argc, argv, &resv_cmd);
442 }
443