Skip to content

Commit 4a826c8

Browse files
committed
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 <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent eaf9615 commit 4a826c8

File tree

7 files changed

+520
-2
lines changed

7 files changed

+520
-2
lines changed

drivers/nvdimm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ libnvdimm-y += dimm.o
1010
libnvdimm-y += region_devs.o
1111
libnvdimm-y += region.o
1212
libnvdimm-y += namespace_devs.o
13+
libnvdimm-y += label.o

drivers/nvdimm/dimm.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/slab.h>
1919
#include <linux/mm.h>
2020
#include <linux/nd.h>
21+
#include "label.h"
2122
#include "nd.h"
2223

2324
static void free_data(struct nvdimm_drvdata *ndd)
@@ -42,6 +43,11 @@ static int nvdimm_probe(struct device *dev)
4243
return -ENOMEM;
4344

4445
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;
4551
ndd->dev = dev;
4652

4753
rc = nvdimm_init_nsarea(ndd);
@@ -54,6 +60,17 @@ static int nvdimm_probe(struct device *dev)
5460

5561
dev_dbg(dev, "config data size: %d\n", ndd->nsarea.config_size);
5662

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+
5774
return 0;
5875

5976
err:
@@ -64,7 +81,13 @@ static int nvdimm_probe(struct device *dev)
6481
static int nvdimm_remove(struct device *dev)
6582
{
6683
struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
84+
struct resource *res, *_r;
6785

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);
6891
free_data(ndd);
6992

7093
return 0;

drivers/nvdimm/dimm_devs.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,12 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
9292
if (ndd->data)
9393
return 0;
9494

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);
9699
return -ENXIO;
100+
}
97101

98102
ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL);
99103
if (!ndd->data)
@@ -243,6 +247,30 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
243247
}
244248
EXPORT_SYMBOL_GPL(nvdimm_create);
245249

250+
void 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+
257+
struct 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+
246274
static int count_dimms(struct device *dev, void *c)
247275
{
248276
int *count = c;

drivers/nvdimm/label.c

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
21+
static 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+
36+
size_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+
58+
int 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...
82+
* | |
83+
* +------------+
84+
* | labelN |
85+
* +------------+
86+
*/
87+
struct nd_namespace_index *nsindex[] = {
88+
to_namespace_index(ndd, 0),
89+
to_namespace_index(ndd, 1),
90+
};
91+
const int num_index = ARRAY_SIZE(nsindex);
92+
struct device *dev = ndd->dev;
93+
bool valid[2] = { 0 };
94+
int i, num_valid = 0;
95+
u32 seq;
96+
97+
for (i = 0; i < num_index; i++) {
98+
u32 nslot;
99+
u8 sig[NSINDEX_SIG_LEN];
100+
u64 sum_save, sum, size;
101+
102+
memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN);
103+
if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) {
104+
dev_dbg(dev, "%s: nsindex%d signature invalid\n",
105+
__func__, i);
106+
continue;
107+
}
108+
sum_save = __le64_to_cpu(nsindex[i]->checksum);
109+
nsindex[i]->checksum = __cpu_to_le64(0);
110+
sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1);
111+
nsindex[i]->checksum = __cpu_to_le64(sum_save);
112+
if (sum != sum_save) {
113+
dev_dbg(dev, "%s: nsindex%d checksum invalid\n",
114+
__func__, i);
115+
continue;
116+
}
117+
118+
seq = __le32_to_cpu(nsindex[i]->seq);
119+
if ((seq & NSINDEX_SEQ_MASK) == 0) {
120+
dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n",
121+
__func__, i, seq);
122+
continue;
123+
}
124+
125+
/* sanity check the index against expected values */
126+
if (__le64_to_cpu(nsindex[i]->myoff)
127+
!= i * sizeof_namespace_index(ndd)) {
128+
dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n",
129+
__func__, i, (unsigned long long)
130+
__le64_to_cpu(nsindex[i]->myoff));
131+
continue;
132+
}
133+
if (__le64_to_cpu(nsindex[i]->otheroff)
134+
!= (!i) * sizeof_namespace_index(ndd)) {
135+
dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n",
136+
__func__, i, (unsigned long long)
137+
__le64_to_cpu(nsindex[i]->otheroff));
138+
continue;
139+
}
140+
141+
size = __le64_to_cpu(nsindex[i]->mysize);
142+
if (size > sizeof_namespace_index(ndd)
143+
|| size < sizeof(struct nd_namespace_index)) {
144+
dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n",
145+
__func__, i, size);
146+
continue;
147+
}
148+
149+
nslot = __le32_to_cpu(nsindex[i]->nslot);
150+
if (nslot * sizeof(struct nd_namespace_label)
151+
+ 2 * sizeof_namespace_index(ndd)
152+
> ndd->nsarea.config_size) {
153+
dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n",
154+
__func__, i, nslot,
155+
ndd->nsarea.config_size);
156+
continue;
157+
}
158+
valid[i] = true;
159+
num_valid++;
160+
}
161+
162+
switch (num_valid) {
163+
case 0:
164+
break;
165+
case 1:
166+
for (i = 0; i < num_index; i++)
167+
if (valid[i])
168+
return i;
169+
/* can't have num_valid > 0 but valid[] = { false, false } */
170+
WARN_ON(1);
171+
break;
172+
default:
173+
/* pick the best index... */
174+
seq = best_seq(__le32_to_cpu(nsindex[0]->seq),
175+
__le32_to_cpu(nsindex[1]->seq));
176+
if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK))
177+
return 1;
178+
else
179+
return 0;
180+
break;
181+
}
182+
183+
return -1;
184+
}
185+
186+
void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst,
187+
struct nd_namespace_index *src)
188+
{
189+
if (dst && src)
190+
/* pass */;
191+
else
192+
return;
193+
194+
memcpy(dst, src, sizeof_namespace_index(ndd));
195+
}
196+
197+
static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd)
198+
{
199+
void *base = to_namespace_index(ndd, 0);
200+
201+
return base + 2 * sizeof_namespace_index(ndd);
202+
}
203+
204+
#define for_each_clear_bit_le(bit, addr, size) \
205+
for ((bit) = find_next_zero_bit_le((addr), (size), 0); \
206+
(bit) < (size); \
207+
(bit) = find_next_zero_bit_le((addr), (size), (bit) + 1))
208+
209+
/**
210+
* preamble_current - common variable initialization for nd_label_* routines
211+
* @ndd: dimm container for the relevant label set
212+
* @nsindex_out: on return set to the currently active namespace index
213+
* @free: on return set to the free label bitmap in the index
214+
* @nslot: on return set to the number of slots in the label space
215+
*/
216+
static bool preamble_current(struct nvdimm_drvdata *ndd,
217+
struct nd_namespace_index **nsindex_out,
218+
unsigned long **free, u32 *nslot)
219+
{
220+
struct nd_namespace_index *nsindex;
221+
222+
nsindex = to_current_namespace_index(ndd);
223+
if (nsindex == NULL)
224+
return false;
225+
226+
*free = (unsigned long *) nsindex->free;
227+
*nslot = __le32_to_cpu(nsindex->nslot);
228+
*nsindex_out = nsindex;
229+
230+
return true;
231+
}
232+
233+
static char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)
234+
{
235+
if (!label_id || !uuid)
236+
return NULL;
237+
snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb",
238+
flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid);
239+
return label_id->id;
240+
}
241+
242+
static bool slot_valid(struct nd_namespace_label *nd_label, u32 slot)
243+
{
244+
/* check that we are written where we expect to be written */
245+
if (slot != __le32_to_cpu(nd_label->slot))
246+
return false;
247+
248+
/* check that DPA allocations are page aligned */
249+
if ((__le64_to_cpu(nd_label->dpa)
250+
| __le64_to_cpu(nd_label->rawsize)) % SZ_4K)
251+
return false;
252+
253+
return true;
254+
}
255+
256+
int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
257+
{
258+
struct nd_namespace_index *nsindex;
259+
unsigned long *free;
260+
u32 nslot, slot;
261+
262+
if (!preamble_current(ndd, &nsindex, &free, &nslot))
263+
return 0; /* no label, nothing to reserve */
264+
265+
for_each_clear_bit_le(slot, free, nslot) {
266+
struct nd_namespace_label *nd_label;
267+
struct nd_region *nd_region = NULL;
268+
u8 label_uuid[NSLABEL_UUID_LEN];
269+
struct nd_label_id label_id;
270+
struct resource *res;
271+
u32 flags;
272+
273+
nd_label = nd_label_base(ndd) + slot;
274+
275+
if (!slot_valid(nd_label, slot))
276+
continue;
277+
278+
memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
279+
flags = __le32_to_cpu(nd_label->flags);
280+
nd_label_gen_id(&label_id, label_uuid, flags);
281+
res = nvdimm_allocate_dpa(ndd, &label_id,
282+
__le64_to_cpu(nd_label->dpa),
283+
__le64_to_cpu(nd_label->rawsize));
284+
nd_dbg_dpa(nd_region, ndd, res, "reserve\n");
285+
if (!res)
286+
return -EBUSY;
287+
}
288+
289+
return 0;
290+
}

0 commit comments

Comments
 (0)