aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2017-02-28 21:32:48 -0500
committerDan Williams <dan.j.williams@intel.com>2017-03-01 03:49:42 -0500
commit86ef58a4e35e8fa66afb5898cf6dec6a3bb29f67 (patch)
tree95f6525d1319cc5bd7803518a96beb1e227f173a
parentdf06a2d57711a1472ced72207373eeb6422d4721 (diff)
nfit, libnvdimm: fix interleave set cookie calculation
The interleave-set cookie is a sum that sanity checks the composition of an interleave set has not changed from when the namespace was initially created. The checksum is calculated by sorting the DIMMs by their location in the interleave-set. The comparison for the sort must be 64-bit wide, not byte-by-byte as performed by memcmp() in the broken case. Fix the implementation to accept correct cookie values in addition to the Linux "memcmp" order cookies, but only allow correct cookies to be generated going forward. It does mean that namespaces created by third-party-tooling, or created by newer kernels with this fix, will not validate on older kernels. However, there are a couple mitigating conditions: 1/ platforms with namespace-label capable NVDIMMs are not widely available. 2/ interleave-sets with a single-dimm are by definition not affected (nothing to sort). This covers the QEMU-KVM NVDIMM emulation case. The cookie stored in the namespace label will be fixed by any write the namespace label, the most straightforward way to achieve this is to write to the "alt_name" attribute of a namespace in sysfs. Cc: <stable@vger.kernel.org> Fixes: eaf961536e16 ("libnvdimm, nfit: add interleave-set state-tracking infrastructure") Reported-by: Nicholas Moulin <nicholas.w.moulin@linux.intel.com> Tested-by: Nicholas Moulin <nicholas.w.moulin@linux.intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/acpi/nfit/core.c16
-rw-r--r--drivers/nvdimm/namespace_devs.c18
-rw-r--r--drivers/nvdimm/nd.h1
-rw-r--r--drivers/nvdimm/region_devs.c9
-rw-r--r--include/linux/libnvdimm.h2
5 files changed, 41 insertions, 5 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 7361d00818e2..662036bdc65e 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -1603,7 +1603,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
1603 + num_mappings * sizeof(struct nfit_set_info_map); 1603 + num_mappings * sizeof(struct nfit_set_info_map);
1604} 1604}
1605 1605
1606static int cmp_map(const void *m0, const void *m1) 1606static int cmp_map_compat(const void *m0, const void *m1)
1607{ 1607{
1608 const struct nfit_set_info_map *map0 = m0; 1608 const struct nfit_set_info_map *map0 = m0;
1609 const struct nfit_set_info_map *map1 = m1; 1609 const struct nfit_set_info_map *map1 = m1;
@@ -1612,6 +1612,14 @@ static int cmp_map(const void *m0, const void *m1)
1612 sizeof(u64)); 1612 sizeof(u64));
1613} 1613}
1614 1614
1615static int cmp_map(const void *m0, const void *m1)
1616{
1617 const struct nfit_set_info_map *map0 = m0;
1618 const struct nfit_set_info_map *map1 = m1;
1619
1620 return map0->region_offset - map1->region_offset;
1621}
1622
1615/* Retrieve the nth entry referencing this spa */ 1623/* Retrieve the nth entry referencing this spa */
1616static struct acpi_nfit_memory_map *memdev_from_spa( 1624static struct acpi_nfit_memory_map *memdev_from_spa(
1617 struct acpi_nfit_desc *acpi_desc, u16 range_index, int n) 1625 struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1667,6 +1675,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
1667 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), 1675 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
1668 cmp_map, NULL); 1676 cmp_map, NULL);
1669 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); 1677 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1678
1679 /* support namespaces created with the wrong sort order */
1680 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
1681 cmp_map_compat, NULL);
1682 nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1683
1670 ndr_desc->nd_set = nd_set; 1684 ndr_desc->nd_set = nd_set;
1671 devm_kfree(dev, info); 1685 devm_kfree(dev, info);
1672 1686
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index ce3e8dfa10ad..1b481a5fb966 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1700,6 +1700,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
1700struct device *create_namespace_pmem(struct nd_region *nd_region, 1700struct device *create_namespace_pmem(struct nd_region *nd_region,
1701 struct nd_namespace_label *nd_label) 1701 struct nd_namespace_label *nd_label)
1702{ 1702{
1703 u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
1703 u64 cookie = nd_region_interleave_set_cookie(nd_region); 1704 u64 cookie = nd_region_interleave_set_cookie(nd_region);
1704 struct nd_label_ent *label_ent; 1705 struct nd_label_ent *label_ent;
1705 struct nd_namespace_pmem *nspm; 1706 struct nd_namespace_pmem *nspm;
@@ -1718,7 +1719,11 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
1718 if (__le64_to_cpu(nd_label->isetcookie) != cookie) { 1719 if (__le64_to_cpu(nd_label->isetcookie) != cookie) {
1719 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", 1720 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
1720 nd_label->uuid); 1721 nd_label->uuid);
1721 return ERR_PTR(-EAGAIN); 1722 if (__le64_to_cpu(nd_label->isetcookie) != altcookie)
1723 return ERR_PTR(-EAGAIN);
1724
1725 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
1726 nd_label->uuid);
1722 } 1727 }
1723 1728
1724 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); 1729 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
@@ -1733,9 +1738,14 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
1733 res->name = dev_name(&nd_region->dev); 1738 res->name = dev_name(&nd_region->dev);
1734 res->flags = IORESOURCE_MEM; 1739 res->flags = IORESOURCE_MEM;
1735 1740
1736 for (i = 0; i < nd_region->ndr_mappings; i++) 1741 for (i = 0; i < nd_region->ndr_mappings; i++) {
1737 if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) 1742 if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1738 break; 1743 continue;
1744 if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
1745 continue;
1746 break;
1747 }
1748
1739 if (i < nd_region->ndr_mappings) { 1749 if (i < nd_region->ndr_mappings) {
1740 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); 1750 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]);
1741 1751
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 35dd75057e16..2a99c83aa19f 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -328,6 +328,7 @@ struct nd_region *to_nd_region(struct device *dev);
328int nd_region_to_nstype(struct nd_region *nd_region); 328int nd_region_to_nstype(struct nd_region *nd_region);
329int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 329int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
330u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); 330u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
331u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
331void nvdimm_bus_lock(struct device *dev); 332void nvdimm_bus_lock(struct device *dev);
332void nvdimm_bus_unlock(struct device *dev); 333void nvdimm_bus_unlock(struct device *dev);
333bool is_nvdimm_bus_locked(struct device *dev); 334bool is_nvdimm_bus_locked(struct device *dev);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 7cd705f3247c..b7cb5066d961 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -505,6 +505,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
505 return 0; 505 return 0;
506} 506}
507 507
508u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region)
509{
510 struct nd_interleave_set *nd_set = nd_region->nd_set;
511
512 if (nd_set)
513 return nd_set->altcookie;
514 return 0;
515}
516
508void nd_mapping_free_labels(struct nd_mapping *nd_mapping) 517void nd_mapping_free_labels(struct nd_mapping *nd_mapping)
509{ 518{
510 struct nd_label_ent *label_ent, *e; 519 struct nd_label_ent *label_ent, *e;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 8458c5351e56..77e7af32543f 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -70,6 +70,8 @@ struct nd_cmd_desc {
70 70
71struct nd_interleave_set { 71struct nd_interleave_set {
72 u64 cookie; 72 u64 cookie;
73 /* compatibility with initial buggy Linux implementation */
74 u64 altcookie;
73}; 75};
74 76
75struct nd_mapping_desc { 77struct nd_mapping_desc {