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