Spdk/examples/nvme/nvme_manage/nvme_manage.c

704 lines
17 KiB
C
Raw Normal View History

/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdlib.h>
#include <rte_config.h>
#include <rte_malloc.h>
#include <rte_mempool.h>
#include <rte_lcore.h>
#include "spdk/nvme.h"
#include "spdk/pci.h"
struct rte_mempool *request_mempool;
#define MAX_DEVS 64
struct dev {
struct spdk_pci_device *pci_dev;
struct spdk_nvme_ctrlr *ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
};
static struct dev devs[MAX_DEVS];
static int num_devs = 0;
#define foreach_dev(iter) \
for (iter = devs; iter - devs < num_devs; iter++)
enum controller_display_model {
CONTROLLER_DISPLAY_ALL = 0x0,
CONTROLLER_DISPLAY_SIMPLISTIC = 0x1,
};
static uint64_t
get_pci_addr(struct spdk_pci_device *pci_dev)
{
uint64_t cmp;
cmp = (uint64_t)spdk_pci_device_get_domain(pci_dev) << 24;
cmp |= (uint64_t)spdk_pci_device_get_bus(pci_dev) << 16;
cmp |= (uint64_t)spdk_pci_device_get_dev(pci_dev) << 8;
cmp |= (uint64_t)spdk_pci_device_get_func(pci_dev);
return cmp;
}
static int
cmp_devs(const void *ap, const void *bp)
{
const struct dev *a = ap, *b = bp;
uint64_t cmp_a = get_pci_addr(a->pci_dev);
uint64_t cmp_b = get_pci_addr(b->pci_dev);
if (cmp_a < cmp_b) {
return -1;
} else if (cmp_a > cmp_b) {
return 1;
} else {
return 0;
}
}
static bool
probe_cb(void *cb_ctx, struct spdk_pci_device *dev, struct spdk_nvme_ctrlr_opts *opts)
{
if (spdk_pci_device_has_non_uio_driver(dev)) {
fprintf(stderr, "non-uio kernel driver attached to NVMe\n");
fprintf(stderr, " controller at PCI address %04x:%02x:%02x.%02x\n",
spdk_pci_device_get_domain(dev),
spdk_pci_device_get_bus(dev),
spdk_pci_device_get_dev(dev),
spdk_pci_device_get_func(dev));
fprintf(stderr, " skipping...\n");
return false;
}
return true;
}
static void
attach_cb(void *cb_ctx, struct spdk_pci_device *pci_dev, struct spdk_nvme_ctrlr *ctrlr,
const struct spdk_nvme_ctrlr_opts *opts)
{
struct dev *dev;
/* add to dev list */
dev = &devs[num_devs++];
dev->pci_dev = pci_dev;
dev->ctrlr = ctrlr;
}
static const char *ealargs[] = {
"nvme_manage",
"-c 0x1",
"-n 4",
};
static void usage(void)
{
printf("NVMe Management Options");
printf("\n");
printf("\t[1: list controllers]\n");
printf("\t[2: create namespace]\n");
printf("\t[3: delete namespace]\n");
printf("\t[4: attach namespace to controller]\n");
printf("\t[5: detach namespace from controller]\n");
printf("\t[6: format namespace or controller]\n");
printf("\t[7: quit]\n");
}
static void
display_namespace(struct spdk_nvme_ns *ns)
{
const struct spdk_nvme_ns_data *nsdata;
uint32_t i;
nsdata = spdk_nvme_ns_get_data(ns);
printf("Namespace ID:%d\n", spdk_nvme_ns_get_id(ns));
printf("Size (in LBAs): %lld (%lldM)\n",
(long long)nsdata->nsze,
(long long)nsdata->nsze / 1024 / 1024);
printf("Capacity (in LBAs): %lld (%lldM)\n",
(long long)nsdata->ncap,
(long long)nsdata->ncap / 1024 / 1024);
printf("Utilization (in LBAs): %lld (%lldM)\n",
(long long)nsdata->nuse,
(long long)nsdata->nuse / 1024 / 1024);
printf("Format Progress Indicator: %s\n",
nsdata->fpi.fpi_supported ? "Supported" : "Not Supported");
if (nsdata->fpi.fpi_supported && nsdata->fpi.percentage_remaining)
printf("Formatted Percentage: %d%%\n", 100 - nsdata->fpi.percentage_remaining);
printf("Number of LBA Formats: %d\n", nsdata->nlbaf + 1);
printf("Current LBA Format: LBA Format #%02d\n",
nsdata->flbas.format);
for (i = 0; i <= nsdata->nlbaf; i++)
printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n",
i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms);
printf("\n");
}
static void
display_controller(struct dev *dev, int model)
{
const struct spdk_nvme_ctrlr_data *cdata;
uint8_t str[128];
uint32_t i;
cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr);
if (model == CONTROLLER_DISPLAY_SIMPLISTIC) {
printf("%04x:%02x:%02x.%02x ",
spdk_pci_device_get_domain(dev->pci_dev), spdk_pci_device_get_bus(dev->pci_dev),
spdk_pci_device_get_dev(dev->pci_dev), spdk_pci_device_get_func(dev->pci_dev));
printf("%-40.40s %-20.20s ",
cdata->mn, cdata->sn);
printf("%5d ", cdata->cntlid);
printf("\n");
return;
}
printf("=====================================================\n");
printf("NVMe Controller: %04x:%02x:%02x.%02x\n",
spdk_pci_device_get_domain(dev->pci_dev), spdk_pci_device_get_bus(dev->pci_dev),
spdk_pci_device_get_dev(dev->pci_dev), spdk_pci_device_get_func(dev->pci_dev));
printf("============================\n");
printf("Controller Capabilities/Features\n");
printf("Controller ID: %d\n", cdata->cntlid);
snprintf(str, sizeof(cdata->sn) + 1, "%s", cdata->sn);
printf("Serial Number: %s\n", str);
printf("\n");
printf("Admin Command Set Attributes\n");
printf("============================\n");
printf("Namespace Manage And Attach: %s\n",
cdata->oacs.ns_manage ? "Supported" : "Not Supported");
printf("Namespace Format: %s\n",
cdata->oacs.format ? "Supported" : "Not Supported");
printf("\n");
printf("NVM Command Set Attributes\n");
printf("============================\n");
if (cdata->fna.format_all_ns) {
printf("Namespace format operation applies to all namespaces\n");
} else {
printf("Namespace format operation applies to per namespace\n");
}
printf("\n");
printf("Namespace Attributes\n");
printf("============================\n");
for (i = 1; i <= spdk_nvme_ctrlr_get_num_ns(dev->ctrlr); i++) {
display_namespace(spdk_nvme_ctrlr_get_ns(dev->ctrlr, i));
}
}
static void
display_controller_list(void)
{
struct dev *iter;
foreach_dev(iter) {
display_controller(iter, CONTROLLER_DISPLAY_ALL);
}
}
static struct dev *
get_controller(void)
{
unsigned int domain;
unsigned int bus;
unsigned int devid;
unsigned int function;
uint64_t pci_addr;
char address[64];
char *p;
int ch;
struct dev *iter;
const struct spdk_nvme_ctrlr_data *cdata;
memset(address, 0, sizeof(address));
foreach_dev(iter) {
display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC);
}
printf("Please Input PCI Address(domain:bus:dev.func): \n");
while ((ch = getchar()) != '\n' && ch != EOF);
p = fgets(address, 64, stdin);
if (p == NULL) {
return NULL;
}
while (isspace(*p)) {
p++;
}
if (sscanf(p, "%x:%x:%x.%x", &domain, &bus, &devid, &function) == 4) {
/* Matched a full address - all variables are initialized */
} else if (sscanf(p, "%x:%x:%x", &domain, &bus, &devid) == 3) {
function = 0;
} else if (sscanf(p, "%x:%x.%x", &bus, &devid, &function) == 3) {
domain = 0;
} else if (sscanf(p, "%x:%x", &bus, &devid) == 2) {
domain = 0;
function = 0;
} else {
return NULL;
}
pci_addr = (uint64_t)domain << 24;
pci_addr |= (uint64_t)bus << 16;
pci_addr |= (uint64_t)devid << 8;
pci_addr |= (uint64_t)function;
foreach_dev(iter) {
if (pci_addr == get_pci_addr(iter->pci_dev)) {
cdata = spdk_nvme_ctrlr_get_data(iter->ctrlr);
iter->cdata = cdata;
return iter;
}
}
return NULL;
}
static void
ns_attach(struct dev *device, int attachment_op, int ctrlr_id, int ns_id)
{
int ret = 0;
struct spdk_nvme_ctrlr_list *ctrlr_list;
ctrlr_list = rte_zmalloc("nvme controller list", sizeof(struct spdk_nvme_ctrlr_list),
4096);
if (ctrlr_list == NULL) {
printf("Allocation error (controller list)\n");
exit(1);
}
ctrlr_list->ctrlr_count = 1;
ctrlr_list->ctrlr_list[0] = ctrlr_id;
if (attachment_op == SPDK_NVME_NS_CTRLR_ATTACH) {
ret = spdk_nvme_ctrlr_attach_ns(device->ctrlr, ns_id, ctrlr_list);
} else if (attachment_op == SPDK_NVME_NS_CTRLR_DETACH) {
ret = spdk_nvme_ctrlr_detach_ns(device->ctrlr, ns_id, ctrlr_list);
}
if (ret) {
fprintf(stdout, "ns attach: Failed\n");
}
rte_free(ctrlr_list);
}
static void
ns_manage_add(struct dev *device, uint64_t ns_size, uint64_t ns_capacity, int ns_lbasize)
{
int ret = 0;
struct spdk_nvme_ns_data *ndata;
ndata = rte_zmalloc("nvme namespace data", sizeof(struct spdk_nvme_ns_data), 4096);
if (ndata == NULL) {
printf("Allocation error (namespace data)\n");
exit(1);
}
ndata->nsze = ns_size;
ndata->ncap = ns_capacity;
ndata->flbas.format = ns_lbasize;
ret = spdk_nvme_ctrlr_create_ns(device->ctrlr, ndata);
if (ret) {
fprintf(stdout, "ns manage: Failed\n");
}
rte_free(ndata);
}
static void
ns_manage_delete(struct dev *device, int ns_id)
{
int ret = 0;
ret = spdk_nvme_ctrlr_delete_ns(device->ctrlr, ns_id);
if (ret) {
fprintf(stdout, "ns manage: Failed\n");
return;
}
}
static void
nvme_manage_format(struct dev *device, int ns_id, int ses, int pi, int pil, int ms, int lbaf)
{
int ret = 0;
struct spdk_nvme_format format = {};
format.lbaf = lbaf;
format.ms = ms;
format.pi = pi;
format.pil = pil;
format.ses = ses;
ret = spdk_nvme_ctrlr_format(device->ctrlr, ns_id, &format);
if (ret) {
fprintf(stdout, "nvme format: Failed\n");
return;
}
}
static void
attach_and_detach_ns(int attachment_op)
{
int ns_id;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
printf("Please Input Namespace ID: \n");
if (!scanf("%d", &ns_id)) {
printf("Invalid Namespace ID\n");
while (getchar() != '\n');
return;
}
ns_attach(ctrlr, attachment_op, ctrlr->cdata->cntlid, ns_id);
}
static void
add_ns(void)
{
uint64_t ns_size;
uint64_t ns_capacity;
int ns_lbasize;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
printf("Please Input Namespace Size (in LBAs): \n");
if (!scanf("%" SCNi64, &ns_size)) {
printf("Invalid Namespace Size\n");
while (getchar() != '\n');
return;
}
printf("Please Input Namespace Capacity (in LBAs): \n");
if (!scanf("%" SCNi64, &ns_capacity)) {
printf("Invalid Namespace Capacity\n");
while (getchar() != '\n');
return;
}
printf("Please Input LBA Format Number (0 - 15): \n");
if (!scanf("%d", &ns_lbasize)) {
printf("Invalid LBA format size\n");
while (getchar() != '\n');
return;
}
ns_manage_add(ctrlr, ns_size, ns_capacity, ns_lbasize);
}
static void
delete_ns(void)
{
int ns_id;
struct dev *ctrlr;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI Address.\n");
return;
}
if (!ctrlr->cdata->oacs.ns_manage) {
printf("Controller does not support ns management\n");
return;
}
printf("Please Input Namespace ID: \n");
if (!scanf("%d", &ns_id)) {
printf("Invalid Namespace ID\n");
while (getchar() != '\n');
return;
}
ns_manage_delete(ctrlr, ns_id);
}
static void
format_nvm(void)
{
int i;
int ns_id;
int ses;
int pil;
int pi;
int ms;
int lbaf;
char option;
struct dev *ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
struct spdk_nvme_ns *ns;
const struct spdk_nvme_ns_data *nsdata;
ctrlr = get_controller();
if (ctrlr == NULL) {
printf("Invalid controller PCI BDF.\n");
return;
}
cdata = ctrlr->cdata;
if (!cdata->oacs.format) {
printf("Controller does not support Format NVM command\n");
return;
}
if (cdata->fna.format_all_ns) {
ns_id = SPDK_NVME_GLOBAL_NS_TAG;
ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, 1);
} else {
printf("Please Input Namespace ID (1 - %d): \n", cdata->nn);
if (!scanf("%d", &ns_id)) {
printf("Invalid Namespace ID\n");
while (getchar() != '\n');
return;
}
ns = spdk_nvme_ctrlr_get_ns(ctrlr->ctrlr, ns_id);
}
if (ns == NULL) {
printf("Namespace ID %d not found\n", ns_id);
while (getchar() != '\n');
return;
}
nsdata = spdk_nvme_ns_get_data(ns);
printf("Please Input Secure Erase Setting: \n");
printf(" 0: No secure erase operation requested\n");
printf(" 1: User data erase\n");
if (cdata->fna.crypto_erase_supported) {
printf(" 2: Cryptographic erase\n");
}
if (!scanf("%d", &ses)) {
printf("Invalid Secure Erase Setting\n");
while (getchar() != '\n');
return;
}
for (i = 0; i <= nsdata->nlbaf; i++) {
printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n",
i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms);
}
printf("Please Input LBA Format Number (0 - %d): \n", nsdata->nlbaf);
if (!scanf("%d", &lbaf)) {
printf("Invalid LBA format size\n");
while (getchar() != '\n');
return;
}
if (lbaf > nsdata->nlbaf) {
printf("Invalid LBA format number\n");
while (getchar() != '\n');
return;
}
if (nsdata->lbaf[lbaf].ms) {
printf("Please Input Protection Information: \n");
printf(" 0: Protection information is not enabled\n");
printf(" 1: Protection information is enabled, Type 1\n");
printf(" 2: Protection information is enabled, Type 2\n");
printf(" 3: Protection information is enabled, Type 3\n");
if (!scanf("%d", &pi)) {
printf("Invalid protection information\n");
while (getchar() != '\n');
return;
}
if (pi) {
printf("Please Input Protection Information Location: \n");
printf(" 0: Protection information transferred as the last eight bytes of metadata\n");
printf(" 1: Protection information transferred as the first eight bytes of metadata\n");
if (!scanf("%d", &pil)) {
printf("Invalid protection information location\n");
while (getchar() != '\n');
return;
}
} else {
pil = 0;
}
printf("Please Input Metadata Setting: \n");
printf(" 0: Metadata is transferred as part of a separate buffer\n");
printf(" 1: Metadata is transferred as part of an extended data LBA\n");
if (!scanf("%d", &ms)) {
printf("Invalid metadata setting\n");
while (getchar() != '\n');
return;
}
} else {
ms = 0;
pi = 0;
pil = 0;
}
printf("Warning: use this utility at your own risk.\n"
"This command will format your namespace and all data will be lost.\n"
"This command may take several minutes to complete,\n"
"so do not interrupt the utility until it completes.\n"
"Press 'Y' to continue with the format operation.\n");
while (getchar() != '\n');
if (!scanf("%c", &option)) {
printf("Invalid option\n");
while (getchar() != '\n');
return;
}
if (option == 'y' || option == 'Y') {
nvme_manage_format(ctrlr, ns_id, ses, pi, pil, ms, lbaf);
} else {
printf("NVMe format abort\n");
}
}
int main(int argc, char **argv)
{
int rc, i;
rc = rte_eal_init(sizeof(ealargs) / sizeof(ealargs[0]),
(char **)(void *)(uintptr_t)ealargs);
if (rc < 0) {
fprintf(stderr, "could not initialize dpdk\n");
exit(1);
}
request_mempool = rte_mempool_create("nvme_request", 8192,
spdk_nvme_request_size(), 128, 0,
NULL, NULL, NULL, NULL,
SOCKET_ID_ANY, 0);
if (request_mempool == NULL) {
fprintf(stderr, "could not initialize request mempool\n");
exit(1);
}
if (spdk_nvme_probe(NULL, probe_cb, attach_cb) != 0) {
fprintf(stderr, "spdk_nvme_probe() failed\n");
return 1;
}
qsort(devs, num_devs, sizeof(devs[0]), cmp_devs);
usage();
while (1) {
int cmd;
bool exit_flag = false;
if (!scanf("%d", &cmd)) {
printf("Invalid Command\n");
while (getchar() != '\n');
return 0;
}
switch (cmd) {
case 1:
display_controller_list();
break;
case 2:
add_ns();
break;
case 3:
delete_ns();
break;
case 4:
attach_and_detach_ns(SPDK_NVME_NS_CTRLR_ATTACH);
break;
case 5:
attach_and_detach_ns(SPDK_NVME_NS_CTRLR_DETACH);
break;
case 6:
format_nvm();
break;
case 7:
exit_flag = true;
break;
default:
printf("Invalid Command\n");
break;
}
if (exit_flag)
break;
while (getchar() != '\n');
printf("press Enter to display cmd menu ...\n");
while (getchar() != '\n');
usage();
}
printf("Cleaning up...\n");
for (i = 0; i < num_devs; i++) {
struct dev *dev = &devs[i];
spdk_nvme_detach(dev->ctrlr);
}
return rc;
}