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