]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright (c) Intel Corporation. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #include <ctype.h> | |
35 | #include <string.h> | |
36 | #include <stdbool.h> | |
37 | #include <stdio.h> | |
38 | #include <unistd.h> | |
39 | #include <inttypes.h> | |
40 | #include <stdlib.h> | |
41 | #include <sys/types.h> | |
42 | #include <sys/stat.h> | |
43 | #include <fcntl.h> | |
44 | ||
45 | #include "spdk/nvme.h" | |
46 | #include "spdk/env.h" | |
47 | #include "spdk/util.h" | |
48 | ||
49 | #define MAX_DEVS 64 | |
50 | ||
51 | struct dev { | |
52 | struct spdk_pci_addr pci_addr; | |
53 | struct spdk_nvme_ctrlr *ctrlr; | |
54 | const struct spdk_nvme_ctrlr_data *cdata; | |
55 | struct spdk_nvme_ns_data *common_ns_data; | |
56 | int outstanding_admin_cmds; | |
57 | }; | |
58 | ||
59 | static struct dev devs[MAX_DEVS]; | |
60 | static int num_devs = 0; | |
61 | ||
62 | #define foreach_dev(iter) \ | |
63 | for (iter = devs; iter - devs < num_devs; iter++) | |
64 | ||
65 | enum controller_display_model { | |
66 | CONTROLLER_DISPLAY_ALL = 0x0, | |
67 | CONTROLLER_DISPLAY_SIMPLISTIC = 0x1, | |
68 | }; | |
69 | ||
70 | static int | |
71 | cmp_devs(const void *ap, const void *bp) | |
72 | { | |
73 | const struct dev *a = ap, *b = bp; | |
74 | ||
75 | return spdk_pci_addr_compare(&a->pci_addr, &b->pci_addr); | |
76 | } | |
77 | ||
78 | static bool | |
79 | probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, | |
80 | struct spdk_nvme_ctrlr_opts *opts) | |
81 | { | |
82 | return true; | |
83 | } | |
84 | ||
85 | static void | |
86 | identify_common_ns_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl) | |
87 | { | |
88 | struct dev *dev = cb_arg; | |
89 | ||
90 | if (cpl->status.sc != SPDK_NVME_SC_SUCCESS) { | |
91 | /* Identify Namespace for NSID = FFFFFFFFh is optional, so failure is not fatal. */ | |
92 | spdk_free(dev->common_ns_data); | |
93 | dev->common_ns_data = NULL; | |
94 | } | |
95 | ||
96 | dev->outstanding_admin_cmds--; | |
97 | } | |
98 | ||
99 | static void | |
100 | attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, | |
101 | struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) | |
102 | { | |
103 | struct dev *dev; | |
104 | struct spdk_nvme_cmd cmd; | |
105 | ||
106 | /* add to dev list */ | |
107 | dev = &devs[num_devs++]; | |
108 | spdk_pci_addr_parse(&dev->pci_addr, trid->traddr); | |
109 | dev->ctrlr = ctrlr; | |
110 | ||
111 | /* Retrieve controller data */ | |
112 | dev->cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr); | |
113 | ||
114 | dev->common_ns_data = spdk_zmalloc(sizeof(struct spdk_nvme_ns_data), 4096, NULL); | |
115 | if (dev->common_ns_data == NULL) { | |
116 | fprintf(stderr, "common_ns_data allocation failure\n"); | |
117 | return; | |
118 | } | |
119 | ||
120 | /* Identify Namespace with NSID set to FFFFFFFFh to get common namespace capabilities. */ | |
121 | memset(&cmd, 0, sizeof(cmd)); | |
122 | cmd.opc = SPDK_NVME_OPC_IDENTIFY; | |
123 | cmd.cdw10 = 0; /* CNS = 0 (Identify Namespace) */ | |
124 | cmd.nsid = SPDK_NVME_GLOBAL_NS_TAG; | |
125 | ||
126 | dev->outstanding_admin_cmds++; | |
127 | if (spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, dev->common_ns_data, | |
128 | sizeof(struct spdk_nvme_ns_data), identify_common_ns_cb, dev) != 0) { | |
129 | dev->outstanding_admin_cmds--; | |
130 | spdk_free(dev->common_ns_data); | |
131 | dev->common_ns_data = NULL; | |
132 | } | |
133 | ||
134 | while (dev->outstanding_admin_cmds) { | |
135 | spdk_nvme_ctrlr_process_admin_completions(ctrlr); | |
136 | } | |
137 | } | |
138 | ||
139 | static void usage(void) | |
140 | { | |
141 | printf("NVMe Management Options"); | |
142 | printf("\n"); | |
143 | printf("\t[1: list controllers]\n"); | |
144 | printf("\t[2: create namespace]\n"); | |
145 | printf("\t[3: delete namespace]\n"); | |
146 | printf("\t[4: attach namespace to controller]\n"); | |
147 | printf("\t[5: detach namespace from controller]\n"); | |
148 | printf("\t[6: format namespace or controller]\n"); | |
149 | printf("\t[7: firmware update]\n"); | |
150 | printf("\t[8: quit]\n"); | |
151 | } | |
152 | ||
153 | static void | |
154 | display_namespace_dpc(const struct spdk_nvme_ns_data *nsdata) | |
155 | { | |
156 | if (nsdata->dpc.pit1 || nsdata->dpc.pit2 || nsdata->dpc.pit3) { | |
157 | if (nsdata->dpc.pit1) { | |
158 | printf("PIT1 "); | |
159 | } | |
160 | ||
161 | if (nsdata->dpc.pit2) { | |
162 | printf("PIT2 "); | |
163 | } | |
164 | ||
165 | if (nsdata->dpc.pit3) { | |
166 | printf("PIT3 "); | |
167 | } | |
168 | } else { | |
169 | printf("Not Supported\n"); | |
170 | return; | |
171 | } | |
172 | ||
173 | if (nsdata->dpc.md_start && nsdata->dpc.md_end) { | |
174 | printf("Location: Head or Tail\n"); | |
175 | } else if (nsdata->dpc.md_start) { | |
176 | printf("Location: Head\n"); | |
177 | } else if (nsdata->dpc.md_end) { | |
178 | printf("Location: Tail\n"); | |
179 | } else { | |
180 | printf("Not Supported\n"); | |
181 | } | |
182 | } | |
183 | ||
184 | static void | |
185 | display_namespace(struct spdk_nvme_ns *ns) | |
186 | { | |
187 | const struct spdk_nvme_ns_data *nsdata; | |
188 | uint32_t i; | |
189 | ||
190 | nsdata = spdk_nvme_ns_get_data(ns); | |
191 | ||
192 | printf("Namespace ID:%d\n", spdk_nvme_ns_get_id(ns)); | |
193 | ||
194 | printf("Size (in LBAs): %lld (%lldM)\n", | |
195 | (long long)nsdata->nsze, | |
196 | (long long)nsdata->nsze / 1024 / 1024); | |
197 | printf("Capacity (in LBAs): %lld (%lldM)\n", | |
198 | (long long)nsdata->ncap, | |
199 | (long long)nsdata->ncap / 1024 / 1024); | |
200 | printf("Utilization (in LBAs): %lld (%lldM)\n", | |
201 | (long long)nsdata->nuse, | |
202 | (long long)nsdata->nuse / 1024 / 1024); | |
203 | printf("Format Progress Indicator: %s\n", | |
204 | nsdata->fpi.fpi_supported ? "Supported" : "Not Supported"); | |
205 | if (nsdata->fpi.fpi_supported && nsdata->fpi.percentage_remaining) | |
206 | printf("Formatted Percentage: %d%%\n", 100 - nsdata->fpi.percentage_remaining); | |
207 | printf("Number of LBA Formats: %d\n", nsdata->nlbaf + 1); | |
208 | printf("Current LBA Format: LBA Format #%02d\n", | |
209 | nsdata->flbas.format); | |
210 | for (i = 0; i <= nsdata->nlbaf; i++) | |
211 | printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n", | |
212 | i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms); | |
213 | printf("Data Protection Capabilities:"); | |
214 | display_namespace_dpc(nsdata); | |
215 | if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE == nsdata->dps.pit) { | |
216 | printf("Data Protection Setting: N/A\n"); | |
217 | } else { | |
218 | printf("Data Protection Setting: PIT%d Location: %s\n", | |
219 | nsdata->dps.pit, nsdata->dps.md_start ? "Head" : "Tail"); | |
220 | } | |
221 | printf("Multipath IO and Sharing: %s\n", | |
222 | nsdata->nmic.can_share ? "Supported" : "Not Supported"); | |
223 | printf("\n"); | |
224 | } | |
225 | ||
226 | static void | |
227 | display_controller(struct dev *dev, int model) | |
228 | { | |
229 | struct spdk_nvme_ns *ns; | |
230 | const struct spdk_nvme_ctrlr_data *cdata; | |
231 | uint8_t str[128]; | |
232 | uint32_t i; | |
233 | ||
234 | cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr); | |
235 | ||
236 | if (model == CONTROLLER_DISPLAY_SIMPLISTIC) { | |
237 | printf("%04x:%02x:%02x.%02x ", | |
238 | dev->pci_addr.domain, dev->pci_addr.bus, dev->pci_addr.dev, dev->pci_addr.func); | |
239 | printf("%-40.40s %-20.20s ", | |
240 | cdata->mn, cdata->sn); | |
241 | printf("%5d ", cdata->cntlid); | |
242 | printf("\n"); | |
243 | return; | |
244 | } | |
245 | ||
246 | printf("=====================================================\n"); | |
247 | printf("NVMe Controller: %04x:%02x:%02x.%02x\n", | |
248 | dev->pci_addr.domain, dev->pci_addr.bus, dev->pci_addr.dev, dev->pci_addr.func); | |
249 | printf("============================\n"); | |
250 | printf("Controller Capabilities/Features\n"); | |
251 | printf("Controller ID: %d\n", cdata->cntlid); | |
252 | snprintf(str, sizeof(cdata->sn) + 1, "%s", cdata->sn); | |
253 | printf("Serial Number: %s\n", str); | |
254 | printf("\n"); | |
255 | ||
256 | printf("Admin Command Set Attributes\n"); | |
257 | printf("============================\n"); | |
258 | printf("Namespace Manage And Attach: %s\n", | |
259 | cdata->oacs.ns_manage ? "Supported" : "Not Supported"); | |
260 | printf("Namespace Format: %s\n", | |
261 | cdata->oacs.format ? "Supported" : "Not Supported"); | |
262 | printf("\n"); | |
263 | printf("NVM Command Set Attributes\n"); | |
264 | printf("============================\n"); | |
265 | if (cdata->fna.format_all_ns) { | |
266 | printf("Namespace format operation applies to all namespaces\n"); | |
267 | } else { | |
268 | printf("Namespace format operation applies to per namespace\n"); | |
269 | } | |
270 | printf("\n"); | |
271 | printf("Namespace Attributes\n"); | |
272 | printf("============================\n"); | |
273 | for (i = 1; i <= spdk_nvme_ctrlr_get_num_ns(dev->ctrlr); i++) { | |
274 | ns = spdk_nvme_ctrlr_get_ns(dev->ctrlr, i); | |
275 | if (ns == NULL) { | |
276 | continue; | |
277 | } | |
278 | display_namespace(ns); | |
279 | } | |
280 | } | |
281 | ||
282 | static void | |
283 | display_controller_list(void) | |
284 | { | |
285 | struct dev *iter; | |
286 | ||
287 | foreach_dev(iter) { | |
288 | display_controller(iter, CONTROLLER_DISPLAY_ALL); | |
289 | } | |
290 | } | |
291 | ||
292 | static struct dev * | |
293 | get_controller(void) | |
294 | { | |
295 | struct spdk_pci_addr pci_addr; | |
296 | char address[64]; | |
297 | char *p; | |
298 | int ch; | |
299 | struct dev *iter; | |
300 | ||
301 | memset(address, 0, sizeof(address)); | |
302 | ||
303 | foreach_dev(iter) { | |
304 | display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC); | |
305 | } | |
306 | ||
307 | printf("Please Input PCI Address(domain:bus:dev.func): \n"); | |
308 | ||
309 | while ((ch = getchar()) != '\n' && ch != EOF); | |
310 | p = fgets(address, 64, stdin); | |
311 | if (p == NULL) { | |
312 | return NULL; | |
313 | } | |
314 | ||
315 | while (isspace(*p)) { | |
316 | p++; | |
317 | } | |
318 | ||
319 | if (spdk_pci_addr_parse(&pci_addr, p) < 0) { | |
320 | return NULL; | |
321 | } | |
322 | ||
323 | foreach_dev(iter) { | |
324 | if (spdk_pci_addr_compare(&pci_addr, &iter->pci_addr) == 0) { | |
325 | return iter; | |
326 | } | |
327 | } | |
328 | return NULL; | |
329 | } | |
330 | ||
331 | static int | |
332 | get_lba_format(const struct spdk_nvme_ns_data *ns_data) | |
333 | { | |
334 | int lbaf, i; | |
335 | ||
336 | printf("\nSupported LBA formats:\n"); | |
337 | for (i = 0; i <= ns_data->nlbaf; i++) { | |
338 | printf("%2d: %d data bytes", i, 1 << ns_data->lbaf[i].lbads); | |
339 | if (ns_data->lbaf[i].ms) { | |
340 | printf(" + %d metadata bytes", ns_data->lbaf[i].ms); | |
341 | } | |
342 | printf("\n"); | |
343 | } | |
344 | ||
345 | printf("Please input LBA format index (0 - %d):\n", ns_data->nlbaf); | |
346 | if (scanf("%d", &lbaf) != 1 || lbaf > ns_data->nlbaf) { | |
347 | return -1; | |
348 | } | |
349 | ||
350 | return lbaf; | |
351 | } | |
352 | ||
353 | static void | |
354 | identify_allocated_ns_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl) | |
355 | { | |
356 | struct dev *dev = cb_arg; | |
357 | ||
358 | dev->outstanding_admin_cmds--; | |
359 | } | |
360 | ||
361 | static uint32_t | |
362 | get_allocated_nsid(struct dev *dev) | |
363 | { | |
364 | uint32_t nsid; | |
365 | size_t i; | |
366 | struct spdk_nvme_ns_list *ns_list; | |
367 | struct spdk_nvme_cmd cmd = {0}; | |
368 | ||
369 | ns_list = spdk_zmalloc(sizeof(*ns_list), 4096, NULL); | |
370 | if (ns_list == NULL) { | |
371 | printf("Allocation error\n"); | |
372 | return 0; | |
373 | } | |
374 | ||
375 | cmd.opc = SPDK_NVME_OPC_IDENTIFY; | |
376 | cmd.cdw10 = SPDK_NVME_IDENTIFY_ALLOCATED_NS_LIST; | |
377 | cmd.nsid = 0; | |
378 | ||
379 | dev->outstanding_admin_cmds++; | |
380 | if (spdk_nvme_ctrlr_cmd_admin_raw(dev->ctrlr, &cmd, ns_list, sizeof(*ns_list), | |
381 | identify_allocated_ns_cb, dev)) { | |
382 | printf("Identify command failed\n"); | |
383 | spdk_free(ns_list); | |
384 | return 0; | |
385 | } | |
386 | ||
387 | while (dev->outstanding_admin_cmds) { | |
388 | spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); | |
389 | } | |
390 | ||
391 | printf("Allocated Namespace IDs:\n"); | |
392 | for (i = 0; i < SPDK_COUNTOF(ns_list->ns_list); i++) { | |
393 | if (ns_list->ns_list[i] == 0) { | |
394 | break; | |
395 | } | |
396 | printf("%u\n", ns_list->ns_list[i]); | |
397 | } | |
398 | ||
399 | spdk_free(ns_list); | |
400 | ||
401 | printf("Please Input Namespace ID: \n"); | |
402 | if (!scanf("%u", &nsid)) { | |
403 | printf("Invalid Namespace ID\n"); | |
404 | nsid = 0; | |
405 | } | |
406 | ||
407 | return nsid; | |
408 | } | |
409 | ||
410 | static void | |
411 | ns_attach(struct dev *device, int attachment_op, int ctrlr_id, int ns_id) | |
412 | { | |
413 | int ret = 0; | |
414 | struct spdk_nvme_ctrlr_list *ctrlr_list; | |
415 | ||
416 | ctrlr_list = spdk_zmalloc(sizeof(struct spdk_nvme_ctrlr_list), | |
417 | 4096, NULL); | |
418 | if (ctrlr_list == NULL) { | |
419 | printf("Allocation error (controller list)\n"); | |
420 | exit(1); | |
421 | } | |
422 | ||
423 | ctrlr_list->ctrlr_count = 1; | |
424 | ctrlr_list->ctrlr_list[0] = ctrlr_id; | |
425 | ||
426 | if (attachment_op == SPDK_NVME_NS_CTRLR_ATTACH) { | |
427 | ret = spdk_nvme_ctrlr_attach_ns(device->ctrlr, ns_id, ctrlr_list); | |
428 | } else if (attachment_op == SPDK_NVME_NS_CTRLR_DETACH) { | |
429 | ret = spdk_nvme_ctrlr_detach_ns(device->ctrlr, ns_id, ctrlr_list); | |
430 | } | |
431 | ||
432 | if (ret) { | |
433 | fprintf(stdout, "ns attach: Failed\n"); | |
434 | } | |
435 | ||
436 | spdk_free(ctrlr_list); | |
437 | } | |
438 | ||
439 | static void | |
440 | ns_manage_add(struct dev *device, uint64_t ns_size, uint64_t ns_capacity, int ns_lbasize, | |
441 | uint8_t ns_dps_type, uint8_t ns_dps_location, uint8_t ns_nmic) | |
442 | { | |
443 | uint32_t nsid; | |
444 | struct spdk_nvme_ns_data *ndata; | |
445 | ||
446 | ndata = spdk_zmalloc(sizeof(struct spdk_nvme_ns_data), 4096, NULL); | |
447 | if (ndata == NULL) { | |
448 | printf("Allocation error (namespace data)\n"); | |
449 | exit(1); | |
450 | } | |
451 | ||
452 | ndata->nsze = ns_size; | |
453 | ndata->ncap = ns_capacity; | |
454 | ndata->flbas.format = ns_lbasize; | |
455 | if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE != ns_dps_type) { | |
456 | ndata->dps.pit = ns_dps_type; | |
457 | ndata->dps.md_start = ns_dps_location; | |
458 | } | |
459 | ndata->nmic.can_share = ns_nmic; | |
460 | nsid = spdk_nvme_ctrlr_create_ns(device->ctrlr, ndata); | |
461 | if (nsid == 0) { | |
462 | fprintf(stdout, "ns manage: Failed\n"); | |
463 | } else { | |
464 | printf("Created namespace ID %u\n", nsid); | |
465 | } | |
466 | ||
467 | spdk_free(ndata); | |
468 | } | |
469 | ||
470 | static void | |
471 | ns_manage_delete(struct dev *device, int ns_id) | |
472 | { | |
473 | int ret = 0; | |
474 | ||
475 | ret = spdk_nvme_ctrlr_delete_ns(device->ctrlr, ns_id); | |
476 | if (ret) { | |
477 | fprintf(stdout, "ns manage: Failed\n"); | |
478 | return; | |
479 | } | |
480 | } | |
481 | ||
482 | static void | |
483 | nvme_manage_format(struct dev *device, int ns_id, int ses, int pi, int pil, int ms, int lbaf) | |
484 | { | |
485 | int ret = 0; | |
486 | struct spdk_nvme_format format = {}; | |
487 | ||
488 | format.lbaf = lbaf; | |
489 | format.ms = ms; | |
490 | format.pi = pi; | |
491 | format.pil = pil; | |
492 | format.ses = ses; | |
493 | ret = spdk_nvme_ctrlr_format(device->ctrlr, ns_id, &format); | |
494 | if (ret) { | |
495 | fprintf(stdout, "nvme format: Failed\n"); | |
496 | return; | |
497 | } | |
498 | } | |
499 | ||
500 | static void | |
501 | attach_and_detach_ns(int attachment_op) | |
502 | { | |
503 | uint32_t nsid; | |
504 | struct dev *ctrlr; | |
505 | ||
506 | ctrlr = get_controller(); | |
507 | if (ctrlr == NULL) { | |
508 | printf("Invalid controller PCI Address.\n"); | |
509 | return; | |
510 | } | |
511 | ||
512 | if (!ctrlr->cdata->oacs.ns_manage) { | |
513 | printf("Controller does not support ns management\n"); | |
514 | return; | |
515 | } | |
516 | ||
517 | nsid = get_allocated_nsid(ctrlr); | |
518 | if (nsid == 0) { | |
519 | printf("Invalid Namespace ID\n"); | |
520 | return; | |
521 | } | |
522 | ||
523 | ns_attach(ctrlr, attachment_op, ctrlr->cdata->cntlid, nsid); | |
524 | } | |
525 | ||
526 | static void | |
527 | add_ns(void) | |
528 | { | |
529 | uint64_t ns_size = 0; | |
530 | uint64_t ns_capacity = 0; | |
531 | int ns_lbasize; | |
532 | int ns_dps_type = 0; | |
533 | int ns_dps_location = 0; | |
534 | int ns_nmic = 0; | |
535 | struct dev *ctrlr = NULL; | |
536 | ||
537 | ctrlr = get_controller(); | |
538 | if (ctrlr == NULL) { | |
539 | printf("Invalid controller PCI Address.\n"); | |
540 | return; | |
541 | } | |
542 | ||
543 | if (!ctrlr->cdata->oacs.ns_manage) { | |
544 | printf("Controller does not support ns management\n"); | |
545 | return; | |
546 | } | |
547 | ||
548 | if (!ctrlr->common_ns_data) { | |
549 | printf("Controller did not return common namespace capabilities\n"); | |
550 | return; | |
551 | } | |
552 | ||
553 | ns_lbasize = get_lba_format(ctrlr->common_ns_data); | |
554 | if (ns_lbasize < 0) { | |
555 | printf("Invalid LBA format number\n"); | |
556 | return; | |
557 | } | |
558 | ||
559 | printf("Please Input Namespace Size (in LBAs): \n"); | |
560 | if (!scanf("%" SCNi64, &ns_size)) { | |
561 | printf("Invalid Namespace Size\n"); | |
562 | while (getchar() != '\n'); | |
563 | return; | |
564 | } | |
565 | ||
566 | printf("Please Input Namespace Capacity (in LBAs): \n"); | |
567 | if (!scanf("%" SCNi64, &ns_capacity)) { | |
568 | printf("Invalid Namespace Capacity\n"); | |
569 | while (getchar() != '\n'); | |
570 | return; | |
571 | } | |
572 | ||
573 | printf("Please Input Data Protection Type (0 - 3): \n"); | |
574 | if (!scanf("%d", &ns_dps_type)) { | |
575 | printf("Invalid Data Protection Type\n"); | |
576 | while (getchar() != '\n'); | |
577 | return; | |
578 | } | |
579 | ||
580 | if (SPDK_NVME_FMT_NVM_PROTECTION_DISABLE != ns_dps_type) { | |
581 | printf("Please Input Data Protection Location (1: Head; 0: Tail): \n"); | |
582 | if (!scanf("%d", &ns_dps_location)) { | |
583 | printf("Invalid Data Protection Location\n"); | |
584 | while (getchar() != '\n'); | |
585 | return; | |
586 | } | |
587 | } | |
588 | ||
589 | printf("Please Input Multi-path IO and Sharing Capabilities (1: Share; 0: Private): \n"); | |
590 | if (!scanf("%d", &ns_nmic)) { | |
591 | printf("Invalid Multi-path IO and Sharing Capabilities\n"); | |
592 | while (getchar() != '\n'); | |
593 | return; | |
594 | } | |
595 | ||
596 | ns_manage_add(ctrlr, ns_size, ns_capacity, ns_lbasize, | |
597 | ns_dps_type, ns_dps_location, ns_nmic); | |
598 | } | |
599 | ||
600 | static void | |
601 | delete_ns(void) | |
602 | { | |
603 | int ns_id; | |
604 | struct dev *ctrlr; | |
605 | ||
606 | ctrlr = get_controller(); | |
607 | if (ctrlr == NULL) { | |
608 | printf("Invalid controller PCI Address.\n"); | |
609 | return; | |
610 | } | |
611 | ||
612 | if (!ctrlr->cdata->oacs.ns_manage) { | |
613 | printf("Controller does not support ns management\n"); | |
614 | return; | |
615 | } | |
616 | ||
617 | printf("Please Input Namespace ID: \n"); | |
618 | if (!scanf("%d", &ns_id)) { | |
619 | printf("Invalid Namespace ID\n"); | |
620 | while (getchar() != '\n'); | |
621 | return; | |
622 | } | |
623 | ||
624 | ns_manage_delete(ctrlr, ns_id); | |
625 | } | |
626 | ||
627 | static void | |
628 | format_nvm(void) | |
629 | { | |
630 | int ns_id; | |
631 | int ses; | |
632 | int pil; | |
633 | int pi; | |
634 | int ms; | |
635 | int lbaf; | |
636 | char option; | |
637 | struct dev *ctrlr; | |
638 | const struct spdk_nvme_ctrlr_data *cdata; | |
639 | struct spdk_nvme_ns *ns; | |
640 | const struct spdk_nvme_ns_data *nsdata; | |
641 | ||
642 | ctrlr = get_controller(); | |
643 | if (ctrlr == NULL) { | |
644 | printf("Invalid controller PCI BDF.\n"); | |
645 | return; | |
646 | } | |
647 | ||
648 | cdata = ctrlr->cdata; | |
649 | ||
650 | if (!cdata->oacs.format) { | |
651 | printf("Controller does not support Format NVM command\n"); | |
652 | return; | |
653 | } | |
654 | ||
655 | if (cdata->fna.format_all_ns) { | |
656 | ns_id = SPDK_NVME_GLOBAL_NS_TAG; | |
657 | ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, 1); | |
658 | } else { | |
659 | printf("Please Input Namespace ID (1 - %d): \n", cdata->nn); | |
660 | if (!scanf("%d", &ns_id)) { | |
661 | printf("Invalid Namespace ID\n"); | |
662 | while (getchar() != '\n'); | |
663 | return; | |
664 | } | |
665 | ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, ns_id); | |
666 | } | |
667 | ||
668 | if (ns == NULL) { | |
669 | printf("Namespace ID %d not found\n", ns_id); | |
670 | while (getchar() != '\n'); | |
671 | return; | |
672 | } | |
673 | ||
674 | nsdata = spdk_nvme_ns_get_data(ns); | |
675 | ||
676 | printf("Please Input Secure Erase Setting: \n"); | |
677 | printf(" 0: No secure erase operation requested\n"); | |
678 | printf(" 1: User data erase\n"); | |
679 | if (cdata->fna.crypto_erase_supported) { | |
680 | printf(" 2: Cryptographic erase\n"); | |
681 | } | |
682 | if (!scanf("%d", &ses)) { | |
683 | printf("Invalid Secure Erase Setting\n"); | |
684 | while (getchar() != '\n'); | |
685 | return; | |
686 | } | |
687 | ||
688 | lbaf = get_lba_format(nsdata); | |
689 | if (lbaf < 0) { | |
690 | printf("Invalid LBA format number\n"); | |
691 | return; | |
692 | } | |
693 | ||
694 | if (nsdata->lbaf[lbaf].ms) { | |
695 | printf("Please Input Protection Information: \n"); | |
696 | printf(" 0: Protection information is not enabled\n"); | |
697 | printf(" 1: Protection information is enabled, Type 1\n"); | |
698 | printf(" 2: Protection information is enabled, Type 2\n"); | |
699 | printf(" 3: Protection information is enabled, Type 3\n"); | |
700 | if (!scanf("%d", &pi)) { | |
701 | printf("Invalid protection information\n"); | |
702 | while (getchar() != '\n'); | |
703 | return; | |
704 | } | |
705 | ||
706 | if (pi) { | |
707 | printf("Please Input Protection Information Location: \n"); | |
708 | printf(" 0: Protection information transferred as the last eight bytes of metadata\n"); | |
709 | printf(" 1: Protection information transferred as the first eight bytes of metadata\n"); | |
710 | if (!scanf("%d", &pil)) { | |
711 | printf("Invalid protection information location\n"); | |
712 | while (getchar() != '\n'); | |
713 | return; | |
714 | } | |
715 | } else { | |
716 | pil = 0; | |
717 | } | |
718 | ||
719 | printf("Please Input Metadata Setting: \n"); | |
720 | printf(" 0: Metadata is transferred as part of a separate buffer\n"); | |
721 | printf(" 1: Metadata is transferred as part of an extended data LBA\n"); | |
722 | if (!scanf("%d", &ms)) { | |
723 | printf("Invalid metadata setting\n"); | |
724 | while (getchar() != '\n'); | |
725 | return; | |
726 | } | |
727 | } else { | |
728 | ms = 0; | |
729 | pi = 0; | |
730 | pil = 0; | |
731 | } | |
732 | ||
733 | printf("Warning: use this utility at your own risk.\n" | |
734 | "This command will format your namespace and all data will be lost.\n" | |
735 | "This command may take several minutes to complete,\n" | |
736 | "so do not interrupt the utility until it completes.\n" | |
737 | "Press 'Y' to continue with the format operation.\n"); | |
738 | ||
739 | while (getchar() != '\n'); | |
740 | if (!scanf("%c", &option)) { | |
741 | printf("Invalid option\n"); | |
742 | while (getchar() != '\n'); | |
743 | return; | |
744 | } | |
745 | ||
746 | if (option == 'y' || option == 'Y') { | |
747 | nvme_manage_format(ctrlr, ns_id, ses, pi, pil, ms, lbaf); | |
748 | } else { | |
749 | printf("NVMe format abort\n"); | |
750 | } | |
751 | } | |
752 | ||
753 | static void | |
754 | update_firmware_image(void) | |
755 | { | |
756 | int rc; | |
757 | int fd = -1; | |
758 | int slot; | |
759 | unsigned int size; | |
760 | struct stat fw_stat; | |
761 | char path[256]; | |
762 | void *fw_image; | |
763 | struct dev *ctrlr; | |
764 | const struct spdk_nvme_ctrlr_data *cdata; | |
765 | ||
766 | ctrlr = get_controller(); | |
767 | if (ctrlr == NULL) { | |
768 | printf("Invalid controller PCI BDF.\n"); | |
769 | return; | |
770 | } | |
771 | ||
772 | cdata = ctrlr->cdata; | |
773 | ||
774 | if (!cdata->oacs.firmware) { | |
775 | printf("Controller does not support firmware download and commit command\n"); | |
776 | return; | |
777 | } | |
778 | ||
779 | printf("Please Input The Path Of Firmware Image\n"); | |
780 | ||
781 | if (fgets(path, 256, stdin) == NULL) { | |
782 | printf("Invalid path setting\n"); | |
783 | while (getchar() != '\n'); | |
784 | return; | |
785 | } | |
786 | ||
787 | fd = open(path, O_RDONLY); | |
788 | if (fd < 0) { | |
789 | perror("Open file failed"); | |
790 | return; | |
791 | } | |
792 | rc = fstat(fd, &fw_stat); | |
793 | if (rc < 0) { | |
794 | printf("Fstat failed\n"); | |
795 | close(fd); | |
796 | return; | |
797 | } | |
798 | ||
799 | if (fw_stat.st_size % 4) { | |
800 | printf("Firmware image size is not multiple of 4\n"); | |
801 | close(fd); | |
802 | return; | |
803 | } | |
804 | ||
805 | size = fw_stat.st_size; | |
806 | ||
807 | fw_image = spdk_zmalloc(size, 4096, NULL); | |
808 | if (fw_image == NULL) { | |
809 | printf("Allocation error\n"); | |
810 | close(fd); | |
811 | return; | |
812 | } | |
813 | ||
814 | if (read(fd, fw_image, size) != ((ssize_t)(size))) { | |
815 | printf("Read firmware image failed\n"); | |
816 | close(fd); | |
817 | spdk_free(fw_image); | |
818 | return; | |
819 | } | |
820 | close(fd); | |
821 | ||
822 | printf("Please Input Slot(0 - 7): \n"); | |
823 | if (!scanf("%d", &slot)) { | |
824 | printf("Invalid Slot\n"); | |
825 | spdk_free(fw_image); | |
826 | while (getchar() != '\n'); | |
827 | return; | |
828 | } | |
829 | ||
830 | rc = spdk_nvme_ctrlr_update_firmware(ctrlr->ctrlr, fw_image, size, slot); | |
831 | if (rc) { | |
832 | printf("spdk_nvme_ctrlr_update_firmware failed\n"); | |
833 | } else { | |
834 | printf("spdk_nvme_ctrlr_update_firmware success\n"); | |
835 | } | |
836 | spdk_free(fw_image); | |
837 | } | |
838 | ||
839 | int main(int argc, char **argv) | |
840 | { | |
841 | int i; | |
842 | struct spdk_env_opts opts; | |
843 | ||
844 | spdk_env_opts_init(&opts); | |
845 | opts.name = "nvme_manage"; | |
846 | opts.core_mask = "0x1"; | |
847 | spdk_env_init(&opts); | |
848 | ||
849 | if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) { | |
850 | fprintf(stderr, "spdk_nvme_probe() failed\n"); | |
851 | return 1; | |
852 | } | |
853 | ||
854 | qsort(devs, num_devs, sizeof(devs[0]), cmp_devs); | |
855 | ||
856 | usage(); | |
857 | ||
858 | while (1) { | |
859 | int cmd; | |
860 | bool exit_flag = false; | |
861 | ||
862 | if (!scanf("%d", &cmd)) { | |
863 | printf("Invalid Command\n"); | |
864 | while (getchar() != '\n'); | |
865 | return 0; | |
866 | } | |
867 | switch (cmd) { | |
868 | case 1: | |
869 | display_controller_list(); | |
870 | break; | |
871 | case 2: | |
872 | add_ns(); | |
873 | break; | |
874 | case 3: | |
875 | delete_ns(); | |
876 | break; | |
877 | case 4: | |
878 | attach_and_detach_ns(SPDK_NVME_NS_CTRLR_ATTACH); | |
879 | break; | |
880 | case 5: | |
881 | attach_and_detach_ns(SPDK_NVME_NS_CTRLR_DETACH); | |
882 | break; | |
883 | case 6: | |
884 | format_nvm(); | |
885 | break; | |
886 | case 7: | |
887 | update_firmware_image(); | |
888 | break; | |
889 | case 8: | |
890 | exit_flag = true; | |
891 | break; | |
892 | default: | |
893 | printf("Invalid Command\n"); | |
894 | break; | |
895 | } | |
896 | ||
897 | if (exit_flag) | |
898 | break; | |
899 | ||
900 | while (getchar() != '\n'); | |
901 | printf("press Enter to display cmd menu ...\n"); | |
902 | while (getchar() != '\n'); | |
903 | usage(); | |
904 | } | |
905 | ||
906 | printf("Cleaning up...\n"); | |
907 | ||
908 | for (i = 0; i < num_devs; i++) { | |
909 | struct dev *dev = &devs[i]; | |
910 | spdk_nvme_detach(dev->ctrlr); | |
911 | } | |
912 | ||
913 | return 0; | |
914 | } |