}
EXPORT_SYMBOL_GPL(nvdimm_region_notify);
+struct clear_badblocks_context {
+ resource_size_t phys, cleared;
+};
+
+static int nvdimm_clear_badblocks_region(struct device *dev, void *data)
+{
+ struct clear_badblocks_context *ctx = data;
+ struct nd_region *nd_region;
+ resource_size_t ndr_end;
+ sector_t sector;
+
+ /* make sure device is a region */
+ if (!is_nd_pmem(dev))
+ return 0;
+
+ nd_region = to_nd_region(dev);
+ ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1;
+
+ /* make sure we are in the region */
+ if (ctx->phys < nd_region->ndr_start
+ || (ctx->phys + ctx->cleared) > ndr_end)
+ return 0;
+
+ sector = (ctx->phys - nd_region->ndr_start) / 512;
+ badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512);
+
+ return 0;
+}
+
+static void nvdimm_clear_badblocks_regions(struct nvdimm_bus *nvdimm_bus,
+ phys_addr_t phys, u64 cleared)
+{
+ struct clear_badblocks_context ctx = {
+ .phys = phys,
+ .cleared = cleared,
+ };
+
+ device_for_each_child(&nvdimm_bus->dev, &ctx,
+ nvdimm_clear_badblocks_region);
+}
+
+static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus,
+ phys_addr_t phys, u64 cleared)
+{
+ if (cleared > 0)
+ nvdimm_forget_poison(nvdimm_bus, phys, cleared);
+
+ if (cleared > 0 && cleared / 512)
+ nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared);
+}
+
long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
unsigned int len)
{
if (cmd_rc < 0)
return cmd_rc;
- if (clear_err.cleared > 0)
- nvdimm_forget_poison(nvdimm_bus, phys, clear_err.cleared);
+ nvdimm_account_cleared_poison(nvdimm_bus, phys, clear_err.cleared);
return clear_err.cleared;
}
EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
-void __nvdimm_bus_badblocks_clear(struct nvdimm_bus *nvdimm_bus,
- struct resource *res)
-{
- lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
- device_for_each_child(&nvdimm_bus->dev, (void *)res,
- nvdimm_region_badblocks_clear);
-}
-EXPORT_SYMBOL_GPL(__nvdimm_bus_badblocks_clear);
-
static int nvdimm_bus_match(struct device *dev, struct device_driver *drv);
static struct bus_type nvdimm_bus_type = {
if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) {
struct nd_cmd_clear_error *clear_err = buf;
- struct resource res;
-
- if (clear_err->cleared) {
- /* clearing the poison list we keep track of */
- nvdimm_forget_poison(nvdimm_bus, clear_err->address,
- clear_err->cleared);
- /* now sync the badblocks lists */
- res.start = clear_err->address;
- res.end = clear_err->address + clear_err->cleared - 1;
- __nvdimm_bus_badblocks_clear(nvdimm_bus, &res);
- }
+ nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address,
+ clear_err->cleared);
}
nvdimm_bus_unlock(&nvdimm_bus->dev);
device_for_each_child(dev, &event, child_notify);
}
-int nvdimm_region_badblocks_clear(struct device *dev, void *data)
-{
- struct resource *res = (struct resource *)data;
- struct nd_region *nd_region;
- resource_size_t ndr_end;
- sector_t sector;
-
- /* make sure device is a region */
- if (!is_nd_pmem(dev))
- return 0;
-
- nd_region = to_nd_region(dev);
- ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1;
-
- /* make sure we are in the region */
- if (res->start < nd_region->ndr_start || res->end > ndr_end)
- return 0;
-
- sector = (res->start - nd_region->ndr_start) >> 9;
- badblocks_clear(&nd_region->bb, sector, resource_size(res) >> 9);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nvdimm_region_badblocks_clear);
-
static struct nd_device_driver nd_region_driver = {
.probe = nd_region_probe,
.remove = nd_region_remove,
}
static DEVICE_ATTR_RW(read_only);
-static ssize_t nd_badblocks_show(struct device *dev,
+static ssize_t region_badblocks_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nd_region *nd_region = to_nd_region(dev);
return badblocks_show(&nd_region->bb, buf, 0);
}
-static struct device_attribute dev_attr_nd_badblocks = {
- .attr = {
- .name = "badblocks",
- .mode = S_IRUGO
- },
- .show = nd_badblocks_show,
-};
+
+static DEVICE_ATTR(badblocks, 0444, region_badblocks_show, NULL);
static ssize_t resource_show(struct device *dev,
struct device_attribute *attr, char *buf)
&dev_attr_available_size.attr,
&dev_attr_namespace_seed.attr,
&dev_attr_init_namespaces.attr,
- &dev_attr_nd_badblocks.attr,
+ &dev_attr_badblocks.attr,
&dev_attr_resource.attr,
NULL,
};
if (!is_nd_pmem(dev) && a == &dev_attr_dax_seed.attr)
return 0;
- if (!is_nd_pmem(dev) && a == &dev_attr_nd_badblocks.attr)
+ if (!is_nd_pmem(dev) && a == &dev_attr_badblocks.attr)
return 0;
if (!is_nd_pmem(dev) && a == &dev_attr_resource.attr)