Commit 62771fe0 authored by Matias Bjørling's avatar Matias Bjørling Committed by Jens Axboe

lightnvm: add 2.0 geometry identification

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: default avatarMatias Bjørling <mb@lightnvm.io>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent c6ac3f35
...@@ -931,11 +931,9 @@ static int nvm_init(struct nvm_dev *dev) ...@@ -931,11 +931,9 @@ static int nvm_init(struct nvm_dev *dev)
goto err; goto err;
} }
pr_debug("nvm: ver:%x nvm_vendor:%x\n", if (dev->identity.ver_id != 1 && dev->identity.ver_id != 2) {
dev->identity.ver_id, dev->identity.vmnt); pr_err("nvm: device ver_id %d not supported by kernel.\n",
dev->identity.ver_id);
if (dev->identity.ver_id != 1) {
pr_err("nvm: device not supported by kernel.");
goto err; goto err;
} }
......
...@@ -184,6 +184,58 @@ struct nvme_nvm_bb_tbl { ...@@ -184,6 +184,58 @@ struct nvme_nvm_bb_tbl {
__u8 blk[0]; __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;
__le32 mccap;
__u8 resv2[12];
__u8 wit;
__u8 resv3[31];
/* Geometry */
__le16 num_grp;
__le16 num_pu;
__le32 num_chk;
__le32 clba;
__u8 resv4[52];
/* Write data requirements */
__le32 ws_min;
__le32 ws_opt;
__le32 mw_cunits;
__le32 maxoc;
__le32 maxocpu;
__u8 resv5[44];
/* Performance related metrics */
__le32 trdt;
__le32 trdm;
__le32 twrt;
__le32 twrm;
__le32 tcrst;
__le32 tcrsm;
__u8 resv6[40];
/* Reserved area */
__u8 resv7[2816];
/* Vendor specific */
__u8 vs[1024];
};
/* /*
* Check we didn't inadvertently grow the command struct * Check we didn't inadvertently grow the command struct
*/ */
...@@ -198,6 +250,8 @@ static inline void _nvme_nvm_check_size(void) ...@@ -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_addrf) != 16);
BUILD_BUG_ON(sizeof(struct nvme_nvm_id12) != NVME_IDENTIFY_DATA_SIZE); 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_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) 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) ...@@ -256,6 +310,49 @@ static int init_grp(struct nvm_id *nvm_id, struct nvme_nvm_id12 *id12)
return 0; 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) static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id)
{ {
struct nvme_ns *ns = nvmdev->q->queuedata; 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) ...@@ -277,14 +374,24 @@ static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id)
goto out; goto out;
} }
nvm_id->ver_id = id->ver_id; /*
nvm_id->vmnt = id->vmnt; * The 1.2 and 2.0 specifications share the first byte in their geometry
nvm_id->cap = le32_to_cpu(id->cap); * command to make it possible to know what version a device implements.
nvm_id->dom = le32_to_cpu(id->dom); */
memcpy(&nvm_id->ppaf, &id->ppaf, switch (id->ver_id) {
sizeof(struct nvm_addr_format)); case 1:
ret = nvme_nvm_setup_12(nvmdev, nvm_id, id);
ret = init_grp(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: out:
kfree(id); kfree(id);
return ret; return ret;
...@@ -748,10 +855,36 @@ static ssize_t nvm_dev_attr_show(struct device *dev, ...@@ -748,10 +855,36 @@ static ssize_t nvm_dev_attr_show(struct device *dev,
if (strcmp(attr->name, "version") == 0) { if (strcmp(attr->name, "version") == 0) {
return scnprintf(page, PAGE_SIZE, "%u\n", id->ver_id); 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) { } else if (strcmp(attr->name, "capabilities") == 0) {
return scnprintf(page, PAGE_SIZE, "%u\n", id->cap); 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) { } else if (strcmp(attr->name, "device_mode") == 0) {
return scnprintf(page, PAGE_SIZE, "%u\n", id->dom); return scnprintf(page, PAGE_SIZE, "%u\n", id->dom);
/* kept for compatibility */ /* kept for compatibility */
...@@ -786,10 +919,6 @@ static ssize_t nvm_dev_attr_show(struct device *dev, ...@@ -786,10 +919,6 @@ static ssize_t nvm_dev_attr_show(struct device *dev,
return scnprintf(page, PAGE_SIZE, "%u\n", id->csecs); return scnprintf(page, PAGE_SIZE, "%u\n", id->csecs);
} else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */ } else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */
return scnprintf(page, PAGE_SIZE, "%u\n", id->sos); 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) { } else if (strcmp(attr->name, "prog_typ") == 0) {
return scnprintf(page, PAGE_SIZE, "%u\n", id->tprt); return scnprintf(page, PAGE_SIZE, "%u\n", id->tprt);
} else if (strcmp(attr->name, "prog_max") == 0) { } else if (strcmp(attr->name, "prog_max") == 0) {
...@@ -808,48 +937,99 @@ static ssize_t nvm_dev_attr_show(struct device *dev, ...@@ -808,48 +937,99 @@ static ssize_t nvm_dev_attr_show(struct device *dev,
} else { } else {
return scnprintf(page, return scnprintf(page,
PAGE_SIZE, PAGE_SIZE,
"Unhandled attr(%s) in `nvm_dev_attr_show`\n", "Unhandled attr(%s) in `nvm_dev_attr_show_12`\n",
attr->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); attr->name);
} }
} }
#define NVM_DEV_ATTR_RO(_name) \ #define NVM_DEV_ATTR_RO(_name) \
DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL) 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(version);
static NVM_DEV_ATTR_RO(vendor_opcode);
static NVM_DEV_ATTR_RO(capabilities); 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_typ);
static NVM_DEV_ATTR_RO(read_max); static NVM_DEV_ATTR_RO(read_max);
static NVM_DEV_ATTR_RO(prog_typ);
static NVM_DEV_ATTR_RO(prog_max); /* 1.2 values */
static NVM_DEV_ATTR_RO(erase_typ); static NVM_DEV_ATTR_12_RO(vendor_opcode);
static NVM_DEV_ATTR_RO(erase_max); static NVM_DEV_ATTR_12_RO(device_mode);
static NVM_DEV_ATTR_RO(multiplane_modes); static NVM_DEV_ATTR_12_RO(ppa_format);
static NVM_DEV_ATTR_RO(media_capabilities); static NVM_DEV_ATTR_12_RO(media_manager);
static NVM_DEV_ATTR_RO(max_phys_secs); static NVM_DEV_ATTR_12_RO(media_type);
static NVM_DEV_ATTR_12_RO(flash_media_type);
static struct attribute *nvm_dev_attrs[] = { 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_version.attr,
&dev_attr_vendor_opcode.attr,
&dev_attr_capabilities.attr, &dev_attr_capabilities.attr,
&dev_attr_vendor_opcode.attr,
&dev_attr_device_mode.attr, &dev_attr_device_mode.attr,
&dev_attr_media_manager.attr, &dev_attr_media_manager.attr,
&dev_attr_ppa_format.attr, &dev_attr_ppa_format.attr,
&dev_attr_media_type.attr, &dev_attr_media_type.attr,
&dev_attr_flash_media_type.attr, &dev_attr_flash_media_type.attr,
...@@ -870,22 +1050,82 @@ static struct attribute *nvm_dev_attrs[] = { ...@@ -870,22 +1050,82 @@ static struct attribute *nvm_dev_attrs[] = {
&dev_attr_multiplane_modes.attr, &dev_attr_multiplane_modes.attr,
&dev_attr_media_capabilities.attr, &dev_attr_media_capabilities.attr,
&dev_attr_max_phys_secs.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, NULL,
}; };
static const struct attribute_group nvm_dev_attr_group = { static const struct attribute_group nvm_dev_attr_group_20 = {
.name = "lightnvm", .name = "lightnvm",
.attrs = nvm_dev_attrs, .attrs = nvm_dev_attrs_20,
}; };
int nvme_nvm_register_sysfs(struct nvme_ns *ns) int nvme_nvm_register_sysfs(struct nvme_ns *ns)
{ {
if (!ns->ndev)
return -EINVAL;
switch (ns->ndev->identity.ver_id) {
case 1:
return sysfs_create_group(&disk_to_dev(ns->disk)->kobj, return sysfs_create_group(&disk_to_dev(ns->disk)->kobj,
&nvm_dev_attr_group); &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) void nvme_nvm_unregister_sysfs(struct nvme_ns *ns)
{ {
switch (ns->ndev->identity.ver_id) {
case 1:
sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
&nvm_dev_attr_group); &nvm_dev_attr_group_12);
break;
case 2:
sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
&nvm_dev_attr_group_20);
break;
}
} }
...@@ -184,10 +184,9 @@ struct nvm_id { ...@@ -184,10 +184,9 @@ struct nvm_id {
u16 csecs; u16 csecs;
u16 sos; u16 sos;
u16 ws_min; u32 ws_min;
u16 ws_opt; u32 ws_opt;
u16 ws_seq; u32 mw_cunits;
u16 ws_per_chk;
u32 trdt; u32 trdt;
u32 trdm; u32 trdm;
...@@ -199,6 +198,10 @@ struct nvm_id { ...@@ -199,6 +198,10 @@ struct nvm_id {
u32 mccap; u32 mccap;
u16 cpar; u16 cpar;
/* calculated values */
u16 ws_seq;
u16 ws_per_chk;
/* 1.2 compatibility */ /* 1.2 compatibility */
u8 mtype; u8 mtype;
u8 fmtype; u8 fmtype;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment