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: Matias Bjørling <mb@lightnvm.io> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
c6ac3f35d4
commit
62771fe0aa
3 changed files with 297 additions and 56 deletions
|
@ -931,11 +931,9 @@ static int nvm_init(struct nvm_dev *dev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
pr_debug("nvm: ver:%x nvm_vendor:%x\n",
|
||||
dev->identity.ver_id, dev->identity.vmnt);
|
||||
|
||||
if (dev->identity.ver_id != 1) {
|
||||
pr_err("nvm: device not supported by kernel.");
|
||||
if (dev->identity.ver_id != 1 && dev->identity.ver_id != 2) {
|
||||
pr_err("nvm: device ver_id %d not supported by kernel.\n",
|
||||
dev->identity.ver_id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
__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
|
||||
*/
|
||||
|
@ -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_vendor_opcode.attr,
|
||||
&dev_attr_capabilities.attr,
|
||||
|
||||
&dev_attr_vendor_opcode.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 = {
|
||||
static const struct attribute_group nvm_dev_attr_group_12 = {
|
||||
.name = "lightnvm",
|
||||
.attrs = nvm_dev_attrs,
|
||||
.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_20 = {
|
||||
.name = "lightnvm",
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue