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 pmem_do_bvec(struct pmem_device *pmem, struct page *page, unsigned int len, unsigned int off, int rw, sector_t sector) { int rc = 0; bool bad_pmem = false; void *mem = kmap_atomic(page); phys_addr_t pmem_off = sector * 512 + pmem->data_offset; void __pmem *pmem_addr = pmem->virt_addr + pmem_off; if (unlikely(is_bad_pmem(&pmem->bb, sector, len))) bad_pmem = true; if (rw == READ) { if (unlikely(bad_pmem)) rc = -EIO; else { rc = memcpy_from_pmem(mem + off, pmem_addr, len); flush_dcache_page(page); } } else { /* * Note that we write the data both before and after * clearing poison. The write before clear poison * handles situations where the latest written data is * preserved and the clear poison operation simply marks * the address range as valid without changing the data. * In this case application software can assume that an * interrupted write will either return the new good * data or an error. * * However, if pmem_clear_poison() leaves the data in an * indeterminate state we need to perform the write * after clear poison. */ flush_dcache_page(page); memcpy_to_pmem(pmem_addr, mem + off, len); if (unlikely(bad_pmem)) { pmem_clear_poison(pmem, pmem_off, len); memcpy_to_pmem(pmem_addr, mem + off, len); } } kunmap_atomic(mem); return rc; }
static void pmem_do_bvec(struct pmem_device *pmem, struct page *page, unsigned int len, unsigned int off, int rw, sector_t sector) { void *mem = kmap_atomic(page); phys_addr_t pmem_off = sector * 512 + pmem->data_offset; void __pmem *pmem_addr = pmem->virt_addr + pmem_off; if (rw == READ) { memcpy_from_pmem(mem + off, pmem_addr, len); flush_dcache_page(page); } else { flush_dcache_page(page); memcpy_to_pmem(pmem_addr, mem + off, len); } kunmap_atomic(mem); }
static int pmem_rw_bytes(struct nd_namespace_common *ndns, resource_size_t offset, void *buf, size_t size, int rw) { struct pmem_device *pmem = dev_get_drvdata(ndns->claim); if (unlikely(offset + size > pmem->size)) { dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); return -EFAULT; } if (rw == READ) memcpy_from_pmem(buf, pmem->virt_addr + offset, size); else { memcpy_to_pmem(pmem->virt_addr + offset, buf, size); wmb_pmem(); } return 0; }
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); if (unlikely(offset + size > nsio->size)) { dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); return -EFAULT; } if (rw == READ) { unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); if (unlikely(is_bad_pmem(&nsio->bb, offset / 512, sz_align))) return -EIO; return memcpy_from_pmem(buf, nsio->addr + offset, size); } else { memcpy_to_pmem(nsio->addr + offset, buf, size); nvdimm_flush(to_nd_region(ndns->dev.parent)); } return 0; }