Skip to content

Commit 5faecf4

Browse files
committed
libnvdimm: protect nvdimm_{bus|namespace}_add_poison() with nvdimm_bus_lock()
In preparation for making poison list retrieval asynchronus to region registration, add protection for walking and mutating the bus-level poison list. Cc: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent aef2533 commit 5faecf4

File tree

1 file changed

+63
-38
lines changed

1 file changed

+63
-38
lines changed

drivers/nvdimm/core.c

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -408,71 +408,81 @@ static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len)
408408
set_badblock(bb, start_sector, num_sectors);
409409
}
410410

411-
/**
412-
* nvdimm_namespace_add_poison() - Convert a list of poison ranges to badblocks
413-
* @ndns: the namespace containing poison ranges
414-
* @bb: badblocks instance to populate
415-
* @offset: offset at the start of the namespace before 'sector 0'
416-
*
417-
* The poison list generated during NFIT initialization may contain multiple,
418-
* possibly overlapping ranges in the SPA (System Physical Address) space.
419-
* Compare each of these ranges to the namespace currently being initialized,
420-
* and add badblocks to the gendisk for all matching sub-ranges
421-
*/
422-
void nvdimm_namespace_add_poison(struct nd_namespace_common *ndns,
423-
struct badblocks *bb, resource_size_t offset)
411+
static void namespace_add_poison(struct list_head *poison_list,
412+
struct badblocks *bb, struct resource *res)
424413
{
425-
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
426-
struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
427-
struct nvdimm_bus *nvdimm_bus;
428-
struct list_head *poison_list;
429-
u64 ns_start, ns_end, ns_size;
430414
struct nd_poison *pl;
431415

432-
ns_size = nvdimm_namespace_capacity(ndns) - offset;
433-
ns_start = nsio->res.start + offset;
434-
ns_end = nsio->res.end;
435-
436-
nvdimm_bus = to_nvdimm_bus(nd_region->dev.parent);
437-
poison_list = &nvdimm_bus->poison_list;
438416
if (list_empty(poison_list))
439417
return;
440418

441419
list_for_each_entry(pl, poison_list, list) {
442420
u64 pl_end = pl->start + pl->length - 1;
443421

444422
/* Discard intervals with no intersection */
445-
if (pl_end < ns_start)
423+
if (pl_end < res->start)
446424
continue;
447-
if (pl->start > ns_end)
425+
if (pl->start > res->end)
448426
continue;
449427
/* Deal with any overlap after start of the namespace */
450-
if (pl->start >= ns_start) {
428+
if (pl->start >= res->start) {
451429
u64 start = pl->start;
452430
u64 len;
453431

454-
if (pl_end <= ns_end)
432+
if (pl_end <= res->end)
455433
len = pl->length;
456434
else
457-
len = ns_start + ns_size - pl->start;
458-
__add_badblock_range(bb, start - ns_start, len);
435+
len = res->start + resource_size(res)
436+
- pl->start;
437+
__add_badblock_range(bb, start - res->start, len);
459438
continue;
460439
}
461440
/* Deal with overlap for poison starting before the namespace */
462-
if (pl->start < ns_start) {
441+
if (pl->start < res->start) {
463442
u64 len;
464443

465-
if (pl_end < ns_end)
466-
len = pl->start + pl->length - ns_start;
444+
if (pl_end < res->end)
445+
len = pl->start + pl->length - res->start;
467446
else
468-
len = ns_size;
447+
len = resource_size(res);
469448
__add_badblock_range(bb, 0, len);
470449
}
471450
}
472451
}
452+
453+
/**
454+
* nvdimm_namespace_add_poison() - Convert a list of poison ranges to badblocks
455+
* @ndns: the namespace containing poison ranges
456+
* @bb: badblocks instance to populate
457+
* @offset: offset at the start of the namespace before 'sector 0'
458+
*
459+
* The poison list generated during NFIT initialization may contain multiple,
460+
* possibly overlapping ranges in the SPA (System Physical Address) space.
461+
* Compare each of these ranges to the namespace currently being initialized,
462+
* and add badblocks to the gendisk for all matching sub-ranges
463+
*/
464+
void nvdimm_namespace_add_poison(struct nd_namespace_common *ndns,
465+
struct badblocks *bb, resource_size_t offset)
466+
{
467+
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
468+
struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
469+
struct nvdimm_bus *nvdimm_bus;
470+
struct list_head *poison_list;
471+
struct resource res = {
472+
.start = nsio->res.start + offset,
473+
.end = nsio->res.end,
474+
};
475+
476+
nvdimm_bus = to_nvdimm_bus(nd_region->dev.parent);
477+
poison_list = &nvdimm_bus->poison_list;
478+
479+
nvdimm_bus_lock(&nvdimm_bus->dev);
480+
namespace_add_poison(poison_list, bb, &res);
481+
nvdimm_bus_unlock(&nvdimm_bus->dev);
482+
}
473483
EXPORT_SYMBOL_GPL(nvdimm_namespace_add_poison);
474484

475-
static int __add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
485+
static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
476486
{
477487
struct nd_poison *pl;
478488

@@ -487,12 +497,12 @@ static int __add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
487497
return 0;
488498
}
489499

490-
int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
500+
static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
491501
{
492502
struct nd_poison *pl;
493503

494504
if (list_empty(&nvdimm_bus->poison_list))
495-
return __add_poison(nvdimm_bus, addr, length);
505+
return add_poison(nvdimm_bus, addr, length);
496506

497507
/*
498508
* There is a chance this is a duplicate, check for those first.
@@ -512,7 +522,18 @@ int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
512522
* as any overlapping ranges will get resolved when the list is consumed
513523
* and converted to badblocks
514524
*/
515-
return __add_poison(nvdimm_bus, addr, length);
525+
return add_poison(nvdimm_bus, addr, length);
526+
}
527+
528+
int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
529+
{
530+
int rc;
531+
532+
nvdimm_bus_lock(&nvdimm_bus->dev);
533+
rc = bus_add_poison(nvdimm_bus, addr, length);
534+
nvdimm_bus_unlock(&nvdimm_bus->dev);
535+
536+
return rc;
516537
}
517538
EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison);
518539

@@ -553,7 +574,11 @@ void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
553574

554575
nd_synchronize();
555576
device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
577+
578+
nvdimm_bus_lock(&nvdimm_bus->dev);
556579
free_poison_list(&nvdimm_bus->poison_list);
580+
nvdimm_bus_unlock(&nvdimm_bus->dev);
581+
557582
nvdimm_bus_destroy_ndctl(nvdimm_bus);
558583

559584
device_unregister(&nvdimm_bus->dev);

0 commit comments

Comments
 (0)