1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2012-2013 Intel Corporation
5 * All rights reserved.
6 * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32
33 #include <ctype.h>
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 #include "nvmecontrol_ext.h"
46
47 #define NONE 0xfffffffeu
48
49 static struct options {
50 bool hex;
51 bool verbose;
52 const char *dev;
53 uint32_t nsid;
54 } opt = {
55 .hex = false,
56 .verbose = false,
57 .dev = NULL,
58 .nsid = NONE,
59 };
60
61 void
print_namespace(struct nvme_namespace_data * nsdata)62 print_namespace(struct nvme_namespace_data *nsdata)
63 {
64 char cbuf[UINT128_DIG + 1];
65 uint32_t i;
66 uint32_t lbaf, lbads, ms, rp;
67 uint8_t thin_prov, ptype;
68 uint8_t flbas_fmt, t;
69
70 thin_prov = NVMEV(NVME_NS_DATA_NSFEAT_THIN_PROV, nsdata->nsfeat);
71
72 flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas);
73
74 printf("Size: %lld blocks\n",
75 (long long)nsdata->nsze);
76 printf("Capacity: %lld blocks\n",
77 (long long)nsdata->ncap);
78 printf("Utilization: %lld blocks\n",
79 (long long)nsdata->nuse);
80 printf("Thin Provisioning: %s\n",
81 thin_prov ? "Supported" : "Not Supported");
82 printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1);
83 printf("Current LBA Format: LBA Format #%02d", flbas_fmt);
84 if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata->lbaf[flbas_fmt]) != 0)
85 printf(" %s metadata\n",
86 NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsdata->flbas) != 0 ?
87 "Extended" : "Separate");
88 else
89 printf("\n");
90 printf("Metadata Capabilities\n");
91 printf(" Extended: %s\n",
92 NVMEV(NVME_NS_DATA_MC_EXTENDED, nsdata->mc) != 0 ? "Supported" :
93 "Not Supported");
94 printf(" Separate: %s\n",
95 NVMEV(NVME_NS_DATA_MC_POINTER, nsdata->mc) != 0 ? "Supported" :
96 "Not Supported");
97 printf("Data Protection Caps: %s%s%s%s%s%s\n",
98 (nsdata->dpc == 0) ? "Not Supported" : "",
99 NVMEV(NVME_NS_DATA_DPC_MD_END, nsdata->dpc) != 0 ? "Last Bytes, " :
100 "",
101 NVMEV(NVME_NS_DATA_DPC_MD_START, nsdata->dpc) != 0 ?
102 "First Bytes, " : "",
103 NVMEV(NVME_NS_DATA_DPC_PIT3, nsdata->dpc) != 0 ? "Type 3, " : "",
104 NVMEV(NVME_NS_DATA_DPC_PIT2, nsdata->dpc) != 0 ? "Type 2, " : "",
105 NVMEV(NVME_NS_DATA_DPC_PIT1, nsdata->dpc) != 0 ? "Type 1" : "");
106 printf("Data Protection Settings: ");
107 ptype = NVMEV(NVME_NS_DATA_DPS_PIT, nsdata->dps);
108 if (ptype != 0) {
109 printf("Type %d, %s Bytes\n", ptype,
110 NVMEV(NVME_NS_DATA_DPS_MD_START, nsdata->dps) != 0 ?
111 "First" : "Last");
112 } else {
113 printf("Not Enabled\n");
114 }
115 printf("Multi-Path I/O Capabilities: %s%s\n",
116 (nsdata->nmic == 0) ? "Not Supported" : "",
117 NVMEV(NVME_NS_DATA_NMIC_MAY_BE_SHARED, nsdata->nmic) != 0 ?
118 "May be shared" : "");
119 printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n",
120 (nsdata->rescap == 0) ? "Not Supported" : "",
121 NVMEV(NVME_NS_DATA_RESCAP_IEKEY13, nsdata->rescap) != 0 ?
122 "IEKEY13, " : "",
123 NVMEV(NVME_NS_DATA_RESCAP_EX_AC_AR, nsdata->rescap) != 0 ?
124 "EX_AC_AR, " : "",
125 NVMEV(NVME_NS_DATA_RESCAP_WR_EX_AR, nsdata->rescap) != 0 ?
126 "WR_EX_AR, " : "",
127 NVMEV(NVME_NS_DATA_RESCAP_EX_AC_RO, nsdata->rescap) != 0 ?
128 "EX_AC_RO, " : "",
129 NVMEV(NVME_NS_DATA_RESCAP_WR_EX_RO, nsdata->rescap) != 0 ?
130 "WR_EX_RO, " : "",
131 NVMEV(NVME_NS_DATA_RESCAP_EX_AC, nsdata->rescap) != 0 ?
132 "EX_AC, " : "",
133 NVMEV(NVME_NS_DATA_RESCAP_WR_EX, nsdata->rescap) != 0 ?
134 "WR_EX, " : "",
135 NVMEV(NVME_NS_DATA_RESCAP_PTPL, nsdata->rescap) != 0 ? "PTPL" : "");
136 printf("Format Progress Indicator: ");
137 if (NVMEV(NVME_NS_DATA_FPI_SUPP, nsdata->fpi) != 0) {
138 printf("%u%% remains\n",
139 NVMEV(NVME_NS_DATA_FPI_PERC, nsdata->fpi));
140 } else
141 printf("Not Supported\n");
142 t = NVMEV(NVME_NS_DATA_DLFEAT_READ, nsdata->dlfeat);
143 printf("Deallocate Logical Block: Read %s%s%s\n",
144 (t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" :
145 (t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" :
146 (t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown",
147 NVMEV(NVME_NS_DATA_DLFEAT_DWZ, nsdata->dlfeat) != 0 ?
148 ", Write Zero" : "",
149 NVMEV(NVME_NS_DATA_DLFEAT_GCRC, nsdata->dlfeat) != 0 ?
150 ", Guard CRC" : "");
151 printf("Optimal I/O Boundary: %u blocks\n", nsdata->noiob);
152 printf("NVM Capacity: %s bytes\n",
153 uint128_to_str(to128(nsdata->nvmcap), cbuf, sizeof(cbuf)));
154 if (NVMEV(NVME_NS_DATA_NSFEAT_NPVALID, nsdata->nsfeat) != 0) {
155 printf("Preferred Write Granularity: %u blocks\n",
156 nsdata->npwg + 1);
157 printf("Preferred Write Alignment: %u blocks\n",
158 nsdata->npwa + 1);
159 printf("Preferred Deallocate Granul: %u blocks\n",
160 nsdata->npdg + 1);
161 printf("Preferred Deallocate Align: %u blocks\n",
162 nsdata->npda + 1);
163 printf("Optimal Write Size: %u blocks\n",
164 nsdata->nows + 1);
165 }
166 printf("Globally Unique Identifier: ");
167 for (i = 0; i < sizeof(nsdata->nguid); i++)
168 printf("%02x", nsdata->nguid[i]);
169 printf("\n");
170 printf("IEEE EUI64: ");
171 for (i = 0; i < sizeof(nsdata->eui64); i++)
172 printf("%02x", nsdata->eui64[i]);
173 printf("\n");
174 for (i = 0; i <= nsdata->nlbaf; i++) {
175 lbaf = nsdata->lbaf[i];
176 lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, lbaf);
177 if (lbads == 0)
178 continue;
179 ms = NVMEV(NVME_NS_DATA_LBAF_MS, lbaf);
180 rp = NVMEV(NVME_NS_DATA_LBAF_RP, lbaf);
181 printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d"
182 " Performance: %s\n",
183 i, 1 << lbads, ms, (rp == 0) ? "Best" :
184 (rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded");
185 }
186 }
187
188 static void
identify_ctrlr(int fd)189 identify_ctrlr(int fd)
190 {
191 struct nvme_controller_data cdata;
192 int hexlength;
193
194 if (read_controller_data(fd, &cdata))
195 errx(EX_IOERR, "Identify request failed");
196 close(fd);
197
198 if (opt.hex) {
199 if (opt.verbose)
200 hexlength = sizeof(struct nvme_controller_data);
201 else
202 hexlength = offsetof(struct nvme_controller_data,
203 reserved8);
204 print_hex(&cdata, hexlength);
205 exit(0);
206 }
207
208 nvme_print_controller(&cdata);
209 exit(0);
210 }
211
212 static void
identify_ns(int fd,uint32_t nsid)213 identify_ns(int fd, uint32_t nsid)
214 {
215 struct nvme_namespace_data nsdata;
216 int hexlength;
217
218 if (read_namespace_data(fd, nsid, &nsdata))
219 errx(EX_IOERR, "Identify request failed");
220 close(fd);
221
222 if (opt.hex) {
223 if (opt.verbose)
224 hexlength = sizeof(struct nvme_namespace_data);
225 else
226 hexlength = offsetof(struct nvme_namespace_data,
227 reserved6);
228 print_hex(&nsdata, hexlength);
229 exit(0);
230 }
231
232 print_namespace(&nsdata);
233 exit(0);
234 }
235
236 static void
identify(const struct cmd * f,int argc,char * argv[])237 identify(const struct cmd *f, int argc, char *argv[])
238 {
239 char *path;
240 int fd;
241 uint32_t nsid;
242
243 if (arg_parse(argc, argv, f))
244 return;
245
246 open_dev(opt.dev, &fd, 0, 1);
247 get_nsid(fd, &path, &nsid);
248 if (nsid != 0) {
249 /*
250 * We got namespace device, but we need to send IDENTIFY
251 * commands to the controller, not the namespace, since it
252 * is an admin cmd. The namespace ID will be specified in
253 * the IDENTIFY command itself.
254 */
255 close(fd);
256 open_dev(path, &fd, 0, 1);
257 }
258 free(path);
259 if (opt.nsid != NONE)
260 nsid = opt.nsid;
261
262 if (nsid == 0)
263 identify_ctrlr(fd);
264 else
265 identify_ns(fd, nsid);
266 }
267
268 static const struct opts identify_opts[] = {
269 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
270 OPT("hex", 'x', arg_none, opt, hex,
271 "Print identity information in hex"),
272 OPT("verbose", 'v', arg_none, opt, verbose,
273 "More verbosity: print entire identify table"),
274 OPT("nsid", 'n', arg_uint32, opt, nsid,
275 "Namespace ID to use if not in device name"),
276 { NULL, 0, arg_none, NULL, NULL }
277 };
278 #undef OPT
279
280 static const struct args identify_args[] = {
281 { arg_string, &opt.dev, "controller-id|namespace-id" },
282 { arg_none, NULL, NULL },
283 };
284
285 static struct cmd identify_cmd = {
286 .name = "identify",
287 .fn = identify,
288 .descr = "Print summary of the IDENTIFY information",
289 .ctx_size = sizeof(opt),
290 .opts = identify_opts,
291 .args = identify_args,
292 };
293
294 CMD_COMMAND(identify_cmd);
295