diff options
author | Dan Williams <dan.j.williams@intel.com> | 2016-03-11 13:15:36 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2016-05-09 18:35:42 -0400 |
commit | cd03412a51ac4cb3001a8cdfae4560c9602f3387 (patch) | |
tree | c2d501759d7d96fab9a0f70eefba139876bb12de /drivers/nvdimm | |
parent | 0bfb8dd3edd6e423b5053c86e10c97e92cf205ea (diff) |
libnvdimm, dax: introduce device-dax infrastructure
Device DAX is the device-centric analogue of Filesystem DAX
(CONFIG_FS_DAX). It allows persistent memory ranges to be allocated and
mapped without need of an intervening file system. This initial
infrastructure arranges for a libnvdimm pfn-device to be represented as
a different device-type so that it can be attached to a driver other
than the pmem driver.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r-- | drivers/nvdimm/Kconfig | 13 | ||||
-rw-r--r-- | drivers/nvdimm/Makefile | 1 | ||||
-rw-r--r-- | drivers/nvdimm/bus.c | 4 | ||||
-rw-r--r-- | drivers/nvdimm/claim.c | 2 | ||||
-rw-r--r-- | drivers/nvdimm/dax_devs.c | 99 | ||||
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 19 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 1 | ||||
-rw-r--r-- | drivers/nvdimm/nd.h | 25 | ||||
-rw-r--r-- | drivers/nvdimm/pfn_devs.c | 100 | ||||
-rw-r--r-- | drivers/nvdimm/region.c | 2 | ||||
-rw-r--r-- | drivers/nvdimm/region_devs.c | 29 |
11 files changed, 261 insertions, 34 deletions
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig index 53c11621d5b1..7c8a3bf07884 100644 --- a/drivers/nvdimm/Kconfig +++ b/drivers/nvdimm/Kconfig | |||
@@ -88,4 +88,17 @@ config NVDIMM_PFN | |||
88 | 88 | ||
89 | Select Y if unsure | 89 | Select Y if unsure |
90 | 90 | ||
91 | config NVDIMM_DAX | ||
92 | bool "NVDIMM DAX: Raw access to persistent memory" | ||
93 | default LIBNVDIMM | ||
94 | depends on NVDIMM_PFN | ||
95 | help | ||
96 | Support raw device dax access to a persistent memory | ||
97 | namespace. For environments that want to hard partition | ||
98 | peristent memory, this capability provides a mechanism to | ||
99 | sub-divide a namespace into character devices that can only be | ||
100 | accessed via DAX (mmap(2)). | ||
101 | |||
102 | Select Y if unsure | ||
103 | |||
91 | endif | 104 | endif |
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index ea84d3c4e8e5..909554c3f955 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile | |||
@@ -23,3 +23,4 @@ libnvdimm-y += label.o | |||
23 | libnvdimm-$(CONFIG_ND_CLAIM) += claim.o | 23 | libnvdimm-$(CONFIG_ND_CLAIM) += claim.o |
24 | libnvdimm-$(CONFIG_BTT) += btt_devs.o | 24 | libnvdimm-$(CONFIG_BTT) += btt_devs.o |
25 | libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o | 25 | libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o |
26 | libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o | ||
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 19f822d7f652..97589e3cb852 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c | |||
@@ -40,6 +40,8 @@ static int to_nd_device_type(struct device *dev) | |||
40 | return ND_DEVICE_REGION_PMEM; | 40 | return ND_DEVICE_REGION_PMEM; |
41 | else if (is_nd_blk(dev)) | 41 | else if (is_nd_blk(dev)) |
42 | return ND_DEVICE_REGION_BLK; | 42 | return ND_DEVICE_REGION_BLK; |
43 | else if (is_nd_dax(dev)) | ||
44 | return ND_DEVICE_DAX_PMEM; | ||
43 | else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent)) | 45 | else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent)) |
44 | return nd_region_to_nstype(to_nd_region(dev->parent)); | 46 | return nd_region_to_nstype(to_nd_region(dev->parent)); |
45 | 47 | ||
@@ -246,6 +248,8 @@ static void nd_async_device_unregister(void *d, async_cookie_t cookie) | |||
246 | 248 | ||
247 | void __nd_device_register(struct device *dev) | 249 | void __nd_device_register(struct device *dev) |
248 | { | 250 | { |
251 | if (!dev) | ||
252 | return; | ||
249 | dev->bus = &nvdimm_bus_type; | 253 | dev->bus = &nvdimm_bus_type; |
250 | get_device(dev); | 254 | get_device(dev); |
251 | async_schedule_domain(nd_async_device_register, dev, | 255 | async_schedule_domain(nd_async_device_register, dev, |
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 6bbd0a36994a..5f53db59a058 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c | |||
@@ -85,6 +85,8 @@ static bool is_idle(struct device *dev, struct nd_namespace_common *ndns) | |||
85 | seed = nd_region->btt_seed; | 85 | seed = nd_region->btt_seed; |
86 | else if (is_nd_pfn(dev)) | 86 | else if (is_nd_pfn(dev)) |
87 | seed = nd_region->pfn_seed; | 87 | seed = nd_region->pfn_seed; |
88 | else if (is_nd_dax(dev)) | ||
89 | seed = nd_region->dax_seed; | ||
88 | 90 | ||
89 | if (seed == dev || ndns || dev->driver) | 91 | if (seed == dev || ndns || dev->driver) |
90 | return false; | 92 | return false; |
diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c new file mode 100644 index 000000000000..f90f7549e7f4 --- /dev/null +++ b/drivers/nvdimm/dax_devs.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2013-2016 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/device.h> | ||
14 | #include <linux/sizes.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include "nd-core.h" | ||
18 | #include "nd.h" | ||
19 | |||
20 | static void nd_dax_release(struct device *dev) | ||
21 | { | ||
22 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
23 | struct nd_dax *nd_dax = to_nd_dax(dev); | ||
24 | struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; | ||
25 | |||
26 | dev_dbg(dev, "%s\n", __func__); | ||
27 | nd_detach_ndns(dev, &nd_pfn->ndns); | ||
28 | ida_simple_remove(&nd_region->dax_ida, nd_pfn->id); | ||
29 | kfree(nd_pfn->uuid); | ||
30 | kfree(nd_dax); | ||
31 | } | ||
32 | |||
33 | static struct device_type nd_dax_device_type = { | ||
34 | .name = "nd_dax", | ||
35 | .release = nd_dax_release, | ||
36 | }; | ||
37 | |||
38 | bool is_nd_dax(struct device *dev) | ||
39 | { | ||
40 | return dev ? dev->type == &nd_dax_device_type : false; | ||
41 | } | ||
42 | EXPORT_SYMBOL(is_nd_dax); | ||
43 | |||
44 | struct nd_dax *to_nd_dax(struct device *dev) | ||
45 | { | ||
46 | struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev); | ||
47 | |||
48 | WARN_ON(!is_nd_dax(dev)); | ||
49 | return nd_dax; | ||
50 | } | ||
51 | EXPORT_SYMBOL(to_nd_dax); | ||
52 | |||
53 | static const struct attribute_group *nd_dax_attribute_groups[] = { | ||
54 | &nd_pfn_attribute_group, | ||
55 | &nd_device_attribute_group, | ||
56 | &nd_numa_attribute_group, | ||
57 | NULL, | ||
58 | }; | ||
59 | |||
60 | static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) | ||
61 | { | ||
62 | struct nd_pfn *nd_pfn; | ||
63 | struct nd_dax *nd_dax; | ||
64 | struct device *dev; | ||
65 | |||
66 | nd_dax = kzalloc(sizeof(*nd_dax), GFP_KERNEL); | ||
67 | if (!nd_dax) | ||
68 | return NULL; | ||
69 | |||
70 | nd_pfn = &nd_dax->nd_pfn; | ||
71 | nd_pfn->id = ida_simple_get(&nd_region->dax_ida, 0, 0, GFP_KERNEL); | ||
72 | if (nd_pfn->id < 0) { | ||
73 | kfree(nd_dax); | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | dev = &nd_pfn->dev; | ||
78 | dev_set_name(dev, "dax%d.%d", nd_region->id, nd_pfn->id); | ||
79 | dev->groups = nd_dax_attribute_groups; | ||
80 | dev->type = &nd_dax_device_type; | ||
81 | dev->parent = &nd_region->dev; | ||
82 | |||
83 | return nd_dax; | ||
84 | } | ||
85 | |||
86 | struct device *nd_dax_create(struct nd_region *nd_region) | ||
87 | { | ||
88 | struct device *dev = NULL; | ||
89 | struct nd_dax *nd_dax; | ||
90 | |||
91 | if (!is_nd_pmem(&nd_region->dev)) | ||
92 | return NULL; | ||
93 | |||
94 | nd_dax = nd_dax_alloc(nd_region); | ||
95 | if (nd_dax) | ||
96 | dev = nd_pfn_devinit(&nd_dax->nd_pfn, NULL); | ||
97 | __nd_device_register(dev); | ||
98 | return dev; | ||
99 | } | ||
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index e5ad5162bf34..c5e3196c45b0 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c | |||
@@ -1288,6 +1288,8 @@ static ssize_t mode_show(struct device *dev, | |||
1288 | mode = "safe"; | 1288 | mode = "safe"; |
1289 | else if (claim && is_nd_pfn(claim)) | 1289 | else if (claim && is_nd_pfn(claim)) |
1290 | mode = "memory"; | 1290 | mode = "memory"; |
1291 | else if (claim && is_nd_dax(claim)) | ||
1292 | mode = "dax"; | ||
1291 | else if (!claim && pmem_should_map_pages(dev)) | 1293 | else if (!claim && pmem_should_map_pages(dev)) |
1292 | mode = "memory"; | 1294 | mode = "memory"; |
1293 | else | 1295 | else |
@@ -1379,14 +1381,17 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) | |||
1379 | { | 1381 | { |
1380 | struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; | 1382 | struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; |
1381 | struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL; | 1383 | struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL; |
1384 | struct nd_dax *nd_dax = is_nd_dax(dev) ? to_nd_dax(dev) : NULL; | ||
1382 | struct nd_namespace_common *ndns = NULL; | 1385 | struct nd_namespace_common *ndns = NULL; |
1383 | resource_size_t size; | 1386 | resource_size_t size; |
1384 | 1387 | ||
1385 | if (nd_btt || nd_pfn) { | 1388 | if (nd_btt || nd_pfn || nd_dax) { |
1386 | if (nd_btt) | 1389 | if (nd_btt) |
1387 | ndns = nd_btt->ndns; | 1390 | ndns = nd_btt->ndns; |
1388 | else if (nd_pfn) | 1391 | else if (nd_pfn) |
1389 | ndns = nd_pfn->ndns; | 1392 | ndns = nd_pfn->ndns; |
1393 | else if (nd_dax) | ||
1394 | ndns = nd_dax->nd_pfn.ndns; | ||
1390 | 1395 | ||
1391 | if (!ndns) | 1396 | if (!ndns) |
1392 | return ERR_PTR(-ENODEV); | 1397 | return ERR_PTR(-ENODEV); |
@@ -1779,6 +1784,18 @@ void nd_region_create_blk_seed(struct nd_region *nd_region) | |||
1779 | nd_device_register(nd_region->ns_seed); | 1784 | nd_device_register(nd_region->ns_seed); |
1780 | } | 1785 | } |
1781 | 1786 | ||
1787 | void nd_region_create_dax_seed(struct nd_region *nd_region) | ||
1788 | { | ||
1789 | WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); | ||
1790 | nd_region->dax_seed = nd_dax_create(nd_region); | ||
1791 | /* | ||
1792 | * Seed creation failures are not fatal, provisioning is simply | ||
1793 | * disabled until memory becomes available | ||
1794 | */ | ||
1795 | if (!nd_region->dax_seed) | ||
1796 | dev_err(&nd_region->dev, "failed to create dax namespace\n"); | ||
1797 | } | ||
1798 | |||
1782 | void nd_region_create_pfn_seed(struct nd_region *nd_region) | 1799 | void nd_region_create_pfn_seed(struct nd_region *nd_region) |
1783 | { | 1800 | { |
1784 | WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); | 1801 | WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); |
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 1d1500f3d8b5..cb65308c0329 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h | |||
@@ -54,6 +54,7 @@ struct nd_region; | |||
54 | void nd_region_create_blk_seed(struct nd_region *nd_region); | 54 | void nd_region_create_blk_seed(struct nd_region *nd_region); |
55 | void nd_region_create_btt_seed(struct nd_region *nd_region); | 55 | void nd_region_create_btt_seed(struct nd_region *nd_region); |
56 | void nd_region_create_pfn_seed(struct nd_region *nd_region); | 56 | void nd_region_create_pfn_seed(struct nd_region *nd_region); |
57 | void nd_region_create_dax_seed(struct nd_region *nd_region); | ||
57 | void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev); | 58 | void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev); |
58 | int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus); | 59 | int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus); |
59 | void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus); | 60 | void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus); |
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 6c36509662e4..46910b8f32b1 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h | |||
@@ -101,10 +101,12 @@ struct nd_region { | |||
101 | struct ida ns_ida; | 101 | struct ida ns_ida; |
102 | struct ida btt_ida; | 102 | struct ida btt_ida; |
103 | struct ida pfn_ida; | 103 | struct ida pfn_ida; |
104 | struct ida dax_ida; | ||
104 | unsigned long flags; | 105 | unsigned long flags; |
105 | struct device *ns_seed; | 106 | struct device *ns_seed; |
106 | struct device *btt_seed; | 107 | struct device *btt_seed; |
107 | struct device *pfn_seed; | 108 | struct device *pfn_seed; |
109 | struct device *dax_seed; | ||
108 | u16 ndr_mappings; | 110 | u16 ndr_mappings; |
109 | u64 ndr_size; | 111 | u64 ndr_size; |
110 | u64 ndr_start; | 112 | u64 ndr_start; |
@@ -161,6 +163,10 @@ struct nd_pfn { | |||
161 | struct nd_namespace_common *ndns; | 163 | struct nd_namespace_common *ndns; |
162 | }; | 164 | }; |
163 | 165 | ||
166 | struct nd_dax { | ||
167 | struct nd_pfn nd_pfn; | ||
168 | }; | ||
169 | |||
164 | enum nd_async_mode { | 170 | enum nd_async_mode { |
165 | ND_SYNC, | 171 | ND_SYNC, |
166 | ND_ASYNC, | 172 | ND_ASYNC, |
@@ -224,7 +230,10 @@ struct nd_pfn *to_nd_pfn(struct device *dev); | |||
224 | int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns); | 230 | int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns); |
225 | bool is_nd_pfn(struct device *dev); | 231 | bool is_nd_pfn(struct device *dev); |
226 | struct device *nd_pfn_create(struct nd_region *nd_region); | 232 | struct device *nd_pfn_create(struct nd_region *nd_region); |
233 | struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, | ||
234 | struct nd_namespace_common *ndns); | ||
227 | int nd_pfn_validate(struct nd_pfn *nd_pfn); | 235 | int nd_pfn_validate(struct nd_pfn *nd_pfn); |
236 | extern struct attribute_group nd_pfn_attribute_group; | ||
228 | #else | 237 | #else |
229 | static inline int nd_pfn_probe(struct device *dev, | 238 | static inline int nd_pfn_probe(struct device *dev, |
230 | struct nd_namespace_common *ndns) | 239 | struct nd_namespace_common *ndns) |
@@ -248,6 +257,22 @@ static inline int nd_pfn_validate(struct nd_pfn *nd_pfn) | |||
248 | } | 257 | } |
249 | #endif | 258 | #endif |
250 | 259 | ||
260 | struct nd_dax *to_nd_dax(struct device *dev); | ||
261 | #if IS_ENABLED(CONFIG_NVDIMM_DAX) | ||
262 | bool is_nd_dax(struct device *dev); | ||
263 | struct device *nd_dax_create(struct nd_region *nd_region); | ||
264 | #else | ||
265 | static inline bool is_nd_dax(struct device *dev) | ||
266 | { | ||
267 | return false; | ||
268 | } | ||
269 | |||
270 | static inline struct device *nd_dax_create(struct nd_region *nd_region) | ||
271 | { | ||
272 | return NULL; | ||
273 | } | ||
274 | #endif | ||
275 | |||
251 | struct nd_region *to_nd_region(struct device *dev); | 276 | struct nd_region *to_nd_region(struct device *dev); |
252 | int nd_region_to_nstype(struct nd_region *nd_region); | 277 | int nd_region_to_nstype(struct nd_region *nd_region); |
253 | int nd_region_register_namespaces(struct nd_region *nd_region, int *err); | 278 | int nd_region_register_namespaces(struct nd_region *nd_region, int *err); |
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index e8693fe65e49..6ade2eb7615d 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. | 2 | * Copyright(c) 2013-2016 Intel Corporation. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of version 2 of the GNU General Public License as |
@@ -54,10 +54,29 @@ struct nd_pfn *to_nd_pfn(struct device *dev) | |||
54 | } | 54 | } |
55 | EXPORT_SYMBOL(to_nd_pfn); | 55 | EXPORT_SYMBOL(to_nd_pfn); |
56 | 56 | ||
57 | static struct nd_pfn *to_nd_pfn_safe(struct device *dev) | ||
58 | { | ||
59 | /* | ||
60 | * pfn device attributes are re-used by dax device instances, so we | ||
61 | * need to be careful to correct device-to-nd_pfn conversion. | ||
62 | */ | ||
63 | if (is_nd_pfn(dev)) | ||
64 | return to_nd_pfn(dev); | ||
65 | |||
66 | if (is_nd_dax(dev)) { | ||
67 | struct nd_dax *nd_dax = to_nd_dax(dev); | ||
68 | |||
69 | return &nd_dax->nd_pfn; | ||
70 | } | ||
71 | |||
72 | WARN_ON(1); | ||
73 | return NULL; | ||
74 | } | ||
75 | |||
57 | static ssize_t mode_show(struct device *dev, | 76 | static ssize_t mode_show(struct device *dev, |
58 | struct device_attribute *attr, char *buf) | 77 | struct device_attribute *attr, char *buf) |
59 | { | 78 | { |
60 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 79 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
61 | 80 | ||
62 | switch (nd_pfn->mode) { | 81 | switch (nd_pfn->mode) { |
63 | case PFN_MODE_RAM: | 82 | case PFN_MODE_RAM: |
@@ -72,7 +91,7 @@ static ssize_t mode_show(struct device *dev, | |||
72 | static ssize_t mode_store(struct device *dev, | 91 | static ssize_t mode_store(struct device *dev, |
73 | struct device_attribute *attr, const char *buf, size_t len) | 92 | struct device_attribute *attr, const char *buf, size_t len) |
74 | { | 93 | { |
75 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 94 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
76 | ssize_t rc = 0; | 95 | ssize_t rc = 0; |
77 | 96 | ||
78 | device_lock(dev); | 97 | device_lock(dev); |
@@ -106,7 +125,7 @@ static DEVICE_ATTR_RW(mode); | |||
106 | static ssize_t align_show(struct device *dev, | 125 | static ssize_t align_show(struct device *dev, |
107 | struct device_attribute *attr, char *buf) | 126 | struct device_attribute *attr, char *buf) |
108 | { | 127 | { |
109 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 128 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
110 | 129 | ||
111 | return sprintf(buf, "%lx\n", nd_pfn->align); | 130 | return sprintf(buf, "%lx\n", nd_pfn->align); |
112 | } | 131 | } |
@@ -134,7 +153,7 @@ static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf) | |||
134 | static ssize_t align_store(struct device *dev, | 153 | static ssize_t align_store(struct device *dev, |
135 | struct device_attribute *attr, const char *buf, size_t len) | 154 | struct device_attribute *attr, const char *buf, size_t len) |
136 | { | 155 | { |
137 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 156 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
138 | ssize_t rc; | 157 | ssize_t rc; |
139 | 158 | ||
140 | device_lock(dev); | 159 | device_lock(dev); |
@@ -152,7 +171,7 @@ static DEVICE_ATTR_RW(align); | |||
152 | static ssize_t uuid_show(struct device *dev, | 171 | static ssize_t uuid_show(struct device *dev, |
153 | struct device_attribute *attr, char *buf) | 172 | struct device_attribute *attr, char *buf) |
154 | { | 173 | { |
155 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 174 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
156 | 175 | ||
157 | if (nd_pfn->uuid) | 176 | if (nd_pfn->uuid) |
158 | return sprintf(buf, "%pUb\n", nd_pfn->uuid); | 177 | return sprintf(buf, "%pUb\n", nd_pfn->uuid); |
@@ -162,7 +181,7 @@ static ssize_t uuid_show(struct device *dev, | |||
162 | static ssize_t uuid_store(struct device *dev, | 181 | static ssize_t uuid_store(struct device *dev, |
163 | struct device_attribute *attr, const char *buf, size_t len) | 182 | struct device_attribute *attr, const char *buf, size_t len) |
164 | { | 183 | { |
165 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 184 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
166 | ssize_t rc; | 185 | ssize_t rc; |
167 | 186 | ||
168 | device_lock(dev); | 187 | device_lock(dev); |
@@ -178,7 +197,7 @@ static DEVICE_ATTR_RW(uuid); | |||
178 | static ssize_t namespace_show(struct device *dev, | 197 | static ssize_t namespace_show(struct device *dev, |
179 | struct device_attribute *attr, char *buf) | 198 | struct device_attribute *attr, char *buf) |
180 | { | 199 | { |
181 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 200 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
182 | ssize_t rc; | 201 | ssize_t rc; |
183 | 202 | ||
184 | nvdimm_bus_lock(dev); | 203 | nvdimm_bus_lock(dev); |
@@ -191,7 +210,7 @@ static ssize_t namespace_show(struct device *dev, | |||
191 | static ssize_t namespace_store(struct device *dev, | 210 | static ssize_t namespace_store(struct device *dev, |
192 | struct device_attribute *attr, const char *buf, size_t len) | 211 | struct device_attribute *attr, const char *buf, size_t len) |
193 | { | 212 | { |
194 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 213 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
195 | ssize_t rc; | 214 | ssize_t rc; |
196 | 215 | ||
197 | device_lock(dev); | 216 | device_lock(dev); |
@@ -209,7 +228,7 @@ static DEVICE_ATTR_RW(namespace); | |||
209 | static ssize_t resource_show(struct device *dev, | 228 | static ssize_t resource_show(struct device *dev, |
210 | struct device_attribute *attr, char *buf) | 229 | struct device_attribute *attr, char *buf) |
211 | { | 230 | { |
212 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 231 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
213 | ssize_t rc; | 232 | ssize_t rc; |
214 | 233 | ||
215 | device_lock(dev); | 234 | device_lock(dev); |
@@ -235,7 +254,7 @@ static DEVICE_ATTR_RO(resource); | |||
235 | static ssize_t size_show(struct device *dev, | 254 | static ssize_t size_show(struct device *dev, |
236 | struct device_attribute *attr, char *buf) | 255 | struct device_attribute *attr, char *buf) |
237 | { | 256 | { |
238 | struct nd_pfn *nd_pfn = to_nd_pfn(dev); | 257 | struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); |
239 | ssize_t rc; | 258 | ssize_t rc; |
240 | 259 | ||
241 | device_lock(dev); | 260 | device_lock(dev); |
@@ -270,7 +289,7 @@ static struct attribute *nd_pfn_attributes[] = { | |||
270 | NULL, | 289 | NULL, |
271 | }; | 290 | }; |
272 | 291 | ||
273 | static struct attribute_group nd_pfn_attribute_group = { | 292 | struct attribute_group nd_pfn_attribute_group = { |
274 | .attrs = nd_pfn_attributes, | 293 | .attrs = nd_pfn_attributes, |
275 | }; | 294 | }; |
276 | 295 | ||
@@ -281,15 +300,31 @@ static const struct attribute_group *nd_pfn_attribute_groups[] = { | |||
281 | NULL, | 300 | NULL, |
282 | }; | 301 | }; |
283 | 302 | ||
284 | static struct device *__nd_pfn_create(struct nd_region *nd_region, | 303 | struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, |
285 | struct nd_namespace_common *ndns) | 304 | struct nd_namespace_common *ndns) |
286 | { | 305 | { |
287 | struct nd_pfn *nd_pfn; | 306 | struct device *dev = &nd_pfn->dev; |
288 | struct device *dev; | ||
289 | 307 | ||
290 | /* we can only create pages for contiguous ranged of pmem */ | 308 | if (!nd_pfn) |
291 | if (!is_nd_pmem(&nd_region->dev)) | 309 | return NULL; |
310 | |||
311 | nd_pfn->mode = PFN_MODE_NONE; | ||
312 | nd_pfn->align = HPAGE_SIZE; | ||
313 | dev = &nd_pfn->dev; | ||
314 | device_initialize(&nd_pfn->dev); | ||
315 | if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) { | ||
316 | dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n", | ||
317 | __func__, dev_name(ndns->claim)); | ||
318 | put_device(dev); | ||
292 | return NULL; | 319 | return NULL; |
320 | } | ||
321 | return dev; | ||
322 | } | ||
323 | |||
324 | static struct nd_pfn *nd_pfn_alloc(struct nd_region *nd_region) | ||
325 | { | ||
326 | struct nd_pfn *nd_pfn; | ||
327 | struct device *dev; | ||
293 | 328 | ||
294 | nd_pfn = kzalloc(sizeof(*nd_pfn), GFP_KERNEL); | 329 | nd_pfn = kzalloc(sizeof(*nd_pfn), GFP_KERNEL); |
295 | if (!nd_pfn) | 330 | if (!nd_pfn) |
@@ -301,29 +336,27 @@ static struct device *__nd_pfn_create(struct nd_region *nd_region, | |||
301 | return NULL; | 336 | return NULL; |
302 | } | 337 | } |
303 | 338 | ||
304 | nd_pfn->mode = PFN_MODE_NONE; | ||
305 | nd_pfn->align = HPAGE_SIZE; | ||
306 | dev = &nd_pfn->dev; | 339 | dev = &nd_pfn->dev; |
307 | dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id); | 340 | dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id); |
308 | dev->parent = &nd_region->dev; | ||
309 | dev->type = &nd_pfn_device_type; | ||
310 | dev->groups = nd_pfn_attribute_groups; | 341 | dev->groups = nd_pfn_attribute_groups; |
311 | device_initialize(&nd_pfn->dev); | 342 | dev->type = &nd_pfn_device_type; |
312 | if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) { | 343 | dev->parent = &nd_region->dev; |
313 | dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n", | 344 | |
314 | __func__, dev_name(ndns->claim)); | 345 | return nd_pfn; |
315 | put_device(dev); | ||
316 | return NULL; | ||
317 | } | ||
318 | return dev; | ||
319 | } | 346 | } |
320 | 347 | ||
321 | struct device *nd_pfn_create(struct nd_region *nd_region) | 348 | struct device *nd_pfn_create(struct nd_region *nd_region) |
322 | { | 349 | { |
323 | struct device *dev = __nd_pfn_create(nd_region, NULL); | 350 | struct nd_pfn *nd_pfn; |
351 | struct device *dev; | ||
352 | |||
353 | if (!is_nd_pmem(&nd_region->dev)) | ||
354 | return NULL; | ||
355 | |||
356 | nd_pfn = nd_pfn_alloc(nd_region); | ||
357 | dev = nd_pfn_devinit(nd_pfn, NULL); | ||
324 | 358 | ||
325 | if (dev) | 359 | __nd_device_register(dev); |
326 | __nd_device_register(dev); | ||
327 | return dev; | 360 | return dev; |
328 | } | 361 | } |
329 | 362 | ||
@@ -423,7 +456,8 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) | |||
423 | return -ENODEV; | 456 | return -ENODEV; |
424 | 457 | ||
425 | nvdimm_bus_lock(&ndns->dev); | 458 | nvdimm_bus_lock(&ndns->dev); |
426 | pfn_dev = __nd_pfn_create(nd_region, ndns); | 459 | nd_pfn = nd_pfn_alloc(nd_region); |
460 | pfn_dev = nd_pfn_devinit(nd_pfn, ndns); | ||
427 | nvdimm_bus_unlock(&ndns->dev); | 461 | nvdimm_bus_unlock(&ndns->dev); |
428 | if (!pfn_dev) | 462 | if (!pfn_dev) |
429 | return -ENOMEM; | 463 | return -ENOMEM; |
diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index 4b7715e29cff..05a912359939 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c | |||
@@ -54,6 +54,7 @@ static int nd_region_probe(struct device *dev) | |||
54 | 54 | ||
55 | nd_region->btt_seed = nd_btt_create(nd_region); | 55 | nd_region->btt_seed = nd_btt_create(nd_region); |
56 | nd_region->pfn_seed = nd_pfn_create(nd_region); | 56 | nd_region->pfn_seed = nd_pfn_create(nd_region); |
57 | nd_region->dax_seed = nd_dax_create(nd_region); | ||
57 | if (err == 0) | 58 | if (err == 0) |
58 | return 0; | 59 | return 0; |
59 | 60 | ||
@@ -86,6 +87,7 @@ static int nd_region_remove(struct device *dev) | |||
86 | nd_region->ns_seed = NULL; | 87 | nd_region->ns_seed = NULL; |
87 | nd_region->btt_seed = NULL; | 88 | nd_region->btt_seed = NULL; |
88 | nd_region->pfn_seed = NULL; | 89 | nd_region->pfn_seed = NULL; |
90 | nd_region->dax_seed = NULL; | ||
89 | dev_set_drvdata(dev, NULL); | 91 | dev_set_drvdata(dev, NULL); |
90 | nvdimm_bus_unlock(dev); | 92 | nvdimm_bus_unlock(dev); |
91 | 93 | ||
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 139bf71ca549..9e1b054e0e61 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c | |||
@@ -306,6 +306,23 @@ static ssize_t pfn_seed_show(struct device *dev, | |||
306 | } | 306 | } |
307 | static DEVICE_ATTR_RO(pfn_seed); | 307 | static DEVICE_ATTR_RO(pfn_seed); |
308 | 308 | ||
309 | static ssize_t dax_seed_show(struct device *dev, | ||
310 | struct device_attribute *attr, char *buf) | ||
311 | { | ||
312 | struct nd_region *nd_region = to_nd_region(dev); | ||
313 | ssize_t rc; | ||
314 | |||
315 | nvdimm_bus_lock(dev); | ||
316 | if (nd_region->dax_seed) | ||
317 | rc = sprintf(buf, "%s\n", dev_name(nd_region->dax_seed)); | ||
318 | else | ||
319 | rc = sprintf(buf, "\n"); | ||
320 | nvdimm_bus_unlock(dev); | ||
321 | |||
322 | return rc; | ||
323 | } | ||
324 | static DEVICE_ATTR_RO(dax_seed); | ||
325 | |||
309 | static ssize_t read_only_show(struct device *dev, | 326 | static ssize_t read_only_show(struct device *dev, |
310 | struct device_attribute *attr, char *buf) | 327 | struct device_attribute *attr, char *buf) |
311 | { | 328 | { |
@@ -335,6 +352,7 @@ static struct attribute *nd_region_attributes[] = { | |||
335 | &dev_attr_mappings.attr, | 352 | &dev_attr_mappings.attr, |
336 | &dev_attr_btt_seed.attr, | 353 | &dev_attr_btt_seed.attr, |
337 | &dev_attr_pfn_seed.attr, | 354 | &dev_attr_pfn_seed.attr, |
355 | &dev_attr_dax_seed.attr, | ||
338 | &dev_attr_read_only.attr, | 356 | &dev_attr_read_only.attr, |
339 | &dev_attr_set_cookie.attr, | 357 | &dev_attr_set_cookie.attr, |
340 | &dev_attr_available_size.attr, | 358 | &dev_attr_available_size.attr, |
@@ -353,6 +371,9 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) | |||
353 | if (!is_nd_pmem(dev) && a == &dev_attr_pfn_seed.attr) | 371 | if (!is_nd_pmem(dev) && a == &dev_attr_pfn_seed.attr) |
354 | return 0; | 372 | return 0; |
355 | 373 | ||
374 | if (!is_nd_pmem(dev) && a == &dev_attr_dax_seed.attr) | ||
375 | return 0; | ||
376 | |||
356 | if (a != &dev_attr_set_cookie.attr | 377 | if (a != &dev_attr_set_cookie.attr |
357 | && a != &dev_attr_available_size.attr) | 378 | && a != &dev_attr_available_size.attr) |
358 | return a->mode; | 379 | return a->mode; |
@@ -441,6 +462,13 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, | |||
441 | nd_region_create_pfn_seed(nd_region); | 462 | nd_region_create_pfn_seed(nd_region); |
442 | nvdimm_bus_unlock(dev); | 463 | nvdimm_bus_unlock(dev); |
443 | } | 464 | } |
465 | if (is_nd_dax(dev) && probe) { | ||
466 | nd_region = to_nd_region(dev->parent); | ||
467 | nvdimm_bus_lock(dev); | ||
468 | if (nd_region->dax_seed == dev) | ||
469 | nd_region_create_dax_seed(nd_region); | ||
470 | nvdimm_bus_unlock(dev); | ||
471 | } | ||
444 | } | 472 | } |
445 | 473 | ||
446 | void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev) | 474 | void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev) |
@@ -718,6 +746,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, | |||
718 | ida_init(&nd_region->ns_ida); | 746 | ida_init(&nd_region->ns_ida); |
719 | ida_init(&nd_region->btt_ida); | 747 | ida_init(&nd_region->btt_ida); |
720 | ida_init(&nd_region->pfn_ida); | 748 | ida_init(&nd_region->pfn_ida); |
749 | ida_init(&nd_region->dax_ida); | ||
721 | dev = &nd_region->dev; | 750 | dev = &nd_region->dev; |
722 | dev_set_name(dev, "region%d", nd_region->id); | 751 | dev_set_name(dev, "region%d", nd_region->id); |
723 | dev->parent = &nvdimm_bus->dev; | 752 | dev->parent = &nvdimm_bus->dev; |