summaryrefslogtreecommitdiff
path: root/drivers/nvdimm
diff options
context:
space:
mode:
authorDave Jiang <dave.jiang@intel.com>2016-11-11 12:37:36 -0700
committerDan Williams <dan.j.williams@intel.com>2016-11-11 20:35:31 -0800
commit82bf1037f2cab2d6960a08ae08513f2c3c0b335a (patch)
treebe634c78d511c071166a65cdb44c0e6821ad48f7 /drivers/nvdimm
parentdafb1048740c36c008fc093bea3532637c143f91 (diff)
libnvdimm: check and clear poison before writing to pmem
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>
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r--drivers/nvdimm/claim.c30
1 files changed, 26 insertions, 4 deletions
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c
index d5dc80c48b4c..8d66fbb779ed 100644
--- a/drivers/nvdimm/claim.c
+++ b/drivers/nvdimm/claim.c
@@ -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)