Skip to content

Commit a1f3e4d

Browse files
committed
libnvdimm, region: update nd_region_available_dpa() for multi-pmem support
The free dpa (dimm-physical-address) space calculation reports how much free space is available with consideration for aliased BLK + PMEM regions. Recall that BLK capacity is allocated from high addresses and PMEM is allocated from low addresses in their respective regions. nd_region_available_dpa() accounts for the fact that the largest encroachment (lowest starting address) into PMEM capacity by a BLK allocation limits the available capacity to that point, regardless if there is BLK allocation hole at a higher address. Similarly, for the multi-pmem case we need to track the largest encroachment (highest ending address) of a PMEM allocation in BLK capacity regardless of whether there is an allocation hole that a BLK allocation could fill at a lower address. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 6ff3e91 commit a1f3e4d

File tree

3 files changed

+139
-42
lines changed

3 files changed

+139
-42
lines changed

drivers/nvdimm/dimm_devs.c

Lines changed: 136 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -386,40 +386,148 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
386386
}
387387
EXPORT_SYMBOL_GPL(nvdimm_create);
388388

389+
struct blk_alloc_info {
390+
struct nd_mapping *nd_mapping;
391+
resource_size_t available, busy;
392+
struct resource *res;
393+
};
394+
395+
static int alias_dpa_busy(struct device *dev, void *data)
396+
{
397+
resource_size_t map_end, blk_start, new, busy;
398+
struct blk_alloc_info *info = data;
399+
struct nd_mapping *nd_mapping;
400+
struct nd_region *nd_region;
401+
struct nvdimm_drvdata *ndd;
402+
struct resource *res;
403+
int i;
404+
405+
if (!is_nd_pmem(dev))
406+
return 0;
407+
408+
nd_region = to_nd_region(dev);
409+
for (i = 0; i < nd_region->ndr_mappings; i++) {
410+
nd_mapping = &nd_region->mapping[i];
411+
if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
412+
break;
413+
}
414+
415+
if (i >= nd_region->ndr_mappings)
416+
return 0;
417+
418+
ndd = to_ndd(nd_mapping);
419+
map_end = nd_mapping->start + nd_mapping->size - 1;
420+
blk_start = nd_mapping->start;
421+
retry:
422+
/*
423+
* Find the free dpa from the end of the last pmem allocation to
424+
* the end of the interleave-set mapping that is not already
425+
* covered by a blk allocation.
426+
*/
427+
busy = 0;
428+
for_each_dpa_resource(ndd, res) {
429+
if ((res->start >= blk_start && res->start < map_end)
430+
|| (res->end >= blk_start
431+
&& res->end <= map_end)) {
432+
if (strncmp(res->name, "pmem", 4) == 0) {
433+
new = max(blk_start, min(map_end + 1,
434+
res->end + 1));
435+
if (new != blk_start) {
436+
blk_start = new;
437+
goto retry;
438+
}
439+
} else
440+
busy += min(map_end, res->end)
441+
- max(nd_mapping->start, res->start) + 1;
442+
} else if (nd_mapping->start > res->start
443+
&& map_end < res->end) {
444+
/* total eclipse of the PMEM region mapping */
445+
busy += nd_mapping->size;
446+
break;
447+
}
448+
}
449+
450+
info->available -= blk_start - nd_mapping->start + busy;
451+
return 0;
452+
}
453+
454+
static int blk_dpa_busy(struct device *dev, void *data)
455+
{
456+
struct blk_alloc_info *info = data;
457+
struct nd_mapping *nd_mapping;
458+
struct nd_region *nd_region;
459+
resource_size_t map_end;
460+
int i;
461+
462+
if (!is_nd_pmem(dev))
463+
return 0;
464+
465+
nd_region = to_nd_region(dev);
466+
for (i = 0; i < nd_region->ndr_mappings; i++) {
467+
nd_mapping = &nd_region->mapping[i];
468+
if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
469+
break;
470+
}
471+
472+
if (i >= nd_region->ndr_mappings)
473+
return 0;
474+
475+
map_end = nd_mapping->start + nd_mapping->size - 1;
476+
if (info->res->start >= nd_mapping->start
477+
&& info->res->start < map_end) {
478+
if (info->res->end <= map_end) {
479+
info->busy = 0;
480+
return 1;
481+
} else {
482+
info->busy -= info->res->end - map_end;
483+
return 0;
484+
}
485+
} else if (info->res->end >= nd_mapping->start
486+
&& info->res->end <= map_end) {
487+
info->busy -= nd_mapping->start - info->res->start;
488+
return 0;
489+
} else {
490+
info->busy -= nd_mapping->size;
491+
return 0;
492+
}
493+
}
494+
389495
/**
390496
* nd_blk_available_dpa - account the unused dpa of BLK region
391497
* @nd_mapping: container of dpa-resource-root + labels
392498
*
393-
* Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges.
499+
* Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but
500+
* we arrange for them to never start at an lower dpa than the last
501+
* PMEM allocation in an aliased region.
394502
*/
395-
resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping)
503+
resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
396504
{
505+
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
506+
struct nd_mapping *nd_mapping = &nd_region->mapping[0];
397507
struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
398-
resource_size_t map_end, busy = 0, available;
508+
struct blk_alloc_info info = {
509+
.nd_mapping = nd_mapping,
510+
.available = nd_mapping->size,
511+
};
399512
struct resource *res;
400513

401514
if (!ndd)
402515
return 0;
403516

404-
map_end = nd_mapping->start + nd_mapping->size - 1;
405-
for_each_dpa_resource(ndd, res)
406-
if (res->start >= nd_mapping->start && res->start < map_end) {
407-
resource_size_t end = min(map_end, res->end);
517+
device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy);
408518

409-
busy += end - res->start + 1;
410-
} else if (res->end >= nd_mapping->start
411-
&& res->end <= map_end) {
412-
busy += res->end - nd_mapping->start;
413-
} else if (nd_mapping->start > res->start
414-
&& nd_mapping->start < res->end) {
415-
/* total eclipse of the BLK region mapping */
416-
busy += nd_mapping->size;
417-
}
519+
/* now account for busy blk allocations in unaliased dpa */
520+
for_each_dpa_resource(ndd, res) {
521+
if (strncmp(res->name, "blk", 3) != 0)
522+
continue;
418523

419-
available = map_end - nd_mapping->start + 1;
420-
if (busy < available)
421-
return available - busy;
422-
return 0;
524+
info.res = res;
525+
info.busy = resource_size(res);
526+
device_for_each_child(&nvdimm_bus->dev, &info, blk_dpa_busy);
527+
info.available -= info.busy;
528+
}
529+
530+
return info.available;
423531
}
424532

425533
/**
@@ -451,21 +559,16 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
451559
map_start = nd_mapping->start;
452560
map_end = map_start + nd_mapping->size - 1;
453561
blk_start = max(map_start, map_end + 1 - *overlap);
454-
for_each_dpa_resource(ndd, res)
562+
for_each_dpa_resource(ndd, res) {
455563
if (res->start >= map_start && res->start < map_end) {
456564
if (strncmp(res->name, "blk", 3) == 0)
457-
blk_start = min(blk_start, res->start);
458-
else if (res->start != map_start) {
565+
blk_start = min(blk_start,
566+
max(map_start, res->start));
567+
else if (res->end > map_end) {
459568
reason = "misaligned to iset";
460569
goto err;
461-
} else {
462-
if (busy) {
463-
reason = "duplicate overlapping PMEM reservations?";
464-
goto err;
465-
}
570+
} else
466571
busy += resource_size(res);
467-
continue;
468-
}
469572
} else if (res->end >= map_start && res->end <= map_end) {
470573
if (strncmp(res->name, "blk", 3) == 0) {
471574
/*
@@ -474,15 +577,14 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
474577
* be used for BLK.
475578
*/
476579
blk_start = map_start;
477-
} else {
478-
reason = "misaligned to iset";
479-
goto err;
480-
}
580+
} else
581+
busy += resource_size(res);
481582
} else if (map_start > res->start && map_start < res->end) {
482583
/* total eclipse of the mapping */
483584
busy += nd_mapping->size;
484585
blk_start = map_start;
485586
}
587+
}
486588

487589
*overlap = map_end + 1 - blk_start;
488590
available = blk_start - map_start;
@@ -491,10 +593,6 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
491593
return 0;
492594

493595
err:
494-
/*
495-
* Something is wrong, PMEM must align with the start of the
496-
* interleave set, and there can only be one allocation per set.
497-
*/
498596
nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
499597
return 0;
500598
}

drivers/nvdimm/nd-core.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ struct nd_mapping;
7676
void nd_mapping_free_labels(struct nd_mapping *nd_mapping);
7777
resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
7878
struct nd_mapping *nd_mapping, resource_size_t *overlap);
79-
resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping);
79+
resource_size_t nd_blk_available_dpa(struct nd_region *nd_region);
8080
resource_size_t nd_region_available_dpa(struct nd_region *nd_region);
8181
resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
8282
struct nd_label_id *label_id);

drivers/nvdimm/region_devs.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,8 @@ resource_size_t nd_region_available_dpa(struct nd_region *nd_region)
294294
blk_max_overlap = overlap;
295295
goto retry;
296296
}
297-
} else if (is_nd_blk(&nd_region->dev)) {
298-
available += nd_blk_available_dpa(nd_mapping);
299-
}
297+
} else if (is_nd_blk(&nd_region->dev))
298+
available += nd_blk_available_dpa(nd_region);
300299
}
301300

302301
return available;

0 commit comments

Comments
 (0)