aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2015-06-09 16:09:36 -0400
committerDan Williams <dan.j.williams@intel.com>2015-06-24 21:24:10 -0400
commit4a826c83db4edc040da3a66dbefd53f0cfcf457d (patch)
treeff26d9df31e46d3f7d8ff12fa288982460beacc3
parenteaf961536e1622ad21247ac8d44acd48ba65566e (diff)
libnvdimm: namespace indices: read and validate
This on media label format [1] consists of two index blocks followed by an array of labels. None of these structures are ever updated in place. A sequence number tracks the current active index and the next one to write, while labels are written to free slots. +------------+ | | | nsindex0 | | | +------------+ | | | nsindex1 | | | +------------+ | label0 | +------------+ | label1 | +------------+ | | ....nslot... | | +------------+ | labelN | +------------+ After reading valid labels, store the dpa ranges they claim into per-dimm resource trees. [1]: http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf Cc: Neil Brown <neilb@suse.de> Acked-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/nvdimm/Makefile1
-rw-r--r--drivers/nvdimm/dimm.c23
-rw-r--r--drivers/nvdimm/dimm_devs.c30
-rw-r--r--drivers/nvdimm/label.c290
-rw-r--r--drivers/nvdimm/label.h128
-rw-r--r--drivers/nvdimm/nd.h49
-rw-r--r--include/uapi/linux/ndctl.h1
7 files changed, 520 insertions, 2 deletions
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
index 4d2a27f52faa..abce98f87f16 100644
--- a/drivers/nvdimm/Makefile
+++ b/drivers/nvdimm/Makefile
@@ -10,3 +10,4 @@ libnvdimm-y += dimm.o
10libnvdimm-y += region_devs.o 10libnvdimm-y += region_devs.o
11libnvdimm-y += region.o 11libnvdimm-y += region.o
12libnvdimm-y += namespace_devs.o 12libnvdimm-y += namespace_devs.o
13libnvdimm-y += label.o
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index eb20fc2df32b..2df97c3c3b34 100644
--- a/drivers/nvdimm/dimm.c
+++ b/drivers/nvdimm/dimm.c
@@ -18,6 +18,7 @@
18#include <linux/slab.h> 18#include <linux/slab.h>
19#include <linux/mm.h> 19#include <linux/mm.h>
20#include <linux/nd.h> 20#include <linux/nd.h>
21#include "label.h"
21#include "nd.h" 22#include "nd.h"
22 23
23static void free_data(struct nvdimm_drvdata *ndd) 24static void free_data(struct nvdimm_drvdata *ndd)
@@ -42,6 +43,11 @@ static int nvdimm_probe(struct device *dev)
42 return -ENOMEM; 43 return -ENOMEM;
43 44
44 dev_set_drvdata(dev, ndd); 45 dev_set_drvdata(dev, ndd);
46 ndd->dpa.name = dev_name(dev);
47 ndd->ns_current = -1;
48 ndd->ns_next = -1;
49 ndd->dpa.start = 0;
50 ndd->dpa.end = -1;
45 ndd->dev = dev; 51 ndd->dev = dev;
46 52
47 rc = nvdimm_init_nsarea(ndd); 53 rc = nvdimm_init_nsarea(ndd);
@@ -54,6 +60,17 @@ static int nvdimm_probe(struct device *dev)
54 60
55 dev_dbg(dev, "config data size: %d\n", ndd->nsarea.config_size); 61 dev_dbg(dev, "config data size: %d\n", ndd->nsarea.config_size);
56 62
63 nvdimm_bus_lock(dev);
64 ndd->ns_current = nd_label_validate(ndd);
65 ndd->ns_next = nd_label_next_nsindex(ndd->ns_current);
66 nd_label_copy(ndd, to_next_namespace_index(ndd),
67 to_current_namespace_index(ndd));
68 rc = nd_label_reserve_dpa(ndd);
69 nvdimm_bus_unlock(dev);
70
71 if (rc)
72 goto err;
73
57 return 0; 74 return 0;
58 75
59 err: 76 err:
@@ -64,7 +81,13 @@ static int nvdimm_probe(struct device *dev)
64static int nvdimm_remove(struct device *dev) 81static int nvdimm_remove(struct device *dev)
65{ 82{
66 struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); 83 struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
84 struct resource *res, *_r;
67 85
86 nvdimm_bus_lock(dev);
87 dev_set_drvdata(dev, NULL);
88 for_each_dpa_resource_safe(ndd, res, _r)
89 nvdimm_free_dpa(ndd, res);
90 nvdimm_bus_unlock(dev);
68 free_data(ndd); 91 free_data(ndd);
69 92
70 return 0; 93 return 0;
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index bdf8241b6525..d2ef02e4be6c 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -92,8 +92,12 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
92 if (ndd->data) 92 if (ndd->data)
93 return 0; 93 return 0;
94 94
95 if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0) 95 if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0
96 || ndd->nsarea.config_size < ND_LABEL_MIN_SIZE) {
97 dev_dbg(ndd->dev, "failed to init config data area: (%d:%d)\n",
98 ndd->nsarea.max_xfer, ndd->nsarea.config_size);
96 return -ENXIO; 99 return -ENXIO;
100 }
97 101
98 ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL); 102 ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL);
99 if (!ndd->data) 103 if (!ndd->data)
@@ -243,6 +247,30 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
243} 247}
244EXPORT_SYMBOL_GPL(nvdimm_create); 248EXPORT_SYMBOL_GPL(nvdimm_create);
245 249
250void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
251{
252 WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
253 kfree(res->name);
254 __release_region(&ndd->dpa, res->start, resource_size(res));
255}
256
257struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
258 struct nd_label_id *label_id, resource_size_t start,
259 resource_size_t n)
260{
261 char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
262 struct resource *res;
263
264 if (!name)
265 return NULL;
266
267 WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
268 res = __request_region(&ndd->dpa, start, n, name, 0);
269 if (!res)
270 kfree(name);
271 return res;
272}
273
246static int count_dimms(struct device *dev, void *c) 274static int count_dimms(struct device *dev, void *c)
247{ 275{
248 int *count = c; 276 int *count = c;
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
new file mode 100644
index 000000000000..db5d7492dc8d
--- /dev/null
+++ b/drivers/nvdimm/label.c
@@ -0,0 +1,290 @@
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/ndctl.h>
15#include <linux/io.h>
16#include <linux/nd.h>
17#include "nd-core.h"
18#include "label.h"
19#include "nd.h"
20
21static u32 best_seq(u32 a, u32 b)
22{
23 a &= NSINDEX_SEQ_MASK;
24 b &= NSINDEX_SEQ_MASK;
25
26 if (a == 0 || a == b)
27 return b;
28 else if (b == 0)
29 return a;
30 else if (nd_inc_seq(a) == b)
31 return b;
32 else
33 return a;
34}
35
36size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
37{
38 u32 index_span;
39
40 if (ndd->nsindex_size)
41 return ndd->nsindex_size;
42
43 /*
44 * The minimum index space is 512 bytes, with that amount of
45 * index we can describe ~1400 labels which is less than a byte
46 * of overhead per label. Round up to a byte of overhead per
47 * label and determine the size of the index region. Yes, this
48 * starts to waste space at larger config_sizes, but it's
49 * unlikely we'll ever see anything but 128K.
50 */
51 index_span = ndd->nsarea.config_size / 129;
52 index_span /= NSINDEX_ALIGN * 2;
53 ndd->nsindex_size = index_span * NSINDEX_ALIGN;
54
55 return ndd->nsindex_size;
56}
57
58int nd_label_validate(struct nvdimm_drvdata *ndd)
59{
60 /*
61 * On media label format consists of two index blocks followed
62 * by an array of labels. None of these structures are ever
63 * updated in place. A sequence number tracks the current
64 * active index and the next one to write, while labels are
65 * written to free slots.
66 *
67 * +------------+
68 * | |
69 * | nsindex0 |
70 * | |
71 * +------------+
72 * | |
73 * | nsindex1 |
74 * | |
75 * +------------+
76 * | label0 |
77 * +------------+
78 * | label1 |
79 * +------------+
80 * | |
81 * ....nslot...