aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2017-04-04 18:08:36 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-21 03:31:21 -0400
commit5f377c4ad27190523099bfd0ac69197961ed2637 (patch)
tree675ea2a2c4edc9f67a02b0838732d9ddf4e5de17
parent0c6172ccbb59e7fab17c19dc7c9a753c3429cfd7 (diff)
libnvdimm: fix blk free space accounting
commit fe514739d8538783749d3ce72f78e5a999ea5668 upstream. Commit a1f3e4d6a0c3 "libnvdimm, region: update nd_region_available_dpa() for multi-pmem support" reworked blk dpa (DIMM Physical Address) accounting to comprehend multiple pmem namespace allocations aliasing with a given blk-dpa range. The following call trace is a result of failing to account for allocated blk capacity. WARNING: CPU: 1 PID: 2433 at tools/testing/nvdimm/../../../drivers/nvdimm/names 4 size_store+0x6f3/0x930 [libnvdimm] nd_region region5: allocation underrun: 0x0 of 0x1000000 bytes [..] Call Trace: dump_stack+0x86/0xc3 __warn+0xcb/0xf0 warn_slowpath_fmt+0x5f/0x80 size_store+0x6f3/0x930 [libnvdimm] dev_attr_store+0x18/0x30 If a given blk-dpa allocation does not alias with any pmem ranges then the full allocation should be accounted as busy space, not the size of the current pmem contribution to the region. The thinkos that led to this confusion was not realizing that the struct resource management is already guaranteeing no collisions between pmem allocations and blk allocations on the same dimm. Also, we do not try to support blk allocations in aliased pmem holes. This patch also fixes a case where the available blk goes negative. Fixes: a1f3e4d6a0c3 ("libnvdimm, region: update nd_region_available_dpa() for multi-pmem support"). Reported-by: Dariusz Dokupil <dariusz.dokupil@intel.com> Reported-by: Dave Jiang <dave.jiang@intel.com> Reported-by: Vishal Verma <vishal.l.verma@intel.com> Tested-by: Dave Jiang <dave.jiang@intel.com> Tested-by: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/nvdimm/dimm_devs.c77
1 files changed, 11 insertions, 66 deletions
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index d614493ad5ac..dcb32f34a302 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -388,7 +388,7 @@ EXPORT_SYMBOL_GPL(nvdimm_create);
388 388
389int alias_dpa_busy(struct device *dev, void *data) 389int alias_dpa_busy(struct device *dev, void *data)
390{ 390{
391 resource_size_t map_end, blk_start, new, busy; 391 resource_size_t map_end, blk_start, new;
392 struct blk_alloc_info *info = data; 392 struct blk_alloc_info *info = data;
393 struct nd_mapping *nd_mapping; 393 struct nd_mapping *nd_mapping;
394 struct nd_region *nd_region; 394 struct nd_region *nd_region;
@@ -429,29 +429,19 @@ int alias_dpa_busy(struct device *dev, void *data)
429 retry: 429 retry:
430 /* 430 /*
431 * Find the free dpa from the end of the last pmem allocation to 431 * Find the free dpa from the end of the last pmem allocation to
432 * the end of the interleave-set mapping that is not already 432 * the end of the interleave-set mapping.
433 * covered by a blk allocation.
434 */ 433 */
435 busy = 0;
436 for_each_dpa_resource(ndd, res) { 434 for_each_dpa_resource(ndd, res) {
435 if (strncmp(res->name, "pmem", 4) != 0)
436 continue;
437 if ((res->start >= blk_start && res->start < map_end) 437 if ((res->start >= blk_start && res->start < map_end)
438 || (res->end >= blk_start 438 || (res->end >= blk_start
439 && res->end <= map_end)) { 439 && res->end <= map_end)) {
440 if (strncmp(res->name, "pmem", 4) == 0) { 440 new = max(blk_start, min(map_end + 1, res->end + 1));
441 new = max(blk_start, min(map_end + 1, 441 if (new != blk_start) {
442 res->end + 1)); 442 blk_start = new;
443 if (new != blk_start) { 443 goto retry;
444 blk_start = new; 444 }
445 goto retry;
446 }
447 } else
448 busy += min(map_end, res->end)
449 - max(nd_mapping->start, res->start) + 1;
450 } else if (nd_mapping->start > res->start
451 && map_end < res->end) {
452 /* total eclipse of the PMEM region mapping */
453 busy += nd_mapping->size;
454 break;
455 } 445 }
456 } 446 }
457 447
@@ -463,52 +453,11 @@ int alias_dpa_busy(struct device *dev, void *data)
463 return 1; 453 return 1;
464 } 454 }
465 455
466 info->available -= blk_start - nd_mapping->start + busy; 456 info->available -= blk_start - nd_mapping->start;
467 457
468 return 0; 458 return 0;
469} 459}
470 460
471static int blk_dpa_busy(struct device *dev, void *data)
472{
473 struct blk_alloc_info *info = data;
474 struct nd_mapping *nd_mapping;
475 struct nd_region *nd_region;
476 resource_size_t map_end;
477 int i;
478
479 if (!is_nd_pmem(dev))
480 return 0;
481
482 nd_region = to_nd_region(dev);
483 for (i = 0; i < nd_region->ndr_mappings; i++) {
484 nd_mapping = &nd_region->mapping[i];
485 if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
486 break;
487 }
488
489 if (i >= nd_region->ndr_mappings)
490 return 0;
491
492 map_end = nd_mapping->start + nd_mapping->size - 1;
493 if (info->res->start >= nd_mapping->start
494 && info->res->start < map_end) {
495 if (info->res->end <= map_end) {
496 info->busy = 0;
497 return 1;
498 } else {
499 info->busy -= info->res->end - map_end;
500 return 0;
501 }
502 } else if (info->res->end >= nd_mapping->start
503 && info->res->end <= map_end) {
504 info->busy -= nd_mapping->start - info->res->start;
505 return 0;
506 } else {
507 info->busy -= nd_mapping->size;
508 return 0;
509 }
510}
511
512/** 461/**
513 * nd_blk_available_dpa - account the unused dpa of BLK region 462 * nd_blk_available_dpa - account the unused dpa of BLK region
514 * @nd_mapping: container of dpa-resource-root + labels 463 * @nd_mapping: container of dpa-resource-root + labels
@@ -538,11 +487,7 @@ resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
538 for_each_dpa_resource(ndd, res) { 487 for_each_dpa_resource(ndd, res) {
539 if (strncmp(res->name, "blk", 3) != 0) 488 if (strncmp(res->name, "blk", 3) != 0)
540 continue; 489 continue;
541 490 info.available -= resource_size(res);
542 info.res = res;
543 info.busy = resource_size(res);
544 device_for_each_child(&nvdimm_bus->dev, &info, blk_dpa_busy);
545 info.available -= info.busy;
546 } 491 }
547 492
548 return info.available; 493 return info.available;