-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lightnvm: expose device geometry through sysfs
For a host to access an Open-Channel SSD, it has to know its geometry, so that it writes and reads at the appropriate device bounds. Currently, the geometry information is kept within the kernel, and not exported to user-space for consumption. This patch exposes the configuration through sysfs and enables user-space libraries, such as liblightnvm, to use the sysfs implementation to get the geometry of an Open-Channel SSD. The sysfs entries are stored within the device hierarchy, and can be found using the "lightnvm" device type. An example configuration looks like this: /sys/class/nvme/ └── nvme0n1 ├── capabilities: 3 ├── device_mode: 1 ├── erase_max: 1000000 ├── erase_typ: 1000000 ├── flash_media_type: 0 ├── media_capabilities: 0x00000001 ├── media_type: 0 ├── multiplane: 0x00010101 ├── num_blocks: 1022 ├── num_channels: 1 ├── num_luns: 4 ├── num_pages: 64 ├── num_planes: 1 ├── page_size: 4096 ├── prog_max: 100000 ├── prog_typ: 100000 ├── read_max: 10000 ├── read_typ: 10000 ├── sector_oob_size: 0 ├── sector_size: 4096 ├── media_manager: gennvm ├── ppa_format: 0x380830082808001010102008 ├── vendor_opcode: 0 ├── max_phys_secs: 64 └── version: 1 Signed-off-by: Simon A. F. Lund <slund@cnexlabs.com> Signed-off-by: Matias Bjørling <m@bjorling.me> Signed-off-by: Jens Axboe <axboe@fb.com>
- Loading branch information
Simon A. F. Lund
authored and
Jens Axboe
committed
Sep 21, 2016
1 parent
b0b4e09
commit 40267ef
Showing
8 changed files
with
278 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (C) 2016 CNEX Labs. All rights reserved. | ||
* Initial release: Matias Bjorling <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 LIGHTNVM_H | ||
#define LIGHTNVM_H | ||
|
||
#include <linux/lightnvm.h> | ||
|
||
/* core -> sysfs.c */ | ||
int nvm_sysfs_register_dev(struct nvm_dev *); | ||
void nvm_sysfs_unregister_dev(struct nvm_dev *); | ||
int nvm_sysfs_register(void); | ||
void nvm_sysfs_unregister(void); | ||
|
||
/* sysfs > core */ | ||
void nvm_free(struct nvm_dev *); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
#include <linux/kernel.h> | ||
#include <linux/lightnvm.h> | ||
#include <linux/miscdevice.h> | ||
#include <linux/kobject.h> | ||
#include <linux/blk-mq.h> | ||
|
||
#include "lightnvm.h" | ||
|
||
static ssize_t nvm_dev_attr_show(struct device *dev, | ||
struct device_attribute *dattr, char *page) | ||
{ | ||
struct nvm_dev *ndev = container_of(dev, struct nvm_dev, dev); | ||
struct nvm_id *id = &ndev->identity; | ||
struct nvm_id_group *grp = &id->groups[0]; | ||
struct attribute *attr = &dattr->attr; | ||
|
||
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, "device_mode") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", id->dom); | ||
} else if (strcmp(attr->name, "media_manager") == 0) { | ||
if (!ndev->mt) | ||
return scnprintf(page, PAGE_SIZE, "%s\n", "none"); | ||
return scnprintf(page, PAGE_SIZE, "%s\n", ndev->mt->name); | ||
} else if (strcmp(attr->name, "ppa_format") == 0) { | ||
return scnprintf(page, PAGE_SIZE, | ||
"0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", | ||
id->ppaf.ch_offset, id->ppaf.ch_len, | ||
id->ppaf.lun_offset, id->ppaf.lun_len, | ||
id->ppaf.pln_offset, id->ppaf.pln_len, | ||
id->ppaf.blk_offset, id->ppaf.blk_len, | ||
id->ppaf.pg_offset, id->ppaf.pg_len, | ||
id->ppaf.sect_offset, id->ppaf.sect_len); | ||
} else if (strcmp(attr->name, "media_type") == 0) { /* u8 */ | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->mtype); | ||
} else if (strcmp(attr->name, "flash_media_type") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->fmtype); | ||
} else if (strcmp(attr->name, "num_channels") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_ch); | ||
} else if (strcmp(attr->name, "num_luns") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_lun); | ||
} else if (strcmp(attr->name, "num_planes") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pln); | ||
} else if (strcmp(attr->name, "num_blocks") == 0) { /* u16 */ | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_blk); | ||
} else if (strcmp(attr->name, "num_pages") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pg); | ||
} else if (strcmp(attr->name, "page_size") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->fpg_sz); | ||
} else if (strcmp(attr->name, "hw_sector_size") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->csecs); | ||
} else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */ | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->sos); | ||
} else if (strcmp(attr->name, "read_typ") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdt); | ||
} else if (strcmp(attr->name, "read_max") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdm); | ||
} else if (strcmp(attr->name, "prog_typ") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprt); | ||
} else if (strcmp(attr->name, "prog_max") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprm); | ||
} else if (strcmp(attr->name, "erase_typ") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbet); | ||
} else if (strcmp(attr->name, "erase_max") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbem); | ||
} else if (strcmp(attr->name, "multiplane_modes") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mpos); | ||
} else if (strcmp(attr->name, "media_capabilities") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mccap); | ||
} else if (strcmp(attr->name, "max_phys_secs") == 0) { | ||
return scnprintf(page, PAGE_SIZE, "%u\n", | ||
ndev->ops->max_phys_sect); | ||
} else { | ||
return scnprintf(page, | ||
PAGE_SIZE, | ||
"Unhandled attr(%s) in `nvm_dev_attr_show`\n", | ||
attr->name); | ||
} | ||
} | ||
|
||
#define NVM_DEV_ATTR_RO(_name) \ | ||
DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL) | ||
|
||
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); | ||
|
||
#define NVM_DEV_ATTR(_name) (dev_attr_##_name##) | ||
|
||
static struct attribute *nvm_dev_attrs[] = { | ||
&dev_attr_version.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, | ||
&dev_attr_num_channels.attr, | ||
&dev_attr_num_luns.attr, | ||
&dev_attr_num_planes.attr, | ||
&dev_attr_num_blocks.attr, | ||
&dev_attr_num_pages.attr, | ||
&dev_attr_page_size.attr, | ||
&dev_attr_hw_sector_size.attr, | ||
&dev_attr_oob_sector_size.attr, | ||
&dev_attr_read_typ.attr, | ||
&dev_attr_read_max.attr, | ||
&dev_attr_prog_typ.attr, | ||
&dev_attr_prog_max.attr, | ||
&dev_attr_erase_typ.attr, | ||
&dev_attr_erase_max.attr, | ||
&dev_attr_multiplane_modes.attr, | ||
&dev_attr_media_capabilities.attr, | ||
&dev_attr_max_phys_secs.attr, | ||
NULL, | ||
}; | ||
|
||
static struct attribute_group nvm_dev_attr_group = { | ||
.name = "lightnvm", | ||
.attrs = nvm_dev_attrs, | ||
}; | ||
|
||
static const struct attribute_group *nvm_dev_attr_groups[] = { | ||
&nvm_dev_attr_group, | ||
NULL, | ||
}; | ||
|
||
static void nvm_dev_release(struct device *device) | ||
{ | ||
struct nvm_dev *dev = container_of(device, struct nvm_dev, dev); | ||
struct request_queue *q = dev->q; | ||
|
||
pr_debug("nvm/sysfs: `nvm_dev_release`\n"); | ||
|
||
blk_mq_unregister_dev(device, q); | ||
|
||
nvm_free(dev); | ||
} | ||
|
||
static struct device_type nvm_type = { | ||
.name = "lightnvm", | ||
.groups = nvm_dev_attr_groups, | ||
.release = nvm_dev_release, | ||
}; | ||
|
||
int nvm_sysfs_register_dev(struct nvm_dev *dev) | ||
{ | ||
if (!dev->parent_dev) | ||
return 0; | ||
|
||
dev->dev.parent = dev->parent_dev; | ||
dev_set_name(&dev->dev, "%s", dev->name); | ||
dev->dev.type = &nvm_type; | ||
device_initialize(&dev->dev); | ||
device_add(&dev->dev); | ||
|
||
blk_mq_register_dev(&dev->dev, dev->q); | ||
|
||
return 0; | ||
} | ||
|
||
void nvm_sysfs_unregister_dev(struct nvm_dev *dev) | ||
{ | ||
if (dev && dev->parent_dev) | ||
kobject_put(&dev->dev.kobj); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.