@@ -17,6 +17,7 @@
#include <linux/badblocks.h>
#include <linux/seqlock.h>
+#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/stddef.h>
@@ -522,21 +523,26 @@ ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
}
EXPORT_SYMBOL_GPL(badblocks_store);
-struct badblocks *badblocks_alloc(void)
+static int __badblocks_init(struct device *dev, struct badblocks *bb,
+ int enable)
{
- struct badblocks *bb;
- int rc;
-
- bb = kzalloc(sizeof(*bb), GFP_KERNEL);
- if (!bb)
- return ERR_PTR(-ENOMEM);
+ bb->count = 0;
+ if (enable)
+ bb->shift = 0;
+ else
+ bb->shift = -1;
+ if (dev)
+ bb->page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
+ else
+ bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (bb->page == (u64 *)0) {
+ bb->shift = -1;
+ return -ENOMEM;
+ }
+ seqlock_init(&bb->lock);
- rc = badblocks_init(bb, 1);
- if (rc)
- return ERR_PTR(rc);
- return bb;
+ return 0;
}
-EXPORT_SYMBOL_GPL(badblocks_alloc);
/**
* badblocks_init() - initialize the badblocks structure
@@ -549,21 +555,41 @@ EXPORT_SYMBOL_GPL(badblocks_alloc);
*/
int badblocks_init(struct badblocks *bb, int enable)
{
- bb->count = 0;
- if (enable)
- bb->shift = 0;
+ return __badblocks_init(NULL, bb, enable);
+}
+EXPORT_SYMBOL_GPL(badblocks_init);
+
+static struct badblocks *__badblocks_alloc(struct device *dev)
+{
+ struct badblocks *bb;
+ int rc;
+
+ if (dev)
+ bb = devm_kzalloc(dev, sizeof(*bb), GFP_KERNEL);
else
- bb->shift = -1;
- bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (bb->page == (u64 *)0) {
- bb->shift = -1;
- return -ENOMEM;
- }
- seqlock_init(&bb->lock);
+ bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+ if (!bb)
+ return ERR_PTR(-ENOMEM);
- return 0;
+ rc = __badblocks_init(dev, bb, 1);
+ if (rc)
+ return ERR_PTR(rc);
+ return bb;
}
-EXPORT_SYMBOL_GPL(badblocks_init);
+
+struct badblocks *badblocks_alloc(void)
+{
+ return __badblocks_alloc(NULL);
+}
+EXPORT_SYMBOL_GPL(badblocks_alloc);
+
+struct badblocks *devm_alloc_badblocks(struct device *dev)
+{
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+ return __badblocks_alloc(dev);
+}
+EXPORT_SYMBOL_GPL(devm_alloc_badblocks);
/**
* badblocks_free() - free the badblocks structure
@@ -363,6 +363,7 @@ EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
/**
* __add_badblock_range() - Convert a physical address range to bad sectors
+ * @dev: host device for allocations
* @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
@@ -371,8 +372,8 @@ 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 badblocks **bb, u64 ns_offset,
- u64 len)
+static int __add_badblock_range(struct device *dev, struct badblocks **bb,
+ u64 ns_offset, u64 len)
{
const unsigned int sector_size = 512;
sector_t start_sector;
@@ -386,7 +387,7 @@ static int __add_badblock_range(struct badblocks **bb, u64 ns_offset,
num_sectors++;
if (!*bb) {
- *bb = badblocks_alloc();
+ *bb = devm_alloc_badblocks(dev);
if (IS_ERR(*bb))
return PTR_ERR(*bb);
}
@@ -472,7 +473,8 @@ struct badblocks *nvdimm_namespace_badblocks(struct nd_namespace_common *ndns,
else
len = ns_start + ns_size - pl->start;
- rc = __add_badblock_range(&bb, start - ns_start, len);
+ rc = __add_badblock_range(&ndns->dev, &bb,
+ start - ns_start, len);
if (rc)
return ERR_PTR(rc);
dev_info(&nvdimm_bus->dev,
@@ -489,7 +491,7 @@ struct badblocks *nvdimm_namespace_badblocks(struct nd_namespace_common *ndns,
else
len = ns_size;
- rc = __add_badblock_range(&bb, 0, len);
+ rc = __add_badblock_range(&ndns->dev, &bb, 0, len);
if (rc)
return ERR_PTR(rc);
dev_info(&nvdimm_bus->dev,
@@ -185,7 +185,6 @@ static void pmem_detach_disk(struct pmem_device *pmem)
if (!disk)
return;
- badblocks_free(disk->bb);
del_gendisk_queue(disk);
put_disk(disk);
}
@@ -49,6 +49,8 @@ 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);
+struct device;
+struct badblocks *devm_alloc_badblocks(struct device *dev);
void badblocks_free(struct badblocks *bb);
#endif
Provide a devres interface for allocating a badblocks instance. The pmem driver has several scenarios where it will be beneficial to have this structure automatically freed when the device is disabled / fails probe. Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- block/badblocks.c | 74 ++++++++++++++++++++++++++++++--------------- drivers/nvdimm/core.c | 12 ++++--- drivers/nvdimm/pmem.c | 1 - include/linux/badblocks.h | 2 + 4 files changed, 59 insertions(+), 30 deletions(-)