NVME_DPKG_VERSION=1~`lsb_release -sc`
-nvme: nvme.c ./linux/nvme.h argconfig.o suffix.o nvme-print.o nvme-ioctl.o NVME-VERSION-FILE
- $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c $(LDFLAGS) -o $(NVME) argconfig.o suffix.o nvme-print.o nvme-ioctl.o
+nvme: nvme.c ./linux/nvme.h argconfig.o suffix.o nvme-print.o nvme-ioctl.o nvme-lightnvm.o NVME-VERSION-FILE
+ $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c $(LDFLAGS) -o $(NVME) argconfig.o suffix.o nvme-print.o nvme-ioctl.o nvme-lightnvm.o
nvme-ioctl.o: nvme-ioctl.c nvme-ioctl.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c nvme-ioctl.c
nvme-print.o: nvme-print.c nvme-print.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c nvme-print.c
+nvme-lightnvm.o: nvme-lightnvm.h nvme-lightnvm.h
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c nvme-lightnvm.c
+
doc: $(NVME)
$(MAKE) -C Documentation
--- /dev/null
+/*
+ * Copyright (C) 2015 CNEX Labs. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ */
+
+#ifndef _UAPI_LINUX_LIGHTNVM_H
+#define _UAPI_LINUX_LIGHTNVM_H
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/ioctl.h>
+#else /* __KERNEL__ */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#define DISK_NAME_LEN 32
+#endif /* __KERNEL__ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define NVM_TTYPE_NAME_MAX 48
+#define NVM_TTYPE_MAX 63
+#define NVM_MMTYPE_LEN 8
+
+#define NVM_CTRL_FILE "/dev/lightnvm/control"
+
+struct nvm_ioctl_info_tgt {
+ __u32 version[3];
+ __u32 reserved;
+ char tgtname[NVM_TTYPE_NAME_MAX];
+};
+
+struct nvm_ioctl_info {
+ __u32 version[3]; /* in/out - major, minor, patch */
+ __u16 tgtsize; /* number of targets */
+ __u16 reserved16; /* pad to 4K page */
+ __u32 reserved[12];
+ struct nvm_ioctl_info_tgt tgts[NVM_TTYPE_MAX];
+};
+
+enum {
+ NVM_DEVICE_ACTIVE = 1 << 0,
+};
+
+struct nvm_ioctl_device_info {
+ char devname[DISK_NAME_LEN];
+ char bmname[NVM_TTYPE_NAME_MAX];
+ __u32 bmversion[3];
+ __u32 flags;
+ __u32 reserved[8];
+};
+
+struct nvm_ioctl_get_devices {
+ __u32 nr_devices;
+ __u32 reserved[31];
+ struct nvm_ioctl_device_info info[31];
+};
+
+struct nvm_ioctl_create_simple {
+ __u32 lun_begin;
+ __u32 lun_end;
+};
+
+enum {
+ NVM_CONFIG_TYPE_SIMPLE = 0,
+};
+
+struct nvm_ioctl_create_conf {
+ __u32 type;
+ union {
+ struct nvm_ioctl_create_simple s;
+ };
+};
+
+struct nvm_ioctl_create {
+ char dev[DISK_NAME_LEN]; /* open-channel SSD device */
+ char tgttype[NVM_TTYPE_NAME_MAX]; /* target type name */
+ char tgtname[DISK_NAME_LEN]; /* dev to expose target as */
+
+ __u32 flags;
+
+ struct nvm_ioctl_create_conf conf;
+};
+
+struct nvm_ioctl_remove {
+ char tgtname[DISK_NAME_LEN];
+
+ __u32 flags;
+};
+
+struct nvm_ioctl_dev_init {
+ char dev[DISK_NAME_LEN]; /* open-channel SSD device */
+ char mmtype[NVM_MMTYPE_LEN]; /* register to media manager */
+
+ __u32 flags;
+};
+
+enum {
+ NVM_FACTORY_ERASE_ONLY_USER = 1 << 0, /* erase only blocks used as
+ * host blks or grown blks */
+ NVM_FACTORY_RESET_HOST_BLKS = 1 << 1, /* remove host blk marks */
+ NVM_FACTORY_RESET_GRWN_BBLKS = 1 << 2, /* remove grown blk marks */
+ NVM_FACTORY_NR_BITS = 1 << 3, /* stops here */
+};
+
+struct nvm_ioctl_dev_factory {
+ char dev[DISK_NAME_LEN];
+
+ __u32 flags;
+};
+
+/* The ioctl type, 'L', 0x20 - 0x2F documented in ioctl-number.txt */
+enum {
+ /* top level cmds */
+ NVM_INFO_CMD = 0x20,
+ NVM_GET_DEVICES_CMD,
+
+ /* device level cmds */
+ NVM_DEV_CREATE_CMD,
+ NVM_DEV_REMOVE_CMD,
+
+ /* Init a device to support LightNVM media managers */
+ NVM_DEV_INIT_CMD,
+
+ /* Factory reset device */
+ NVM_DEV_FACTORY_CMD,
+};
+
+#define NVM_IOCTL 'L' /* 0x4c */
+
+#define NVM_INFO _IOWR(NVM_IOCTL, NVM_INFO_CMD, \
+ struct nvm_ioctl_info)
+#define NVM_GET_DEVICES _IOR(NVM_IOCTL, NVM_GET_DEVICES_CMD, \
+ struct nvm_ioctl_get_devices)
+#define NVM_DEV_CREATE _IOW(NVM_IOCTL, NVM_DEV_CREATE_CMD, \
+ struct nvm_ioctl_create)
+#define NVM_DEV_REMOVE _IOW(NVM_IOCTL, NVM_DEV_REMOVE_CMD, \
+ struct nvm_ioctl_remove)
+#define NVM_DEV_INIT _IOW(NVM_IOCTL, NVM_DEV_INIT_CMD, \
+ struct nvm_ioctl_dev_init)
+#define NVM_DEV_FACTORY _IOW(NVM_IOCTL, NVM_DEV_FACTORY_CMD, \
+ struct nvm_ioctl_dev_factory)
+
+#define NVM_VERSION_MAJOR 1
+#define NVM_VERSION_MINOR 0
+#define NVM_VERSION_PATCHLEVEL 0
+
+#endif
--- /dev/null
+/*
+ * lightnvm.c -- LightNVM NVMe integration.
+ *
+ * Copyright (c) 2016, CNEX Labs.
+ *
+ * Written by Matias Bjoerling <matias@cnexlabs.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "nvme-lightnvm.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+
+static int lnvm_open(void)
+{
+ char dev[FILENAME_MAX] = NVM_CTRL_FILE;
+ int fd;
+
+ fd = open(dev, O_WRONLY);
+ if (fd < 0) {
+ printf("Failed to open LightNVM mgmt interface\n");
+ perror(dev);
+ exit(errno);
+ }
+
+ return fd;
+}
+
+static void lnvm_close(int fd)
+{
+ close(fd);
+}
+
+int lnvm_do_init(char *dev, char *mmtype)
+{
+ struct nvm_ioctl_dev_init init;
+ int fd, ret;
+
+ fd = lnvm_open();
+
+ memset(&init, 0, sizeof(struct nvm_ioctl_dev_init));
+ strncpy(init.dev, dev, DISK_NAME_LEN);
+ strncpy(init.mmtype, mmtype, NVM_MMTYPE_LEN);
+
+ ret = ioctl(fd, NVM_DEV_INIT, &init);
+ switch (errno) {
+ case EINVAL:
+ printf("Initialization failed.\n");
+ break;
+ case EEXIST:
+ printf("Device has already been initialized.\n");
+ break;
+ case 0:
+ break;
+ default:
+ printf("Unknown error occurred (%d)\n", errno);
+ break;
+ }
+
+ lnvm_close(fd);
+
+ return ret;
+}
+
+int lnvm_do_list_devices(void)
+{
+ struct nvm_ioctl_get_devices devs;
+ int fd, ret, i;
+
+ fd = lnvm_open();
+
+ ret = ioctl(fd, NVM_GET_DEVICES, &devs);
+ if (ret)
+ return ret;
+
+ printf("Number of devices: %u\n", devs.nr_devices);
+ printf("%-12s\t%-12s\tVersion\n", "Device", "Block manager");
+
+ for (i = 0; i < devs.nr_devices && i < 31; i++) {
+ struct nvm_ioctl_device_info *info = &devs.info[i];
+
+ printf("%-12s\t%-12s\t(%u,%u,%u)\n", info->devname, info->bmname,
+ info->bmversion[0], info->bmversion[1],
+ info->bmversion[2]);
+ }
+
+ lnvm_close(fd);
+
+ return 0;
+}
+
+int lnvm_do_info(void)
+{
+ struct nvm_ioctl_info c;
+ int fd, ret, i;
+
+ fd = lnvm_open();
+
+ memset(&c, 0, sizeof(struct nvm_ioctl_info));
+ ret = ioctl(fd, NVM_INFO, &c);
+ if (ret)
+ return ret;
+
+ printf("LightNVM (%u,%u,%u). %u target type(s) registered.\n",
+ c.version[0], c.version[1], c.version[2], c.tgtsize);
+ printf("Type\tVersion\n");
+
+ for (i = 0; i < c.tgtsize; i++) {
+ struct nvm_ioctl_info_tgt *tgt = &c.tgts[i];
+
+ printf("%s\t(%u,%u,%u)\n",
+ tgt->tgtname, tgt->version[0], tgt->version[1],
+ tgt->version[2]);
+ }
+
+ lnvm_close(fd);
+ return 0;
+}
+
+int lnvm_do_create_tgt(char *devname, char *tgtname, char *tgttype,
+ int lun_begin, int lun_end)
+{
+ struct nvm_ioctl_create c;
+ int fd, ret;
+
+ fd = lnvm_open();
+
+ strncpy(c.dev, devname, DISK_NAME_LEN);
+ strncpy(c.tgtname, tgtname, DISK_NAME_LEN);
+ strncpy(c.tgttype, tgttype, NVM_TTYPE_NAME_MAX);
+ c.flags = 0;
+ c.conf.type = 0;
+ c.conf.s.lun_begin = lun_begin;
+ c.conf.s.lun_end = lun_end;
+
+ ret = ioctl(fd, NVM_DEV_CREATE, &c);
+ if (ret)
+ fprintf(stderr, "Creation of target failed. Please see dmesg.\n");
+
+ lnvm_close(fd);
+ return ret;
+}
+
+int lnvm_do_remove_tgt(char *tgtname)
+{
+ struct nvm_ioctl_remove c;
+ int fd, ret;
+
+ fd = lnvm_open();
+
+ strncpy(c.tgtname, tgtname, DISK_NAME_LEN);
+ c.flags = 0;
+
+ ret = ioctl(fd, NVM_DEV_REMOVE, &c);
+ if (ret)
+ fprintf(stderr, "Remove of target failed. Please see dmesg.\n");
+
+ lnvm_close(fd);
+ return ret;
+}
+
+int lnvm_do_factory_init(char *devname, int erase_only_marked,
+ int clear_host_marks,
+ int clear_bb_marks)
+{
+ struct nvm_ioctl_dev_factory fact;
+ int fd, ret;
+
+ fd = lnvm_open();
+
+ memset(&fact, 0, sizeof(struct nvm_ioctl_dev_factory));
+
+ strncpy(fact.dev, devname, DISK_NAME_LEN);
+ if (erase_only_marked)
+ fact.flags |= NVM_FACTORY_ERASE_ONLY_USER;
+ if (clear_host_marks)
+ fact.flags |= NVM_FACTORY_RESET_HOST_BLKS;
+ if (clear_bb_marks)
+ fact.flags |= NVM_FACTORY_RESET_GRWN_BBLKS;
+
+ ret = ioctl(fd, NVM_DEV_FACTORY, &fact);
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "Factory reset failed.\n");
+ break;
+ case 0:
+ break;
+ default:
+ fprintf(stderr, "Unknown error occurred (%d)\n", errno);
+ break;
+ }
+
+ lnvm_close(fd);
+ return ret;
+}
+
+void show_lnvm_id_grp(struct nvme_nvm_id_group *grp)
+{
+ printf(" mtype : %d\n", grp->mtype);
+ printf(" fmtype : %d\n", grp->fmtype);
+ printf(" chnls : %d\n", grp->num_ch);
+ printf(" luns : %d\n", grp->num_lun);
+ printf(" plns : %d\n", grp->num_pln);
+ printf(" blks : %d\n", (uint16_t)le16toh(grp->num_blk));
+ printf(" pgs : %d\n", (uint16_t)le16toh(grp->num_pg));
+ printf(" fpg_sz : %d\n", (uint16_t)le16toh(grp->fpg_sz));
+ printf(" csecs : %d\n", (uint16_t)le16toh(grp->csecs));
+ printf(" sos : %d\n", (uint16_t)le16toh(grp->sos));
+ printf(" trdt : %d\n", (uint32_t)le32toh(grp->trdt));
+ printf(" trdm : %d\n", (uint32_t)le32toh(grp->trdm));
+ printf(" tprt : %d\n", (uint32_t)le32toh(grp->tprt));
+ printf(" tprm : %d\n", (uint32_t)le32toh(grp->tprm));
+ printf(" tbet : %d\n", (uint32_t)le32toh(grp->tbet));
+ printf(" tbem : %d\n", (uint32_t)le32toh(grp->tbem));
+ printf(" mpos : %#x\n", (uint32_t)le32toh(grp->mpos));
+ printf(" mccap : %#x\n", (uint32_t)le32toh(grp->mccap));
+ printf(" cpar : %#x\n", (uint16_t)le16toh(grp->cpar));
+}
+
+void show_lnvm_ppaf(struct nvme_nvm_addr_format *ppaf)
+{
+ printf("ppaf :\n");
+ printf(" ch offs : %d ch bits : %d\n",
+ ppaf->ch_offset, ppaf->ch_len);
+ printf(" lun offs: %d lun bits : %d\n",
+ ppaf->lun_offset, ppaf->lun_len);
+ printf(" pl offs : %d pl bits : %d\n",
+ ppaf->pln_offset, ppaf->pln_len);
+ printf(" blk offs: %d blk bits : %d\n",
+ ppaf->blk_offset, ppaf->blk_len);
+ printf(" pg offs : %d pg bits : %d\n",
+ ppaf->pg_offset, ppaf->pg_len);
+ printf(" sec offs: %d sec bits : %d\n",
+ ppaf->sect_offset, ppaf->sect_len);
+}
+
+void show_lnvm_id_ns(struct nvme_nvm_id *id)
+{
+ int i;
+
+ if (id->cgrps > 4) {
+ fprintf(stderr, "invalid identify geometry returned\n");
+ return;
+ }
+
+ printf("verid : %#x\n", id->ver_id);
+ printf("vmnt : %#x\n", id->vmnt);
+ printf("cgrps : %d\n", id->cgrps);
+ printf("cap : %#x\n", (uint32_t)le32toh(id->cap));
+ printf("dom : %#x\n", (uint32_t)le32toh(id->dom));
+ show_lnvm_ppaf(&id->ppaf);
+
+ for (i = 0; i < id->cgrps; i++) {
+ printf("grp : %d\n", i);
+ show_lnvm_id_grp(&id->groups[i]);
+ }
+}
+
+int lnvm_get_identity(int fd, int nsid, struct nvme_nvm_id *nvm_id)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_nvm_admin_identity,
+ .nsid = nsid,
+ .addr = (__u64)(uintptr_t)nvm_id,
+ .data_len = 0x1000,
+ };
+
+ return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+}
+
+int lnvm_do_id_ns(int fd, int nsid, unsigned int flags)
+{
+ struct nvme_nvm_id nvm_id;
+ int err;
+
+ err = lnvm_get_identity(fd, nsid, &nvm_id);
+ if (!err) {
+ if (flags & RAW)
+ d_raw((unsigned char *)&nvm_id, sizeof(nvm_id));
+ else {
+ printf("LightNVM Identify Geometry (%d):\n", nsid);
+ show_lnvm_id_ns(&nvm_id);
+ }
+ }
+ else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x) NSID:%d\n",
+ nvme_status_to_string(err), err, nsid);
+ return err;
+}
+
+static void show_lnvm_bbtbl(struct nvme_nvm_bb_tbl *tbl)
+{
+ printf("verid : %#x\n", (uint16_t)le16toh(tbl->verid));
+ printf("tblks : %d\n", (uint32_t)le32toh(tbl->tblks));
+ printf("tfact : %d\n", (uint32_t)le32toh(tbl->tfact));
+ printf("tgrown : %d\n", (uint32_t)le32toh(tbl->tgrown));
+ printf("tdresv : %d\n", (uint32_t)le32toh(tbl->tdresv));
+ printf("thresv : %d\n", (uint32_t)le32toh(tbl->thresv));
+ printf("Use raw output to retrieve table.\n");
+}
+
+static int __lnvm_do_get_bbtbl(int fd, struct nvme_nvm_id *id,
+ struct ppa_addr ppa,
+ unsigned int flags)
+{
+ struct nvme_nvm_id_group *grp = &id->groups[0];
+ int bbtblsz = ((uint16_t)le16toh(grp->num_blk) * grp->num_pln);
+ int bufsz = bbtblsz + sizeof(struct nvme_nvm_bb_tbl);
+ struct nvme_nvm_bb_tbl *bbtbl;
+ int err;
+
+ bbtbl = calloc(1, bufsz);
+ if (!bbtbl)
+ return -ENOMEM;
+
+ struct nvme_nvm_getbbtbl cmd = {
+ .opcode = nvme_nvm_admin_get_bb_tbl,
+ .nsid = 1,
+ .addr = (__u64)(uintptr_t)bbtbl,
+ .data_len = bufsz,
+ .ppa = htole64(ppa.ppa),
+ };
+
+ err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,
+ (struct nvme_passthru_cmd *)&cmd);
+ if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ free(bbtbl);
+ return err;
+ }
+
+ if (flags & RAW)
+ d_raw((unsigned char *)&bbtbl->blk, bbtblsz);
+ else {
+ printf("LightNVM Bad Block Stats:\n");
+ show_lnvm_bbtbl(bbtbl);
+ }
+
+ free(bbtbl);
+ return 0;
+}
+
+int lnvm_do_get_bbtbl(int fd, int nsid, int lunid, int chid, unsigned int flags)
+{
+ struct nvme_nvm_id nvm_id;
+ struct ppa_addr ppa;
+ int err;
+
+ err = lnvm_get_identity(fd, nsid, &nvm_id);
+ if (err) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ return err;
+ }
+
+ if (chid >= nvm_id.groups[0].num_ch ||
+ lunid >= nvm_id.groups[0].num_lun) {
+ fprintf(stderr, "Out of bound channel id or LUN id\n");
+ return -EINVAL;
+ }
+
+ ppa.ppa = 0;
+ ppa.g.lun = lunid;
+ ppa.g.ch = chid;
+
+ ppa = generic_to_dev_addr(&nvm_id.ppaf, ppa);
+
+ return __lnvm_do_get_bbtbl(fd, &nvm_id, ppa, flags);
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 CNEX Labs. All rights reserved.
+ *
+ * Author: Matias Bjoerling <matias@cnexlabs.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ */
+
+#ifndef NVME_LIGHTNVM_H_
+#define NVME_LIGHTNVM_H_
+
+#include "linux/lightnvm.h"
+
+enum nvme_nvm_admin_opcode {
+ nvme_nvm_admin_identity = 0xe2,
+ nvme_nvm_admin_get_bb_tbl = 0xf2,
+ nvme_nvm_admin_set_bb_tbl = 0xf1,
+};
+
+struct nvme_nvm_identity {
+ __u8 opcode;
+ __u8 flags;
+ __u16 command_id;
+ __u32 nsid;
+ __u64 rsvd[2];
+ __u64 prp1;
+ __u64 prp2;
+ __u32 chnl_off;
+ __u32 rsvd11[5];
+};
+
+struct nvme_nvm_getbbtbl {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u64 ppa;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 result;
+};
+
+struct nvme_nvm_command {
+ union {
+ struct nvme_nvm_identity identity;
+ struct nvme_nvm_getbbtbl get_bb;
+ };
+};
+
+struct nvme_nvm_completion {
+ __u64 result; /* Used by LightNVM to return ppa completions */
+ __u16 sq_head; /* how much of this queue may be reclaimed */
+ __u16 sq_id; /* submission queue that generated this entry */
+ __u16 command_id; /* of the command which completed */
+ __u16 status; /* did the command fail, and if so, why? */
+};
+
+#define NVME_NVM_LP_MLC_PAIRS 886
+struct nvme_nvm_lp_mlc {
+ __u16 num_pairs;
+ __u8 pairs[NVME_NVM_LP_MLC_PAIRS];
+};
+
+struct nvme_nvm_lp_tbl {
+ __u8 id[8];
+ struct nvme_nvm_lp_mlc mlc;
+};
+
+struct nvme_nvm_id_group {
+ __u8 mtype;
+ __u8 fmtype;
+ __u16 res16;
+ __u8 num_ch;
+ __u8 num_lun;
+ __u8 num_pln;
+ __u8 rsvd1;
+ __u16 num_blk;
+ __u16 num_pg;
+ __u16 fpg_sz;
+ __u16 csecs;
+ __u16 sos;
+ __u16 rsvd2;
+ __u32 trdt;
+ __u32 trdm;
+ __u32 tprt;
+ __u32 tprm;
+ __u32 tbet;
+ __u32 tbem;
+ __u32 mpos;
+ __u32 mccap;
+ __u16 cpar;
+ __u8 reserved[10];
+ struct nvme_nvm_lp_tbl lptbl;
+} __attribute__((packed));
+
+struct nvme_nvm_addr_format {
+ __u8 ch_offset;
+ __u8 ch_len;
+ __u8 lun_offset;
+ __u8 lun_len;
+ __u8 pln_offset;
+ __u8 pln_len;
+ __u8 blk_offset;
+ __u8 blk_len;
+ __u8 pg_offset;
+ __u8 pg_len;
+ __u8 sect_offset;
+ __u8 sect_len;
+ __u8 res[4];
+} __attribute__((packed));
+
+struct nvme_nvm_id {
+ __u8 ver_id;
+ __u8 vmnt;
+ __u8 cgrps;
+ __u8 res;
+ __u32 cap;
+ __u32 dom;
+ struct nvme_nvm_addr_format ppaf;
+ __u8 resv[228];
+ struct nvme_nvm_id_group groups[4];
+} __attribute__((packed));
+
+struct nvme_nvm_bb_tbl {
+ __u8 tblid[4];
+ __u16 verid;
+ __u16 revid;
+ __u32 rvsd1;
+ __u32 tblks;
+ __u32 tfact;
+ __u32 tgrown;
+ __u32 tdresv;
+ __u32 thresv;
+ __u32 rsvd2[8];
+ __u8 blk[0];
+};
+
+#define NVM_BLK_BITS (16)
+#define NVM_PG_BITS (16)
+#define NVM_SEC_BITS (8)
+#define NVM_PL_BITS (8)
+#define NVM_LUN_BITS (8)
+#define NVM_CH_BITS (7)
+
+struct ppa_addr {
+ /* Generic structure for all addresses */
+ union {
+ struct {
+ __u64 blk : NVM_BLK_BITS;
+ __u64 pg : NVM_PG_BITS;
+ __u64 sec : NVM_SEC_BITS;
+ __u64 pl : NVM_PL_BITS;
+ __u64 lun : NVM_LUN_BITS;
+ __u64 ch : NVM_CH_BITS;
+ __u64 reserved : 1;
+ } g;
+
+ __u64 ppa;
+ };
+};
+
+static inline struct ppa_addr generic_to_dev_addr(
+ struct nvme_nvm_addr_format *ppaf, struct ppa_addr r)
+{
+ struct ppa_addr l;
+
+ l.ppa = ((__u64)r.g.blk) << ppaf->blk_offset;
+ l.ppa |= ((__u64)r.g.pg) << ppaf->pg_offset;
+ l.ppa |= ((__u64)r.g.sec) << ppaf->sect_offset;
+ l.ppa |= ((__u64)r.g.pl) << ppaf->pln_offset;
+ l.ppa |= ((__u64)r.g.lun) << ppaf->lun_offset;
+ l.ppa |= ((__u64)r.g.ch) << ppaf->ch_offset;
+
+ return l;
+}
+
+int lnvm_do_init(char *, char *);
+int lnvm_do_list_devices(void);
+int lnvm_do_info(void);
+int lnvm_do_create_tgt(char *, char *, char *, int, int);
+int lnvm_do_remove_tgt(char *);
+int lnvm_do_factory_init(char *, int, int, int);
+int lnvm_do_id_ns(int, int, unsigned int);
+int lnvm_do_get_bbtbl(int, int, int, int, unsigned int);
+
+#endif
#include "nvme-print.h"
#include "nvme-ioctl.h"
+#include "nvme-lightnvm.h"
#include "src/argconfig.h"
#include "src/suffix.h"
ENTRY(RESET, "reset", "Resets the controller", reset) \
ENTRY(SUBSYS_RESET, "subsystem-reset", "Resets the controller", subsystem_reset) \
ENTRY(REGISTERS, "show-regs", "Shows the controller registers. Requires admin character device", show_registers) \
+ ENTRY(LNVM_LIST, "lnvm-list", "List available LightNVM devices", lnvm_list) \
+ ENTRY(LNVM_INFO, "lnvm-info", "List general information and available target engines", lnvm_info) \
+ ENTRY(LNVM_ID_NS, "lnvm-id-ns", "List geometry for LightNVM device", lnvm_id_ns) \
+ ENTRY(LNVM_INIT, "lnvm-init", "Initialize media manager on LightNVM device", lnvm_init) \
+ ENTRY(LNVM_CREATE, "lnvm-create", "Create target on top of a LightNVM device", lnvm_create_tgt) \
+ ENTRY(LNVM_REMOVE, "lnvm-remove", "Remove target from device", lnvm_remove_tgt) \
+ ENTRY(LNVM_FACTORY, "lnvm-factory", "Reset device to factory state", lnvm_factory_init) \
+ ENTRY(LNVM_BBTBL_GET, "lnvm-diag-bbtbl", "Diagnose bad block table", lnvm_get_bbtbl) \
ENTRY(VERSION, "version", "Shows the program version", version) \
ENTRY(HELP, "help", "Display this help", help)
return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, desc);
}
+static int lnvm_init(int argc, char **argv)
+{
+ const char *desc = "Initialize LightNVM device. A LightNVM/Open-Channel SSD"\
+ " must have a media manager associated before it can "\
+ " be exposed to the user. The default is to initialize"
+ " the general media manager on top of the device.\n\n"
+ "Example:"
+ " lnvm-init -d nvme0n1";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *mmtype = "media manager to initialize on top of device. Default: gennvm.";
+
+ struct config
+ {
+ char *devname;
+ char *mmtype;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ .mmtype = "gennvm",
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"device-name", 'd', "DEVICE", CFG_STRING, &cfg.devname, required_argument, devname},
+ {"mediamgr-name", 'm', "MM", CFG_STRING, &cfg.mmtype, no_argument, mmtype},
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ return lnvm_do_init(cfg.devname, cfg.mmtype);
+}
+
+static int lnvm_list(int argc, char **argv)
+{
+ const char *desc = "List all devices registered with LightNVM.";
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, NULL, 0);
+
+ return lnvm_do_list_devices();
+}
+
+static int lnvm_info(int argc, char **argv)
+{
+ const char *desc = "Show general information and registered target types with LightNVM";
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, NULL, 0);
+
+ return lnvm_do_info();
+}
+
+static int lnvm_id_ns(int argc, char **argv)
+{
+ const char *desc = "Send an Identify Geometry command to the "\
+ "given LightNVM device, returns properties of the specified"\
+ "namespace in either human-readable or binary format.";
+ const char *force = "Return this namespace, even if not supported";
+ const char *raw_binary = "show infos in binary format";
+ const char *human_readable = "show infos in readable format";
+ const char *namespace_id = "identifier of desired namespace. default: 1";
+ unsigned int flags = 0;
+
+ struct config {
+ __u32 namespace_id;
+ __u8 raw_binary;
+ __u8 human_readable;
+ __u8 force;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"namespace-id", 'n', "NUM", CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace_id},
+ {"force", 'f', "FLAG", CFG_NONE, &cfg.force, no_argument, force},
+ {"raw-binary", 'b', "FLAG", CFG_NONE, &cfg.raw_binary, no_argument, raw_binary},
+ {"human-readable", 'H', "FLAG", CFG_NONE, &cfg.human_readable, no_argument, human_readable},
+ {0}
+ };
+
+ parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ if (cfg.human_readable)
+ flags |= HUMAN;
+ else if (cfg.raw_binary)
+ flags |= RAW;
+
+ return lnvm_do_id_ns(fd, cfg.namespace_id, flags);
+}
+
+
+static int lnvm_create_tgt(int argc, char **argv)
+{
+ const char *desc = "Instantiate a target on top of a LightNVM enabled device.";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *tgtname = "target name of the device to initialize. e.g. target0.";
+ const char *tgttype = "identifier of target type. e.g. pblk.";
+ const char *lun_begin = "Define begin of luns to use for target.";
+ const char *lun_end = "Define set of luns to use for target.";
+
+ struct config
+ {
+ char *devname;
+ char *tgtname;
+ char *tgttype;
+ int lun_begin;
+ int lun_end;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ .tgtname = "",
+ .tgttype = "",
+ .lun_begin = 0,
+ .lun_end = 0,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"device-name", 'd', "DEVICE", CFG_STRING, &cfg.devname, required_argument, devname},
+ {"target-name", 'n', "TARGET", CFG_STRING, &cfg.tgtname, required_argument, tgtname},
+ {"target-type", 't', "TARGETTYPE", CFG_STRING, &cfg.tgttype, required_argument, tgttype},
+ {"lun-begin", 'b', "NUM", CFG_POSITIVE, &cfg.tgttype, no_argument, lun_begin},
+ {"lun-end", 'e', "NUM", CFG_POSITIVE, &cfg.tgttype, no_argument, lun_end},
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ if (!strlen(cfg.devname)) {
+ fprintf(stderr, "device name missing %d\n", (int)strlen(cfg.devname));
+ return -EINVAL;
+ }
+ if (!strlen(cfg.tgtname)) {
+ fprintf(stderr, "target name missing\n");
+ return -EINVAL;
+ }
+ if (!strlen(cfg.tgttype)) {
+ fprintf(stderr, "target type missing\n");
+ return -EINVAL;
+ }
+
+ return lnvm_do_create_tgt(cfg.devname, cfg.tgtname, cfg.tgttype, cfg.lun_begin, cfg.lun_end);
+}
+
+static int lnvm_remove_tgt(int argc, char **argv)
+{
+ const char *desc = "Remove an initialized LightNVM target.";
+ const char *tgtname = "target name of the device to initialize. e.g. target0.";
+
+ struct config
+ {
+ char *tgtname;
+ };
+
+ struct config cfg = {
+ .tgtname = "",
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"target-name", 'n', "TARGET", CFG_STRING, &cfg.tgtname, required_argument, tgtname},
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ if (!strlen(cfg.tgtname)) {
+ fprintf(stderr, "target name missing\n");
+ return -EINVAL;
+ }
+
+ return lnvm_do_remove_tgt(cfg.tgtname);
+}
+
+static int lnvm_factory_init(int argc, char **argv)
+{
+ const char *desc = "Factory initialize a LightNVM enabled device.";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *erase_only_marked = "only erase marked blocks. default: all blocks.";
+ const char *host_marks = "remove host side blocks list. default: keep.";
+ const char *bb_marks = "remove grown bad blocks list. default: keep";
+
+ struct config
+ {
+ char *devname;
+ __u8 erase_only_marked;
+ __u8 clear_host_marks;
+ __u8 clear_bb_marks;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"device-name", 'd', "DEVICE", CFG_STRING, &cfg.devname, required_argument, devname},
+ {"erase-only-marked", 'e', "", CFG_NONE, &cfg.erase_only_marked, no_argument, erase_only_marked},
+ {"clear-host-side-blks", 's', "", CFG_NONE, &cfg.clear_host_marks, no_argument, host_marks},
+ {"clear-bb-blks", 'b', "", CFG_NONE, &cfg.clear_bb_marks, no_argument, bb_marks},
+ {0}
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, &cfg,
+ sizeof(cfg));
+
+ return lnvm_do_factory_init(cfg.devname, cfg.erase_only_marked,
+ cfg.clear_host_marks, cfg.clear_bb_marks);
+}
+
+static int lnvm_get_bbtbl(int argc, char **argv)
+{
+ const char *desc = "Receive bad block table from a LightNVM compatible"\
+ " device.";
+ const char *namespace = "(optional) desired namespace";
+ const char *ch = "channel identifier";
+ const char *lun = "lun identifier (within a channel)";
+ const char *raw_binary = "show infos in binary format";
+ unsigned int flags = 0;
+
+ struct config
+ {
+ __u32 namespace_id;
+ __u16 lunid;
+ __u16 chid;
+ __u8 raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ .lunid = 0,
+ .chid = 0,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"namespace-id", 'n', "NUM", CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace},
+ {"channel-id", 'c', "", CFG_SHORT, &cfg.chid, required_argument, ch},
+ {"lun-id", 'l', "", CFG_SHORT, &cfg.lunid, required_argument, lun},
+ {"raw-binary", 'b', "FLAG", CFG_NONE, &cfg.raw_binary, no_argument, raw_binary},
+ {0}
+ };
+
+ parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ if (cfg.raw_binary)
+ flags |= RAW;
+
+ return lnvm_do_get_bbtbl(fd, cfg.namespace_id, cfg.lunid, cfg.chid,
+ flags);
+}
+
static void usage()
{
printf("usage: nvme <command> [<device>] [<args>]\n");