Commit | Line | Data |
---|---|---|
4a826c83 DW |
1 | /* |
2 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of version 2 of the GNU General Public License as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | */ | |
13 | #include <linux/device.h> | |
14 | #include <linux/ndctl.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/nd.h> | |
17 | #include "nd-core.h" | |
18 | #include "label.h" | |
19 | #include "nd.h" | |
20 | ||
21 | static u32 best_seq(u32 a, u32 b) | |
22 | { | |
23 | a &= NSINDEX_SEQ_MASK; | |
24 | b &= NSINDEX_SEQ_MASK; | |
25 | ||
26 | if (a == 0 || a == b) | |
27 | return b; | |
28 | else if (b == 0) | |
29 | return a; | |
30 | else if (nd_inc_seq(a) == b) | |
31 | return b; | |
32 | else | |
33 | return a; | |
34 | } | |
35 | ||
36 | size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) | |
37 | { | |
38 | u32 index_span; | |
39 | ||
40 | if (ndd->nsindex_size) | |
41 | return ndd->nsindex_size; | |
42 | ||
43 | /* | |
44 | * The minimum index space is 512 bytes, with that amount of | |
45 | * index we can describe ~1400 labels which is less than a byte | |
46 | * of overhead per label. Round up to a byte of overhead per | |
47 | * label and determine the size of the index region. Yes, this | |
48 | * starts to waste space at larger config_sizes, but it's | |
49 | * unlikely we'll ever see anything but 128K. | |
50 | */ | |
51 | index_span = ndd->nsarea.config_size / 129; | |
52 | index_span /= NSINDEX_ALIGN * 2; | |
53 | ndd->nsindex_size = index_span * NSINDEX_ALIGN; | |
54 | ||
55 | return ndd->nsindex_size; | |
56 | } | |
57 | ||
58 | int nd_label_validate(struct nvdimm_drvdata *ndd) | |
59 | { | |
60 | /* | |
61 | * On media label format consists of two index blocks followed | |
62 | * by an array of labels. None of these structures are ever | |
63 | * updated in place. A sequence number tracks the current | |
64 | * active index and the next one to write, while labels are | |
65 | * written to free slots. | |
66 | * | |
67 | * +------------+ | |
68 | * | | | |
69 | * | nsindex0 | | |
70 | * | | | |
71 | * +------------+ | |
72 | * | | | |
73 | * | nsindex1 | | |
74 | * | | | |
75 | * +------------+ | |
76 | * | label0 | | |
77 | * +------------+ | |
78 | * | label1 | | |
79 | * +------------+ | |
80 | * | | | |
81 | * ....nslot... | |
82 | * | | | |
83 | * +------------+ | |
84 | * | labelN | | |
85 | * +------------+ | |
86 | */ | |
87 | struct nd_namespace_index *nsindex[] = { | |
88 | to_namespace_index(ndd, 0), | |
89 | to_namespace_index(ndd, 1), | |
90 | }; | |
91 | const int num_index = ARRAY_SIZE(nsindex); | |
92 | struct device *dev = ndd->dev; | |
93 | bool valid[2] = { 0 }; | |
94 | int i, num_valid = 0; | |
95 | u32 seq; | |
96 | ||
97 | for (i = 0; i < num_index; i++) { | |
98 | u32 nslot; | |
99 | u8 sig[NSINDEX_SIG_LEN]; | |
100 | u64 sum_save, sum, size; | |
101 | ||
102 | memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); | |
103 | if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { | |
104 | dev_dbg(dev, "%s: nsindex%d signature invalid\n", | |
105 | __func__, i); | |
106 | continue; | |
107 | } | |
108 | sum_save = __le64_to_cpu(nsindex[i]->checksum); | |
109 | nsindex[i]->checksum = __cpu_to_le64(0); | |
110 | sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); | |
111 | nsindex[i]->checksum = __cpu_to_le64(sum_save); | |
112 | if (sum != sum_save) { | |
113 | dev_dbg(dev, "%s: nsindex%d checksum invalid\n", | |
114 | __func__, i); | |
115 | continue; | |
116 | } | |
117 | ||
118 | seq = __le32_to_cpu(nsindex[i]->seq); | |
119 | if ((seq & NSINDEX_SEQ_MASK) == 0) { | |
120 | dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n", | |
121 | __func__, i, seq); | |
122 | continue; | |
123 | } | |
124 | ||
125 | /* sanity check the index against expected values */ | |
126 | if (__le64_to_cpu(nsindex[i]->myoff) | |
127 | != i * sizeof_namespace_index(ndd)) { | |
128 | dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n", | |
129 | __func__, i, (unsigned long long) | |
130 | __le64_to_cpu(nsindex[i]->myoff)); | |
131 | continue; | |
132 | } | |
133 | if (__le64_to_cpu(nsindex[i]->otheroff) | |
134 | != (!i) * sizeof_namespace_index(ndd)) { | |
135 | dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n", | |
136 | __func__, i, (unsigned long long) | |
137 | __le64_to_cpu(nsindex[i]->otheroff)); | |
138 | continue; | |
139 | } | |
140 | ||
141 | size = __le64_to_cpu(nsindex[i]->mysize); | |
142 | if (size > sizeof_namespace_index(ndd) | |
143 | || size < sizeof(struct nd_namespace_index)) { | |
144 | dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n", | |
145 | __func__, i, size); | |
146 | continue; | |
147 | } | |
148 | ||
149 | nslot = __le32_to_cpu(nsindex[i]->nslot); | |
150 | if (nslot * sizeof(struct nd_namespace_label) | |
151 | + 2 * sizeof_namespace_index(ndd) | |
152 | > ndd->nsarea.config_size) { | |
153 | dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n", | |
154 | __func__, i, nslot, | |
155 | ndd->nsarea.config_size); | |
156 | continue; | |
157 | } | |
158 | valid[i] = true; | |
159 | num_valid++; | |
160 | } | |
161 | ||
162 | switch (num_valid) { | |
163 | case 0: | |
164 | break; | |
165 | case 1: | |
166 | for (i = 0; i < num_index; i++) | |
167 | if (valid[i]) | |
168 | return i; | |
169 | /* can't have num_valid > 0 but valid[] = { false, false } */ | |
170 | WARN_ON(1); | |
171 | break; | |
172 | default: | |
173 | /* pick the best index... */ | |
174 | seq = best_seq(__le32_to_cpu(nsindex[0]->seq), | |
175 | __le32_to_cpu(nsindex[1]->seq)); | |
176 | if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) | |
177 | return 1; | |
178 | else | |
179 | return 0; | |
180 | break; | |
181 | } | |
182 | ||
183 | return -1; | |
184 | } | |
185 | ||
186 | void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst, | |
187 | struct nd_namespace_index *src) | |
188 | { | |
189 | if (dst && src) | |
190 | /* pass */; | |
191 | else | |
192 | return; | |
193 | ||
194 | memcpy(dst, src, sizeof_namespace_index(ndd)); | |
195 | } | |
196 | ||
197 | static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd) | |
198 | { | |
199 | void *base = to_namespace_index(ndd, 0); | |
200 | ||
201 | return base + 2 * sizeof_namespace_index(ndd); | |
202 | } | |
203 | ||
204 | #define for_each_clear_bit_le(bit, addr, size) \ | |
205 | for ((bit) = find_next_zero_bit_le((addr), (size), 0); \ | |
206 | (bit) < (size); \ | |
207 | (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1)) | |
208 | ||
209 | /** | |
210 | * preamble_current - common variable initialization for nd_label_* routines | |
211 | * @ndd: dimm container for the relevant label set | |
212 | * @nsindex_out: on return set to the currently active namespace index | |
213 | * @free: on return set to the free label bitmap in the index | |
214 | * @nslot: on return set to the number of slots in the label space | |
215 | */ | |
216 | static bool preamble_current(struct nvdimm_drvdata *ndd, | |
217 | struct nd_namespace_index **nsindex_out, | |
218 | unsigned long **free, u32 *nslot) | |
219 | { | |
220 | struct nd_namespace_index *nsindex; | |
221 | ||
222 | nsindex = to_current_namespace_index(ndd); | |
223 | if (nsindex == NULL) | |
224 | return false; | |
225 | ||
226 | *free = (unsigned long *) nsindex->free; | |
227 | *nslot = __le32_to_cpu(nsindex->nslot); | |
228 | *nsindex_out = nsindex; | |
229 | ||
230 | return true; | |
231 | } | |
232 | ||
233 | static char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags) | |
234 | { | |
235 | if (!label_id || !uuid) | |
236 | return NULL; | |
237 | snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb", | |
238 | flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid); | |
239 | return label_id->id; | |
240 | } | |
241 | ||
242 | static bool slot_valid(struct nd_namespace_label *nd_label, u32 slot) | |
243 | { | |
244 | /* check that we are written where we expect to be written */ | |
245 | if (slot != __le32_to_cpu(nd_label->slot)) | |
246 | return false; | |
247 | ||
248 | /* check that DPA allocations are page aligned */ | |
249 | if ((__le64_to_cpu(nd_label->dpa) | |
250 | | __le64_to_cpu(nd_label->rawsize)) % SZ_4K) | |
251 | return false; | |
252 | ||
253 | return true; | |
254 | } | |
255 | ||
256 | int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) | |
257 | { | |
258 | struct nd_namespace_index *nsindex; | |
259 | unsigned long *free; | |
260 | u32 nslot, slot; | |
261 | ||
262 | if (!preamble_current(ndd, &nsindex, &free, &nslot)) | |
263 | return 0; /* no label, nothing to reserve */ | |
264 | ||
265 | for_each_clear_bit_le(slot, free, nslot) { | |
266 | struct nd_namespace_label *nd_label; | |
267 | struct nd_region *nd_region = NULL; | |
268 | u8 label_uuid[NSLABEL_UUID_LEN]; | |
269 | struct nd_label_id label_id; | |
270 | struct resource *res; | |
271 | u32 flags; | |
272 | ||
273 | nd_label = nd_label_base(ndd) + slot; | |
274 | ||
275 | if (!slot_valid(nd_label, slot)) | |
276 | continue; | |
277 | ||
278 | memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); | |
279 | flags = __le32_to_cpu(nd_label->flags); | |
280 | nd_label_gen_id(&label_id, label_uuid, flags); | |
281 | res = nvdimm_allocate_dpa(ndd, &label_id, | |
282 | __le64_to_cpu(nd_label->dpa), | |
283 | __le64_to_cpu(nd_label->rawsize)); | |
284 | nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); | |
285 | if (!res) | |
286 | return -EBUSY; | |
287 | } | |
288 | ||
289 | return 0; | |
290 | } |