summaryrefslogtreecommitdiffstats
path: root/drivers/lightnvm
diff options
context:
space:
mode:
authorSimon A. F. Lund <slund@cnexlabs.com>2016-09-16 08:25:08 -0400
committerJens Axboe <axboe@fb.com>2016-09-21 09:57:31 -0400
commit40267efddc296190d50c61d96daf277151447cf6 (patch)
treed461e3fd467fa8ba7c0a316a399e6560888839b7 /drivers/lightnvm
parentb0b4e09c1ae71c4ec33df0616b830ae050006e9b (diff)
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>
Diffstat (limited to 'drivers/lightnvm')
-rw-r--r--drivers/lightnvm/Makefile2
-rw-r--r--drivers/lightnvm/core.c20
-rw-r--r--drivers/lightnvm/lightnvm.h35
-rw-r--r--drivers/lightnvm/sysfs.c195
4 files changed, 245 insertions, 7 deletions
diff --git a/drivers/lightnvm/Makefile b/drivers/lightnvm/Makefile
index a7a0a22cf1a5..1f6b6521016a 100644
--- a/drivers/lightnvm/Makefile
+++ b/drivers/lightnvm/Makefile
@@ -2,6 +2,6 @@
2# Makefile for Open-Channel SSDs. 2# Makefile for Open-Channel SSDs.
3# 3#
4 4
5obj-$(CONFIG_NVM) := core.o sysblk.o 5obj-$(CONFIG_NVM) := core.o sysblk.o sysfs.o
6obj-$(CONFIG_NVM_GENNVM) += gennvm.o 6obj-$(CONFIG_NVM_GENNVM) += gennvm.o
7obj-$(CONFIG_NVM_RRPC) += rrpc.o 7obj-$(CONFIG_NVM_RRPC) += rrpc.o
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index a99b59d1eb36..a2393e1ef82e 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -27,6 +27,8 @@
27#include <linux/lightnvm.h> 27#include <linux/lightnvm.h>
28#include <linux/sched/sysctl.h> 28#include <linux/sched/sysctl.h>
29 29
30#include "lightnvm.h"
31
30static LIST_HEAD(nvm_tgt_types); 32static LIST_HEAD(nvm_tgt_types);
31static DECLARE_RWSEM(nvm_tgtt_lock); 33static DECLARE_RWSEM(nvm_tgtt_lock);
32static LIST_HEAD(nvm_mgrs); 34static LIST_HEAD(nvm_mgrs);
@@ -598,15 +600,19 @@ static void nvm_free_mgr(struct nvm_dev *dev)
598 dev->mt = NULL; 600 dev->mt = NULL;
599} 601}
600 602
601static void nvm_free(struct nvm_dev *dev) 603void nvm_free(struct nvm_dev *dev)
602{ 604{
603 if (!dev) 605 if (!dev)
604 return; 606 return;
605 607
606 nvm_free_mgr(dev); 608 nvm_free_mgr(dev);
607 609
610 if (dev->dma_pool)
611 dev->ops->destroy_dma_pool(dev->dma_pool);
612
608 kfree(dev->lptbl); 613 kfree(dev->lptbl);
609 kfree(dev->lun_map); 614 kfree(dev->lun_map);
615 kfree(dev);
610} 616}
611 617
612static int nvm_init(struct nvm_dev *dev) 618static int nvm_init(struct nvm_dev *dev)
@@ -653,11 +659,7 @@ err:
653 659
654static void nvm_exit(struct nvm_dev *dev) 660static void nvm_exit(struct nvm_dev *dev)
655{ 661{
656 if (dev->dma_pool) 662 nvm_sysfs_unregister_dev(dev);
657 dev->ops->destroy_dma_pool(dev->dma_pool);
658 nvm_free(dev);
659
660 pr_info("nvm: successfully unloaded\n");
661} 663}
662 664
663struct nvm_dev *nvm_alloc_dev(int node) 665struct nvm_dev *nvm_alloc_dev(int node)
@@ -689,6 +691,10 @@ int nvm_register(struct nvm_dev *dev)
689 } 691 }
690 } 692 }
691 693
694 ret = nvm_sysfs_register_dev(dev);
695 if (ret)
696 goto err_ppalist;
697
692 if (dev->identity.cap & NVM_ID_DCAP_BBLKMGMT) { 698 if (dev->identity.cap & NVM_ID_DCAP_BBLKMGMT) {
693 ret = nvm_get_sysblock(dev, &dev->sb); 699 ret = nvm_get_sysblock(dev, &dev->sb);
694 if (!ret) 700 if (!ret)
@@ -705,6 +711,8 @@ int nvm_register(struct nvm_dev *dev)
705 up_write(&nvm_lock); 711 up_write(&nvm_lock);
706 712
707 return 0; 713 return 0;
714err_ppalist:
715 dev->ops->destroy_dma_pool(dev->dma_pool);
708err_init: 716err_init:
709 kfree(dev->lun_map); 717 kfree(dev->lun_map);
710 return ret; 718 return ret;
diff --git a/drivers/lightnvm/lightnvm.h b/drivers/lightnvm/lightnvm.h
new file mode 100644
index 000000000000..93f1aacc9f02
--- /dev/null
+++ b/drivers/lightnvm/lightnvm.h
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2016 CNEX Labs. All rights reserved.
3 * Initial release: Matias Bjorling <matias@cnexlabs.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License version
7 * 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
17 * USA.
18 *
19 */
20
21#ifndef LIGHTNVM_H
22#define LIGHTNVM_H
23
24#include <linux/lightnvm.h>
25
26/* core -> sysfs.c */
27int nvm_sysfs_register_dev(struct nvm_dev *);
28void nvm_sysfs_unregister_dev(struct nvm_dev *);
29int nvm_sysfs_register(void);
30void nvm_sysfs_unregister(void);
31
32/* sysfs > core */
33void nvm_free(struct nvm_dev *);
34
35#endif
diff --git a/drivers/lightnvm/sysfs.c b/drivers/lightnvm/sysfs.c
new file mode 100644
index 000000000000..72ad089c0269
--- /dev/null
+++ b/drivers/lightnvm/sysfs.c
@@ -0,0 +1,195 @@
1#include <linux/kernel.h>
2#include <linux/lightnvm.h>
3#include <linux/miscdevice.h>
4#include <linux/kobject.h>
5#include <linux/blk-mq.h>
6
7#include "lightnvm.h"
8
9static ssize_t nvm_dev_attr_show(struct device *dev,
10 struct device_attribute *dattr, char *page)
11{
12 struct nvm_dev *ndev = container_of(dev, struct nvm_dev, dev);
13 struct nvm_id *id = &ndev->identity;
14 struct nvm_id_group *grp = &id->groups[0];
15 struct attribute *attr = &dattr->attr;
16
17 if (strcmp(attr->name, "version") == 0) {
18 return scnprintf(page, PAGE_SIZE, "%u\n", id->ver_id);
19 } else if (strcmp(attr->name, "vendor_opcode") == 0) {
20 return scnprintf(page, PAGE_SIZE, "%u\n", id->vmnt);
21 } else if (strcmp(attr->name, "capabilities") == 0) {
22 return scnprintf(page, PAGE_SIZE, "%u\n", id->cap);
23 } else if (strcmp(attr->name, "device_mode") == 0) {
24 return scnprintf(page, PAGE_SIZE, "%u\n", id->dom);
25 } else if (strcmp(attr->name, "media_manager") == 0) {
26 if (!ndev->mt)
27 return scnprintf(page, PAGE_SIZE, "%s\n", "none");
28 return scnprintf(page, PAGE_SIZE, "%s\n", ndev->mt->name);
29 } else if (strcmp(attr->name, "ppa_format") == 0) {
30 return scnprintf(page, PAGE_SIZE,
31 "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
32 id->ppaf.ch_offset, id->ppaf.ch_len,
33 id->ppaf.lun_offset, id->ppaf.lun_len,
34 id->ppaf.pln_offset, id->ppaf.pln_len,
35 id->ppaf.blk_offset, id->ppaf.blk_len,
36 id->ppaf.pg_offset, id->ppaf.pg_len,
37 id->ppaf.sect_offset, id->ppaf.sect_len);
38 } else if (strcmp(attr->name, "media_type") == 0) { /* u8 */
39 return scnprintf(page, PAGE_SIZE, "%u\n", grp->mtype);
40 } else if (strcmp(attr->name, "flash_media_type") == 0) {
41 return scnprintf(page, PAGE_SIZE, "%u\n", grp->fmtype);
42 } else if (strcmp(attr->name, "num_channels") == 0) {
43 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_ch);
44 } else if (strcmp(attr->name, "num_luns") == 0) {
45 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_lun);
46 } else if (strcmp(attr->name, "num_planes") == 0) {
47 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pln);
48 } else if (strcmp(attr->name, "num_blocks") == 0) { /* u16 */
49 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_blk);
50 } else if (strcmp(attr->name, "num_pages") == 0) {
51 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pg);
52 } else if (strcmp(attr->name, "page_size") == 0) {
53 return scnprintf(page, PAGE_SIZE, "%u\n", grp->fpg_sz);
54 } else if (strcmp(attr->name, "hw_sector_size") == 0) {
55 return scnprintf(page, PAGE_SIZE, "%u\n", grp->csecs);
56 } else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */
57 return scnprintf(page, PAGE_SIZE, "%u\n", grp->sos);
58 } else if (strcmp(attr->name, "read_typ") == 0) {
59 return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdt);
60 } else if (strcmp(attr->name, "read_max") == 0) {
61 return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdm);
62 } else if (strcmp(attr->name, "prog_typ") == 0) {
63 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprt);
64 } else if (strcmp(attr->name, "prog_max") == 0) {
65 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprm);
66 } else if (strcmp(attr->name, "erase_typ") == 0) {
67 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbet);
68 } else if (strcmp(attr->name, "erase_max") == 0) {
69 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbem);
70 } else if (strcmp(attr->name, "multiplane_modes") == 0) {
71 return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mpos);
72 } else if (strcmp(attr->name, "media_capabilities") == 0) {
73 return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mccap);
74 } else if (strcmp(attr->name, "max_phys_secs") == 0) {
75 return scnprintf(page, PAGE_SIZE, "%u\n",
76 ndev->ops->max_phys_sect);
77 } else {
78 return scnprintf(page,
79 PAGE_SIZE,
80 "Unhandled attr(%s) in `nvm_dev_attr_show`\n",
81 attr->name);
82 }
83}
84
85#define NVM_DEV_ATTR_RO(_name) \
86 DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL)
87
88static NVM_DEV_ATTR_RO(version);
89static NVM_DEV_ATTR_RO(vendor_opcode);
90static NVM_DEV_ATTR_RO(capabilities);
91static NVM_DEV_ATTR_RO(device_mode);
92static NVM_DEV_ATTR_RO(ppa_format);
93static NVM_DEV_ATTR_RO(media_manager);
94
95static NVM_DEV_ATTR_RO(media_type);
96static NVM_DEV_ATTR_RO(flash_media_type);
97static NVM_DEV_ATTR_RO(num_channels);
98static NVM_DEV_ATTR_RO(num_luns);
99static NVM_DEV_ATTR_RO(num_planes);
100static NVM_DEV_ATTR_RO(num_blocks);
101static NVM_DEV_ATTR_RO(num_pages);
102static NVM_DEV_ATTR_RO(page_size);
103static NVM_DEV_ATTR_RO(hw_sector_size);
104static NVM_DEV_ATTR_RO(oob_sector_size);
105static NVM_DEV_ATTR_RO(read_typ);
106static NVM_DEV_ATTR_RO(read_max);
107static NVM_DEV_ATTR_RO(prog_typ);
108static NVM_DEV_ATTR_RO(prog_max);
109static NVM_DEV_ATTR_RO(erase_typ);
110static NVM_DEV_ATTR_RO(erase_max);
111static NVM_DEV_ATTR_RO(multiplane_modes);
112static NVM_DEV_ATTR_RO(media_capabilities);
113static NVM_DEV_ATTR_RO(max_phys_secs);
114
115#define NVM_DEV_ATTR(_name) (dev_attr_##_name##)
116
117static struct attribute *nvm_dev_attrs[] = {
118 &dev_attr_version.attr,
119 &dev_attr_vendor_opcode.attr,
120 &dev_attr_capabilities.attr,
121 &dev_attr_device_mode.attr,
122 &dev_attr_media_manager.attr,
123
124 &dev_attr_ppa_format.attr,
125 &dev_attr_media_type.attr,
126 &dev_attr_flash_media_type.attr,
127 &dev_attr_num_channels.attr,
128 &dev_attr_num_luns.attr,
129 &dev_attr_num_planes.attr,
130 &dev_attr_num_blocks.attr,
131 &dev_attr_num_pages.attr,
132 &dev_attr_page_size.attr,
133 &dev_attr_hw_sector_size.attr,
134 &dev_attr_oob_sector_size.attr,
135 &dev_attr_read_typ.attr,
136 &dev_attr_read_max.attr,
137 &dev_attr_prog_typ.attr,
138 &dev_attr_prog_max.attr,
139 &dev_attr_erase_typ.attr,
140 &dev_attr_erase_max.attr,
141 &dev_attr_multiplane_modes.attr,
142 &dev_attr_media_capabilities.attr,
143 &dev_attr_max_phys_secs.attr,
144 NULL,
145};
146
147static struct attribute_group nvm_dev_attr_group = {
148 .name = "lightnvm",
149 .attrs = nvm_dev_attrs,
150};
151
152static const struct attribute_group *nvm_dev_attr_groups[] = {
153 &nvm_dev_attr_group,
154 NULL,
155};
156
157static void nvm_dev_release(struct device *device)
158{
159 struct nvm_dev *dev = container_of(device, struct nvm_dev, dev);
160 struct request_queue *q = dev->q;
161
162 pr_debug("nvm/sysfs: `nvm_dev_release`\n");
163
164 blk_mq_unregister_dev(device, q);
165
166 nvm_free(dev);
167}
168
169static struct device_type nvm_type = {
170 .name = "lightnvm",
171 .groups = nvm_dev_attr_groups,
172 .release = nvm_dev_release,
173};
174
175int nvm_sysfs_register_dev(struct nvm_dev *dev)
176{
177 if (!dev->parent_dev)
178 return 0;
179
180 dev->dev.parent = dev->parent_dev;
181 dev_set_name(&dev->dev, "%s", dev->name);
182 dev->dev.type = &nvm_type;
183 device_initialize(&dev->dev);
184 device_add(&dev->dev);
185
186 blk_mq_register_dev(&dev->dev, dev->q);
187
188 return 0;
189}
190
191void nvm_sysfs_unregister_dev(struct nvm_dev *dev)
192{
193 if (dev && dev->parent_dev)
194 kobject_put(&dev->dev.kobj);
195}