summaryrefslogtreecommitdiffstats
path: root/drivers/nvdimm/region_devs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nvdimm/region_devs.c')
-rw-r--r--drivers/nvdimm/region_devs.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 5d97b127b715..e8d5ba7b29af 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -14,6 +14,7 @@
#include <linux/highmem.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/hash.h>
#include <linux/pmem.h>
#include <linux/sort.h>
#include <linux/io.h>
@@ -28,6 +29,7 @@
#include <linux/io-64-nonatomic-hi-lo.h>
static DEFINE_IDA(region_ida);
+static DEFINE_PER_CPU(int, flush_idx);
static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
struct nd_region_data *ndrd)
@@ -67,7 +69,7 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
int nd_region_activate(struct nd_region *nd_region)
{
- int i;
+ int i, num_flush = 0;
struct nd_region_data *ndrd;
struct device *dev = &nd_region->dev;
size_t flush_data_size = sizeof(void *);
@@ -79,6 +81,7 @@ int nd_region_activate(struct nd_region *nd_region)
/* at least one null hint slot per-dimm for the "no-hint" case */
flush_data_size += sizeof(void *);
+ num_flush = min_not_zero(num_flush, nvdimm->num_flush);
if (!nvdimm->num_flush)
continue;
flush_data_size += nvdimm->num_flush * sizeof(void *);
@@ -90,6 +93,7 @@ int nd_region_activate(struct nd_region *nd_region)
return -ENOMEM;
dev_set_drvdata(dev, ndrd);
+ ndrd->flush_mask = (1 << ilog2(num_flush)) - 1;
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm *nvdimm = nd_mapping->nvdimm;
@@ -878,7 +882,14 @@ EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create);
void nvdimm_flush(struct nd_region *nd_region)
{
struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev);
- int i;
+ int i, idx;
+
+ /*
+ * Try to encourage some diversity in flush hint addresses
+ * across cpus assuming a limited number of flush hints.
+ */
+ idx = this_cpu_read(flush_idx);
+ idx = this_cpu_add_return(flush_idx, hash_32(current->pid + idx, 8));
/*
* The first wmb() is needed to 'sfence' all previous writes
@@ -890,7 +901,7 @@ void nvdimm_flush(struct nd_region *nd_region)
wmb();
for (i = 0; i < nd_region->ndr_mappings; i++)
if (ndrd->flush_wpq[i][0])
- writeq(1, ndrd->flush_wpq[i][0]);
+ writeq(1, ndrd->flush_wpq[i][idx & ndrd->flush_mask]);
wmb();
}
EXPORT_SYMBOL_GPL(nvdimm_flush);