]>
Commit | Line | Data |
---|---|---|
484c4e84 GKH |
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 | --- | |
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 |