Implement the geometry data structures for 2.0 and enable a drive to be identified as one, including exposing the appropriate 2.0 sysfs entries. Signed-off-by: Matias Bjørling <mb@xxxxxxxxxxx> --- drivers/lightnvm/core.c | 2 +- drivers/nvme/host/lightnvm.c | 334 +++++++++++++++++++++++++++++++++++++------ include/linux/lightnvm.h | 11 +- 3 files changed, 295 insertions(+), 52 deletions(-) diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index c72863b36439..250e74dfa120 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -934,7 +934,7 @@ static int nvm_init(struct nvm_dev *dev) pr_debug("nvm: ver:%x nvm_vendor:%x\n", dev->identity.ver_id, dev->identity.vmnt); - if (dev->identity.ver_id != 1) { + if (dev->identity.ver_id != 1 && dev->identity.ver_id != 2) { pr_err("nvm: device not supported by kernel."); goto err; } diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index 6412551ecc65..a9c010655ccc 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c @@ -184,6 +184,58 @@ struct nvme_nvm_bb_tbl { __u8 blk[0]; }; +struct nvme_nvm_id20_addrf { + __u8 grp_len; + __u8 pu_len; + __u8 chk_len; + __u8 lba_len; + __u8 resv[4]; +}; + +struct nvme_nvm_id20 { + __u8 mjr; + __u8 mnr; + __u8 resv[6]; + + struct nvme_nvm_id20_addrf lbaf; + + __u32 mccap; + __u8 resv2[12]; + + __u8 wit; + __u8 resv3[31]; + + /* Geometry */ + __u16 num_grp; + __u16 num_pu; + __u32 num_chk; + __u32 clba; + __u8 resv4[52]; + + /* Write data requirements */ + __u32 ws_min; + __u32 ws_opt; + __u32 mw_cunits; + __u32 maxoc; + __u32 maxocpu; + __u8 resv5[44]; + + /* Performance related metrics */ + __u32 trdt; + __u32 trdm; + __u32 twrt; + __u32 twrm; + __u32 tcrst; + __u32 tcrsm; + __u8 resv6[40]; + + /* Reserved area */ + __u8 resv7[2816]; + + /* Vendor specific */ + __u8 vs[1024]; +}; + /* * Check we didn't inadvertently grow the command struct */ @@ -198,6 +250,8 @@ static inline void _nvme_nvm_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_nvm_id12_addrf) != 16); BUILD_BUG_ON(sizeof(struct nvme_nvm_id12) != NVME_IDENTIFY_DATA_SIZE); BUILD_BUG_ON(sizeof(struct nvme_nvm_bb_tbl) != 64); + BUILD_BUG_ON(sizeof(struct nvme_nvm_id20_addrf) != 8); + BUILD_BUG_ON(sizeof(struct nvme_nvm_id20) != NVME_IDENTIFY_DATA_SIZE); } static int init_grp(struct nvm_id *nvm_id, struct nvme_nvm_id12 *id12) @@ -256,6 +310,49 @@ static int init_grp(struct nvm_id *nvm_id, struct nvme_nvm_id12 *id12) return 0; } +static int nvme_nvm_setup_12(struct nvm_dev *nvmdev, struct nvm_id *nvm_id, + struct nvme_nvm_id12 *id) +{ + nvm_id->ver_id = id->ver_id; + nvm_id->vmnt = id->vmnt; + nvm_id->cap = le32_to_cpu(id->cap); + nvm_id->dom = le32_to_cpu(id->dom); + memcpy(&nvm_id->ppaf, &id->ppaf, + sizeof(struct nvm_addr_format)); + + return init_grp(nvm_id, id); +} + +static int nvme_nvm_setup_20(struct nvm_dev *nvmdev, struct nvm_id *nvm_id, + struct nvme_nvm_id20 *id) +{ + nvm_id->ver_id = id->mjr; + + nvm_id->num_ch = le16_to_cpu(id->num_grp); + nvm_id->num_lun = le16_to_cpu(id->num_pu); + nvm_id->num_chk = le32_to_cpu(id->num_chk); + nvm_id->clba = le32_to_cpu(id->clba); + + nvm_id->ws_min = le32_to_cpu(id->ws_min); + nvm_id->ws_opt = le32_to_cpu(id->ws_opt); + nvm_id->mw_cunits = le32_to_cpu(id->mw_cunits); + + nvm_id->trdt = le32_to_cpu(id->trdt); + nvm_id->trdm = le32_to_cpu(id->trdm); + nvm_id->tprt = le32_to_cpu(id->twrt); + nvm_id->tprm = le32_to_cpu(id->twrm); + nvm_id->tbet = le32_to_cpu(id->tcrst); + nvm_id->tbem = le32_to_cpu(id->tcrsm); + + /* calculated values */ + nvm_id->ws_per_chk = nvm_id->clba / nvm_id->ws_min; + + /* 1.2 compatibility */ + nvm_id->ws_seq = NVM_IO_SNGL_ACCESS; + + return 0; +} + static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id) { struct nvme_ns *ns = nvmdev->q->queuedata; @@ -277,14 +374,24 @@ static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id) goto out; } - nvm_id->ver_id = id->ver_id; - nvm_id->vmnt = id->vmnt; - nvm_id->cap = le32_to_cpu(id->cap); - nvm_id->dom = le32_to_cpu(id->dom); - memcpy(&nvm_id->ppaf, &id->ppaf, - sizeof(struct nvm_addr_format)); - - ret = init_grp(nvm_id, id); + /* + * The 1.2 and 2.0 specifications share the first byte in their geometry + * command to make it possible to know what version a device implements. + */ + switch (id->ver_id) { + case 1: + ret = nvme_nvm_setup_12(nvmdev, nvm_id, id); + break; + case 2: + ret = nvme_nvm_setup_20(nvmdev, nvm_id, + (struct nvme_nvm_id20 *)id); + break; + default: + dev_err(ns->ctrl->device, + "OCSSD revision not supported (%d)\n", + nvm_id->ver_id); + ret = -EINVAL; + } out: kfree(id); return ret; @@ -733,7 +840,7 @@ void nvme_nvm_unregister(struct nvme_ns *ns) } static ssize_t nvm_dev_attr_show(struct device *dev, - struct device_attribute *dattr, char *page) + struct device_attribute *dattr, char *page) { struct nvme_ns *ns = nvme_get_ns_from_dev(dev); struct nvm_dev *ndev = ns->ndev; @@ -748,10 +855,36 @@ static ssize_t nvm_dev_attr_show(struct device *dev, if (strcmp(attr->name, "version") == 0) { return scnprintf(page, PAGE_SIZE, "%u\n", id->ver_id); - } else if (strcmp(attr->name, "vendor_opcode") == 0) { - return scnprintf(page, PAGE_SIZE, "%u\n", id->vmnt); } else if (strcmp(attr->name, "capabilities") == 0) { return scnprintf(page, PAGE_SIZE, "%u\n", id->cap); + } else if (strcmp(attr->name, "read_typ") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->trdt); + } else if (strcmp(attr->name, "read_max") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->trdm); + } else { + return scnprintf(page, + PAGE_SIZE, + "Unhandled attr(%s) in `nvm_dev_attr_show`\n", + attr->name); + } +} + +static ssize_t nvm_dev_attr_show_12(struct device *dev, + struct device_attribute *dattr, char *page) +{ + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + struct nvm_dev *ndev = ns->ndev; + struct nvm_id *id; + struct attribute *attr; + + if (!ndev) + return 0; + + id = &ndev->identity; + attr = &dattr->attr; + + if (strcmp(attr->name, "vendor_opcode") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->vmnt); } else if (strcmp(attr->name, "device_mode") == 0) { return scnprintf(page, PAGE_SIZE, "%u\n", id->dom); /* kept for compatibility */ @@ -786,10 +919,6 @@ static ssize_t nvm_dev_attr_show(struct device *dev, return scnprintf(page, PAGE_SIZE, "%u\n", id->csecs); } else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */ return scnprintf(page, PAGE_SIZE, "%u\n", id->sos); - } else if (strcmp(attr->name, "read_typ") == 0) { - return scnprintf(page, PAGE_SIZE, "%u\n", id->trdt); - } else if (strcmp(attr->name, "read_max") == 0) { - return scnprintf(page, PAGE_SIZE, "%u\n", id->trdm); } else if (strcmp(attr->name, "prog_typ") == 0) { return scnprintf(page, PAGE_SIZE, "%u\n", id->tprt); } else if (strcmp(attr->name, "prog_max") == 0) { @@ -808,48 +937,99 @@ static ssize_t nvm_dev_attr_show(struct device *dev, } else { return scnprintf(page, PAGE_SIZE, - "Unhandled attr(%s) in `nvm_dev_attr_show`\n", + "Unhandled attr(%s) in `nvm_dev_attr_show_12`\n", attr->name); } } -#define NVM_DEV_ATTR_RO(_name) \ +static ssize_t nvm_dev_attr_show_20(struct device *dev, + struct device_attribute *dattr, char *page) +{ + struct nvme_ns *ns = nvme_get_ns_from_dev(dev); + struct nvm_dev *ndev = ns->ndev; + struct nvm_id *id; + struct attribute *attr; + + if (!ndev) + return 0; + + id = &ndev->identity; + attr = &dattr->attr; + + if (strcmp(attr->name, "groups") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->num_ch); + } else if (strcmp(attr->name, "punits") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->num_lun); + } else if (strcmp(attr->name, "chunks") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->num_chk); + } else if (strcmp(attr->name, "clba") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->clba); + } else if (strcmp(attr->name, "ws_min") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->ws_min); + } else if (strcmp(attr->name, "ws_opt") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->ws_opt); + } else if (strcmp(attr->name, "mw_cunits") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->mw_cunits); + } else if (strcmp(attr->name, "write_typ") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->tprt); + } else if (strcmp(attr->name, "write_max") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->tprm); + } else if (strcmp(attr->name, "reset_typ") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->tbet); + } else if (strcmp(attr->name, "reset_max") == 0) { + return scnprintf(page, PAGE_SIZE, "%u\n", id->tbem); + } else { + return scnprintf(page, + PAGE_SIZE, + "Unhandled attr(%s) in `nvm_dev_attr_show_20`\n", + attr->name); + } +} + +#define NVM_DEV_ATTR_RO(_name) \ DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL) +#define NVM_DEV_ATTR_12_RO(_name) \ + DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_12, NULL) +#define NVM_DEV_ATTR_20_RO(_name) \ + DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_20, NULL) +/* general attributes */ static NVM_DEV_ATTR_RO(version); -static NVM_DEV_ATTR_RO(vendor_opcode); static NVM_DEV_ATTR_RO(capabilities); -static NVM_DEV_ATTR_RO(device_mode); -static NVM_DEV_ATTR_RO(ppa_format); -static NVM_DEV_ATTR_RO(media_manager); -static NVM_DEV_ATTR_RO(media_type); -static NVM_DEV_ATTR_RO(flash_media_type); -static NVM_DEV_ATTR_RO(num_channels); -static NVM_DEV_ATTR_RO(num_luns); -static NVM_DEV_ATTR_RO(num_planes); -static NVM_DEV_ATTR_RO(num_blocks); -static NVM_DEV_ATTR_RO(num_pages); -static NVM_DEV_ATTR_RO(page_size); -static NVM_DEV_ATTR_RO(hw_sector_size); -static NVM_DEV_ATTR_RO(oob_sector_size); static NVM_DEV_ATTR_RO(read_typ); static NVM_DEV_ATTR_RO(read_max); -static NVM_DEV_ATTR_RO(prog_typ); -static NVM_DEV_ATTR_RO(prog_max); -static NVM_DEV_ATTR_RO(erase_typ); -static NVM_DEV_ATTR_RO(erase_max); -static NVM_DEV_ATTR_RO(multiplane_modes); -static NVM_DEV_ATTR_RO(media_capabilities); -static NVM_DEV_ATTR_RO(max_phys_secs); -static struct attribute *nvm_dev_attrs[] = { +/* 1.2 values */ +static NVM_DEV_ATTR_12_RO(vendor_opcode); +static NVM_DEV_ATTR_12_RO(device_mode); +static NVM_DEV_ATTR_12_RO(ppa_format); +static NVM_DEV_ATTR_12_RO(media_manager); +static NVM_DEV_ATTR_12_RO(media_type); +static NVM_DEV_ATTR_12_RO(flash_media_type); +static NVM_DEV_ATTR_12_RO(num_channels); +static NVM_DEV_ATTR_12_RO(num_luns); +static NVM_DEV_ATTR_12_RO(num_planes); +static NVM_DEV_ATTR_12_RO(num_blocks); +static NVM_DEV_ATTR_12_RO(num_pages); +static NVM_DEV_ATTR_12_RO(page_size); +static NVM_DEV_ATTR_12_RO(hw_sector_size); +static NVM_DEV_ATTR_12_RO(oob_sector_size); +static NVM_DEV_ATTR_12_RO(prog_typ); +static NVM_DEV_ATTR_12_RO(prog_max); +static NVM_DEV_ATTR_12_RO(erase_typ); +static NVM_DEV_ATTR_12_RO(erase_max); +static NVM_DEV_ATTR_12_RO(multiplane_modes); +static NVM_DEV_ATTR_12_RO(media_capabilities); +static NVM_DEV_ATTR_12_RO(max_phys_secs); + +static struct attribute *nvm_dev_attrs_12[] = { &dev_attr_version.attr, + &dev_attr_capabilities.attr, + &dev_attr_vendor_opcode.attr, - &dev_attr_capabilities.attr, &dev_attr_device_mode.attr, &dev_attr_media_manager.attr, - &dev_attr_ppa_format.attr, &dev_attr_media_type.attr, &dev_attr_flash_media_type.attr, @@ -870,22 +1050,82 @@ static struct attribute *nvm_dev_attrs[] = { &dev_attr_multiplane_modes.attr, &dev_attr_media_capabilities.attr, &dev_attr_max_phys_secs.attr, + + NULL, +}; + +static const struct attribute_group nvm_dev_attr_group_12 = { + .name = "lightnvm", + .attrs = nvm_dev_attrs_12, +}; + +/* 2.0 values */ +static NVM_DEV_ATTR_20_RO(groups); +static NVM_DEV_ATTR_20_RO(punits); +static NVM_DEV_ATTR_20_RO(chunks); +static NVM_DEV_ATTR_20_RO(clba); +static NVM_DEV_ATTR_20_RO(ws_min); +static NVM_DEV_ATTR_20_RO(ws_opt); +static NVM_DEV_ATTR_20_RO(mw_cunits); +static NVM_DEV_ATTR_20_RO(write_typ); +static NVM_DEV_ATTR_20_RO(write_max); +static NVM_DEV_ATTR_20_RO(reset_typ); +static NVM_DEV_ATTR_20_RO(reset_max); + +static struct attribute *nvm_dev_attrs_20[] = { + &dev_attr_version.attr, + &dev_attr_capabilities.attr, + + &dev_attr_groups.attr, + &dev_attr_punits.attr, + &dev_attr_chunks.attr, + &dev_attr_clba.attr, + &dev_attr_ws_min.attr, + &dev_attr_ws_opt.attr, + &dev_attr_mw_cunits.attr, + + &dev_attr_read_typ.attr, + &dev_attr_read_max.attr, + &dev_attr_write_typ.attr, + &dev_attr_write_max.attr, + &dev_attr_reset_typ.attr, + &dev_attr_reset_max.attr, + NULL, }; -static const struct attribute_group nvm_dev_attr_group = { +static const struct attribute_group nvm_dev_attr_group_20 = { .name = "lightnvm", - .attrs = nvm_dev_attrs, + .attrs = nvm_dev_attrs_20, }; int nvme_nvm_register_sysfs(struct nvme_ns *ns) { - return sysfs_create_group(&disk_to_dev(ns->disk)->kobj, - &nvm_dev_attr_group); + if (!ns->ndev) + return -EINVAL; + + switch (ns->ndev->identity.ver_id) { + case 1: + return sysfs_create_group(&disk_to_dev(ns->disk)->kobj, + &nvm_dev_attr_group_12); + case 2: + return sysfs_create_group(&disk_to_dev(ns->disk)->kobj, + &nvm_dev_attr_group_20); + } + + return -EINVAL; } void nvme_nvm_unregister_sysfs(struct nvme_ns *ns) { - sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, - &nvm_dev_attr_group); + switch (ns->ndev->identity.ver_id) { + case 1: + sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, + &nvm_dev_attr_group_12); + break; + case 2: + sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, + &nvm_dev_attr_group_20); + break; + } } diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index 94b704a8d83d..b717c000b712 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h @@ -184,10 +184,9 @@ struct nvm_id { u16 csecs; u16 sos; - u16 ws_min; - u16 ws_opt; - u16 ws_seq; - u16 ws_per_chk; + u32 ws_min; + u32 ws_opt; + u32 mw_cunits; u32 trdt; u32 trdm; @@ -199,6 +198,10 @@ struct nvm_id { u32 mccap; u16 cpar; + /* calculated values */ + u16 ws_seq; + u16 ws_per_chk; + /* 1.2 compatibility */ u8 mtype; u8 fmtype; -- 2.11.0