Skip to content

Commit 3d88002

Browse files
committed
libnvdimm: support for legacy (non-aliasing) nvdimms
The libnvdimm region driver is an intermediary driver that translates non-volatile "region"s into "namespace" sub-devices that are surfaced by persistent memory block-device drivers (PMEM and BLK). ACPI 6 introduces the concept that a given nvdimm may simultaneously offer multiple access modes to its media through direct PMEM load/store access, or windowed BLK mode. Existing nvdimms mostly implement a PMEM interface, some offer a BLK-like mode, but never both as ACPI 6 defines. If an nvdimm is single interfaced, then there is no need for dimm metadata labels. For these devices we can take the region boundaries directly to create a child namespace device (nd_namespace_io). Acked-by: Christoph Hellwig <[email protected]> Tested-by: Toshi Kani <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 1f7df6f commit 3d88002

File tree

13 files changed

+383
-8
lines changed

13 files changed

+383
-8
lines changed

drivers/acpi/nfit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ static struct attribute_group acpi_nfit_region_attribute_group = {
780780
static const struct attribute_group *acpi_nfit_region_attribute_groups[] = {
781781
&nd_region_attribute_group,
782782
&nd_mapping_attribute_group,
783+
&nd_device_attribute_group,
783784
&acpi_nfit_region_attribute_group,
784785
NULL,
785786
};

drivers/nvdimm/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ libnvdimm-y += bus.o
55
libnvdimm-y += dimm_devs.o
66
libnvdimm-y += dimm.o
77
libnvdimm-y += region_devs.o
8+
libnvdimm-y += region.o
9+
libnvdimm-y += namespace_devs.o

drivers/nvdimm/bus.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1414
#include <linux/vmalloc.h>
1515
#include <linux/uaccess.h>
16+
#include <linux/module.h>
1617
#include <linux/fcntl.h>
1718
#include <linux/async.h>
1819
#include <linux/ndctl.h>
@@ -33,6 +34,12 @@ static int to_nd_device_type(struct device *dev)
3334
{
3435
if (is_nvdimm(dev))
3536
return ND_DEVICE_DIMM;
37+
else if (is_nd_pmem(dev))
38+
return ND_DEVICE_REGION_PMEM;
39+
else if (is_nd_blk(dev))
40+
return ND_DEVICE_REGION_BLK;
41+
else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent))
42+
return nd_region_to_nstype(to_nd_region(dev->parent));
3643

3744
return 0;
3845
}
@@ -50,27 +57,46 @@ static int nvdimm_bus_match(struct device *dev, struct device_driver *drv)
5057
return test_bit(to_nd_device_type(dev), &nd_drv->type);
5158
}
5259

60+
static struct module *to_bus_provider(struct device *dev)
61+
{
62+
/* pin bus providers while regions are enabled */
63+
if (is_nd_pmem(dev) || is_nd_blk(dev)) {
64+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
65+
66+
return nvdimm_bus->module;
67+
}
68+
return NULL;
69+
}
70+
5371
static int nvdimm_bus_probe(struct device *dev)
5472
{
5573
struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
74+
struct module *provider = to_bus_provider(dev);
5675
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
5776
int rc;
5877

78+
if (!try_module_get(provider))
79+
return -ENXIO;
80+
5981
rc = nd_drv->probe(dev);
6082
dev_dbg(&nvdimm_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name,
6183
dev_name(dev), rc);
84+
if (rc != 0)
85+
module_put(provider);
6286
return rc;
6387
}
6488

6589
static int nvdimm_bus_remove(struct device *dev)
6690
{
6791
struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
92+
struct module *provider = to_bus_provider(dev);
6893
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
6994
int rc;
7095

7196
rc = nd_drv->remove(dev);
7297
dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
7398
dev_name(dev), rc);
99+
module_put(provider);
74100
return rc;
75101
}
76102

drivers/nvdimm/core.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,36 @@ LIST_HEAD(nvdimm_bus_list);
2424
DEFINE_MUTEX(nvdimm_bus_list_mutex);
2525
static DEFINE_IDA(nd_ida);
2626

27+
void nvdimm_bus_lock(struct device *dev)
28+
{
29+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
30+
31+
if (!nvdimm_bus)
32+
return;
33+
mutex_lock(&nvdimm_bus->reconfig_mutex);
34+
}
35+
EXPORT_SYMBOL(nvdimm_bus_lock);
36+
37+
void nvdimm_bus_unlock(struct device *dev)
38+
{
39+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
40+
41+
if (!nvdimm_bus)
42+
return;
43+
mutex_unlock(&nvdimm_bus->reconfig_mutex);
44+
}
45+
EXPORT_SYMBOL(nvdimm_bus_unlock);
46+
47+
bool is_nvdimm_bus_locked(struct device *dev)
48+
{
49+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
50+
51+
if (!nvdimm_bus)
52+
return false;
53+
return mutex_is_locked(&nvdimm_bus->reconfig_mutex);
54+
}
55+
EXPORT_SYMBOL(is_nvdimm_bus_locked);
56+
2757
static void nvdimm_bus_release(struct device *dev)
2858
{
2959
struct nvdimm_bus *nvdimm_bus;
@@ -135,8 +165,8 @@ struct attribute_group nvdimm_bus_attribute_group = {
135165
};
136166
EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
137167

138-
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
139-
struct nvdimm_bus_descriptor *nd_desc)
168+
struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
169+
struct nvdimm_bus_descriptor *nd_desc, struct module *module)
140170
{
141171
struct nvdimm_bus *nvdimm_bus;
142172
int rc;
@@ -146,11 +176,13 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
146176
return NULL;
147177
INIT_LIST_HEAD(&nvdimm_bus->list);
148178
nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
179+
mutex_init(&nvdimm_bus->reconfig_mutex);
149180
if (nvdimm_bus->id < 0) {
150181
kfree(nvdimm_bus);
151182
return NULL;
152183
}
153184
nvdimm_bus->nd_desc = nd_desc;
185+
nvdimm_bus->module = module;
154186
nvdimm_bus->dev.parent = parent;
155187
nvdimm_bus->dev.release = nvdimm_bus_release;
156188
nvdimm_bus->dev.groups = nd_desc->attr_groups;
@@ -174,7 +206,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
174206
put_device(&nvdimm_bus->dev);
175207
return NULL;
176208
}
177-
EXPORT_SYMBOL_GPL(nvdimm_bus_register);
209+
EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
178210

179211
static int child_unregister(struct device *dev, void *data)
180212
{
@@ -218,7 +250,12 @@ static __init int libnvdimm_init(void)
218250
rc = nvdimm_init();
219251
if (rc)
220252
goto err_dimm;
253+
rc = nd_region_init();
254+
if (rc)
255+
goto err_region;
221256
return 0;
257+
err_region:
258+
nvdimm_exit();
222259
err_dimm:
223260
nvdimm_bus_exit();
224261
return rc;
@@ -227,6 +264,7 @@ static __init int libnvdimm_init(void)
227264
static __exit void libnvdimm_exit(void)
228265
{
229266
WARN_ON(!list_empty(&nvdimm_bus_list));
267+
nd_region_exit();
230268
nvdimm_exit();
231269
nvdimm_bus_exit();
232270
}

drivers/nvdimm/dimm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ int __init nvdimm_init(void)
8484
return nd_driver_register(&nvdimm_driver);
8585
}
8686

87-
void __exit nvdimm_exit(void)
87+
void nvdimm_exit(void)
8888
{
8989
driver_unregister(&nvdimm_driver.drv);
9090
}

drivers/nvdimm/namespace_devs.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of version 2 of the GNU General Public License as
6+
* published by the Free Software Foundation.
7+
*
8+
* This program is distributed in the hope that it will be useful, but
9+
* WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+
* General Public License for more details.
12+
*/
13+
#include <linux/module.h>
14+
#include <linux/device.h>
15+
#include <linux/slab.h>
16+
#include <linux/nd.h>
17+
#include "nd.h"
18+
19+
static void namespace_io_release(struct device *dev)
20+
{
21+
struct nd_namespace_io *nsio = to_nd_namespace_io(dev);
22+
23+
kfree(nsio);
24+
}
25+
26+
static struct device_type namespace_io_device_type = {
27+
.name = "nd_namespace_io",
28+
.release = namespace_io_release,
29+
};
30+
31+
static ssize_t nstype_show(struct device *dev,
32+
struct device_attribute *attr, char *buf)
33+
{
34+
struct nd_region *nd_region = to_nd_region(dev->parent);
35+
36+
return sprintf(buf, "%d\n", nd_region_to_nstype(nd_region));
37+
}
38+
static DEVICE_ATTR_RO(nstype);
39+
40+
static struct attribute *nd_namespace_attributes[] = {
41+
&dev_attr_nstype.attr,
42+
NULL,
43+
};
44+
45+
static struct attribute_group nd_namespace_attribute_group = {
46+
.attrs = nd_namespace_attributes,
47+
};
48+
49+
static const struct attribute_group *nd_namespace_attribute_groups[] = {
50+
&nd_device_attribute_group,
51+
&nd_namespace_attribute_group,
52+
NULL,
53+
};
54+
55+
static struct device **create_namespace_io(struct nd_region *nd_region)
56+
{
57+
struct nd_namespace_io *nsio;
58+
struct device *dev, **devs;
59+
struct resource *res;
60+
61+
nsio = kzalloc(sizeof(*nsio), GFP_KERNEL);
62+
if (!nsio)
63+
return NULL;
64+
65+
devs = kcalloc(2, sizeof(struct device *), GFP_KERNEL);
66+
if (!devs) {
67+
kfree(nsio);
68+
return NULL;
69+
}
70+
71+
dev = &nsio->dev;
72+
dev->type = &namespace_io_device_type;
73+
dev->parent = &nd_region->dev;
74+
res = &nsio->res;
75+
res->name = dev_name(&nd_region->dev);
76+
res->flags = IORESOURCE_MEM;
77+
res->start = nd_region->ndr_start;
78+
res->end = res->start + nd_region->ndr_size - 1;
79+
80+
devs[0] = dev;
81+
return devs;
82+
}
83+
84+
int nd_region_register_namespaces(struct nd_region *nd_region, int *err)
85+
{
86+
struct device **devs = NULL;
87+
int i;
88+
89+
*err = 0;
90+
switch (nd_region_to_nstype(nd_region)) {
91+
case ND_DEVICE_NAMESPACE_IO:
92+
devs = create_namespace_io(nd_region);
93+
break;
94+
default:
95+
break;
96+
}
97+
98+
if (!devs)
99+
return -ENODEV;
100+
101+
for (i = 0; devs[i]; i++) {
102+
struct device *dev = devs[i];
103+
104+
dev_set_name(dev, "namespace%d.%d", nd_region->id, i);
105+
dev->groups = nd_namespace_attribute_groups;
106+
nd_device_register(dev);
107+
}
108+
kfree(devs);
109+
110+
return i;
111+
}

drivers/nvdimm/nd-core.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ extern int nvdimm_major;
2121

2222
struct nvdimm_bus {
2323
struct nvdimm_bus_descriptor *nd_desc;
24+
struct module *module;
2425
struct list_head list;
2526
struct device dev;
2627
int id;
28+
struct mutex reconfig_mutex;
2729
};
2830

2931
struct nvdimm {
@@ -34,6 +36,9 @@ struct nvdimm {
3436
int id;
3537
};
3638

39+
bool is_nvdimm(struct device *dev);
40+
bool is_nd_blk(struct device *dev);
41+
bool is_nd_pmem(struct device *dev);
3742
struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev);
3843
int __init nvdimm_bus_init(void);
3944
void nvdimm_bus_exit(void);
@@ -43,5 +48,4 @@ void nd_synchronize(void);
4348
int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus);
4449
int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus);
4550
int nd_match_dimm(struct device *dev, void *data);
46-
bool is_nvdimm(struct device *dev);
4751
#endif /* __ND_CORE_H__ */

drivers/nvdimm/nd.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ struct nvdimm_drvdata {
2323
void *data;
2424
};
2525

26+
struct nd_region_namespaces {
27+
int count;
28+
int active;
29+
};
30+
2631
struct nd_region {
2732
struct device dev;
2833
u16 ndr_mappings;
@@ -41,7 +46,15 @@ enum nd_async_mode {
4146
void nd_device_register(struct device *dev);
4247
void nd_device_unregister(struct device *dev, enum nd_async_mode mode);
4348
int __init nvdimm_init(void);
49+
int __init nd_region_init(void);
4450
void nvdimm_exit(void);
51+
void nd_region_exit(void);
4552
int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd);
4653
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd);
54+
struct nd_region *to_nd_region(struct device *dev);
55+
int nd_region_to_nstype(struct nd_region *nd_region);
56+
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
57+
void nvdimm_bus_lock(struct device *dev);
58+
void nvdimm_bus_unlock(struct device *dev);
59+
bool is_nvdimm_bus_locked(struct device *dev);
4760
#endif /* __ND_H__ */

0 commit comments

Comments
 (0)