summaryrefslogtreecommitdiffstats
path: root/drivers/nvdimm
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-07-07 22:44:50 -0400
committerDan Williams <dan.j.williams@intel.com>2016-07-11 19:13:42 -0400
commitf284a4f23752d0334e482d04e0a584d19c9c8cd0 (patch)
tree98bf766185ec55c8f1f3d3251dfb6ad21fdfa92d /drivers/nvdimm
parenta8f720224eff09ea684b7f74aa1c0dff3da03170 (diff)
libnvdimm: introduce nvdimm_flush() and nvdimm_has_flush()
nvdimm_flush() is a replacement for the x86 'pcommit' instruction. It is an optional write flushing mechanism that an nvdimm bus can provide for the pmem driver to consume. In the case of the NFIT nvdimm-bus-provider nvdimm_flush() is implemented as a series of flush-hint-address [1] writes to each dimm in the interleave set (region) that backs the namespace. The nvdimm_has_flush() routine relies on platform firmware to describe the flushing capabilities of a platform. It uses the heuristic of whether an nvdimm bus provider provides flush address data to return a ternary result: 1: flush addresses defined 0: dimm topology described without flush addresses (assume ADR) -errno: no topology information, unable to determine flush mechanism The pmem driver is expected to take the following actions on this ternary result: 1: nvdimm_flush() in response to REQ_FUA / REQ_FLUSH and shutdown 0: do not set, WC or FUA on the queue, take no further action -errno: warn and then operate as if nvdimm_has_flush() returned '0' The caveat of this heuristic is that it can not distinguish the "dimm does not have flush address" case from the "platform firmware is broken and failed to describe a flush address". Given we are already explicitly trusting the NFIT there's not much more we can do beyond blacklisting broken firmwares if they are ever encountered. Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r--drivers/nvdimm/pmem.c27
-rw-r--r--drivers/nvdimm/region_devs.c61
2 files changed, 82 insertions, 6 deletions
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index b6fcb97a601c..e303655f243e 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -33,10 +33,24 @@
33#include "pfn.h" 33#include "pfn.h"
34#include "nd.h" 34#include "nd.h"
35 35
36static struct device *to_dev(struct pmem_device *pmem)
37{
38 /*
39 * nvdimm bus services need a 'dev' parameter, and we record the device
40 * at init in bb.dev.
41 */
42 return pmem->bb.dev;
43}
44
45static struct nd_region *to_region(struct pmem_device *pmem)
46{
47 return to_nd_region(to_dev(pmem)->parent);
48}
49
36static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset, 50static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset,
37 unsigned int len) 51 unsigned int len)
38{ 52{
39 struct device *dev = pmem->bb.dev; 53 struct device *dev = to_dev(pmem);
40 sector_t sector; 54 sector_t sector;
41 long cleared; 55 long cleared;
42 56
@@ -122,7 +136,7 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
122 nd_iostat_end(bio, start); 136 nd_iostat_end(bio, start);
123 137
124 if (bio_data_dir(bio)) 138 if (bio_data_dir(bio))
125 wmb_pmem(); 139 nvdimm_flush(to_region(pmem));
126 140
127 bio_endio(bio); 141 bio_endio(bio);
128 return BLK_QC_T_NONE; 142 return BLK_QC_T_NONE;
@@ -136,7 +150,7 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
136 150
137 rc = pmem_do_bvec(pmem, page, PAGE_SIZE, 0, rw, sector); 151 rc = pmem_do_bvec(pmem, page, PAGE_SIZE, 0, rw, sector);
138 if (rw & WRITE) 152 if (rw & WRITE)
139 wmb_pmem(); 153 nvdimm_flush(to_region(pmem));
140 154
141 /* 155 /*
142 * The ->rw_page interface is subtle and tricky. The core 156 * The ->rw_page interface is subtle and tricky. The core
@@ -193,6 +207,7 @@ static int pmem_attach_disk(struct device *dev,
193 struct nd_namespace_common *ndns) 207 struct nd_namespace_common *ndns)
194{ 208{
195 struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); 209 struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
210 struct nd_region *nd_region = to_nd_region(dev->parent);
196 struct vmem_altmap __altmap, *altmap = NULL; 211 struct vmem_altmap __altmap, *altmap = NULL;
197 struct resource *res = &nsio->res; 212 struct resource *res = &nsio->res;
198 struct nd_pfn *nd_pfn = NULL; 213 struct nd_pfn *nd_pfn = NULL;
@@ -222,7 +237,7 @@ static int pmem_attach_disk(struct device *dev,
222 dev_set_drvdata(dev, pmem); 237 dev_set_drvdata(dev, pmem);
223 pmem->phys_addr = res->start; 238 pmem->phys_addr = res->start;
224 pmem->size = resource_size(res); 239 pmem->size = resource_size(res);
225 if (!arch_has_wmb_pmem()) 240 if (nvdimm_has_flush(nd_region) < 0)
226 dev_warn(dev, "unable to guarantee persistence of writes\n"); 241 dev_warn(dev, "unable to guarantee persistence of writes\n");
227 242
228 if (!devm_request_mem_region(dev, res->start, resource_size(res), 243 if (!devm_request_mem_region(dev, res->start, resource_size(res),
@@ -284,7 +299,7 @@ static int pmem_attach_disk(struct device *dev,
284 / 512); 299 / 512);
285 if (devm_init_badblocks(dev, &pmem->bb)) 300 if (devm_init_badblocks(dev, &pmem->bb))
286 return -ENOMEM; 301 return -ENOMEM;
287 nvdimm_badblocks_populate(to_nd_region(dev->parent), &pmem->bb, res); 302 nvdimm_badblocks_populate(nd_region, &pmem->bb, res);
288 disk->bb = &pmem->bb; 303 disk->bb = &pmem->bb;
289 add_disk(disk); 304 add_disk(disk);
290 305
@@ -331,8 +346,8 @@ static int nd_pmem_remove(struct device *dev)
331 346
332static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) 347static void nd_pmem_notify(struct device *dev, enum nvdimm_event event)
333{ 348{
334 struct nd_region *nd_region = to_nd_region(dev->parent);
335 struct pmem_device *pmem = dev_get_drvdata(dev); 349 struct pmem_device *pmem = dev_get_drvdata(dev);
350 struct nd_region *nd_region = to_region(pmem);
336 resource_size_t offset = 0, end_trunc = 0; 351 resource_size_t offset = 0, end_trunc = 0;
337 struct nd_namespace_common *ndns; 352 struct nd_namespace_common *ndns;
338 struct nd_namespace_io *nsio; 353 struct nd_namespace_io *nsio;
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 67022f74febc..5d97b127b715 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -14,12 +14,19 @@
14#include <linux/highmem.h> 14#include <linux/highmem.h>
15#include <linux/sched.h> 15#include <linux/sched.h>
16#include <linux/slab.h> 16#include <linux/slab.h>
17#include <linux/pmem.h>
17#include <linux/sort.h> 18#include <linux/sort.h>
18#include <linux/io.h> 19#include <linux/io.h>
19#include <linux/nd.h> 20#include <linux/nd.h>
20#include "nd-core.h" 21#include "nd-core.h"
21#include "nd.h" 22#include "nd.h"
22 23
24/*
25 * For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is
26 * irrelevant.
27 */
28#include <linux/io-64-nonatomic-hi-lo.h>
29
23static DEFINE_IDA(region_ida); 30static DEFINE_IDA(region_ida);
24 31
25static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm, 32static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
@@ -864,6 +871,60 @@ struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus,
864} 871}
865EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create); 872EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create);
866 873
874/**
875 * nvdimm_flush - flush any posted write queues between the cpu and pmem media
876 * @nd_region: blk or interleaved pmem region
877 */
878void nvdimm_flush(struct nd_region *nd_region)
879{
880 struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev);
881 int i;
882
883 /*
884 * The first wmb() is needed to 'sfence' all previous writes
885 * such that they are architecturally visible for the platform
886 * buffer flush. Note that we've already arranged for pmem
887 * writes to avoid the cache via arch_memcpy_to_pmem(). The
888 * final wmb() ensures ordering for the NVDIMM flush write.
889 */
890 wmb();
891 for (i = 0; i < nd_region->ndr_mappings; i++)
892 if (ndrd->flush_wpq[i][0])
893 writeq(1, ndrd->flush_wpq[i][0]);
894 wmb();
895}
896EXPORT_SYMBOL_GPL(nvdimm_flush);
897
898/**
899 * nvdimm_has_flush - determine write flushing requirements
900 * @nd_region: blk or interleaved pmem region
901 *
902 * Returns 1 if writes require flushing
903 * Returns 0 if writes do not require flushing
904 * Returns -ENXIO if flushing capability can not be determined
905 */
906int nvdimm_has_flush(struct nd_region *nd_region)
907{
908 struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev);
909 int i;
910
911 /* no nvdimm == flushing capability unknown */
912 if (nd_region->ndr_mappings == 0)
913 return -ENXIO;
914
915 for (i = 0; i < nd_region->ndr_mappings; i++)
916 /* flush hints present, flushing required */
917 if (ndrd->flush_wpq[i][0])
918 return 1;
919
920 /*
921 * The platform defines dimm devices without hints, assume
922 * platform persistence mechanism like ADR
923 */
924 return 0;
925}
926EXPORT_SYMBOL_GPL(nvdimm_has_flush);
927
867void __exit nd_region_devs_exit(void) 928void __exit nd_region_devs_exit(void)
868{ 929{
869 ida_destroy(&region_ida); 930 ida_destroy(&region_ida);