Skip to content

Commit bf9bccc

Browse files
committed
libnvdimm: pmem label sets and namespace instantiation.
A complete label set is a PMEM-label per-dimm per-interleave-set where all the UUIDs match and the interleave set cookie matches the hosting interleave set. Present sysfs attributes for manipulation of a PMEM-namespace's 'alt_name', 'uuid', and 'size' attributes. A later patch will make these settings persistent by writing back the label. Note that PMEM allocations grow forwards from the start of an interleave set (lowest dimm-physical-address (DPA)). BLK-namespaces that alias with a PMEM interleave set will grow allocations backward from the highest DPA. Cc: Greg KH <[email protected]> Cc: Neil Brown <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 4a826c8 commit bf9bccc

File tree

15 files changed

+1506
-31
lines changed

15 files changed

+1506
-31
lines changed

drivers/nvdimm/bus.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ static int nvdimm_bus_probe(struct device *dev)
9797
rc = nd_drv->probe(dev);
9898
if (rc == 0)
9999
nd_region_probe_success(nvdimm_bus, dev);
100+
else
101+
nd_region_disable(nvdimm_bus, dev);
100102
nvdimm_bus_probe_end(nvdimm_bus);
101103

102104
dev_dbg(&nvdimm_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name,
@@ -381,8 +383,10 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
381383
}
382384
EXPORT_SYMBOL_GPL(nd_cmd_out_size);
383385

384-
static void wait_nvdimm_bus_probe_idle(struct nvdimm_bus *nvdimm_bus)
386+
void wait_nvdimm_bus_probe_idle(struct device *dev)
385387
{
388+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
389+
386390
do {
387391
if (nvdimm_bus->probe_active == 0)
388392
break;
@@ -402,7 +406,7 @@ static int nd_cmd_clear_to_send(struct nvdimm *nvdimm, unsigned int cmd)
402406
return 0;
403407

404408
nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
405-
wait_nvdimm_bus_probe_idle(nvdimm_bus);
409+
wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev);
406410

407411
if (atomic_read(&nvdimm->busy))
408412
return -EBUSY;

drivers/nvdimm/core.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/export.h>
1515
#include <linux/module.h>
1616
#include <linux/device.h>
17+
#include <linux/ctype.h>
1718
#include <linux/ndctl.h>
1819
#include <linux/mutex.h>
1920
#include <linux/slab.h>
@@ -109,6 +110,69 @@ struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
109110
return NULL;
110111
}
111112

113+
static bool is_uuid_sep(char sep)
114+
{
115+
if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
116+
return true;
117+
return false;
118+
}
119+
120+
static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,
121+
size_t len)
122+
{
123+
const char *str = buf;
124+
u8 uuid[16];
125+
int i;
126+
127+
for (i = 0; i < 16; i++) {
128+
if (!isxdigit(str[0]) || !isxdigit(str[1])) {
129+
dev_dbg(dev, "%s: pos: %d buf[%zd]: %c buf[%zd]: %c\n",
130+
__func__, i, str - buf, str[0],
131+
str + 1 - buf, str[1]);
132+
return -EINVAL;
133+
}
134+
135+
uuid[i] = (hex_to_bin(str[0]) << 4) | hex_to_bin(str[1]);
136+
str += 2;
137+
if (is_uuid_sep(*str))
138+
str++;
139+
}
140+
141+
memcpy(uuid_out, uuid, sizeof(uuid));
142+
return 0;
143+
}
144+
145+
/**
146+
* nd_uuid_store: common implementation for writing 'uuid' sysfs attributes
147+
* @dev: container device for the uuid property
148+
* @uuid_out: uuid buffer to replace
149+
* @buf: raw sysfs buffer to parse
150+
*
151+
* Enforce that uuids can only be changed while the device is disabled
152+
* (driver detached)
153+
* LOCKING: expects device_lock() is held on entry
154+
*/
155+
int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
156+
size_t len)
157+
{
158+
u8 uuid[16];
159+
int rc;
160+
161+
if (dev->driver)
162+
return -EBUSY;
163+
164+
rc = nd_uuid_parse(dev, uuid, buf, len);
165+
if (rc)
166+
return rc;
167+
168+
kfree(*uuid_out);
169+
*uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
170+
if (!(*uuid_out))
171+
return -ENOMEM;
172+
173+
return 0;
174+
}
175+
112176
static ssize_t commands_show(struct device *dev,
113177
struct device_attribute *attr, char *buf)
114178
{

drivers/nvdimm/dimm.c

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,6 @@
2121
#include "label.h"
2222
#include "nd.h"
2323

24-
static void free_data(struct nvdimm_drvdata *ndd)
25-
{
26-
if (!ndd)
27-
return;
28-
29-
if (ndd->data && is_vmalloc_addr(ndd->data))
30-
vfree(ndd->data);
31-
else
32-
kfree(ndd->data);
33-
kfree(ndd);
34-
}
35-
3624
static int nvdimm_probe(struct device *dev)
3725
{
3826
struct nvdimm_drvdata *ndd;
@@ -49,6 +37,8 @@ static int nvdimm_probe(struct device *dev)
4937
ndd->dpa.start = 0;
5038
ndd->dpa.end = -1;
5139
ndd->dev = dev;
40+
get_device(dev);
41+
kref_init(&ndd->kref);
5242

5343
rc = nvdimm_init_nsarea(ndd);
5444
if (rc)
@@ -74,21 +64,18 @@ static int nvdimm_probe(struct device *dev)
7464
return 0;
7565

7666
err:
77-
free_data(ndd);
67+
put_ndd(ndd);
7868
return rc;
7969
}
8070

8171
static int nvdimm_remove(struct device *dev)
8272
{
8373
struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
84-
struct resource *res, *_r;
8574

8675
nvdimm_bus_lock(dev);
8776
dev_set_drvdata(dev, NULL);
88-
for_each_dpa_resource_safe(ndd, res, _r)
89-
nvdimm_free_dpa(ndd, res);
9077
nvdimm_bus_unlock(dev);
91-
free_data(ndd);
78+
put_ndd(ndd);
9279

9380
return 0;
9481
}

drivers/nvdimm/dimm_devs.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,48 @@ struct nvdimm *to_nvdimm(struct device *dev)
159159
}
160160
EXPORT_SYMBOL_GPL(to_nvdimm);
161161

162+
struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
163+
{
164+
struct nvdimm *nvdimm = nd_mapping->nvdimm;
165+
166+
WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
167+
168+
return dev_get_drvdata(&nvdimm->dev);
169+
}
170+
EXPORT_SYMBOL(to_ndd);
171+
172+
void nvdimm_drvdata_release(struct kref *kref)
173+
{
174+
struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
175+
struct device *dev = ndd->dev;
176+
struct resource *res, *_r;
177+
178+
dev_dbg(dev, "%s\n", __func__);
179+
180+
nvdimm_bus_lock(dev);
181+
for_each_dpa_resource_safe(ndd, res, _r)
182+
nvdimm_free_dpa(ndd, res);
183+
nvdimm_bus_unlock(dev);
184+
185+
if (ndd->data && is_vmalloc_addr(ndd->data))
186+
vfree(ndd->data);
187+
else
188+
kfree(ndd->data);
189+
kfree(ndd);
190+
put_device(dev);
191+
}
192+
193+
void get_ndd(struct nvdimm_drvdata *ndd)
194+
{
195+
kref_get(&ndd->kref);
196+
}
197+
198+
void put_ndd(struct nvdimm_drvdata *ndd)
199+
{
200+
if (ndd)
201+
kref_put(&ndd->kref, nvdimm_drvdata_release);
202+
}
203+
162204
const char *nvdimm_name(struct nvdimm *nvdimm)
163205
{
164206
return dev_name(&nvdimm->dev);
@@ -247,6 +289,83 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
247289
}
248290
EXPORT_SYMBOL_GPL(nvdimm_create);
249291

292+
/**
293+
* nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
294+
* @nd_mapping: container of dpa-resource-root + labels
295+
* @nd_region: constrain available space check to this reference region
296+
* @overlap: calculate available space assuming this level of overlap
297+
*
298+
* Validate that a PMEM label, if present, aligns with the start of an
299+
* interleave set and truncate the available size at the lowest BLK
300+
* overlap point.
301+
*
302+
* The expectation is that this routine is called multiple times as it
303+
* probes for the largest BLK encroachment for any single member DIMM of
304+
* the interleave set. Once that value is determined the PMEM-limit for
305+
* the set can be established.
306+
*/
307+
resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
308+
struct nd_mapping *nd_mapping, resource_size_t *overlap)
309+
{
310+
resource_size_t map_start, map_end, busy = 0, available, blk_start;
311+
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
312+
struct resource *res;
313+
const char *reason;
314+
315+
if (!ndd)
316+
return 0;
317+
318+
map_start = nd_mapping->start;
319+
map_end = map_start + nd_mapping->size - 1;
320+
blk_start = max(map_start, map_end + 1 - *overlap);
321+
for_each_dpa_resource(ndd, res)
322+
if (res->start >= map_start && res->start < map_end) {
323+
if (strncmp(res->name, "blk", 3) == 0)
324+
blk_start = min(blk_start, res->start);
325+
else if (res->start != map_start) {
326+
reason = "misaligned to iset";
327+
goto err;
328+
} else {
329+
if (busy) {
330+
reason = "duplicate overlapping PMEM reservations?";
331+
goto err;
332+
}
333+
busy += resource_size(res);
334+
continue;
335+
}
336+
} else if (res->end >= map_start && res->end <= map_end) {
337+
if (strncmp(res->name, "blk", 3) == 0) {
338+
/*
339+
* If a BLK allocation overlaps the start of
340+
* PMEM the entire interleave set may now only
341+
* be used for BLK.
342+
*/
343+
blk_start = map_start;
344+
} else {
345+
reason = "misaligned to iset";
346+
goto err;
347+
}
348+
} else if (map_start > res->start && map_start < res->end) {
349+
/* total eclipse of the mapping */
350+
busy += nd_mapping->size;
351+
blk_start = map_start;
352+
}
353+
354+
*overlap = map_end + 1 - blk_start;
355+
available = blk_start - map_start;
356+
if (busy < available)
357+
return available - busy;
358+
return 0;
359+
360+
err:
361+
/*
362+
* Something is wrong, PMEM must align with the start of the
363+
* interleave set, and there can only be one allocation per set.
364+
*/
365+
nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
366+
return 0;
367+
}
368+
250369
void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
251370
{
252371
WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
@@ -271,6 +390,24 @@ struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
271390
return res;
272391
}
273392

393+
/**
394+
* nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
395+
* @nvdimm: container of dpa-resource-root + labels
396+
* @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid>
397+
*/
398+
resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
399+
struct nd_label_id *label_id)
400+
{
401+
resource_size_t allocated = 0;
402+
struct resource *res;
403+
404+
for_each_dpa_resource(ndd, res)
405+
if (strcmp(res->name, label_id->id) == 0)
406+
allocated += resource_size(res);
407+
408+
return allocated;
409+
}
410+
274411
static int count_dimms(struct device *dev, void *c)
275412
{
276413
int *count = c;

drivers/nvdimm/label.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ static bool preamble_current(struct nvdimm_drvdata *ndd,
230230
return true;
231231
}
232232

233-
static char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)
233+
char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)
234234
{
235235
if (!label_id || !uuid)
236236
return NULL;
@@ -288,3 +288,56 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
288288

289289
return 0;
290290
}
291+
292+
int nd_label_active_count(struct nvdimm_drvdata *ndd)
293+
{
294+
struct nd_namespace_index *nsindex;
295+
unsigned long *free;
296+
u32 nslot, slot;
297+
int count = 0;
298+
299+
if (!preamble_current(ndd, &nsindex, &free, &nslot))
300+
return 0;
301+
302+
for_each_clear_bit_le(slot, free, nslot) {
303+
struct nd_namespace_label *nd_label;
304+
305+
nd_label = nd_label_base(ndd) + slot;
306+
307+
if (!slot_valid(nd_label, slot)) {
308+
u32 label_slot = __le32_to_cpu(nd_label->slot);
309+
u64 size = __le64_to_cpu(nd_label->rawsize);
310+
u64 dpa = __le64_to_cpu(nd_label->dpa);
311+
312+
dev_dbg(ndd->dev,
313+
"%s: slot%d invalid slot: %d dpa: %llx size: %llx\n",
314+
__func__, slot, label_slot, dpa, size);
315+
continue;
316+
}
317+
count++;
318+
}
319+
return count;
320+
}
321+
322+
struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
323+
{
324+
struct nd_namespace_index *nsindex;
325+
unsigned long *free;
326+
u32 nslot, slot;
327+
328+
if (!preamble_current(ndd, &nsindex, &free, &nslot))
329+
return NULL;
330+
331+
for_each_clear_bit_le(slot, free, nslot) {
332+
struct nd_namespace_label *nd_label;
333+
334+
nd_label = nd_label_base(ndd) + slot;
335+
if (!slot_valid(nd_label, slot))
336+
continue;
337+
338+
if (n-- == 0)
339+
return nd_label_base(ndd) + slot;
340+
}
341+
342+
return NULL;
343+
}

drivers/nvdimm/label.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,6 @@ int nd_label_validate(struct nvdimm_drvdata *ndd);
125125
void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst,
126126
struct nd_namespace_index *src);
127127
size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd);
128+
int nd_label_active_count(struct nvdimm_drvdata *ndd);
129+
struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
128130
#endif /* __LABEL_H__ */

0 commit comments

Comments
 (0)