aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2015-07-30 17:57:47 -0400
committerDan Williams <dan.j.williams@intel.com>2015-08-28 23:39:36 -0400
commite1455744b27c9e6115c3508a7b2902157c2c4347 (patch)
tree6bfbc5d78269c9389f5bcfc1adc35d4f7f2e6fa4
parent96601adb745186ccbcf5b078d4756f13381ec2af (diff)
libnvdimm, pfn: 'struct page' provider infrastructure
Implement the base infrastructure for libnvdimm PFN devices. Similar to BTT devices they take a namespace as a backing device and layer functionality on top. In this case the functionality is reserving space for an array of 'struct page' entries to be handed out through pfn_to_page(). For now this is just the basic libnvdimm-device-model for configuring the base PFN device. As the namespace claiming mechanism for PFN devices is mostly identical to BTT devices drivers/nvdimm/claim.c is created to house the common bits. Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/nvdimm/Kconfig22
-rw-r--r--drivers/nvdimm/Makefile2
-rw-r--r--drivers/nvdimm/btt.c6
-rw-r--r--drivers/nvdimm/btt_devs.c172
-rw-r--r--drivers/nvdimm/claim.c201
-rw-r--r--drivers/nvdimm/namespace_devs.c34
-rw-r--r--drivers/nvdimm/nd-core.h9
-rw-r--r--drivers/nvdimm/nd.h56
-rw-r--r--drivers/nvdimm/pfn.h35
-rw-r--r--drivers/nvdimm/pfn_devs.c336
-rw-r--r--drivers/nvdimm/region.c2
-rw-r--r--drivers/nvdimm/region_devs.c19
-rw-r--r--tools/testing/nvdimm/Kbuild2
13 files changed, 719 insertions, 177 deletions
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index 72226acb5c0f..ace25b53b755 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -21,6 +21,7 @@ config BLK_DEV_PMEM
21 default LIBNVDIMM 21 default LIBNVDIMM
22 depends on HAS_IOMEM 22 depends on HAS_IOMEM
23 select ND_BTT if BTT 23 select ND_BTT if BTT
24 select ND_PFN if NVDIMM_PFN
24 help 25 help
25 Memory ranges for PMEM are described by either an NFIT 26 Memory ranges for PMEM are described by either an NFIT
26 (NVDIMM Firmware Interface Table, see CONFIG_NFIT_ACPI), a 27 (NVDIMM Firmware Interface Table, see CONFIG_NFIT_ACPI), a
@@ -47,12 +48,16 @@ config ND_BLK
47 (CONFIG_ACPI_NFIT), or otherwise exposes BLK-mode 48 (CONFIG_ACPI_NFIT), or otherwise exposes BLK-mode
48 capabilities. 49 capabilities.
49 50
51config ND_CLAIM
52 bool
53
50config ND_BTT 54config ND_BTT
51 tristate 55 tristate
52 56
53config BTT 57config BTT
54 bool "BTT: Block Translation Table (atomic sector updates)" 58 bool "BTT: Block Translation Table (atomic sector updates)"
55 default y if LIBNVDIMM 59 default y if LIBNVDIMM
60 select ND_CLAIM
56 help 61 help
57 The Block Translation Table (BTT) provides atomic sector 62 The Block Translation Table (BTT) provides atomic sector
58 update semantics for persistent memory devices, so that 63 update semantics for persistent memory devices, so that
@@ -65,4 +70,21 @@ config BTT
65 70
66 Select Y if unsure 71 Select Y if unsure
67 72
73config ND_PFN
74 tristate
75
76config NVDIMM_PFN
77 bool "PFN: Map persistent (device) memory"
78 default LIBNVDIMM
79 select ND_CLAIM
80 help
81 Map persistent memory, i.e. advertise it to the memory
82 management sub-system. By default persistent memory does
83 not support direct I/O, RDMA, or any other usage that
84 requires a 'struct page' to mediate an I/O request. This
85 driver allocates and initializes the infrastructure needed
86 to support those use cases.
87
88 Select Y if unsure
89
68endif 90endif
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
index 9bf15db52dee..ea84d3c4e8e5 100644
--- a/drivers/nvdimm/Makefile
+++ b/drivers/nvdimm/Makefile
@@ -20,4 +20,6 @@ libnvdimm-y += region_devs.o
20libnvdimm-y += region.o 20libnvdimm-y += region.o
21libnvdimm-y += namespace_devs.o 21libnvdimm-y += namespace_devs.o
22libnvdimm-y += label.o 22libnvdimm-y += label.o
23libnvdimm-$(CONFIG_ND_CLAIM) += claim.o
23libnvdimm-$(CONFIG_BTT) += btt_devs.o 24libnvdimm-$(CONFIG_BTT) += btt_devs.o
25libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 19588291550b..028d2d137bc5 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -731,6 +731,7 @@ static int create_arenas(struct btt *btt)
731static int btt_arena_write_layout(struct arena_info *arena) 731static int btt_arena_write_layout(struct arena_info *arena)
732{ 732{
733 int ret; 733 int ret;
734 u64 sum;
734 struct btt_sb *super; 735 struct btt_sb *super;
735 struct nd_btt *nd_btt = arena->nd_btt; 736 struct nd_btt *nd_btt = arena->nd_btt;
736 const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev); 737 const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);
@@ -770,7 +771,8 @@ static int btt_arena_write_layout(struct arena_info *arena)
770 super->info2off = cpu_to_le64(arena->info2off - arena->infooff); 771 super->info2off = cpu_to_le64(arena->info2off - arena->infooff);
771 772
772 super->flags = 0; 773 super->flags = 0;
773 super->checksum = cpu_to_le64(nd_btt_sb_checksum(super)); 774 sum = nd_sb_checksum((struct nd_gen_sb *) super);
775 super->checksum = cpu_to_le64(sum);
774 776
775 ret = btt_info_write(arena, super); 777 ret = btt_info_write(arena, super);
776 778
@@ -1422,8 +1424,6 @@ static int __init nd_btt_init(void)
1422{ 1424{
1423 int rc; 1425 int rc;
1424 1426
1425 BUILD_BUG_ON(sizeof(struct btt_sb) != SZ_4K);
1426
1427 btt_major = register_blkdev(0, "btt"); 1427 btt_major = register_blkdev(0, "btt");
1428 if (btt_major < 0) 1428 if (btt_major < 0)
1429 return btt_major; 1429 return btt_major;
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index 242ae1c550ad..59ad54a63d9f 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -21,63 +21,13 @@
21#include "btt.h" 21#include "btt.h"
22#include "nd.h" 22#include "nd.h"
23 23
24static void __nd_btt_detach_ndns(struct nd_btt *nd_btt)
25{
26 struct nd_namespace_common *ndns = nd_btt->ndns;
27
28 dev_WARN_ONCE(&nd_btt->dev, !mutex_is_locked(&ndns->dev.mutex)
29 || ndns->claim != &nd_btt->dev,
30 "%s: invalid claim\n", __func__);
31 ndns->claim = NULL;
32 nd_btt->ndns = NULL;
33 put_device(&ndns->dev);
34}
35
36static void nd_btt_detach_ndns(struct nd_btt *nd_btt)
37{
38 struct nd_namespace_common *ndns = nd_btt->ndns;
39
40 if (!ndns)
41 return;
42 get_device(&ndns->dev);
43 device_lock(&ndns->dev);
44 __nd_btt_detach_ndns(nd_btt);
45 device_unlock(&ndns->dev);
46 put_device(&ndns->dev);
47}
48
49static bool __nd_btt_attach_ndns(struct nd_btt *nd_btt,
50 struct nd_namespace_common *ndns)
51{
52 if (ndns->claim)
53 return false;
54 dev_WARN_ONCE(&nd_btt->dev, !mutex_is_locked(&ndns->dev.mutex)
55 || nd_btt->ndns,
56 "%s: invalid claim\n", __func__);
57 ndns->claim = &nd_btt->dev;
58 nd_btt->ndns = ndns;
59 get_device(&ndns->dev);
60 return true;
61}
62
63static bool nd_btt_attach_ndns(struct nd_btt *nd_btt,
64 struct nd_namespace_common *ndns)
65{
66 bool claimed;
67
68 device_lock(&ndns->dev);
69 claimed = __nd_btt_attach_ndns(nd_btt, ndns);
70 device_unlock(&ndns->dev);
71 return claimed;
72}
73
74static void nd_btt_release(struct device *dev) 24static void nd_btt_release(struct device *dev)
75{ 25{
76 struct nd_region *nd_region = to_nd_region(dev->parent); 26 struct nd_region *nd_region = to_nd_region(dev->parent);
77 struct nd_btt *nd_btt = to_nd_btt(dev); 27 struct nd_btt *nd_btt = to_nd_btt(dev);
78 28
79 dev_dbg(dev, "%s\n", __func__); 29 dev_dbg(dev, "%s\n", __func__);
80 nd_btt_detach_ndns(nd_btt); 30 nd_detach_ndns(&nd_btt->dev, &nd_btt->ndns);
81 ida_simple_remove(&nd_region->btt_ida, nd_btt->id); 31 ida_simple_remove(&nd_region->btt_ida, nd_btt->id);
82 kfree(nd_btt->uuid); 32 kfree(nd_btt->uuid);
83 kfree(nd_btt); 33 kfree(nd_btt);
@@ -172,104 +122,15 @@ static ssize_t namespace_show(struct device *dev,
172 return rc; 122 return rc;
173} 123}
174 124
175static int namespace_match(struct device *dev, void *data)
176{
177 char *name = data;
178
179 return strcmp(name, dev_name(dev)) == 0;
180}
181
182static bool is_nd_btt_idle(struct device *dev)
183{
184 struct nd_region *nd_region = to_nd_region(dev->parent);
185 struct nd_btt *nd_btt = to_nd_btt(dev);
186
187 if (nd_region->btt_seed == dev || nd_btt->ndns || dev->driver)
188 return false;
189 return true;
190}
191
192static ssize_t __namespace_store(struct device *dev,
193 struct device_attribute *attr, const char *buf, size_t len)
194{
195 struct nd_btt *nd_btt = to_nd_btt(dev);
196 struct nd_namespace_common *ndns;
197 struct device *found;
198 char *name;
199
200 if (dev->driver) {
201 dev_dbg(dev, "%s: -EBUSY\n", __func__);
202 return -EBUSY;
203 }
204
205 name = kstrndup(buf, len, GFP_KERNEL);
206 if (!name)
207 return -ENOMEM;
208 strim(name);
209
210 if (strncmp(name, "namespace", 9) == 0 || strcmp(name, "") == 0)
211 /* pass */;
212 else {
213 len = -EINVAL;
214 goto out;
215 }
216
217 ndns = nd_btt->ndns;
218 if (strcmp(name, "") == 0) {
219 /* detach the namespace and destroy / reset the btt device */
220 nd_btt_detach_ndns(nd_btt);
221 if (is_nd_btt_idle(dev))
222 nd_device_unregister(dev, ND_ASYNC);
223 else {
224 nd_btt->lbasize = 0;
225 kfree(nd_btt->uuid);
226 nd_btt->uuid = NULL;
227 }
228 goto out;
229 } else if (ndns) {
230 dev_dbg(dev, "namespace already set to: %s\n",
231 dev_name(&ndns->dev));
232 len = -EBUSY;
233 goto out;
234 }
235
236 found = device_find_child(dev->parent, name, namespace_match);
237 if (!found) {
238 dev_dbg(dev, "'%s' not found under %s\n", name,
239 dev_name(dev->parent));
240 len = -ENODEV;
241 goto out;
242 }
243
244 ndns = to_ndns(found);
245 if (__nvdimm_namespace_capacity(ndns) < SZ_16M) {
246 dev_dbg(dev, "%s too small to host btt\n", name);
247 len = -ENXIO;
248 goto out_attach;
249 }
250
251 WARN_ON_ONCE(!is_nvdimm_bus_locked(&nd_btt->dev));
252 if (!nd_btt_attach_ndns(nd_btt, ndns)) {
253 dev_dbg(dev, "%s already claimed\n",
254 dev_name(&ndns->dev));
255 len = -EBUSY;
256 }
257
258 out_attach:
259 put_device(&ndns->dev); /* from device_find_child */
260 out:
261 kfree(name);
262 return len;
263}
264
265static ssize_t namespace_store(struct device *dev, 125static ssize_t namespace_store(struct device *dev,
266 struct device_attribute *attr, const char *buf, size_t len) 126 struct device_attribute *attr, const char *buf, size_t len)
267{ 127{
128 struct nd_btt *nd_btt = to_nd_btt(dev);
268 ssize_t rc; 129 ssize_t rc;
269 130
270 nvdimm_bus_lock(dev); 131 nvdimm_bus_lock(dev);
271 device_lock(dev); 132 device_lock(dev);
272 rc = __namespace_store(dev, attr, buf, len); 133 rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len);
273 dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, 134 dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
274 rc, buf, buf[len - 1] == '\n' ? "" : "\n"); 135 rc, buf, buf[len - 1] == '\n' ? "" : "\n");
275 device_unlock(dev); 136 device_unlock(dev);
@@ -324,7 +185,7 @@ static struct device *__nd_btt_create(struct nd_region *nd_region,
324 dev->type = &nd_btt_device_type; 185 dev->type = &nd_btt_device_type;
325 dev->groups = nd_btt_attribute_groups; 186 dev->groups = nd_btt_attribute_groups;
326 device_initialize(&nd_btt->dev); 187 device_initialize(&nd_btt->dev);
327 if (ndns && !__nd_btt_attach_ndns(nd_btt, ndns)) { 188 if (ndns && !__nd_attach_ndns(&nd_btt->dev, ndns, &nd_btt->ndns)) {
328 dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n", 189 dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n",
329 __func__, dev_name(ndns->claim)); 190 __func__, dev_name(ndns->claim));
330 put_device(dev); 191 put_device(dev);
@@ -375,7 +236,7 @@ bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)
375 236
376 checksum = le64_to_cpu(super->checksum); 237 checksum = le64_to_cpu(super->checksum);
377 super->checksum = 0; 238 super->checksum = 0;
378 if (checksum != nd_btt_sb_checksum(super)) 239 if (checksum != nd_sb_checksum((struct nd_gen_sb *) super))
379 return false; 240 return false;
380 super->checksum = cpu_to_le64(checksum); 241 super->checksum = cpu_to_le64(checksum);
381 242
@@ -387,25 +248,6 @@ bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)
387} 248}
388EXPORT_SYMBOL(nd_btt_arena_is_valid); 249EXPORT_SYMBOL(nd_btt_arena_is_valid);
389 250
390/*
391 * nd_btt_sb_checksum: compute checksum for btt info block
392 *
393 * Returns a fletcher64 checksum of everything in the given info block
394 * except the last field (since that's where the checksum lives).
395 */
396u64 nd_btt_sb_checksum(struct btt_sb *btt_sb)
397{
398 u64 sum;
399 __le64 sum_save;
400
401 sum_save = btt_sb->checksum;
402 btt_sb->checksum = 0;
403 sum = nd_fletcher64(btt_sb, sizeof(*btt_sb), 1);
404 btt_sb->checksum = sum_save;
405 return sum;
406}
407EXPORT_SYMBOL(nd_btt_sb_checksum);
408
409static int __nd_btt_probe(struct nd_btt *nd_btt, 251static int __nd_btt_probe(struct nd_btt *nd_btt,
410 struct nd_namespace_common *ndns, struct btt_sb *btt_sb) 252 struct nd_namespace_common *ndns, struct btt_sb *btt_sb)
411{ 253{
@@ -453,7 +295,9 @@ int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata)
453 dev_dbg(&ndns->dev, "%s: btt: %s\n", __func__, 295 dev_dbg(&ndns->dev, "%s: btt: %s\n", __func__,
454 rc == 0 ? dev_name(dev) : "<none>"); 296 rc == 0 ? dev_name(dev) : "<none>");
455 if (rc < 0) { 297 if (rc < 0) {
456 __nd_btt_detach_ndns(to_nd_btt(dev)); 298 struct nd_btt *nd_btt = to_nd_btt(dev);
299
300 __nd_detach_ndns(dev, &nd_btt->ndns);
457 put_device(dev); 301 put_device(dev);
458 } 302 }
459 303
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c
new file mode 100644
index 000000000000..e8f03b0e95e4
--- /dev/null
+++ b/drivers/nvdimm/claim.c
@@ -0,0 +1,201 @@
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/device.h>
14#include <linux/sizes.h>
15#include "nd-core.h"
16#include "pfn.h"
17#include "btt.h"
18#include "nd.h"
19
20void __nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns)
21{
22 struct nd_namespace_common *ndns = *_ndns;
23
24 dev_WARN_ONCE(dev, !mutex_is_locked(&ndns->dev.mutex)
25 || ndns->claim != dev,
26 "%s: invalid claim\n", __func__);
27 ndns->claim = NULL;
28 *_ndns = NULL;
29 put_device(&ndns->dev);
30}
31
32void nd_detach_ndns(struct device *dev,
33 struct nd_namespace_common **_ndns)
34{
35 struct nd_namespace_common *ndns = *_ndns;
36
37 if (!ndns)
38 return;
39 get_device(&ndns->dev);
40 device_lock(&ndns->dev);
41 __nd_detach_ndns(dev, _ndns);
42 device_unlock(&ndns->dev);
43 put_device(&ndns->dev);
44}
45
46bool __nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
47 struct nd_namespace_common **_ndns)
48{
49 if (attach->claim)
50 return false;
51 dev_WARN_ONCE(dev, !mutex_is_locked(&attach->dev.mutex)
52 || *_ndns,
53 "%s: invalid claim\n", __func__);
54 attach->claim = dev;
55 *_ndns = attach;
56 get_device(&attach->dev);
57 return true;
58}
59
60bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
61 struct nd_namespace_common **_ndns)
62{
63 bool claimed;
64
65 device_lock(&attach->dev);
66 claimed = __nd_attach_ndns(dev, attach, _ndns);
67 device_unlock(&attach->dev);
68 return claimed;
69}
70
71static int namespace_match(struct device *dev, void *data)
72{
73 char *name = data;
74
75 return strcmp(name, dev_name(dev)) == 0;
76}
77
78static bool is_idle(struct device *dev, struct nd_namespace_common *ndns)
79{
80 struct nd_region *nd_region = to_nd_region(dev->parent);
81 struct device *seed = NULL;
82
83 if (is_nd_btt(dev))
84 seed = nd_region->btt_seed;
85 else if (is_nd_pfn(dev))
86 seed = nd_region->pfn_seed;
87
88 if (seed == dev || ndns || dev->driver)
89 return false;
90 return true;
91}
92
93static void nd_detach_and_reset(struct device *dev,
94 struct nd_namespace_common **_ndns)
95{
96 /* detach the namespace and destroy / reset the device */
97 nd_detach_ndns(dev, _ndns);
98 if (is_idle(dev, *_ndns)) {
99 nd_device_unregister(dev, ND_ASYNC);
100 } else if (is_nd_btt(dev)) {
101 struct nd_btt *nd_btt = to_nd_btt(dev);
102
103 nd_btt->lbasize = 0;
104 kfree(nd_btt->uuid);
105 nd_btt->uuid = NULL;
106 } else if (is_nd_pfn(dev)) {
107 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
108
109 kfree(nd_pfn->uuid);
110 nd_pfn->uuid = NULL;
111 nd_pfn->mode = PFN_MODE_NONE;
112 }
113}
114
115ssize_t nd_namespace_store(struct device *dev,
116 struct nd_namespace_common **_ndns, const char *buf,
117 size_t len)
118{
119 struct nd_namespace_common *ndns;
120 struct device *found;
121 char *name;
122
123 if (dev->driver) {
124 dev_dbg(dev, "%s: -EBUSY\n", __func__);
125 return -EBUSY;
126 }
127
128 name = kstrndup(buf, len, GFP_KERNEL);
129 if (!name)
130 return -ENOMEM;
131 strim(name);
132
133 if (strncmp(name, "namespace", 9) == 0 || strcmp(name, "") == 0)
134 /* pass */;
135 else {
136 len = -EINVAL;
137 goto out;
138 }
139
140 ndns = *_ndns;
141 if (strcmp(name, "") == 0) {
142 nd_detach_and_reset(dev, _ndns);
143 goto out;
144 } else if (ndns) {
145 dev_dbg(dev, "namespace already set to: %s\n",
146 dev_name(&ndns->dev));
147 len = -EBUSY;
148 goto out;
149 }
150
151 found = device_find_child(dev->parent, name, namespace_match);
152 if (!found) {
153 dev_dbg(dev, "'%s' not found under %s\n", name,
154 dev_name(dev->parent));
155 len = -ENODEV;
156 goto out;
157 }
158
159 ndns = to_ndns(found);
160 if (__nvdimm_namespace_capacity(ndns) < SZ_16M) {
161 dev_dbg(dev, "%s too small to host\n", name);
162 len = -ENXIO;
163 goto out_attach;
164 }
165
166 WARN_ON_ONCE(!is_nvdimm_bus_locked(dev));
167 if (!nd_attach_ndns(dev, ndns, _ndns)) {
168 dev_dbg(dev, "%s already claimed\n",
169 dev_name(&ndns->dev));
170 len = -EBUSY;
171 }
172
173 out_attach:
174 put_device(&ndns->dev); /* from device_find_child */
175 out:
176 kfree(name);
177 return len;
178}
179
180/*
181 * nd_sb_checksum: compute checksum for a generic info block
182 *
183 * Returns a fletcher64 checksum of everything in the given info block
184 * except the last field (since that's where the checksum lives).
185 */
186u64 nd_sb_checksum(struct nd_gen_sb *nd_gen_sb)
187{
188 u64 sum;
189 __le64 sum_save;
190
191 BUILD_BUG_ON(sizeof(struct btt_sb) != SZ_4K);
192 BUILD_BUG_ON(sizeof(struct nd_pfn_sb) != SZ_4K);
193 BUILD_BUG_ON(sizeof(struct nd_gen_sb) != SZ_4K);
194
195 sum_save = nd_gen_sb->checksum;
196 nd_gen_sb->checksum = 0;
197 sum = nd_fletcher64(nd_gen_sb, sizeof(*nd_gen_sb), 1);
198 nd_gen_sb->checksum = sum_save;
199 return sum;
200}
201EXPORT_SYMBOL(nd_sb_checksum);
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index b18ffea9d85b..9303ca29be9b 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -82,8 +82,16 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns,
82 struct nd_region *nd_region = to_nd_region(ndns->dev.parent); 82 struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
83 const char *suffix = ""; 83 const char *suffix = "";
84 84
85 if (ndns->claim && is_nd_btt(ndns->claim)) 85 if (ndns->claim) {
86 suffix = "s"; 86 if (is_nd_btt(ndns->claim))
87 suffix = "s";
88 else if (is_nd_pfn(ndns->claim))
89 suffix = "m";
90 else
91 dev_WARN_ONCE(&ndns->dev, 1,
92 "unknown claim type by %s\n",
93 dev_name(ndns->claim));
94 }
87 95
88 if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) 96 if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev))
89 sprintf(name, "pmem%d%s", nd_region->id, suffix); 97 sprintf(name, "pmem%d%s", nd_region->id, suffix);
@@ -1255,12 +1263,22 @@ static const struct attribute_group *nd_namespace_attribute_groups[] = {
1255struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) 1263struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
1256{ 1264{
1257 struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; 1265 struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL;
1266 struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL;
1258 struct nd_namespace_common *ndns; 1267 struct nd_namespace_common *ndns;
1259 resource_size_t size; 1268 resource_size_t size;
1260 1269
1261 if (nd_btt) { 1270 if (nd_btt || nd_pfn) {
1262 ndns = nd_btt->ndns; 1271 struct device *host = NULL;
1263 if (!ndns) 1272
1273 if (nd_btt) {
1274 host = &nd_btt->dev;
1275 ndns = nd_btt->ndns;
1276 } else if (nd_pfn) {
1277 host = &nd_pfn->dev;
1278 ndns = nd_pfn->ndns;
1279 }
1280
1281 if (!ndns || !host)
1264 return ERR_PTR(-ENODEV); 1282 return ERR_PTR(-ENODEV);
1265 1283
1266 /* 1284 /*
@@ -1271,12 +1289,12 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
1271 device_unlock(&ndns->dev); 1289 device_unlock(&ndns->dev);
1272 if (ndns->dev.driver) { 1290 if (ndns->dev.driver) {
1273 dev_dbg(&ndns->dev, "is active, can't bind %s\n", 1291 dev_dbg(&ndns->dev, "is active, can't bind %s\n",
1274 dev_name(&nd_btt->dev)); 1292 dev_name(host));
1275 return ERR_PTR(-EBUSY); 1293 return ERR_PTR(-EBUSY);
1276 } 1294 }
1277 if (dev_WARN_ONCE(&ndns->dev, ndns->claim != &nd_btt->dev, 1295 if (dev_WARN_ONCE(&ndns->dev, ndns->claim != host,
1278 "host (%s) vs claim (%s) mismatch\n", 1296 "host (%s) vs claim (%s) mismatch\n",
1279 dev_name(&nd_btt->dev), 1297 dev_name(host),
1280 dev_name(ndns->claim))) 1298 dev_name(ndns->claim)))
1281 return ERR_PTR(-ENXIO); 1299 return ERR_PTR(-ENXIO);
1282 } else { 1300 } else {
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index e1970c71ad1c..159aed532042 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -80,4 +80,13 @@ struct resource *nsblk_add_resource(struct nd_region *nd_region,
80int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd); 80int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd);
81void get_ndd(struct nvdimm_drvdata *ndd); 81void get_ndd(struct nvdimm_drvdata *ndd);
82resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns); 82resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns);
83void nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns);
84void __nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns);
85bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
86 struct nd_namespace_common **_ndns);
87bool __nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
88 struct nd_namespace_common **_ndns);
89ssize_t nd_namespace_store(struct device *dev,
90 struct nd_namespace_common **_ndns, const char *buf,
91 size_t len);
83#endif /* __ND_CORE_H__ */ 92#endif /* __ND_CORE_H__ */
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index f9615824947b..8da2be1cecb8 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -29,6 +29,13 @@ enum {
29 ND_MAX_LANES = 256, 29 ND_MAX_LANES = 256,
30 SECTOR_SHIFT = 9, 30 SECTOR_SHIFT = 9,
31 INT_LBASIZE_ALIGNMENT = 64, 31 INT_LBASIZE_ALIGNMENT = 64,
32#if IS_ENABLED(CONFIG_NVDIMM_PFN)
33 ND_PFN_ALIGN = PAGES_PER_SECTION * PAGE_SIZE,
34 ND_PFN_MASK = ND_PFN_ALIGN - 1,
35#else
36 ND_PFN_ALIGN = 0,
37 ND_PFN_MASK = 0,
38#endif
32}; 39};
33 40
34struct nvdimm_drvdata { 41struct nvdimm_drvdata {
@@ -92,8 +99,10 @@ struct nd_region {
92 struct device dev; 99 struct device dev;
93 struct ida ns_ida; 100 struct ida ns_ida;
94 struct ida btt_ida; 101 struct ida btt_ida;
102 struct ida pfn_ida;
95 struct device *ns_seed; 103 struct device *ns_seed;
96 struct device *btt_seed; 104 struct device *btt_seed;
105 struct device *pfn_seed;
97 u16 ndr_mappings; 106 u16 ndr_mappings;
98 u64 ndr_size; 107 u64 ndr_size;
99 u64 ndr_start; 108 u64 ndr_start;
@@ -133,6 +142,22 @@ struct nd_btt {
133 int id; 142 int id;
134}; 143};
135 144
145enum nd_pfn_mode {
146 PFN_MODE_NONE,
147 PFN_MODE_RAM,
148 PFN_MODE_PMEM,
149};
150
151struct nd_pfn {
152 int id;
153 u8 *uuid;
154 struct device dev;
155 unsigned long npfns;
156 enum nd_pfn_mode mode;
157 struct nd_pfn_sb *pfn_sb;
158 struct nd_namespace_common *ndns;
159};
160
136enum nd_async_mode { 161enum nd_async_mode {
137 ND_SYNC, 162 ND_SYNC,
138 ND_ASYNC, 163 ND_ASYNC,
@@ -159,8 +184,13 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd);
159int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, 184int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
160 void *buf, size_t len); 185 void *buf, size_t len);
161struct nd_btt *to_nd_btt(struct device *dev); 186struct nd_btt *to_nd_btt(struct device *dev);
162struct btt_sb; 187
163u64 nd_btt_sb_checksum(struct btt_sb *btt_sb); 188struct nd_gen_sb {
189 char reserved[SZ_4K - 8];
190 __le64 checksum;
191};
192
193u64 nd_sb_checksum(struct nd_gen_sb *sb);
164#if IS_ENABLED(CONFIG_BTT) 194#if IS_ENABLED(CONFIG_BTT)
165int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata); 195int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata);
166bool is_nd_btt(struct device *dev); 196bool is_nd_btt(struct device *dev);
@@ -180,8 +210,30 @@ static inline struct device *nd_btt_create(struct nd_region *nd_region)
180{ 210{
181 return NULL; 211 return NULL;
182} 212}
213#endif
183 214
215struct nd_pfn *to_nd_pfn(struct device *dev);
216#if IS_ENABLED(CONFIG_NVDIMM_PFN)
217int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata);
218bool is_nd_pfn(struct device *dev);
219struct device *nd_pfn_create(struct nd_region *nd_region);
220#else
221static inline int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata)
222{
223 return -ENODEV;
224}
225
226static inline bool is_nd_pfn(struct device *dev)
227{
228 return false;
229}
230
231static inline struct device *nd_pfn_create(struct nd_region *nd_region)
232{
233 return NULL;
234}
184#endif 235#endif
236
185struct nd_region *to_nd_region(struct device *dev); 237struct nd_region *to_nd_region(struct device *dev);
186int nd_region_to_nstype(struct nd_region *nd_region); 238int nd_region_to_nstype(struct nd_region *nd_region);
187int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 239int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
diff --git a/drivers/nvdimm/pfn.h b/drivers/nvdimm/pfn.h
new file mode 100644
index 000000000000..cc243754acef
--- /dev/null
+++ b/drivers/nvdimm/pfn.h
@@ -0,0 +1,35 @@
1/*
2 * Copyright (c) 2014-2015, Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#ifndef __NVDIMM_PFN_H
15#define __NVDIMM_PFN_H
16
17#include <linux/types.h>
18
19#define PFN_SIG_LEN 16
20#define PFN_SIG "NVDIMM_PFN_INFO\0"
21
22struct nd_pfn_sb {
23 u8 signature[PFN_SIG_LEN];
24 u8 uuid[16];
25 u8 parent_uuid[16];
26 __le32 flags;
27 __le16 version_major;
28 __le16 version_minor;
29 __le64 dataoff;
30 __le64 npfns;
31 __le32 mode;
32 u8 padding[4012];
33 __le64 checksum;
34};
35#endif /* __NVDIMM_PFN_H */
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
new file mode 100644
index 000000000000..f708d63709a5
--- /dev/null
+++ b/drivers/nvdimm/pfn_devs.c
@@ -0,0 +1,336 @@
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/blkdev.h>
14#include <linux/device.h>
15#include <linux/genhd.h>
16#include <linux/sizes.h>
17#include <linux/slab.h>
18#include <linux/fs.h>
19#include <linux/mm.h>
20#include "nd-core.h"
21#include "pfn.h"
22#include "nd.h"
23
24static void nd_pfn_release(struct device *dev)
25{
26 struct nd_region *nd_region = to_nd_region(dev->parent);
27 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
28
29 dev_dbg(dev, "%s\n", __func__);
30 nd_detach_ndns(&nd_pfn->dev, &nd_pfn->ndns);
31 ida_simple_remove(&nd_region->pfn_ida, nd_pfn->id);
32 kfree(nd_pfn->uuid);
33 kfree(nd_pfn);
34}
35
36static struct device_type nd_pfn_device_type = {
37 .name = "nd_pfn",
38 .release = nd_pfn_release,
39};
40
41bool is_nd_pfn(struct device *dev)
42{
43 return dev ? dev->type == &nd_pfn_device_type : false;
44}
45EXPORT_SYMBOL(is_nd_pfn);
46
47struct nd_pfn *to_nd_pfn(struct device *dev)
48{
49 struct nd_pfn *nd_pfn = container_of(dev, struct nd_pfn, dev);
50
51 WARN_ON(!is_nd_pfn(dev));
52 return nd_pfn;
53}
54EXPORT_SYMBOL(to_nd_pfn);
55
56static ssize_t mode_show(struct device *dev,
57 struct device_attribute *attr, char *buf)
58{
59 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
60
61 switch (nd_pfn->mode) {
62 case PFN_MODE_RAM:
63 return sprintf(buf, "ram\n");
64 case PFN_MODE_PMEM:
65 return sprintf(buf, "pmem\n");
66 default:
67 return sprintf(buf, "none\n");
68 }
69}
70
71static ssize_t mode_store(struct device *dev,
72 struct device_attribute *attr, const char *buf, size_t len)
73{
74 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
75 ssize_t rc = 0;
76
77 device_lock(dev);
78 nvdimm_bus_lock(dev);
79 if (dev->driver)
80 rc = -EBUSY;
81 else {
82 size_t n = len - 1;
83
84 if (strncmp(buf, "pmem\n", n) == 0
85 || strncmp(buf, "pmem", n) == 0) {
86 /* TODO: allocate from PMEM support */
87 rc = -ENOTTY;
88 } else if (strncmp(buf, "ram\n", n) == 0
89 || strncmp(buf, "ram", n) == 0)
90 nd_pfn->mode = PFN_MODE_RAM;
91 else if (strncmp(buf, "none\n", n) == 0
92 || strncmp(buf, "none", n) == 0)
93 nd_pfn->mode = PFN_MODE_NONE;
94 else
95 rc = -EINVAL;
96 }
97 dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
98 rc, buf, buf[len - 1] == '\n' ? "" : "\n");
99 nvdimm_bus_unlock(dev);
100 device_unlock(dev);
101
102 return rc ? rc : len;
103}
104static DEVICE_ATTR_RW(mode);
105
106static ssize_t uuid_show(struct device *dev,
107 struct device_attribute *attr, char *buf)
108{
109 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
110
111 if (nd_pfn->uuid)
112 return sprintf(buf, "%pUb\n", nd_pfn->uuid);
113 return sprintf(buf, "\n");
114}
115
116static ssize_t uuid_store(struct device *dev,
117 struct device_attribute *attr, const char *buf, size_t len)
118{
119 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
120 ssize_t rc;
121
122 device_lock(dev);
123 rc = nd_uuid_store(dev, &nd_pfn->uuid, buf, len);
124 dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
125 rc, buf, buf[len - 1] == '\n' ? "" : "\n");
126 device_unlock(dev);
127
128 return rc ? rc : len;
129}
130static DEVICE_ATTR_RW(uuid);
131
132static ssize_t namespace_show(struct device *dev,
133 struct device_attribute *attr, char *buf)
134{
135 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
136 ssize_t rc;
137
138 nvdimm_bus_lock(dev);
139 rc = sprintf(buf, "%s\n", nd_pfn->ndns
140 ? dev_name(&nd_pfn->ndns->dev) : "");
141 nvdimm_bus_unlock(dev);
142 return rc;
143}
144
145static ssize_t namespace_store(struct device *dev,
146 struct device_attribute *attr, const char *buf, size_t len)
147{
148 struct nd_pfn *nd_pfn = to_nd_pfn(dev);
149 ssize_t rc;
150
151 nvdimm_bus_lock(dev);
152 device_lock(dev);
153 rc = nd_namespace_store(dev, &nd_pfn->ndns, buf, len);
154 dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
155 rc, buf, buf[len - 1] == '\n' ? "" : "\n");
156 device_unlock(dev);
157 nvdimm_bus_unlock(dev);
158
159 return rc;
160}
161static DEVICE_ATTR_RW(namespace);
162
163static struct attribute *nd_pfn_attributes[] = {
164 &dev_attr_mode.attr,
165 &dev_attr_namespace.attr,
166 &dev_attr_uuid.attr,
167 NULL,
168};
169
170static struct attribute_group nd_pfn_attribute_group = {
171 .attrs = nd_pfn_attributes,
172};
173
174static const struct attribute_group *nd_pfn_attribute_groups[] = {
175 &nd_pfn_attribute_group,
176 &nd_device_attribute_group,
177 &nd_numa_attribute_group,
178 NULL,
179};
180
181static struct device *__nd_pfn_create(struct nd_region *nd_region,
182 u8 *uuid, enum nd_pfn_mode mode,
183 struct nd_namespace_common *ndns)
184{
185 struct nd_pfn *nd_pfn;
186 struct device *dev;
187
188 /* we can only create pages for contiguous ranged of pmem */
189 if (!is_nd_pmem(&nd_region->dev))
190 return NULL;
191
192 nd_pfn = kzalloc(sizeof(*nd_pfn), GFP_KERNEL);
193 if (!nd_pfn)
194 return NULL;
195
196 nd_pfn->id = ida_simple_get(&nd_region->pfn_ida, 0, 0, GFP_KERNEL);
197 if (nd_pfn->id < 0) {
198 kfree(nd_pfn);
199 return NULL;
200 }
201
202 nd_pfn->mode = mode;
203 if (uuid)
204 uuid = kmemdup(uuid, 16, GFP_KERNEL);
205 nd_pfn->uuid = uuid;
206 dev = &nd_pfn->dev;
207 dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id);
208 dev->parent = &nd_region->dev;
209 dev->type = &nd_pfn_device_type;
210 dev->groups = nd_pfn_attribute_groups;
211 device_initialize(&nd_pfn->dev);
212 if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) {
213 dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n",
214 __func__, dev_name(ndns->claim));
215 put_device(dev);
216 return NULL;
217 }
218 return dev;
219}
220
221struct device *nd_pfn_create(struct nd_region *nd_region)
222{
223 struct device *dev = __nd_pfn_create(nd_region, NULL, PFN_MODE_NONE,
224 NULL);
225
226 if (dev)
227 __nd_device_register(dev);
228 return dev;
229}
230
231static int nd_pfn_validate(struct nd_pfn *nd_pfn)
232{
233 struct nd_namespace_common *ndns = nd_pfn->ndns;
234 struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
235 struct nd_namespace_io *nsio;
236 u64 checksum, offset;
237
238 if (!pfn_sb || !ndns)
239 return -ENODEV;
240
241 if (!is_nd_pmem(nd_pfn->dev.parent))
242 return -ENODEV;
243
244 /* section alignment for simple hotplug */
245 if (nvdimm_namespace_capacity(ndns) < ND_PFN_ALIGN)
246 return -ENODEV;
247
248 if (nvdimm_read_bytes(ndns, SZ_4K, pfn_sb, sizeof(*pfn_sb)))
249 return -ENXIO;
250
251 if (memcmp(pfn_sb->signature, PFN_SIG, PFN_SIG_LEN) != 0)
252 return -ENODEV;
253
254 checksum = le64_to_cpu(pfn_sb->checksum);
255 pfn_sb->checksum = 0;
256 if (checksum != nd_sb_checksum((struct nd_gen_sb *) pfn_sb))
257 return -ENODEV;
258 pfn_sb->checksum = cpu_to_le64(checksum);
259
260 switch (le32_to_cpu(pfn_sb->mode)) {
261 case PFN_MODE_RAM:
262 break;
263 case PFN_MODE_PMEM:
264 /* TODO: allocate from PMEM support */
265 return -ENOTTY;
266 default:
267 return -ENXIO;
268 }
269
270 if (!nd_pfn->uuid) {
271 /* from probe we allocate */
272 nd_pfn->uuid = kmemdup(pfn_sb->uuid, 16, GFP_KERNEL);
273 if (!nd_pfn->uuid)
274 return -ENOMEM;
275 } else {
276 /* from init we validate */
277 if (memcmp(nd_pfn->uuid, pfn_sb->uuid, 16) != 0)
278 return -EINVAL;
279 }
280
281 /*
282 * These warnings are verbose because they can only trigger in
283 * the case where the physical address alignment of the
284 * namespace has changed since the pfn superblock was
285 * established.
286 */
287 offset = le64_to_cpu(pfn_sb->dataoff);
288 nsio = to_nd_namespace_io(&ndns->dev);
289 if ((nsio->res.start + offset) & (ND_PFN_ALIGN - 1)) {
290 dev_err(&nd_pfn->dev,
291 "init failed: %s with offset %#llx not section aligned\n",
292 dev_name(&ndns->dev), offset);
293 return -EBUSY;
294 } else if (offset >= resource_size(&nsio->res)) {
295 dev_err(&nd_pfn->dev, "pfn array size exceeds capacity of %s\n",
296 dev_name(&ndns->dev));
297 return -EBUSY;
298 }
299
300 return 0;
301}
302
303int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata)
304{
305 int rc;
306 struct device *dev;
307 struct nd_pfn *nd_pfn;
308 struct nd_pfn_sb *pfn_sb;
309 struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
310
311 if (ndns->force_raw)
312 return -ENODEV;
313
314 nvdimm_bus_lock(&ndns->dev);
315 dev = __nd_pfn_create(nd_region, NULL, PFN_MODE_NONE, ndns);
316 nvdimm_bus_unlock(&ndns->dev);
317 if (!dev)
318 return -ENOMEM;
319 dev_set_drvdata(dev, drvdata);
320 pfn_sb = kzalloc(sizeof(*pfn_sb), GFP_KERNEL);
321 nd_pfn = to_nd_pfn(dev);
322 nd_pfn->pfn_sb = pfn_sb;
323 rc = nd_pfn_validate(nd_pfn);
324 nd_pfn->pfn_sb = NULL;
325 kfree(pfn_sb);
326 dev_dbg(&ndns->dev, "%s: pfn: %s\n", __func__,
327 rc == 0 ? dev_name(dev) : "<none>");
328 if (rc < 0) {
329 __nd_detach_ndns(dev, &nd_pfn->ndns);
330 put_device(dev);
331 } else
332 __nd_device_register(&nd_pfn->dev);
333
334 return rc;
335}
336EXPORT_SYMBOL(nd_pfn_probe);
diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c
index f28f78ccff19..7da63eac78ee 100644
--- a/drivers/nvdimm/region.c
+++ b/drivers/nvdimm/region.c
@@ -53,6 +53,7 @@ static int nd_region_probe(struct device *dev)
53 return -ENODEV; 53 return -ENODEV;
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 if (err == 0) 57 if (err == 0)
57 return 0; 58 return 0;
58 59
@@ -84,6 +85,7 @@ static int nd_region_remove(struct device *dev)
84 nvdimm_bus_lock(dev); 85 nvdimm_bus_lock(dev);
85 nd_region->ns_seed = NULL; 86 nd_region->ns_seed = NULL;
86 nd_region->btt_seed = NULL; 87 nd_region->btt_seed = NULL;
88 nd_region->pfn_seed = NULL;
87 dev_set_drvdata(dev, NULL); 89 dev_set_drvdata(dev, NULL);
88 nvdimm_bus_unlock(dev); 90 nvdimm_bus_unlock(dev);
89 91
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 7384455792bf..da4338154ad2 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -345,6 +345,23 @@ static ssize_t btt_seed_show(struct device *dev,
345} 345}
346static DEVICE_ATTR_RO(btt_seed); 346static DEVICE_ATTR_RO(btt_seed);
347 347
348static ssize_t pfn_seed_show(struct device *dev,
349 struct device_attribute *attr, char *buf)
350{
351 struct nd_region *nd_region = to_nd_region(dev);
352 ssize_t rc;
353
354 nvdimm_bus_lock(dev);
355 if (nd_region->pfn_seed)
356 rc = sprintf(buf, "%s\n", dev_name(nd_region->pfn_seed));
357 else
358 rc = sprintf(buf, "\n");
359 nvdimm_bus_unlock(dev);
360
361 return rc;
362}
363static DEVICE_ATTR_RO(pfn_seed);
364
348static ssize_t read_only_show(struct device *dev, 365static ssize_t read_only_show(struct device *dev,
349 struct device_attribute *attr, char *buf) 366 struct device_attribute *attr, char *buf)
350{ 367{
@@ -373,6 +390,7 @@ static struct attribute *nd_region_attributes[] = {
373 &dev_attr_nstype.attr, 390 &dev_attr_nstype.attr,
374 &dev_attr_mappings.attr, 391 &dev_attr_mappings.attr,
375 &dev_attr_btt_seed.attr, 392 &dev_attr_btt_seed.attr,
393 &dev_attr_pfn_seed.attr,
376 &dev_attr_read_only.attr, 394 &dev_attr_read_only.attr,
377 &dev_attr_set_cookie.attr, 395 &dev_attr_set_cookie.attr,
378 &dev_attr_available_size.attr, 396 &dev_attr_available_size.attr,
@@ -744,6 +762,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
744 nd_region->numa_node = ndr_desc->numa_node; 762 nd_region->numa_node = ndr_desc->numa_node;
745 ida_init(&nd_region->ns_ida); 763 ida_init(&nd_region->ns_ida);
746 ida_init(&nd_region->btt_ida); 764 ida_init(&nd_region->btt_ida);
765 ida_init(&nd_region->pfn_ida);
747 dev = &nd_region->dev; 766 dev = &nd_region->dev;
748 dev_set_name(dev, "region%d", nd_region->id); 767 dev_set_name(dev, "region%d", nd_region->id);
749 dev->parent = &nvdimm_bus->dev; 768 dev->parent = &nvdimm_bus->dev;
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild
index 98f2881ba6a2..99e70f0729be 100644
--- a/tools/testing/nvdimm/Kbuild
+++ b/tools/testing/nvdimm/Kbuild
@@ -43,7 +43,9 @@ libnvdimm-y += $(NVDIMM_SRC)/region_devs.o
43libnvdimm-y += $(NVDIMM_SRC)/region.o 43libnvdimm-y += $(NVDIMM_SRC)/region.o
44libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o 44libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o
45libnvdimm-y += $(NVDIMM_SRC)/label.o 45libnvdimm-y += $(NVDIMM_SRC)/label.o
46libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o
46libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o 47libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o
48libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o
47libnvdimm-y += config_check.o 49libnvdimm-y += config_check.o
48 50
49obj-m += test/ 51obj-m += test/