Skip to content

Commit

Permalink
libnvdimm, region: fix flush hint table thinko
Browse files Browse the repository at this point in the history
The definition of the flush hint table as:

	void __iomem *flush_wpq[0][0];

...passed the unit test, but is broken as flush_wpq[0][1] and
flush_wpq[1][0] refer to the same entry.  Fix this to use a helper that
calculates a slot in the table based on the geometry of flush hints in
the region.  This is important to get right since virtualization
solutions use this mechanism to trigger hypervisor flushes to platform
persistence.

Reported-by: Dave Jiang <dave.jiang@intel.com>
Tested-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
  • Loading branch information
djbw committed Sep 24, 2016
1 parent 11294d6 commit 595c730
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 10 deletions.
22 changes: 20 additions & 2 deletions drivers/nvdimm/nd.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,28 @@ struct nvdimm_drvdata {
struct nd_region_data {
int ns_count;
int ns_active;
unsigned int flush_mask;
void __iomem *flush_wpq[0][0];
unsigned int hints_shift;
void __iomem *flush_wpq[0];
};

static inline void __iomem *ndrd_get_flush_wpq(struct nd_region_data *ndrd,
int dimm, int hint)
{
unsigned int num = 1 << ndrd->hints_shift;
unsigned int mask = num - 1;

return ndrd->flush_wpq[dimm * num + (hint & mask)];
}

static inline void ndrd_set_flush_wpq(struct nd_region_data *ndrd, int dimm,
int hint, void __iomem *flush)
{
unsigned int num = 1 << ndrd->hints_shift;
unsigned int mask = num - 1;

ndrd->flush_wpq[dimm * num + (hint & mask)] = flush;
}

static inline struct nd_namespace_index *to_namespace_index(
struct nvdimm_drvdata *ndd, int i)
{
Expand Down
20 changes: 12 additions & 8 deletions drivers/nvdimm/region_devs.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,

dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm),
nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es");
for (i = 0; i < nvdimm->num_flush; i++) {
for (i = 0; i < (1 << ndrd->hints_shift); i++) {
struct resource *res = &nvdimm->flush_wpq[i];
unsigned long pfn = PHYS_PFN(res->start);
void __iomem *flush_page;
Expand All @@ -54,14 +54,15 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,

if (j < i)
flush_page = (void __iomem *) ((unsigned long)
ndrd->flush_wpq[dimm][j] & PAGE_MASK);
ndrd_get_flush_wpq(ndrd, dimm, j)
& PAGE_MASK);
else
flush_page = devm_nvdimm_ioremap(dev,
PFN_PHYS(pfn), PAGE_SIZE);
if (!flush_page)
return -ENXIO;
ndrd->flush_wpq[dimm][i] = flush_page
+ (res->start & ~PAGE_MASK);
ndrd_set_flush_wpq(ndrd, dimm, i, flush_page
+ (res->start & ~PAGE_MASK));
}

return 0;
Expand Down Expand Up @@ -93,7 +94,10 @@ int nd_region_activate(struct nd_region *nd_region)
return -ENOMEM;
dev_set_drvdata(dev, ndrd);

ndrd->flush_mask = (1 << ilog2(num_flush)) - 1;
if (!num_flush)
return 0;

ndrd->hints_shift = ilog2(num_flush);
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm *nvdimm = nd_mapping->nvdimm;
Expand Down Expand Up @@ -900,8 +904,8 @@ 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][idx & ndrd->flush_mask]);
if (ndrd_get_flush_wpq(ndrd, i, 0))
writeq(1, ndrd_get_flush_wpq(ndrd, i, idx));
wmb();
}
EXPORT_SYMBOL_GPL(nvdimm_flush);
Expand All @@ -925,7 +929,7 @@ int nvdimm_has_flush(struct nd_region *nd_region)

for (i = 0; i < nd_region->ndr_mappings; i++)
/* flush hints present, flushing required */
if (ndrd->flush_wpq[i][0])
if (ndrd_get_flush_wpq(ndrd, i, 0))
return 1;

/*
Expand Down

0 comments on commit 595c730

Please sign in to comment.