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, ®ister_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