When reading through the nvdimm_read_bytes() interface we still want to consult the badblocks list, but we do not have access to a gendisk in that path. Convert the badblocks list to be a generic attribute of a namespace. Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- block/badblocks.c | 16 ++++++++++ block/genhd.c | 9 +++--- drivers/nvdimm/core.c | 69 +++++++++++++++++++++++++++------------------ drivers/nvdimm/nd.h | 4 +-- drivers/nvdimm/pmem.c | 13 ++++++-- include/linux/badblocks.h | 1 + 6 files changed, 74 insertions(+), 38 deletions(-) diff --git a/block/badblocks.c b/block/badblocks.c index 9be8bf94f979..0d5030aae715 100644 --- a/block/badblocks.c +++ b/block/badblocks.c @@ -522,6 +522,22 @@ ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len, } EXPORT_SYMBOL_GPL(badblocks_store); +struct badblocks *badblocks_alloc(void) +{ + struct badblocks *bb; + int rc; + + bb = kzalloc(sizeof(*bb), GFP_KERNEL); + if (!bb) + return ERR_PTR(-ENOMEM); + + rc = badblocks_init(bb, 1); + if (rc) + return ERR_PTR(rc); + return bb; +} +EXPORT_SYMBOL_GPL(badblocks_alloc); + /** * badblocks_init() - initialize the badblocks structure * @bb: the badblocks structure that holds all badblock information diff --git a/block/genhd.c b/block/genhd.c index b96012849e26..1118369ab2fd 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -508,11 +508,12 @@ static int exact_lock(dev_t devt, void *data) int disk_alloc_badblocks(struct gendisk *disk) { - disk->bb = kzalloc(sizeof(*(disk->bb)), GFP_KERNEL); - if (!disk->bb) - return -ENOMEM; + struct badblocks *bb = badblocks_alloc(); - return badblocks_init(disk->bb, 1); + if (IS_ERR(bb)) + return PTR_ERR(bb); + disk->bb = bb; + return 0; } EXPORT_SYMBOL(disk_alloc_badblocks); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 21003b7f0b38..c31a699aaed9 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #include <linux/libnvdimm.h> +#include <linux/badblocks.h> #include <linux/export.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -362,7 +363,7 @@ EXPORT_SYMBOL_GPL(__nvdimm_bus_register); /** * __add_badblock_range() - Convert a physical address range to bad sectors - * @disk: the disk associated with the namespace + * @bb: badblocks instance to establish / extend * @ns_offset: namespace offset where the error range begins (in bytes) * @len: number of bytes of poison to be added * @@ -370,9 +371,10 @@ EXPORT_SYMBOL_GPL(__nvdimm_bus_register); * the bounds of physical addresses for this namespace, i.e. lies in the * interval [ns_start, ns_start + ns_size) */ -static int __add_badblock_range(struct gendisk *disk, u64 ns_offset, u64 len) +static int __add_badblock_range(struct badblocks **bb, u64 ns_offset, + u64 len) { - unsigned int sector_size = queue_logical_block_size(disk->queue); + const unsigned int sector_size = 512; sector_t start_sector; u64 num_sectors; u32 rem; @@ -383,10 +385,10 @@ static int __add_badblock_range(struct gendisk *disk, u64 ns_offset, u64 len) if (rem) num_sectors++; - if (!disk->bb) { - rc = disk_alloc_badblocks(disk); - if (rc) - return rc; + if (!*bb) { + *bb = badblocks_alloc(); + if (IS_ERR(*bb)) + return PTR_ERR(*bb); } if (unlikely(num_sectors > (u64)INT_MAX)) { @@ -396,7 +398,7 @@ static int __add_badblock_range(struct gendisk *disk, u64 ns_offset, u64 len) while (remaining) { int done = min_t(u64, remaining, INT_MAX); - rc = disk_set_badblocks(disk, s, done); + rc = badblocks_set(*bb, s, done, 1); if (rc) return rc; remaining -= done; @@ -404,35 +406,45 @@ static int __add_badblock_range(struct gendisk *disk, u64 ns_offset, u64 len) } return 0; } else - return disk_set_badblocks(disk, start_sector, num_sectors); + return badblocks_set(*bb, start_sector, num_sectors, 1); } /** - * nvdimm_namespace_add_poison() - Convert a list of poison ranges to badblocks - * @disk: the gendisk associated with the namespace where badblocks - * will be stored + * nvdimm_namespace_badblocks() - Convert a list of poison ranges to badblocks + * @ndns: namespace hosting potential badblocks * @offset: offset at the start of the namespace before 'sector 0' - * @ndns: the namespace containing poison ranges * - * The poison list generated during NFIT initialization may contain multiple, - * possibly overlapping ranges in the SPA (System Physical Address) space. - * Compare each of these ranges to the namespace currently being initialized, - * and add badblocks to the gendisk for all matching sub-ranges + * The poison list generated during NFIT initialization may contain + * multiple, possibly overlapping ranges in the SPA (System Physical + * Address) space. Compare each of these ranges to the namespace + * currently being initialized, and add badblocks for all matching + * sub-ranges * * Return: - * 0 - Success + * valid badblocks instance on success + * ERR_PTR(-ENOENT) on no badblocks present + * ERR_PTR(-<error>) on failure */ -int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset, - struct nd_namespace_common *ndns) +struct badblocks *nvdimm_namespace_badblocks(struct nd_namespace_common *ndns, + resource_size_t offset) { - struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); struct nd_region *nd_region = to_nd_region(ndns->dev.parent); struct nvdimm_bus *nvdimm_bus; struct list_head *poison_list; u64 ns_start, ns_end, ns_size; + struct nd_namespace_io *nsio; + struct badblocks *bb = NULL; struct nd_poison *pl; int rc; + /* + * For now, the addresses retrieved from an nvdimm bus are + * communicated in terms of system physical address not a dimm + * address (dpa). This implies pmem only. + */ + if (!is_nd_pmem(&nd_region->dev)) + return ERR_PTR(-ENOENT); + nsio = to_nd_namespace_io(&ndns->dev); ns_size = nvdimm_namespace_capacity(ndns) - offset; ns_start = nsio->res.start + offset; ns_end = nsio->res.end; @@ -440,7 +452,7 @@ int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset, nvdimm_bus = to_nvdimm_bus(nd_region->dev.parent); poison_list = &nvdimm_bus->poison_list; if (list_empty(poison_list)) - return 0; + return ERR_PTR(-ENOENT); list_for_each_entry(pl, poison_list, list) { u64 pl_end = pl->start + pl->length - 1; @@ -460,9 +472,9 @@ int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset, else len = ns_start + ns_size - pl->start; - rc = __add_badblock_range(disk, start - ns_start, len); + rc = __add_badblock_range(&bb, start - ns_start, len); if (rc) - return rc; + return ERR_PTR(rc); dev_info(&nvdimm_bus->dev, "Found a poison range (0x%llx, 0x%llx)\n", start, len); @@ -477,18 +489,19 @@ int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset, else len = ns_size; - rc = __add_badblock_range(disk, 0, len); + rc = __add_badblock_range(&bb, 0, len); if (rc) - return rc; + return ERR_PTR(rc); dev_info(&nvdimm_bus->dev, "Found a poison range (0x%llx, 0x%llx)\n", pl->start, len); } } - return 0; + WARN_ON(!bb); + return bb; } -EXPORT_SYMBOL_GPL(nvdimm_namespace_add_poison); +EXPORT_SYMBOL_GPL(nvdimm_namespace_badblocks); static int __add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) { diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 198933da83e5..65f1031f2bf9 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -268,8 +268,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns); int nvdimm_namespace_detach_btt(struct nd_namespace_common *ndns); const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, char *name); -int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset, - struct nd_namespace_common *ndns); +struct badblocks *nvdimm_namespace_badblocks(struct nd_namespace_common *ndns, + resource_size_t offset); int nd_blk_region_init(struct nd_region *nd_region); void __nd_iostat_start(struct bio *bio, unsigned long *start); static inline bool nd_iostat_start(struct bio *bio, unsigned long *start) diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index d8e14e962327..082d6ae11d48 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -194,8 +194,8 @@ static int pmem_attach_disk(struct device *dev, struct nd_namespace_common *ndns, struct pmem_device *pmem) { int nid = dev_to_node(dev); + struct badblocks *bb; struct gendisk *disk; - int ret; pmem->pmem_queue = blk_alloc_queue_node(GFP_KERNEL, nid); if (!pmem->pmem_queue) @@ -224,10 +224,15 @@ static int pmem_attach_disk(struct device *dev, set_capacity(disk, (pmem->size - pmem->data_offset) / 512); pmem->pmem_disk = disk; - ret = nvdimm_namespace_add_poison(disk, pmem->data_offset, ndns); - if (ret) - return ret; + bb = nvdimm_namespace_badblocks(ndns, pmem->data_offset); + if (IS_ERR(bb)) { + if (PTR_ERR(bb) == -ENOENT) + bb = NULL; + else + return PTR_ERR(bb); + } + disk->bb = bb; add_disk(disk); revalidate_disk(disk); diff --git a/include/linux/badblocks.h b/include/linux/badblocks.h index 929344630b51..e74aae7c66e2 100644 --- a/include/linux/badblocks.h +++ b/include/linux/badblocks.h @@ -48,6 +48,7 @@ ssize_t badblocks_show(struct badblocks *bb, char *page, int unack); ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack); int badblocks_init(struct badblocks *bb, int enable); +struct badblocks *badblocks_alloc(void); void badblocks_free(struct badblocks *bb); #endif -- To unsubscribe from this list: send the line "unsubscribe linux-block" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html