libnvdimm, pmem: Add sysfs notifications to badblocks
authorToshi Kani <toshi.kani@hpe.com>
Mon, 12 Jun 2017 22:25:11 +0000 (16:25 -0600)
committerDan Williams <dan.j.williams@intel.com>
Thu, 15 Jun 2017 21:31:41 +0000 (14:31 -0700)
Sysfs "badblocks" information may be updated during run-time that:
 - MCE, SCI, and sysfs "scrub" may add new bad blocks
 - Writes and ioctl() may clear bad blocks

Add support to send sysfs notifications to sysfs "badblocks" file
under region and pmem directories when their badblocks information
is re-evaluated (but is not necessarily changed) during run-time.

Signed-off-by: Toshi Kani <toshi.kani@hpe.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Linda Knippers <linda.knippers@hpe.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/nvdimm/bus.c
drivers/nvdimm/nd.h
drivers/nvdimm/pmem.c
drivers/nvdimm/pmem.h
drivers/nvdimm/region.c

index e9361bffe5ee331eeb89a7d979403ce6f09b4b8f..63ce50d9c1c5869c06996ff83fbc8c0c8228a159 100644 (file)
@@ -198,6 +198,9 @@ static int nvdimm_clear_badblocks_region(struct device *dev, void *data)
        sector = (ctx->phys - nd_region->ndr_start) / 512;
        badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512);
 
+       if (nd_region->bb_state)
+               sysfs_notify_dirent(nd_region->bb_state);
+
        return 0;
 }
 
index 8cabd836df0e4a1bbb6c6ad271894d2341a09153..e802c877d7836bd67272a87718f9478c20577a91 100644 (file)
@@ -161,6 +161,7 @@ struct nd_region {
        u64 ndr_start;
        int id, num_lanes, ro, numa_node;
        void *provider_data;
+       struct kernfs_node *bb_state;
        struct badblocks bb;
        struct nd_interleave_set *nd_set;
        struct nd_percpu_lane __percpu *lane;
index 5c45e178bd4a1ebda2ddd92bc50860ab74a26b92..34189a145ac6e45cba3e2734852d7c27ff779924 100644 (file)
@@ -68,6 +68,8 @@ static int pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset,
                                (unsigned long long) sector, cleared,
                                cleared > 1 ? "s" : "");
                badblocks_clear(&pmem->bb, sector, cleared);
+               if (pmem->bb_state)
+                       sysfs_notify_dirent(pmem->bb_state);
        }
 
        invalidate_pmem(pmem->virt_addr + offset, len);
@@ -378,6 +380,13 @@ static int pmem_attach_disk(struct device *dev,
 
        revalidate_disk(disk);
 
+       pmem->bb_state = sysfs_get_dirent(disk_to_dev(disk)->kobj.sd,
+                                         "badblocks");
+       if (pmem->bb_state)
+               sysfs_put(pmem->bb_state);
+       else
+               dev_warn(dev, "sysfs_get_dirent 'badblocks' failed\n");
+
        return 0;
 }
 
@@ -429,6 +438,7 @@ static void nd_pmem_notify(struct device *dev, enum nvdimm_event event)
        struct nd_namespace_io *nsio;
        struct resource res;
        struct badblocks *bb;
+       struct kernfs_node *bb_state;
 
        if (event != NVDIMM_REVALIDATE_POISON)
                return;
@@ -440,11 +450,13 @@ static void nd_pmem_notify(struct device *dev, enum nvdimm_event event)
                nd_region = to_nd_region(ndns->dev.parent);
                nsio = to_nd_namespace_io(&ndns->dev);
                bb = &nsio->bb;
+               bb_state = NULL;
        } else {
                struct pmem_device *pmem = dev_get_drvdata(dev);
 
                nd_region = to_region(pmem);
                bb = &pmem->bb;
+               bb_state = pmem->bb_state;
 
                if (is_nd_pfn(dev)) {
                        struct nd_pfn *nd_pfn = to_nd_pfn(dev);
@@ -464,6 +476,8 @@ static void nd_pmem_notify(struct device *dev, enum nvdimm_event event)
        res.start = nsio->res.start + offset;
        res.end = nsio->res.end - end_trunc;
        nvdimm_badblocks_populate(nd_region, bb, &res);
+       if (bb_state)
+               sysfs_notify_dirent(bb_state);
 }
 
 MODULE_ALIAS("pmem");
index 7f4dbd72a90a34fac4d57d18b5606373ce61112e..c5917f040fa7ba56a8229a45c4134ae9a79535ea 100644 (file)
@@ -17,6 +17,7 @@ struct pmem_device {
        size_t                  size;
        /* trim size when namespace capacity has been section aligned */
        u32                     pfn_pad;
+       struct kernfs_node      *bb_state;
        struct badblocks        bb;
        struct dax_device       *dax_dev;
        struct gendisk          *disk;
index 869a886c292ebf94f963abb9f747b0c2802fb471..ca94029d20b382e20fa24f422293134fa51ac516 100644 (file)
@@ -58,10 +58,16 @@ static int nd_region_probe(struct device *dev)
 
                if (devm_init_badblocks(dev, &nd_region->bb))
                        return -ENODEV;
+               nd_region->bb_state = sysfs_get_dirent(nd_region->dev.kobj.sd,
+                                                      "badblocks");
+               if (nd_region->bb_state)
+                       sysfs_put(nd_region->bb_state);
+               else
+                       dev_warn(&nd_region->dev,
+                               "sysfs_get_dirent 'badblocks' failed\n");
                ndr_res.start = nd_region->ndr_start;
                ndr_res.end = nd_region->ndr_start + nd_region->ndr_size - 1;
-               nvdimm_badblocks_populate(nd_region,
-                               &nd_region->bb, &ndr_res);
+               nvdimm_badblocks_populate(nd_region, &nd_region->bb, &ndr_res);
        }
 
        nd_region->btt_seed = nd_btt_create(nd_region);
@@ -126,6 +132,8 @@ static void nd_region_notify(struct device *dev, enum nvdimm_event event)
                                nd_region->ndr_size - 1;
                        nvdimm_badblocks_populate(nd_region,
                                        &nd_region->bb, &res);
+                       if (nd_region->bb_state)
+                               sysfs_notify_dirent(nd_region->bb_state);
                }
        }
        device_for_each_child(dev, &event, child_notify);