]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.9.180/libnvdimm-namespace-fix-label-tracking-error.patch
Linux 4.9.180
[thirdparty/kernel/stable-queue.git] / releases / 4.9.180 / libnvdimm-namespace-fix-label-tracking-error.patch
CommitLineData
484c4e84
GKH
1From c4703ce11c23423d4b46e3d59aef7979814fd608 Mon Sep 17 00:00:00 2001
2From: Dan Williams <dan.j.williams@intel.com>
3Date: Tue, 30 Apr 2019 21:51:21 -0700
4Subject: libnvdimm/namespace: Fix label tracking error
5
6From: Dan Williams <dan.j.williams@intel.com>
7
8commit c4703ce11c23423d4b46e3d59aef7979814fd608 upstream.
9
10Users have reported intermittent occurrences of DIMM initialization
11failures due to duplicate allocations of address capacity detected in
12the labels, or errors of the form below, both have the same root cause.
13
14 nd namespace1.4: failed to track label: 0
15 WARNING: CPU: 17 PID: 1381 at drivers/nvdimm/label.c:863
16
17 RIP: 0010:__pmem_label_update+0x56c/0x590 [libnvdimm]
18 Call Trace:
19 ? nd_pmem_namespace_label_update+0xd6/0x160 [libnvdimm]
20 nd_pmem_namespace_label_update+0xd6/0x160 [libnvdimm]
21 uuid_store+0x17e/0x190 [libnvdimm]
22 kernfs_fop_write+0xf0/0x1a0
23 vfs_write+0xb7/0x1b0
24 ksys_write+0x57/0xd0
25 do_syscall_64+0x60/0x210
26
27Unfortunately those reports were typically with a busy parallel
28namespace creation / destruction loop making it difficult to see the
29components of the bug. However, Jane provided a simple reproducer using
30the work-in-progress sub-section implementation.
31
32When ndctl is reconfiguring a namespace it may take an existing defunct
33/ disabled namespace and reconfigure it with a new uuid and other
34parameters. Critically namespace_update_uuid() takes existing address
35resources and renames them for the new namespace to use / reconfigure as
36it sees fit. The bug is that this rename only happens in the resource
37tracking tree. Existing labels with the old uuid are not reaped leading
38to a scenario where multiple active labels reference the same span of
39address range.
40
41Teach namespace_update_uuid() to flag any references to the old uuid for
42reaping at the next label update attempt.
43
44Cc: <stable@vger.kernel.org>
45Fixes: bf9bccc14c05 ("libnvdimm: pmem label sets and namespace instantiation")
46Link: https://github.com/pmem/ndctl/issues/91
47Reported-by: Jane Chu <jane.chu@oracle.com>
48Reported-by: Jeff Moyer <jmoyer@redhat.com>
49Reported-by: Erwin Tsaur <erwin.tsaur@oracle.com>
50Cc: Johannes Thumshirn <jthumshirn@suse.de>
51Signed-off-by: Dan Williams <dan.j.williams@intel.com>
52Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
53
54
55---
56 drivers/nvdimm/label.c | 29 ++++++++++++++++-------------
57 drivers/nvdimm/namespace_devs.c | 15 +++++++++++++++
58 drivers/nvdimm/nd.h | 4 ++++
59 3 files changed, 35 insertions(+), 13 deletions(-)
60
61--- a/drivers/nvdimm/label.c
62+++ b/drivers/nvdimm/label.c
63@@ -490,15 +490,26 @@ static unsigned long nd_label_offset(str
64 - (unsigned long) to_namespace_index(ndd, 0);
65 }
66
67+static void reap_victim(struct nd_mapping *nd_mapping,
68+ struct nd_label_ent *victim)
69+{
70+ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
71+ u32 slot = to_slot(ndd, victim->label);
72+
73+ dev_dbg(ndd->dev, "free: %d\n", slot);
74+ nd_label_free_slot(ndd, slot);
75+ victim->label = NULL;
76+}
77+
78 static int __pmem_label_update(struct nd_region *nd_region,
79 struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm,
80 int pos, unsigned long flags)
81 {
82 u64 cookie = nd_region_interleave_set_cookie(nd_region);
83 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
84- struct nd_label_ent *label_ent, *victim = NULL;
85 struct nd_namespace_label *nd_label;
86 struct nd_namespace_index *nsindex;
87+ struct nd_label_ent *label_ent;
88 struct nd_label_id label_id;
89 struct resource *res;
90 unsigned long *free;
91@@ -551,18 +562,10 @@ static int __pmem_label_update(struct nd
92 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
93 if (!label_ent->label)
94 continue;
95- if (memcmp(nspm->uuid, label_ent->label->uuid,
96- NSLABEL_UUID_LEN) != 0)
97- continue;
98- victim = label_ent;
99- list_move_tail(&victim->list, &nd_mapping->labels);
100- break;
101- }
102- if (victim) {
103- dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot);
104- slot = to_slot(ndd, victim->label);
105- nd_label_free_slot(ndd, slot);
106- victim->label = NULL;
107+ if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)
108+ || memcmp(nspm->uuid, label_ent->label->uuid,
109+ NSLABEL_UUID_LEN) == 0)
110+ reap_victim(nd_mapping, label_ent);
111 }
112
113 /* update index */
114--- a/drivers/nvdimm/namespace_devs.c
115+++ b/drivers/nvdimm/namespace_devs.c
116@@ -1210,12 +1210,27 @@ static int namespace_update_uuid(struct
117 for (i = 0; i < nd_region->ndr_mappings; i++) {
118 struct nd_mapping *nd_mapping = &nd_region->mapping[i];
119 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
120+ struct nd_label_ent *label_ent;
121 struct resource *res;
122
123 for_each_dpa_resource(ndd, res)
124 if (strcmp(res->name, old_label_id.id) == 0)
125 sprintf((void *) res->name, "%s",
126 new_label_id.id);
127+
128+ mutex_lock(&nd_mapping->lock);
129+ list_for_each_entry(label_ent, &nd_mapping->labels, list) {
130+ struct nd_namespace_label *nd_label = label_ent->label;
131+ struct nd_label_id label_id;
132+
133+ if (!nd_label)
134+ continue;
135+ nd_label_gen_id(&label_id, nd_label->uuid,
136+ __le32_to_cpu(nd_label->flags));
137+ if (strcmp(old_label_id.id, label_id.id) == 0)
138+ set_bit(ND_LABEL_REAP, &label_ent->flags);
139+ }
140+ mutex_unlock(&nd_mapping->lock);
141 }
142 kfree(*old_uuid);
143 out:
144--- a/drivers/nvdimm/nd.h
145+++ b/drivers/nvdimm/nd.h
146@@ -113,8 +113,12 @@ struct nd_percpu_lane {
147 spinlock_t lock;
148 };
149
150+enum nd_label_flags {
151+ ND_LABEL_REAP,
152+};
153 struct nd_label_ent {
154 struct list_head list;
155+ unsigned long flags;
156 struct nd_namespace_label *label;
157 };
158