From mboxrd@z Thu Jan 1 00:00:00 1970 From: =?UTF-8?q?Matias=20Bj=C3=B8rling?= Subject: [PATCH v5 5/5] nvme: LightNVM support Date: Wed, 22 Jul 2015 19:51:04 +0200 Message-ID: <1437587464-7964-6-git-send-email-mb@lightnvm.io> References: <1437587464-7964-1-git-send-email-mb@lightnvm.io> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: QUOTED-PRINTABLE Cc: Stephen.Bates@pmcs.com, keith.busch@intel.com, =?UTF-8?q?Matias=20Bj=C3=B8rling?= , =?UTF-8?q?Javier=20Gonz=C3=A1lez?= To: hch@infradead.org, axboe@fb.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-nvme@lists.infradead.org Return-path: In-Reply-To: <1437587464-7964-1-git-send-email-mb@lightnvm.io> Sender: linux-kernel-owner@vger.kernel.org List-Id: linux-fsdevel.vger.kernel.org The first generation of Open-Channel SSDs will be based on NVMe. The integration requires that a NVMe device exposes itself as a LightNVM device. The way this is done currently is by hooking into the Controller Capabilities (CAP register) and a bit in NSFEAT for each namespace. After detection, vendor specific codes are used to identify the device and enumerate supported features. Signed-off-by: Javier Gonz=C3=A1lez Signed-off-by: Matias Bj=C3=B8rling --- drivers/block/Makefile | 2 +- drivers/block/nvme-core.c | 22 +- drivers/block/nvme-lightnvm.c | 504 ++++++++++++++++++++++++++++++++++= ++++++++ include/linux/nvme.h | 6 + include/uapi/linux/nvme.h | 3 + 5 files changed, 533 insertions(+), 4 deletions(-) create mode 100644 drivers/block/nvme-lightnvm.c diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 02b688d..a01d7d8 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -44,6 +44,6 @@ obj-$(CONFIG_BLK_DEV_RSXX) +=3D rsxx/ obj-$(CONFIG_BLK_DEV_NULL_BLK) +=3D null_blk.o obj-$(CONFIG_ZRAM) +=3D zram/ =20 -nvme-y :=3D nvme-core.o nvme-scsi.o +nvme-y :=3D nvme-core.o nvme-scsi.o nvme-lightnvm.o skd-y :=3D skd_main.o swim_mod-y :=3D swim.o swim_asm.o diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 666e994..ab799b0 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include =20 @@ -1751,7 +1752,8 @@ static int nvme_configure_admin_queue(struct nvme= _dev *dev) =20 dev->page_size =3D 1 << page_shift; =20 - dev->ctrl_config =3D NVME_CC_CSS_NVM; + dev->ctrl_config =3D NVME_CAP_LIGHTNVM(cap) ? + NVME_CC_CSS_LIGHTNVM : NVME_CC_CSS_NVM; dev->ctrl_config |=3D (page_shift - 12) << NVME_CC_MPS_SHIFT; dev->ctrl_config |=3D NVME_CC_ARB_RR | NVME_CC_SHN_NONE; dev->ctrl_config |=3D NVME_CC_IOSQES | NVME_CC_IOCQES; @@ -1997,6 +1999,16 @@ static int nvme_revalidate_disk(struct gendisk *= disk) return -ENODEV; } =20 + if ((dev->ctrl_config & NVME_CC_CSS_LIGHTNVM) && + id->nsfeat & NVME_NS_FEAT_NVM && ns->type !=3D NVME_NS_NVM) { + if (nvme_nvm_register(ns->queue, disk)) { + dev_warn(dev->dev, + "%s: LightNVM init failure\n", __func__); + return -ENODEV; + } + ns->type =3D NVME_NS_NVM; + } + old_ms =3D ns->ms; lbaf =3D id->flbas & NVME_NS_FLBAS_LBA_MASK; ns->lba_shift =3D id->lbaf[lbaf].ds; @@ -2028,7 +2040,7 @@ static int nvme_revalidate_disk(struct gendisk *d= isk) !ns->ext) nvme_init_integrity(ns); =20 - if (ns->ms && !blk_get_integrity(disk)) + if ((ns->ms && !blk_get_integrity(disk)) || ns->type =3D=3D NVME_NS_N= VM) set_capacity(disk, 0); else set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); @@ -2146,7 +2158,8 @@ static void nvme_alloc_ns(struct nvme_dev *dev, u= nsigned nsid) if (nvme_revalidate_disk(ns->disk)) goto out_free_disk; =20 - add_disk(ns->disk); + if (ns->type !=3D NVME_NS_NVM) + add_disk(ns->disk); if (ns->ms) { struct block_device *bd =3D bdget_disk(ns->disk, 0); if (!bd) @@ -2345,6 +2358,9 @@ static void nvme_free_namespace(struct nvme_ns *n= s) { list_del(&ns->list); =20 + if (ns->type =3D=3D NVME_NS_NVM) + nvm_unregister(ns->disk); + spin_lock(&dev_list_lock); ns->disk->private_data =3D NULL; spin_unlock(&dev_list_lock); diff --git a/drivers/block/nvme-lightnvm.c b/drivers/block/nvme-lightnv= m.c new file mode 100644 index 0000000..397a66e --- /dev/null +++ b/drivers/block/nvme-lightnvm.c @@ -0,0 +1,504 @@ +/* + * nvme-lightnvm.c - LightNVM NVMe device + * + * Copyright (C) 2014-2015 IT University of Copenhagen + * Initial release: Matias Bjorling + * + * 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. + * + */ + +#include +#include +#include + +#ifdef CONFIG_NVM + +enum nvme_nvm_opcode { + nvme_nvm_cmd_hb_write =3D 0x81, + nvme_nvm_cmd_hb_read =3D 0x02, + nvme_nvm_cmd_phys_write =3D 0x91, + nvme_nvm_cmd_phys_read =3D 0x92, + nvme_nvm_cmd_erase =3D 0x90, +}; + +enum nvme_nvm_admin_opcode { + nvme_nvm_admin_identify =3D 0xe2, + nvme_nvm_admin_get_features =3D 0xe6, + nvme_nvm_admin_set_resp =3D 0xe5, + nvme_nvm_admin_get_l2p_tbl =3D 0xea, + nvme_nvm_admin_get_bb_tbl =3D 0xf2, + nvme_nvm_admin_set_bb_tbl =3D 0xf1, +}; + +struct nvme_nvm_hb_rw { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2; + __le64 metadata; + __le64 prp1; + __le64 prp2; + __le64 slba; + __le16 length; + __le16 control; + __le32 dsmgmt; + __le64 phys_addr; +}; + +struct nvme_nvm_identify { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd[2]; + __le64 prp1; + __le64 prp2; + __le32 chnl_off; + __u32 rsvd11[5]; +}; + +struct nvme_nvm_l2ptbl { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __le32 cdw2[4]; + __le64 prp1; + __le64 prp2; + __le64 slba; + __le32 nlb; + __le16 cdw14[6]; +}; + +struct nvme_nvm_bbtbl { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd[2]; + __le64 prp1; + __le64 prp2; + __le32 prp1_len; + __le32 prp2_len; + __le32 lbb; + __u32 rsvd11[3]; +}; + +struct nvme_nvm_set_resp { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd[2]; + __le64 prp1; + __le64 prp2; + __le64 resp; + __u32 rsvd11[4]; +}; + +struct nvme_nvm_erase_blk { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd[2]; + __le64 prp1; + __le64 prp2; + __le64 blk_addr; + __u32 rsvd11[4]; +}; + +struct nvme_nvm_command { + union { + struct nvme_common_command common; + struct nvme_nvm_identify nvm_identify; + struct nvme_nvm_hb_rw nvm_hb_rw; + struct nvme_nvm_l2ptbl nvm_l2p; + struct nvme_nvm_bbtbl nvm_get_bb; + struct nvme_nvm_bbtbl nvm_set_bb; + struct nvme_nvm_set_resp nvm_resp; + struct nvme_nvm_erase_blk nvm_erase; + }; +}; + +/* + * Check we didin't inadvertently grow the command struct + */ +static inline void _nvme_nvm_check_size(void) +{ + BUILD_BUG_ON(sizeof(struct nvme_nvm_identify) !=3D 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_hb_rw) !=3D 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_l2ptbl) !=3D 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_bbtbl) !=3D 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_set_resp) !=3D 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_erase_blk) !=3D 64); +} + +struct nvme_nvm_id_chnl { + __le64 laddr_begin; + __le64 laddr_end; + __le32 oob_size; + __le32 queue_size; + __le32 gran_read; + __le32 gran_write; + __le32 gran_erase; + __le32 t_r; + __le32 t_sqr; + __le32 t_w; + __le32 t_sqw; + __le32 t_e; + __le16 chnl_parallelism; + __u8 io_sched; + __u8 reserved[133]; +} __packed; + +struct nvme_nvm_id { + __u8 ver_id; + __u8 nvm_type; + __le16 nchannels; + __u8 reserved[252]; + struct nvme_nvm_id_chnl chnls[]; +} __packed; + +#define NVME_NVM_CHNLS_PR_REQ ((4096U - sizeof(struct nvme_nvm_id)) \ + / sizeof(struct nvme_nvm_id_chnl)) + + +static int init_chnls(struct request_queue *q, struct nvm_id *nvm_id, + struct nvme_nvm_id *nvme_nvm_id) +{ + struct nvme_nvm_id_chnl *src =3D nvme_nvm_id->chnls; + struct nvm_id_chnl *dst =3D nvm_id->chnls; + struct nvme_ns *ns =3D q->queuedata; + struct nvme_nvm_command c =3D { + .nvm_identify.opcode =3D nvme_nvm_admin_identify, + .nvm_identify.nsid =3D cpu_to_le32(ns->ns_id), + }; + unsigned int len =3D nvm_id->nchannels; + int i, end, ret, off =3D 0; + + while (len) { + end =3D min_t(u32, NVME_NVM_CHNLS_PR_REQ, len); + + for (i =3D 0; i < end; i++, dst++, src++) { + dst->laddr_begin =3D le64_to_cpu(src->laddr_begin); + dst->laddr_end =3D le64_to_cpu(src->laddr_end); + dst->oob_size =3D le32_to_cpu(src->oob_size); + dst->queue_size =3D le32_to_cpu(src->queue_size); + dst->gran_read =3D le32_to_cpu(src->gran_read); + dst->gran_write =3D le32_to_cpu(src->gran_write); + dst->gran_erase =3D le32_to_cpu(src->gran_erase); + dst->t_r =3D le32_to_cpu(src->t_r); + dst->t_sqr =3D le32_to_cpu(src->t_sqr); + dst->t_w =3D le32_to_cpu(src->t_w); + dst->t_sqw =3D le32_to_cpu(src->t_sqw); + dst->t_e =3D le32_to_cpu(src->t_e); + dst->io_sched =3D src->io_sched; + } + + len -=3D end; + if (!len) + break; + + off +=3D end; + + c.nvm_identify.chnl_off =3D off; + + ret =3D nvme_submit_sync_cmd(q, (struct nvme_command *)&c, + nvme_nvm_id, 4096); + if (ret) + return ret; + } + return 0; +} + +static int nvme_nvm_identify(struct request_queue *q, struct nvm_id *n= vm_id) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_nvm_id *nvme_nvm_id; + struct nvme_nvm_command c =3D { + .nvm_identify.opcode =3D nvme_nvm_admin_identify, + .nvm_identify.nsid =3D cpu_to_le32(ns->ns_id), + .nvm_identify.chnl_off =3D 0, + }; + int ret; + + nvme_nvm_id =3D kmalloc(4096, GFP_KERNEL); + if (!nvme_nvm_id) + return -ENOMEM; + + ret =3D nvme_submit_sync_cmd(q, (struct nvme_command *)&c, nvme_nvm_i= d, + 4096); + if (ret) { + ret =3D -EIO; + goto out; + } + + nvm_id->ver_id =3D nvme_nvm_id->ver_id; + nvm_id->nvm_type =3D nvme_nvm_id->nvm_type; + nvm_id->nchannels =3D le16_to_cpu(nvme_nvm_id->nchannels); + + if (!nvm_id->chnls) + nvm_id->chnls =3D kmalloc(sizeof(struct nvm_id_chnl) + * nvm_id->nchannels, GFP_KERNEL); + if (!nvm_id->chnls) { + ret =3D -ENOMEM; + goto out; + } + + ret =3D init_chnls(q, nvm_id, nvme_nvm_id); +out: + kfree(nvme_nvm_id); + return ret; +} + +static int nvme_nvm_get_features(struct request_queue *q, + struct nvm_get_features *gf) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_nvm_command c =3D { + .common.opcode =3D nvme_nvm_admin_get_features, + .common.nsid =3D ns->ns_id, + }; + int sz =3D sizeof(struct nvm_get_features); + int ret; + u64 *resp; + + resp =3D kmalloc(sz, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + ret =3D nvme_submit_sync_cmd(q, (struct nvme_command *)&c, resp, sz); + if (ret) + goto done; + + gf->rsp =3D le64_to_cpu(resp[0]); + gf->ext =3D le64_to_cpu(resp[1]); + +done: + kfree(resp); + return ret; +} + +static int nvme_nvm_set_resp(struct request_queue *q, u64 resp) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_nvm_command c =3D { + .nvm_resp.opcode =3D nvme_nvm_admin_set_resp, + .nvm_resp.nsid =3D cpu_to_le32(ns->ns_id), + .nvm_resp.resp =3D cpu_to_le64(resp), + }; + + return nvme_submit_sync_cmd(q, (struct nvme_command *)&c, NULL, 0); +} + +static int nvme_nvm_get_l2p_tbl(struct request_queue *q, u64 slba, u64= nlb, + nvm_l2p_update_fn *update_l2p, void *priv) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_dev *dev =3D ns->dev; + struct nvme_nvm_command c =3D { + .nvm_l2p.opcode =3D nvme_nvm_admin_get_l2p_tbl, + .nvm_l2p.nsid =3D cpu_to_le32(ns->ns_id), + }; + u32 len =3D queue_max_hw_sectors(q) << 9; + u64 nlb_pr_rq =3D len / sizeof(u64); + u64 cmd_slba =3D slba; + void *entries; + int ret =3D 0; + + entries =3D kmalloc(len, GFP_KERNEL); + if (!entries) + return -ENOMEM; + + while (nlb) { + u64 cmd_nlb =3D min_t(u64, nlb_pr_rq, nlb); + + c.nvm_l2p.slba =3D cmd_slba; + c.nvm_l2p.nlb =3D cmd_nlb; + + ret =3D nvme_submit_sync_cmd(q, (struct nvme_command *)&c, + entries, len); + if (ret) { + dev_err(dev->dev, "L2P table transfer failed (%d)\n", + ret); + ret =3D -EIO; + goto out; + } + + if (update_l2p(cmd_slba, cmd_nlb, entries, priv)) { + ret =3D -EINTR; + goto out; + } + + cmd_slba +=3D cmd_nlb; + nlb -=3D cmd_nlb; + } + +out: + kfree(entries); + return ret; +} + +static int nvme_nvm_set_bb_tbl(struct request_queue *q, int lunid, + unsigned int nr_blocks, nvm_bb_update_fn *update_bbtbl, void *priv) +{ + return 0; +} + +static int nvme_nvm_get_bb_tbl(struct request_queue *q, int lunid, + unsigned int nr_blocks, nvm_bb_update_fn *update_bbtbl, void *priv) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_dev *dev =3D ns->dev; + struct nvme_nvm_command c =3D { + .nvm_get_bb.opcode =3D nvme_nvm_admin_get_bb_tbl, + .nvm_get_bb.nsid =3D cpu_to_le32(ns->ns_id), + .nvm_get_bb.lbb =3D cpu_to_le32(lunid), + }; + void *bb_bitmap; + u16 bb_bitmap_size; + int ret =3D 0; + + bb_bitmap_size =3D ((nr_blocks >> 15) + 1) * PAGE_SIZE; + bb_bitmap =3D kmalloc(bb_bitmap_size, GFP_KERNEL); + if (!bb_bitmap) + return -ENOMEM; + + bitmap_zero(bb_bitmap, nr_blocks); + + ret =3D nvme_submit_sync_cmd(q, (struct nvme_command *)&c, bb_bitmap, + bb_bitmap_size); + if (ret) { + dev_err(dev->dev, "get bad block table failed (%d)\n", ret); + ret =3D -EIO; + goto out; + } + + ret =3D update_bbtbl(lunid, bb_bitmap, nr_blocks, priv); + if (ret) { + ret =3D -EINTR; + goto out; + } + +out: + kfree(bb_bitmap); + return ret; +} + +static inline void nvme_nvm_rqtocmd(struct request *rq, struct nvm_rq = *rqd, + struct nvme_ns *ns, struct nvme_nvm_command *c) +{ + c->nvm_hb_rw.opcode =3D (rq_data_dir(rq) ? + nvme_nvm_cmd_hb_write : nvme_nvm_cmd_hb_read); + c->nvm_hb_rw.nsid =3D cpu_to_le32(ns->ns_id); + c->nvm_hb_rw.slba =3D cpu_to_le64(nvme_block_nr(ns, + rqd->bio->bi_iter.bi_sector)); + c->nvm_hb_rw.length =3D cpu_to_le16( + (blk_rq_bytes(rq) >> ns->lba_shift) - 1); + c->nvm_hb_rw.phys_addr =3D + cpu_to_le64(nvme_block_nr(ns, rqd->phys_sector)); +} + +static void nvme_nvm_end_io(struct request *rq, int error) +{ + struct nvm_rq *rqd =3D rq->end_io_data; + struct nvm_tgt_instance *ins =3D rqd->ins; + + ins->tt->end_io(rq->end_io_data, error); + + kfree(rq->cmd); + blk_mq_free_request(rq); +} + +static int nvme_nvm_submit_io(struct request_queue *q, struct nvm_rq *= rqd) +{ + struct nvme_ns *ns =3D q->queuedata; + struct request *rq; + struct bio *bio =3D rqd->bio; + struct nvme_nvm_command *cmd; + + rq =3D blk_mq_alloc_request(q, bio_rw(bio), GFP_KERNEL, 0); + if (IS_ERR(rq)) + return -ENOMEM; + + cmd =3D kzalloc(sizeof(struct nvme_nvm_command), GFP_KERNEL); + if (!cmd) { + blk_mq_free_request(rq); + return -ENOMEM; + } + + rq->cmd_type =3D REQ_TYPE_DRV_PRIV; + rq->ioprio =3D bio_prio(bio); + + if (bio_has_data(bio)) + rq->nr_phys_segments =3D bio_phys_segments(q, bio); + + rq->__data_len =3D bio->bi_iter.bi_size; + rq->bio =3D rq->biotail =3D bio; + + nvme_nvm_rqtocmd(rq, rqd, ns, cmd); + + rq->cmd =3D (unsigned char *)cmd; + rq->cmd_len =3D sizeof(struct nvme_nvm_command); + rq->special =3D (void *)0; + + rq->end_io_data =3D rqd; + + blk_execute_rq_nowait(q, NULL, rq, 0, nvme_nvm_end_io); + + return 0; +} + +static int nvme_nvm_erase_block(struct request_queue *q, sector_t bloc= k_id) +{ + struct nvme_ns *ns =3D q->queuedata; + struct nvme_nvm_command c =3D { + .nvm_erase.opcode =3D nvme_nvm_cmd_erase, + .nvm_erase.nsid =3D cpu_to_le32(ns->ns_id), + .nvm_erase.blk_addr =3D cpu_to_le64(block_id), + }; + + return nvme_submit_sync_cmd(q, (struct nvme_command *)&c, NULL, 0); +} + +static struct nvm_dev_ops nvme_nvm_dev_ops =3D { + .identify =3D nvme_nvm_identify, + + .get_features =3D nvme_nvm_get_features, + .set_responsibility =3D nvme_nvm_set_resp, + + .get_l2p_tbl =3D nvme_nvm_get_l2p_tbl, + + .set_bb_tbl =3D nvme_nvm_set_bb_tbl, + .get_bb_tbl =3D nvme_nvm_get_bb_tbl, + + .submit_io =3D nvme_nvm_submit_io, + .erase_block =3D nvme_nvm_erase_block, +}; + +int nvme_nvm_register(struct request_queue *q, struct gendisk *disk) +{ + return nvm_register(q, disk, &nvme_nvm_dev_ops); +} +#else +int nvme_nvm_register(struct request_queue *q, struct gendisk *disk) +{ + return 0; +} + +#endif /* CONFIG_NVM */ diff --git a/include/linux/nvme.h b/include/linux/nvme.h index fa3fe16..fa58242 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -19,6 +19,7 @@ #include #include #include +#include =20 struct nvme_bar { __u64 cap; /* Controller Capabilities */ @@ -41,6 +42,7 @@ struct nvme_bar { #define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) #define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) #define NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) +#define NVME_CAP_LIGHTNVM(cap) (((cap) >> 38) & 0x1) =20 #define NVME_CMB_BIR(cmbloc) ((cmbloc) & 0x7) #define NVME_CMB_OFST(cmbloc) (((cmbloc) >> 12) & 0xfffff) @@ -56,6 +58,7 @@ struct nvme_bar { enum { NVME_CC_ENABLE =3D 1 << 0, NVME_CC_CSS_NVM =3D 0 << 4, + NVME_CC_CSS_LIGHTNVM =3D 1 << 4, NVME_CC_MPS_SHIFT =3D 7, NVME_CC_ARB_RR =3D 0 << 11, NVME_CC_ARB_WRRU =3D 1 << 11, @@ -138,6 +141,7 @@ struct nvme_ns { u16 ms; bool ext; u8 pi_type; + int type; u64 mode_select_num_blocks; u32 mode_select_block_len; }; @@ -184,4 +188,6 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr= __user *u_hdr); int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg); int nvme_sg_get_version_num(int __user *ip); =20 +int nvme_nvm_register(struct request_queue *q, struct gendisk *disk); + #endif /* _LINUX_NVME_H */ diff --git a/include/uapi/linux/nvme.h b/include/uapi/linux/nvme.h index 732b32e..0374f11 100644 --- a/include/uapi/linux/nvme.h +++ b/include/uapi/linux/nvme.h @@ -130,6 +130,7 @@ struct nvme_id_ns { =20 enum { NVME_NS_FEAT_THIN =3D 1 << 0, + NVME_NS_FEAT_NVM =3D 1 << 3, NVME_NS_FLBAS_LBA_MASK =3D 0xf, NVME_NS_FLBAS_META_EXT =3D 0x10, NVME_LBAF_RP_BEST =3D 0, @@ -146,6 +147,8 @@ enum { NVME_NS_DPS_PI_TYPE1 =3D 1, NVME_NS_DPS_PI_TYPE2 =3D 2, NVME_NS_DPS_PI_TYPE3 =3D 3, + + NVME_NS_NVM =3D 1, }; =20 struct nvme_smart_log { --=20 2.1.4