Merge tag 'powerpc-4.9-4' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[cascardo/linux.git] / drivers / nvdimm / pmem.c
index 571a6c7..2461843 100644 (file)
@@ -47,7 +47,7 @@ static struct nd_region *to_region(struct pmem_device *pmem)
        return to_nd_region(to_dev(pmem)->parent);
 }
 
-static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset,
+static int pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset,
                unsigned int len)
 {
        struct device *dev = to_dev(pmem);
@@ -62,8 +62,32 @@ static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset,
                                __func__, (unsigned long long) sector,
                                cleared / 512, cleared / 512 > 1 ? "s" : "");
                badblocks_clear(&pmem->bb, sector, cleared / 512);
+       } else {
+               return -EIO;
        }
+
        invalidate_pmem(pmem->virt_addr + offset, len);
+       return 0;
+}
+
+static void write_pmem(void *pmem_addr, struct page *page,
+               unsigned int off, unsigned int len)
+{
+       void *mem = kmap_atomic(page);
+
+       memcpy_to_pmem(pmem_addr, mem + off, len);
+       kunmap_atomic(mem);
+}
+
+static int read_pmem(struct page *page, unsigned int off,
+               void *pmem_addr, unsigned int len)
+{
+       int rc;
+       void *mem = kmap_atomic(page);
+
+       rc = memcpy_from_pmem(mem + off, pmem_addr, len);
+       kunmap_atomic(mem);
+       return rc;
 }
 
 static int pmem_do_bvec(struct pmem_device *pmem, struct page *page,
@@ -72,7 +96,6 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page,
 {
        int rc = 0;
        bool bad_pmem = false;
-       void *mem = kmap_atomic(page);
        phys_addr_t pmem_off = sector * 512 + pmem->data_offset;
        void *pmem_addr = pmem->virt_addr + pmem_off;
 
@@ -83,7 +106,7 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page,
                if (unlikely(bad_pmem))
                        rc = -EIO;
                else {
-                       rc = memcpy_from_pmem(mem + off, pmem_addr, len);
+                       rc = read_pmem(page, off, pmem_addr, len);
                        flush_dcache_page(page);
                }
        } else {
@@ -102,14 +125,13 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page,
                 * after clear poison.
                 */
                flush_dcache_page(page);
-               memcpy_to_pmem(pmem_addr, mem + off, len);
+               write_pmem(pmem_addr, page, off, len);
                if (unlikely(bad_pmem)) {
-                       pmem_clear_poison(pmem, pmem_off, len);
-                       memcpy_to_pmem(pmem_addr, mem + off, len);
+                       rc = pmem_clear_poison(pmem, pmem_off, len);
+                       write_pmem(pmem_addr, page, off, len);
                }
        }
 
-       kunmap_atomic(mem);
        return rc;
 }