diff options
Diffstat (limited to 'drivers/md/persistent-data/dm-space-map-checker.c')
-rw-r--r-- | drivers/md/persistent-data/dm-space-map-checker.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c new file mode 100644 index 000000000000..bb44a937fe63 --- /dev/null +++ b/drivers/md/persistent-data/dm-space-map-checker.c | |||
@@ -0,0 +1,437 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Red Hat, Inc. | ||
3 | * | ||
4 | * This file is released under the GPL. | ||
5 | */ | ||
6 | |||
7 | #include "dm-space-map-checker.h" | ||
8 | |||
9 | #include <linux/device-mapper.h> | ||
10 | |||
11 | #ifdef CONFIG_DM_DEBUG_SPACE_MAPS | ||
12 | |||
13 | #define DM_MSG_PREFIX "space map checker" | ||
14 | |||
15 | /*----------------------------------------------------------------*/ | ||
16 | |||
17 | struct count_array { | ||
18 | dm_block_t nr; | ||
19 | dm_block_t nr_free; | ||
20 | |||
21 | uint32_t *counts; | ||
22 | }; | ||
23 | |||
24 | static int ca_get_count(struct count_array *ca, dm_block_t b, uint32_t *count) | ||
25 | { | ||
26 | if (b >= ca->nr) | ||
27 | return -EINVAL; | ||
28 | |||
29 | *count = ca->counts[b]; | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static int ca_count_more_than_one(struct count_array *ca, dm_block_t b, int *r) | ||
34 | { | ||
35 | if (b >= ca->nr) | ||
36 | return -EINVAL; | ||
37 | |||
38 | *r = ca->counts[b] > 1; | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static int ca_set_count(struct count_array *ca, dm_block_t b, uint32_t count) | ||
43 | { | ||
44 | uint32_t old_count; | ||
45 | |||
46 | if (b >= ca->nr) | ||
47 | return -EINVAL; | ||
48 | |||
49 | old_count = ca->counts[b]; | ||
50 | |||
51 | if (!count && old_count) | ||
52 | ca->nr_free++; | ||
53 | |||
54 | else if (count && !old_count) | ||
55 | ca->nr_free--; | ||
56 | |||
57 | ca->counts[b] = count; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int ca_inc_block(struct count_array *ca, dm_block_t b) | ||
62 | { | ||
63 | if (b >= ca->nr) | ||
64 | return -EINVAL; | ||
65 | |||
66 | ca_set_count(ca, b, ca->counts[b] + 1); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int ca_dec_block(struct count_array *ca, dm_block_t b) | ||
71 | { | ||
72 | if (b >= ca->nr) | ||
73 | return -EINVAL; | ||
74 | |||
75 | BUG_ON(ca->counts[b] == 0); | ||
76 | ca_set_count(ca, b, ca->counts[b] - 1); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int ca_create(struct count_array *ca, struct dm_space_map *sm) | ||
81 | { | ||
82 | int r; | ||
83 | dm_block_t nr_blocks; | ||
84 | |||
85 | r = dm_sm_get_nr_blocks(sm, &nr_blocks); | ||
86 | if (r) | ||
87 | return r; | ||
88 | |||
89 | ca->nr = nr_blocks; | ||
90 | ca->nr_free = nr_blocks; | ||
91 | ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL); | ||
92 | if (!ca->counts) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int ca_load(struct count_array *ca, struct dm_space_map *sm) | ||
99 | { | ||
100 | int r; | ||
101 | uint32_t count; | ||
102 | dm_block_t nr_blocks, i; | ||
103 | |||
104 | r = dm_sm_get_nr_blocks(sm, &nr_blocks); | ||
105 | if (r) | ||
106 | return r; | ||
107 | |||
108 | BUG_ON(ca->nr != nr_blocks); | ||
109 | |||
110 | DMWARN("Loading debug space map from disk. This may take some time"); | ||
111 | for (i = 0; i < nr_blocks; i++) { | ||
112 | r = dm_sm_get_count(sm, i, &count); | ||
113 | if (r) { | ||
114 | DMERR("load failed"); | ||
115 | return r; | ||
116 | } | ||
117 | |||
118 | ca_set_count(ca, i, count); | ||
119 | } | ||
120 | DMWARN("Load complete"); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int ca_extend(struct count_array *ca, dm_block_t extra_blocks) | ||
126 | { | ||
127 | dm_block_t nr_blocks = ca->nr + extra_blocks; | ||
128 | uint32_t *counts = kzalloc(sizeof(*counts) * nr_blocks, GFP_KERNEL); | ||
129 | if (!counts) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | memcpy(counts, ca->counts, sizeof(*counts) * ca->nr); | ||
133 | kfree(ca->counts); | ||
134 | ca->nr = nr_blocks; | ||
135 | ca->nr_free += extra_blocks; | ||
136 | ca->counts = counts; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int ca_commit(struct count_array *old, struct count_array *new) | ||
141 | { | ||
142 | if (old->nr != new->nr) { | ||
143 | BUG_ON(old->nr > new->nr); | ||
144 | ca_extend(old, new->nr - old->nr); | ||
145 | } | ||
146 | |||
147 | BUG_ON(old->nr != new->nr); | ||
148 | old->nr_free = new->nr_free; | ||
149 | memcpy(old->counts, new->counts, sizeof(*old->counts) * old->nr); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static void ca_destroy(struct count_array *ca) | ||
154 | { | ||
155 | kfree(ca->counts); | ||
156 | } | ||
157 | |||
158 | /*----------------------------------------------------------------*/ | ||
159 | |||
160 | struct sm_checker { | ||
161 | struct dm_space_map sm; | ||
162 | |||
163 | struct count_array old_counts; | ||
164 | struct count_array counts; | ||
165 | |||
166 | struct dm_space_map *real_sm; | ||
167 | }; | ||
168 | |||
169 | static void sm_checker_destroy(struct dm_space_map *sm) | ||
170 | { | ||
171 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
172 | |||
173 | dm_sm_destroy(smc->real_sm); | ||
174 | ca_destroy(&smc->old_counts); | ||
175 | ca_destroy(&smc->counts); | ||
176 | kfree(smc); | ||
177 | } | ||
178 | |||
179 | static int sm_checker_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count) | ||
180 | { | ||
181 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
182 | int r = dm_sm_get_nr_blocks(smc->real_sm, count); | ||
183 | if (!r) | ||
184 | BUG_ON(smc->old_counts.nr != *count); | ||
185 | return r; | ||
186 | } | ||
187 | |||
188 | static int sm_checker_get_nr_free(struct dm_space_map *sm, dm_block_t *count) | ||
189 | { | ||
190 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
191 | int r = dm_sm_get_nr_free(smc->real_sm, count); | ||
192 | if (!r) { | ||
193 | /* | ||
194 | * Slow, but we know it's correct. | ||
195 | */ | ||
196 | dm_block_t b, n = 0; | ||
197 | for (b = 0; b < smc->old_counts.nr; b++) | ||
198 | if (smc->old_counts.counts[b] == 0 && | ||
199 | smc->counts.counts[b] == 0) | ||
200 | n++; | ||
201 | |||
202 | if (n != *count) | ||
203 | DMERR("free block counts differ, checker %u, sm-disk:%u", | ||
204 | (unsigned) n, (unsigned) *count); | ||
205 | } | ||
206 | return r; | ||
207 | } | ||
208 | |||
209 | static int sm_checker_new_block(struct dm_space_map *sm, dm_block_t *b) | ||
210 | { | ||
211 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
212 | int r = dm_sm_new_block(smc->real_sm, b); | ||
213 | |||
214 | if (!r) { | ||
215 | BUG_ON(*b >= smc->old_counts.nr); | ||
216 | BUG_ON(smc->old_counts.counts[*b] != 0); | ||
217 | BUG_ON(*b >= smc->counts.nr); | ||
218 | BUG_ON(smc->counts.counts[*b] != 0); | ||
219 | ca_set_count(&smc->counts, *b, 1); | ||
220 | } | ||
221 | |||
222 | return r; | ||
223 | } | ||
224 | |||
225 | static int sm_checker_inc_block(struct dm_space_map *sm, dm_block_t b) | ||
226 | { | ||
227 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
228 | int r = dm_sm_inc_block(smc->real_sm, b); | ||
229 | int r2 = ca_inc_block(&smc->counts, b); | ||
230 | BUG_ON(r != r2); | ||
231 | return r; | ||
232 | } | ||
233 | |||
234 | static int sm_checker_dec_block(struct dm_space_map *sm, dm_block_t b) | ||
235 | { | ||
236 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
237 | int r = dm_sm_dec_block(smc->real_sm, b); | ||
238 | int r2 = ca_dec_block(&smc->counts, b); | ||
239 | BUG_ON(r != r2); | ||
240 | return r; | ||
241 | } | ||
242 | |||
243 | static int sm_checker_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result) | ||
244 | { | ||
245 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
246 | uint32_t result2 = 0; | ||
247 | int r = dm_sm_get_count(smc->real_sm, b, result); | ||
248 | int r2 = ca_get_count(&smc->counts, b, &result2); | ||
249 | |||
250 | BUG_ON(r != r2); | ||
251 | if (!r) | ||
252 | BUG_ON(*result != result2); | ||
253 | return r; | ||
254 | } | ||
255 | |||
256 | static int sm_checker_count_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result) | ||
257 | { | ||
258 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
259 | int result2 = 0; | ||
260 | int r = dm_sm_count_is_more_than_one(smc->real_sm, b, result); | ||
261 | int r2 = ca_count_more_than_one(&smc->counts, b, &result2); | ||
262 | |||
263 | BUG_ON(r != r2); | ||
264 | if (!r) | ||
265 | BUG_ON(!(*result) && result2); | ||
266 | return r; | ||
267 | } | ||
268 | |||
269 | static int sm_checker_set_count(struct dm_space_map *sm, dm_block_t b, uint32_t count) | ||
270 | { | ||
271 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
272 | uint32_t old_rc; | ||
273 | int r = dm_sm_set_count(smc->real_sm, b, count); | ||
274 | int r2; | ||
275 | |||
276 | BUG_ON(b >= smc->counts.nr); | ||
277 | old_rc = smc->counts.counts[b]; | ||
278 | r2 = ca_set_count(&smc->counts, b, count); | ||
279 | BUG_ON(r != r2); | ||
280 | |||
281 | return r; | ||
282 | } | ||
283 | |||
284 | static int sm_checker_commit(struct dm_space_map *sm) | ||
285 | { | ||
286 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
287 | int r; | ||
288 | |||
289 | r = dm_sm_commit(smc->real_sm); | ||
290 | if (r) | ||
291 | return r; | ||
292 | |||
293 | r = ca_commit(&smc->old_counts, &smc->counts); | ||
294 | if (r) | ||
295 | return r; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int sm_checker_extend(struct dm_space_map *sm, dm_block_t extra_blocks) | ||
301 | { | ||
302 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
303 | int r = dm_sm_extend(smc->real_sm, extra_blocks); | ||
304 | if (r) | ||
305 | return r; | ||
306 | |||
307 | return ca_extend(&smc->counts, extra_blocks); | ||
308 | } | ||
309 | |||
310 | static int sm_checker_root_size(struct dm_space_map *sm, size_t *result) | ||
311 | { | ||
312 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
313 | return dm_sm_root_size(smc->real_sm, result); | ||
314 | } | ||
315 | |||
316 | static int sm_checker_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len) | ||
317 | { | ||
318 | struct sm_checker *smc = container_of(sm, struct sm_checker, sm); | ||
319 | return dm_sm_copy_root(smc->real_sm, copy_to_here_le, len); | ||
320 | } | ||
321 | |||
322 | /*----------------------------------------------------------------*/ | ||
323 | |||
324 | static struct dm_space_map ops_ = { | ||
325 | .destroy = sm_checker_destroy, | ||
326 | .get_nr_blocks = sm_checker_get_nr_blocks, | ||
327 | .get_nr_free = sm_checker_get_nr_free, | ||
328 | .inc_block = sm_checker_inc_block, | ||
329 | .dec_block = sm_checker_dec_block, | ||
330 | .new_block = sm_checker_new_block, | ||
331 | .get_count = sm_checker_get_count, | ||
332 | .count_is_more_than_one = sm_checker_count_more_than_one, | ||
333 | .set_count = sm_checker_set_count, | ||
334 | .commit = sm_checker_commit, | ||
335 | .extend = sm_checker_extend, | ||
336 | .root_size = sm_checker_root_size, | ||
337 | .copy_root = sm_checker_copy_root | ||
338 | }; | ||
339 | |||
340 | struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) | ||
341 | { | ||
342 | int r; | ||
343 | struct sm_checker *smc; | ||
344 | |||
345 | if (!sm) | ||
346 | return NULL; | ||
347 | |||
348 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); | ||
349 | if (!smc) | ||
350 | return NULL; | ||
351 | |||
352 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); | ||
353 | r = ca_create(&smc->old_counts, sm); | ||
354 | if (r) { | ||
355 | kfree(smc); | ||
356 | return NULL; | ||
357 | } | ||
358 | |||
359 | r = ca_create(&smc->counts, sm); | ||
360 | if (r) { | ||
361 | ca_destroy(&smc->old_counts); | ||
362 | kfree(smc); | ||
363 | return NULL; | ||
364 | } | ||
365 | |||
366 | smc->real_sm = sm; | ||
367 | |||
368 | r = ca_load(&smc->counts, sm); | ||
369 | if (r) { | ||
370 | ca_destroy(&smc->counts); | ||
371 | ca_destroy(&smc->old_counts); | ||
372 | kfree(smc); | ||
373 | return NULL; | ||
374 | } | ||
375 | |||
376 | r = ca_commit(&smc->old_counts, &smc->counts); | ||
377 | if (r) { | ||
378 | ca_destroy(&smc->counts); | ||
379 | ca_destroy(&smc->old_counts); | ||
380 | kfree(smc); | ||
381 | return NULL; | ||
382 | } | ||
383 | |||
384 | return &smc->sm; | ||
385 | } | ||
386 | EXPORT_SYMBOL_GPL(dm_sm_checker_create); | ||
387 | |||
388 | struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) | ||
389 | { | ||
390 | int r; | ||
391 | struct sm_checker *smc; | ||
392 | |||
393 | if (!sm) | ||
394 | return NULL; | ||
395 | |||
396 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); | ||
397 | if (!smc) | ||
398 | return NULL; | ||
399 | |||
400 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); | ||
401 | r = ca_create(&smc->old_counts, sm); | ||
402 | if (r) { | ||
403 | kfree(smc); | ||
404 | return NULL; | ||
405 | } | ||
406 | |||
407 | r = ca_create(&smc->counts, sm); | ||
408 | if (r) { | ||
409 | ca_destroy(&smc->old_counts); | ||
410 | kfree(smc); | ||
411 | return NULL; | ||
412 | } | ||
413 | |||
414 | smc->real_sm = sm; | ||
415 | return &smc->sm; | ||
416 | } | ||
417 | EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh); | ||
418 | |||
419 | /*----------------------------------------------------------------*/ | ||
420 | |||
421 | #else | ||
422 | |||
423 | struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) | ||
424 | { | ||
425 | return sm; | ||
426 | } | ||
427 | EXPORT_SYMBOL_GPL(dm_sm_checker_create); | ||
428 | |||
429 | struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) | ||
430 | { | ||
431 | return sm; | ||
432 | } | ||
433 | EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh); | ||
434 | |||
435 | /*----------------------------------------------------------------*/ | ||
436 | |||
437 | #endif | ||