libnvdimm: check and clear poison before writing to pmem
authorDave Jiang <dave.jiang@intel.com>
Fri, 11 Nov 2016 19:37:36 +0000 (12:37 -0700)
committerDan Williams <dan.j.williams@intel.com>
Sat, 12 Nov 2016 04:35:31 +0000 (20:35 -0800)
We need to clear any poison when we are writing to pmem. The granularity
will be sector size. If it's less then we can't do anything about it
barring corruption.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Vishal Verma <vishal.l.verma@intel.com>
[djbw: fixup 0-length write request to succeed]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/nvdimm/claim.c

index d5dc80c48b4cb36a55c54a2383ce9812ae068260..8d66fbb779ed66444abfd5831a2b4fb753c4f878 100644 (file)
@@ -226,6 +226,12 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
                resource_size_t offset, void *buf, size_t size, int rw)
 {
        struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
+       unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512);
+       sector_t sector = offset >> 9;
+       int rc = 0;
+
+       if (unlikely(!size))
+               return 0;
 
        if (unlikely(offset + size > nsio->size)) {
                dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n");
@@ -233,17 +239,33 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
        }
 
        if (rw == READ) {
-               unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512);
-
-               if (unlikely(is_bad_pmem(&nsio->bb, offset / 512, sz_align)))
+               if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align)))
                        return -EIO;
                return memcpy_from_pmem(buf, nsio->addr + offset, size);
        } else {
+
+               if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) {
+                       if (IS_ALIGNED(offset, 512) && IS_ALIGNED(size, 512)) {
+                               long cleared;
+
+                               cleared = nvdimm_clear_poison(&ndns->dev,
+                                                             offset, size);
+                               if (cleared != size) {
+                                       size = cleared;
+                                       rc = -EIO;
+                               }
+
+                               badblocks_clear(&nsio->bb, sector,
+                                               cleared >> 9);
+                       } else
+                               rc = -EIO;
+               }
+
                memcpy_to_pmem(nsio->addr + offset, buf, size);
                nvdimm_flush(to_nd_region(ndns->dev.parent));
        }
 
-       return 0;
+       return rc;
 }
 
 int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio)