@@ -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,23 +523,19 @@ ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
}
EXPORT_SYMBOL_GPL(badblocks_store);
-/**
- * badblocks_init() - initialize the badblocks structure
- * @bb: the badblocks structure that holds all badblock information
- * @enable: weather to enable badblocks accounting
- *
- * Return:
- * 0: success
- * -ve errno: on error
- */
-int badblocks_init(struct badblocks *bb, int enable)
+static int __badblocks_init(struct device *dev, struct badblocks *bb,
+ int enable)
{
+ bb->dev = dev;
bb->count = 0;
if (enable)
bb->shift = 0;
else
bb->shift = -1;
- bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ 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;
@@ -547,8 +544,35 @@ int badblocks_init(struct badblocks *bb, int enable)
return 0;
}
+
+/**
+ * badblocks_init() - initialize the badblocks structure
+ * @bb: the badblocks structure that holds all badblock information
+ * @enable: weather to enable badblocks accounting
+ *
+ * Return:
+ * 0: success
+ * -ve errno: on error
+ */
+int badblocks_init(struct badblocks *bb, int enable)
+{
+ return __badblocks_init(NULL, bb, enable);
+}
EXPORT_SYMBOL_GPL(badblocks_init);
+struct badblocks *devm_alloc_badblocks(struct device *dev)
+{
+ struct badblocks *bb;
+
+ bb = devm_kzalloc(dev, sizeof(*bb), GFP_KERNEL);
+ if (!bb)
+ return NULL;
+ if (__badblocks_init(dev, bb, 1))
+ return NULL;
+ return bb;
+}
+EXPORT_SYMBOL_GPL(devm_alloc_badblocks);
+
/**
* badblocks_exit() - free the badblocks structure
* @bb: the badblocks structure that holds all badblock information
@@ -557,7 +581,10 @@ void badblocks_exit(struct badblocks *bb)
{
if (!bb)
return;
- kfree(bb->page);
+ if (bb->dev)
+ devm_kfree(bb->dev, bb->page);
+ else
+ kfree(bb->page);
bb->page = NULL;
}
EXPORT_SYMBOL_GPL(badblocks_exit);
@@ -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,8 @@ EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
/**
* __add_badblock_range() - Convert a physical address range to bad sectors
- * @disk: the disk associated with the namespace
+ * @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
*
@@ -370,9 +372,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 device *dev, 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 +386,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 = devm_alloc_badblocks(dev);
+ if (*bb == NULL)
+ return -ENOMEM;
}
if (unlikely(num_sectors > (u64)INT_MAX)) {
@@ -396,7 +399,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,7 +407,7 @@ 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);
}
/**
@@ -430,6 +433,7 @@ int nvdimm_namespace_add_poison(struct gendisk *disk, resource_size_t offset,
struct nvdimm_bus *nvdimm_bus;
struct list_head *poison_list;
u64 ns_start, ns_end, ns_size;
+ struct badblocks *bb = NULL;
struct nd_poison *pl;
int rc;
@@ -460,7 +464,8 @@ 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(&ndns->dev, &bb,
+ start - ns_start, len);
if (rc)
return rc;
dev_info(&nvdimm_bus->dev,
@@ -477,7 +482,7 @@ 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(&ndns->dev, &bb, 0, len);
if (rc)
return rc;
dev_info(&nvdimm_bus->dev,
@@ -161,8 +161,6 @@ static void pmem_detach_disk(struct pmem_device *pmem)
if (!disk)
return;
- badblocks_exit(disk->bb);
- kfree(disk->bb);
del_gendisk_queue(disk);
put_disk(disk);
}
@@ -23,6 +23,7 @@
#define MAX_BADBLOCKS (PAGE_SIZE/8)
struct badblocks {
+ struct device *dev; /* set by devm_alloc_badblocks */
int count; /* count of bad blocks */
int unacked_exist; /* there probably are unacknowledged
* bad blocks. This is only cleared
@@ -49,5 +50,7 @@ ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
int unack);
int badblocks_init(struct badblocks *bb, int enable);
void badblocks_exit(struct badblocks *bb);
+struct device;
+struct badblocks *devm_alloc_badblocks(struct device *dev);
#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 | 51 ++++++++++++++++++++++++++++++++++----------- drivers/nvdimm/core.c | 27 ++++++++++++++---------- drivers/nvdimm/pmem.c | 2 -- include/linux/badblocks.h | 3 +++ 4 files changed, 58 insertions(+), 25 deletions(-)