]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/5.0.18/libnvdimm-namespace-fix-label-tracking-error.patch
Linux 5.0.18
[thirdparty/kernel/stable-queue.git] / releases / 5.0.18 / libnvdimm-namespace-fix-label-tracking-error.patch
1 From c4703ce11c23423d4b46e3d59aef7979814fd608 Mon Sep 17 00:00:00 2001
2 From: Dan Williams <dan.j.williams@intel.com>
3 Date: Tue, 30 Apr 2019 21:51:21 -0700
4 Subject: libnvdimm/namespace: Fix label tracking error
5
6 From: Dan Williams <dan.j.williams@intel.com>
7
8 commit c4703ce11c23423d4b46e3d59aef7979814fd608 upstream.
9
10 Users have reported intermittent occurrences of DIMM initialization
11 failures due to duplicate allocations of address capacity detected in
12 the 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
27 Unfortunately those reports were typically with a busy parallel
28 namespace creation / destruction loop making it difficult to see the
29 components of the bug. However, Jane provided a simple reproducer using
30 the work-in-progress sub-section implementation.
31
32 When ndctl is reconfiguring a namespace it may take an existing defunct
33 / disabled namespace and reconfigure it with a new uuid and other
34 parameters. Critically namespace_update_uuid() takes existing address
35 resources and renames them for the new namespace to use / reconfigure as
36 it sees fit. The bug is that this rename only happens in the resource
37 tracking tree. Existing labels with the old uuid are not reaped leading
38 to a scenario where multiple active labels reference the same span of
39 address range.
40
41 Teach namespace_update_uuid() to flag any references to the old uuid for
42 reaping at the next label update attempt.
43
44 Cc: <stable@vger.kernel.org>
45 Fixes: bf9bccc14c05 ("libnvdimm: pmem label sets and namespace instantiation")
46 Link: https://github.com/pmem/ndctl/issues/91
47 Reported-by: Jane Chu <jane.chu@oracle.com>
48 Reported-by: Jeff Moyer <jmoyer@redhat.com>
49 Reported-by: Erwin Tsaur <erwin.tsaur@oracle.com>
50 Cc: Johannes Thumshirn <jthumshirn@suse.de>
51 Signed-off-by: Dan Williams <dan.j.williams@intel.com>
52 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
53
54 ---
55 drivers/nvdimm/label.c | 29 ++++++++++++++++-------------
56 drivers/nvdimm/namespace_devs.c | 15 +++++++++++++++
57 drivers/nvdimm/nd.h | 4 ++++
58 3 files changed, 35 insertions(+), 13 deletions(-)
59
60 --- a/drivers/nvdimm/label.c
61 +++ b/drivers/nvdimm/label.c
62 @@ -753,6 +753,17 @@ static const guid_t *to_abstraction_guid
63 return &guid_null;
64 }
65
66 +static void reap_victim(struct nd_mapping *nd_mapping,
67 + struct nd_label_ent *victim)
68 +{
69 + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
70 + u32 slot = to_slot(ndd, victim->label);
71 +
72 + dev_dbg(ndd->dev, "free: %d\n", slot);
73 + nd_label_free_slot(ndd, slot);
74 + victim->label = NULL;
75 +}
76 +
77 static int __pmem_label_update(struct nd_region *nd_region,
78 struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm,
79 int pos, unsigned long flags)
80 @@ -760,9 +771,9 @@ static int __pmem_label_update(struct nd
81 struct nd_namespace_common *ndns = &nspm->nsio.common;
82 struct nd_interleave_set *nd_set = nd_region->nd_set;
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 @@ -831,18 +842,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, "free: %d\n", 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 @@ -1247,12 +1247,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