diff options
author | Dan Williams <dan.j.williams@intel.com> | 2015-07-30 17:57:47 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2015-08-28 23:39:36 -0400 |
commit | e1455744b27c9e6115c3508a7b2902157c2c4347 (patch) | |
tree | 6bfbc5d78269c9389f5bcfc1adc35d4f7f2e6fa4 | |
parent | 96601adb745186ccbcf5b078d4756f13381ec2af (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/Kconfig | 22 | ||||
-rw-r--r-- | drivers/nvdimm/Makefile | 2 | ||||
-rw-r--r-- | drivers/nvdimm/btt.c | 6 | ||||
-rw-r--r-- | drivers/nvdimm/btt_devs.c | 172 | ||||
-rw-r--r-- | drivers/nvdimm/claim.c | 201 | ||||
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 34 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 9 | ||||
-rw-r--r-- | drivers/nvdimm/nd.h | 56 | ||||
-rw-r--r-- | drivers/nvdimm/pfn.h | 35 | ||||
-rw-r--r-- | drivers/nvdimm/pfn_devs.c | 336 | ||||
-rw-r--r-- | drivers/nvdimm/region.c | 2 | ||||
-rw-r--r-- | drivers/nvdimm/region_devs.c | 19 | ||||
-rw-r--r-- | tools/testing/nvdimm/Kbuild | 2 |
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 | ||
51 | config ND_CLAIM | ||
52 | bool | ||
53 | |||
50 | config ND_BTT | 54 | config ND_BTT |
51 | tristate | 55 | tristate |
52 | 56 | ||
53 | config BTT | 57 | config 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 | ||
73 | config ND_PFN | ||
74 | tristate | ||
75 | |||
76 | config 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 | |||
68 | endif | 90 | endif |
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 | |||
20 | libnvdimm-y += region.o | 20 | libnvdimm-y += region.o |
21 | libnvdimm-y += namespace_devs.o | 21 | libnvdimm-y += namespace_devs.o |
22 | libnvdimm-y += label.o | 22 | libnvdimm-y += label.o |
23 | libnvdimm-$(CONFIG_ND_CLAIM) += claim.o | ||
23 | libnvdimm-$(CONFIG_BTT) += btt_devs.o | 24 | libnvdimm-$(CONFIG_BTT) += btt_devs.o |
25 | libnvdimm-$(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) | |||
731 | static int btt_arena_write_layout(struct arena_info *arena) | 731 | static 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 | ||
24 | static 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 | |||
36 | static 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 | |||
49 | static 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 | |||
63 | static 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 | |||
74 | static void nd_btt_release(struct device *dev) | 24 | static 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 | ||
175 | static int namespace_match(struct device *dev, void *data) | ||
176 | { | ||
177 | char *name = data; | ||
178 | |||
179 | return strcmp(name, dev_name(dev)) == 0; | ||
180 | } | ||
181 | |||
182 | static 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 | |||
192 | static 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 | |||
265 | static ssize_t namespace_store(struct device *dev, | 125 | static 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 | } |
388 | EXPORT_SYMBOL(nd_btt_arena_is_valid); | 249 | EXPORT_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 | */ | ||
396 | u64 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 | } | ||
407 | EXPORT_SYMBOL(nd_btt_sb_checksum); | ||
408 | |||
409 | static int __nd_btt_probe(struct nd_btt *nd_btt, | 251 | static 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 | |||
20 | void __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 | |||
32 | void 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 | |||
46 | bool __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 | |||
60 | bool 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 | |||
71 | static int namespace_match(struct device *dev, void *data) | ||
72 | { | ||
73 | char *name = data; | ||
74 | |||
75 | return strcmp(name, dev_name(dev)) == 0; | ||
76 | } | ||
77 | |||
78 | static 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 | |||
93 | static 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 | |||
115 | ssize_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 | */ | ||
186 | u64 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 | } | ||
201 | EXPORT_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[] = { | |||
1255 | struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) | 1263 | struct 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, | |||
80 | int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd); | 80 | int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd); |
81 | void get_ndd(struct nvdimm_drvdata *ndd); | 81 | void get_ndd(struct nvdimm_drvdata *ndd); |
82 | resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns); | 82 | resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns); |
83 | void nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns); | ||
84 | void __nd_detach_ndns(struct device *dev, struct nd_namespace_common **_ndns); | ||
85 | bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach, | ||
86 | struct nd_namespace_common **_ndns); | ||
87 | bool __nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach, | ||
88 | struct nd_namespace_common **_ndns); | ||
89 | ssize_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 | ||
34 | struct nvdimm_drvdata { | 41 | struct 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 | ||
145 | enum nd_pfn_mode { | ||
146 | PFN_MODE_NONE, | ||
147 | PFN_MODE_RAM, | ||
148 | PFN_MODE_PMEM, | ||
149 | }; | ||
150 | |||
151 | struct 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 | |||
136 | enum nd_async_mode { | 161 | enum 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); | |||
159 | int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, | 184 | int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, |
160 | void *buf, size_t len); | 185 | void *buf, size_t len); |
161 | struct nd_btt *to_nd_btt(struct device *dev); | 186 | struct nd_btt *to_nd_btt(struct device *dev); |
162 | struct btt_sb; | 187 | |
163 | u64 nd_btt_sb_checksum(struct btt_sb *btt_sb); | 188 | struct nd_gen_sb { |
189 | char reserved[SZ_4K - 8]; | ||
190 | __le64 checksum; | ||
191 | }; | ||
192 | |||
193 | u64 nd_sb_checksum(struct nd_gen_sb *sb); | ||
164 | #if IS_ENABLED(CONFIG_BTT) | 194 | #if IS_ENABLED(CONFIG_BTT) |
165 | int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata); | 195 | int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata); |
166 | bool is_nd_btt(struct device *dev); | 196 | bool 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 | ||
215 | struct nd_pfn *to_nd_pfn(struct device *dev); | ||
216 | #if IS_ENABLED(CONFIG_NVDIMM_PFN) | ||
217 | int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata); | ||
218 | bool is_nd_pfn(struct device *dev); | ||
219 | struct device *nd_pfn_create(struct nd_region *nd_region); | ||
220 | #else | ||
221 | static inline int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata) | ||
222 | { | ||
223 | return -ENODEV; | ||
224 | } | ||
225 | |||
226 | static inline bool is_nd_pfn(struct device *dev) | ||
227 | { | ||
228 | return false; | ||
229 | } | ||
230 | |||
231 | static inline struct device *nd_pfn_create(struct nd_region *nd_region) | ||
232 | { | ||
233 | return NULL; | ||
234 | } | ||
184 | #endif | 235 | #endif |
236 | |||
185 | struct nd_region *to_nd_region(struct device *dev); | 237 | struct nd_region *to_nd_region(struct device *dev); |
186 | int nd_region_to_nstype(struct nd_region *nd_region); | 238 | int nd_region_to_nstype(struct nd_region *nd_region); |
187 | int nd_region_register_namespaces(struct nd_region *nd_region, int *err); | 239 | int 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 | |||
22 | struct 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 | |||
24 | static 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 | |||
36 | static struct device_type nd_pfn_device_type = { | ||
37 | .name = "nd_pfn", | ||
38 | .release = nd_pfn_release, | ||
39 | }; | ||
40 | |||
41 | bool is_nd_pfn(struct device *dev) | ||
42 | { | ||
43 | return dev ? dev->type == &nd_pfn_device_type : false; | ||
44 | } | ||
45 | EXPORT_SYMBOL(is_nd_pfn); | ||
46 | |||
47 | struct 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 | } | ||
54 | EXPORT_SYMBOL(to_nd_pfn); | ||
55 | |||
56 | static 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 | |||
71 | static 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 | } | ||
104 | static DEVICE_ATTR_RW(mode); | ||
105 | |||
106 | static 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 | |||
116 | static 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 | } | ||
130 | static DEVICE_ATTR_RW(uuid); | ||
131 | |||
132 | static 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 | |||
145 | static 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 | } | ||
161 | static DEVICE_ATTR_RW(namespace); | ||
162 | |||
163 | static struct attribute *nd_pfn_attributes[] = { | ||
164 | &dev_attr_mode.attr, | ||
165 | &dev_attr_namespace.attr, | ||
166 | &dev_attr_uuid.attr, | ||
167 | NULL, | ||
168 | }; | ||
169 | |||
170 | static struct attribute_group nd_pfn_attribute_group = { | ||
171 | .attrs = nd_pfn_attributes, | ||
172 | }; | ||
173 | |||
174 | static 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 | |||
181 | static 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 | |||
221 | struct 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 | |||
231 | static 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 | |||
303 | int 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 | } | ||
336 | EXPORT_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 | } |
346 | static DEVICE_ATTR_RO(btt_seed); | 346 | static DEVICE_ATTR_RO(btt_seed); |
347 | 347 | ||
348 | static 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 | } | ||
363 | static DEVICE_ATTR_RO(pfn_seed); | ||
364 | |||
348 | static ssize_t read_only_show(struct device *dev, | 365 | static 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 | |||
43 | libnvdimm-y += $(NVDIMM_SRC)/region.o | 43 | libnvdimm-y += $(NVDIMM_SRC)/region.o |
44 | libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o | 44 | libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o |
45 | libnvdimm-y += $(NVDIMM_SRC)/label.o | 45 | libnvdimm-y += $(NVDIMM_SRC)/label.o |
46 | libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o | ||
46 | libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o | 47 | libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o |
48 | libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o | ||
47 | libnvdimm-y += config_check.o | 49 | libnvdimm-y += config_check.o |
48 | 50 | ||
49 | obj-m += test/ | 51 | obj-m += test/ |