libnvdimm, region: fix flush hint table thinko
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>
This commit is contained in:
parent
11294d63ac
commit
595c73071e
@ -52,10 +52,28 @@ struct nvdimm_drvdata {
|
|||||||
struct nd_region_data {
|
struct nd_region_data {
|
||||||
int ns_count;
|
int ns_count;
|
||||||
int ns_active;
|
int ns_active;
|
||||||
unsigned int flush_mask;
|
unsigned int hints_shift;
|
||||||
void __iomem *flush_wpq[0][0];
|
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(
|
static inline struct nd_namespace_index *to_namespace_index(
|
||||||
struct nvdimm_drvdata *ndd, int i)
|
struct nvdimm_drvdata *ndd, int i)
|
||||||
{
|
{
|
||||||
|
@ -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),
|
dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm),
|
||||||
nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es");
|
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];
|
struct resource *res = &nvdimm->flush_wpq[i];
|
||||||
unsigned long pfn = PHYS_PFN(res->start);
|
unsigned long pfn = PHYS_PFN(res->start);
|
||||||
void __iomem *flush_page;
|
void __iomem *flush_page;
|
||||||
@ -54,14 +54,15 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
|
|||||||
|
|
||||||
if (j < i)
|
if (j < i)
|
||||||
flush_page = (void __iomem *) ((unsigned long)
|
flush_page = (void __iomem *) ((unsigned long)
|
||||||
ndrd->flush_wpq[dimm][j] & PAGE_MASK);
|
ndrd_get_flush_wpq(ndrd, dimm, j)
|
||||||
|
& PAGE_MASK);
|
||||||
else
|
else
|
||||||
flush_page = devm_nvdimm_ioremap(dev,
|
flush_page = devm_nvdimm_ioremap(dev,
|
||||||
PFN_PHYS(pfn), PAGE_SIZE);
|
PFN_PHYS(pfn), PAGE_SIZE);
|
||||||
if (!flush_page)
|
if (!flush_page)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
ndrd->flush_wpq[dimm][i] = flush_page
|
ndrd_set_flush_wpq(ndrd, dimm, i, flush_page
|
||||||
+ (res->start & ~PAGE_MASK);
|
+ (res->start & ~PAGE_MASK));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -93,7 +94,10 @@ int nd_region_activate(struct nd_region *nd_region)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dev_set_drvdata(dev, ndrd);
|
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++) {
|
for (i = 0; i < nd_region->ndr_mappings; i++) {
|
||||||
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
|
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
|
||||||
struct nvdimm *nvdimm = nd_mapping->nvdimm;
|
struct nvdimm *nvdimm = nd_mapping->nvdimm;
|
||||||
@ -900,8 +904,8 @@ void nvdimm_flush(struct nd_region *nd_region)
|
|||||||
*/
|
*/
|
||||||
wmb();
|
wmb();
|
||||||
for (i = 0; i < nd_region->ndr_mappings; i++)
|
for (i = 0; i < nd_region->ndr_mappings; i++)
|
||||||
if (ndrd->flush_wpq[i][0])
|
if (ndrd_get_flush_wpq(ndrd, i, 0))
|
||||||
writeq(1, ndrd->flush_wpq[i][idx & ndrd->flush_mask]);
|
writeq(1, ndrd_get_flush_wpq(ndrd, i, idx));
|
||||||
wmb();
|
wmb();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nvdimm_flush);
|
EXPORT_SYMBOL_GPL(nvdimm_flush);
|
||||||
@ -925,7 +929,7 @@ int nvdimm_has_flush(struct nd_region *nd_region)
|
|||||||
|
|
||||||
for (i = 0; i < nd_region->ndr_mappings; i++)
|
for (i = 0; i < nd_region->ndr_mappings; i++)
|
||||||
/* flush hints present, flushing required */
|
/* flush hints present, flushing required */
|
||||||
if (ndrd->flush_wpq[i][0])
|
if (ndrd_get_flush_wpq(ndrd, i, 0))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user