diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-12-11 14:00:30 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-12-11 14:00:30 -0500 |
commit | b9d85451ddd4e7f2d6280506f6fe7f1924356924 (patch) | |
tree | 3c2bb4baa5ea5a1ee0e5871dd41b5b746213ac30 | |
parent | 732c4a9e1404dfcce454122ae695dd9fb7939358 (diff) | |
parent | ed8b45a3679eb49069b094c0711b30833f27c734 (diff) |
Merge tag 'dm-4.4-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper fixes from Mike Snitzer:
"Five stable fixes:
- Two DM btree bufio buffer leak fixes that resolve reported BUG_ONs
during DM thinp metadata close's dm_bufio_client_destroy().
- A DM thinp range discard fix to handle discarding a partially
mapped range.
- A DM thinp metadata snapshot fix to make sure the btree roots saved
in the metadata snapshot are the most current.
- A DM space map metadata refcounting fix that improves both DM thinp
and DM cache metadata"
* tag 'dm-4.4-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
dm btree: fix bufio buffer leaks in dm_btree_del() error path
dm space map metadata: fix ref counting bug when bootstrapping a new space map
dm thin metadata: fix bug when taking a metadata snapshot
dm thin metadata: fix bug in dm_thin_remove_range()
dm btree: fix leak of bufio-backed block in btree_split_sibling error path
-rw-r--r-- | drivers/md/dm-thin-metadata.c | 34 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-btree.c | 101 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-btree.h | 14 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-space-map-metadata.c | 32 |
4 files changed, 161 insertions, 20 deletions
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 1fa45695b68a..c219a053c7f6 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c | |||
@@ -1207,6 +1207,12 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) | |||
1207 | dm_block_t held_root; | 1207 | dm_block_t held_root; |
1208 | 1208 | ||
1209 | /* | 1209 | /* |
1210 | * We commit to ensure the btree roots which we increment in a | ||
1211 | * moment are up to date. | ||
1212 | */ | ||
1213 | __commit_transaction(pmd); | ||
1214 | |||
1215 | /* | ||
1210 | * Copy the superblock. | 1216 | * Copy the superblock. |
1211 | */ | 1217 | */ |
1212 | dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); | 1218 | dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); |
@@ -1538,7 +1544,7 @@ static int __remove(struct dm_thin_device *td, dm_block_t block) | |||
1538 | static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end) | 1544 | static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end) |
1539 | { | 1545 | { |
1540 | int r; | 1546 | int r; |
1541 | unsigned count; | 1547 | unsigned count, total_count = 0; |
1542 | struct dm_pool_metadata *pmd = td->pmd; | 1548 | struct dm_pool_metadata *pmd = td->pmd; |
1543 | dm_block_t keys[1] = { td->id }; | 1549 | dm_block_t keys[1] = { td->id }; |
1544 | __le64 value; | 1550 | __le64 value; |
@@ -1561,11 +1567,29 @@ static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_ | |||
1561 | if (r) | 1567 | if (r) |
1562 | return r; | 1568 | return r; |
1563 | 1569 | ||
1564 | r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count); | 1570 | /* |
1565 | if (r) | 1571 | * Remove leaves stops at the first unmapped entry, so we have to |
1566 | return r; | 1572 | * loop round finding mapped ranges. |
1573 | */ | ||
1574 | while (begin < end) { | ||
1575 | r = dm_btree_lookup_next(&pmd->bl_info, mapping_root, &begin, &begin, &value); | ||
1576 | if (r == -ENODATA) | ||
1577 | break; | ||
1578 | |||
1579 | if (r) | ||
1580 | return r; | ||
1581 | |||
1582 | if (begin >= end) | ||
1583 | break; | ||
1584 | |||
1585 | r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count); | ||
1586 | if (r) | ||
1587 | return r; | ||
1588 | |||
1589 | total_count += count; | ||
1590 | } | ||
1567 | 1591 | ||
1568 | td->mapped_blocks -= count; | 1592 | td->mapped_blocks -= total_count; |
1569 | td->changed = 1; | 1593 | td->changed = 1; |
1570 | 1594 | ||
1571 | /* | 1595 | /* |
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index c573402033b2..b1ced58eb5e1 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c | |||
@@ -63,6 +63,11 @@ int lower_bound(struct btree_node *n, uint64_t key) | |||
63 | return bsearch(n, key, 0); | 63 | return bsearch(n, key, 0); |
64 | } | 64 | } |
65 | 65 | ||
66 | static int upper_bound(struct btree_node *n, uint64_t key) | ||
67 | { | ||
68 | return bsearch(n, key, 1); | ||
69 | } | ||
70 | |||
66 | void inc_children(struct dm_transaction_manager *tm, struct btree_node *n, | 71 | void inc_children(struct dm_transaction_manager *tm, struct btree_node *n, |
67 | struct dm_btree_value_type *vt) | 72 | struct dm_btree_value_type *vt) |
68 | { | 73 | { |
@@ -252,6 +257,16 @@ static void pop_frame(struct del_stack *s) | |||
252 | dm_tm_unlock(s->tm, f->b); | 257 | dm_tm_unlock(s->tm, f->b); |
253 | } | 258 | } |
254 | 259 | ||
260 | static void unlock_all_frames(struct del_stack *s) | ||
261 | { | ||
262 | struct frame *f; | ||
263 | |||
264 | while (unprocessed_frames(s)) { | ||
265 | f = s->spine + s->top--; | ||
266 | dm_tm_unlock(s->tm, f->b); | ||
267 | } | ||
268 | } | ||
269 | |||
255 | int dm_btree_del(struct dm_btree_info *info, dm_block_t root) | 270 | int dm_btree_del(struct dm_btree_info *info, dm_block_t root) |
256 | { | 271 | { |
257 | int r; | 272 | int r; |
@@ -308,9 +323,13 @@ int dm_btree_del(struct dm_btree_info *info, dm_block_t root) | |||
308 | pop_frame(s); | 323 | pop_frame(s); |
309 | } | 324 | } |
310 | } | 325 | } |
311 | |||
312 | out: | 326 | out: |
327 | if (r) { | ||
328 | /* cleanup all frames of del_stack */ | ||
329 | unlock_all_frames(s); | ||
330 | } | ||
313 | kfree(s); | 331 | kfree(s); |
332 | |||
314 | return r; | 333 | return r; |
315 | } | 334 | } |
316 | EXPORT_SYMBOL_GPL(dm_btree_del); | 335 | EXPORT_SYMBOL_GPL(dm_btree_del); |
@@ -392,6 +411,82 @@ int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root, | |||
392 | } | 411 | } |
393 | EXPORT_SYMBOL_GPL(dm_btree_lookup); | 412 | EXPORT_SYMBOL_GPL(dm_btree_lookup); |
394 | 413 | ||
414 | static int dm_btree_lookup_next_single(struct dm_btree_info *info, dm_block_t root, | ||
415 | uint64_t key, uint64_t *rkey, void *value_le) | ||
416 | { | ||
417 | int r, i; | ||
418 | uint32_t flags, nr_entries; | ||
419 | struct dm_block *node; | ||
420 | struct btree_node *n; | ||
421 | |||
422 | r = bn_read_lock(info, root, &node); | ||
423 | if (r) | ||
424 | return r; | ||
425 | |||
426 | n = dm_block_data(node); | ||
427 | flags = le32_to_cpu(n->header.flags); | ||
428 | nr_entries = le32_to_cpu(n->header.nr_entries); | ||
429 | |||
430 | if (flags & INTERNAL_NODE) { | ||
431 | i = lower_bound(n, key); | ||
432 | if (i < 0 || i >= nr_entries) { | ||
433 | r = -ENODATA; | ||
434 | goto out; | ||
435 | } | ||
436 | |||
437 | r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le); | ||
438 | if (r == -ENODATA && i < (nr_entries - 1)) { | ||
439 | i++; | ||
440 | r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le); | ||
441 | } | ||
442 | |||
443 | } else { | ||
444 | i = upper_bound(n, key); | ||
445 | if (i < 0 || i >= nr_entries) { | ||
446 | r = -ENODATA; | ||
447 | goto out; | ||
448 | } | ||
449 | |||
450 | *rkey = le64_to_cpu(n->keys[i]); | ||
451 | memcpy(value_le, value_ptr(n, i), info->value_type.size); | ||
452 | } | ||
453 | out: | ||
454 | dm_tm_unlock(info->tm, node); | ||
455 | return r; | ||
456 | } | ||
457 | |||
458 | int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root, | ||
459 | uint64_t *keys, uint64_t *rkey, void *value_le) | ||
460 | { | ||
461 | unsigned level; | ||
462 | int r = -ENODATA; | ||
463 | __le64 internal_value_le; | ||
464 | struct ro_spine spine; | ||
465 | |||
466 | init_ro_spine(&spine, info); | ||
467 | for (level = 0; level < info->levels - 1u; level++) { | ||
468 | r = btree_lookup_raw(&spine, root, keys[level], | ||
469 | lower_bound, rkey, | ||
470 | &internal_value_le, sizeof(uint64_t)); | ||
471 | if (r) | ||
472 | goto out; | ||
473 | |||
474 | if (*rkey != keys[level]) { | ||
475 | r = -ENODATA; | ||
476 | goto out; | ||
477 | } | ||
478 | |||
479 | root = le64_to_cpu(internal_value_le); | ||
480 | } | ||
481 | |||
482 | r = dm_btree_lookup_next_single(info, root, keys[level], rkey, value_le); | ||
483 | out: | ||
484 | exit_ro_spine(&spine); | ||
485 | return r; | ||
486 | } | ||
487 | |||
488 | EXPORT_SYMBOL_GPL(dm_btree_lookup_next); | ||
489 | |||
395 | /* | 490 | /* |
396 | * Splits a node by creating a sibling node and shifting half the nodes | 491 | * Splits a node by creating a sibling node and shifting half the nodes |
397 | * contents across. Assumes there is a parent node, and it has room for | 492 | * contents across. Assumes there is a parent node, and it has room for |
@@ -473,8 +568,10 @@ static int btree_split_sibling(struct shadow_spine *s, unsigned parent_index, | |||
473 | 568 | ||
474 | r = insert_at(sizeof(__le64), pn, parent_index + 1, | 569 | r = insert_at(sizeof(__le64), pn, parent_index + 1, |
475 | le64_to_cpu(rn->keys[0]), &location); | 570 | le64_to_cpu(rn->keys[0]), &location); |
476 | if (r) | 571 | if (r) { |
572 | unlock_block(s->info, right); | ||
477 | return r; | 573 | return r; |
574 | } | ||
478 | 575 | ||
479 | if (key < le64_to_cpu(rn->keys[0])) { | 576 | if (key < le64_to_cpu(rn->keys[0])) { |
480 | unlock_block(s->info, right); | 577 | unlock_block(s->info, right); |
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h index 11d8cf78621d..c74301fa5a37 100644 --- a/drivers/md/persistent-data/dm-btree.h +++ b/drivers/md/persistent-data/dm-btree.h | |||
@@ -110,6 +110,13 @@ int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root, | |||
110 | uint64_t *keys, void *value_le); | 110 | uint64_t *keys, void *value_le); |
111 | 111 | ||
112 | /* | 112 | /* |
113 | * Tries to find the first key where the bottom level key is >= to that | ||
114 | * given. Useful for skipping empty sections of the btree. | ||
115 | */ | ||
116 | int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root, | ||
117 | uint64_t *keys, uint64_t *rkey, void *value_le); | ||
118 | |||
119 | /* | ||
113 | * Insertion (or overwrite an existing value). O(ln(n)) | 120 | * Insertion (or overwrite an existing value). O(ln(n)) |
114 | */ | 121 | */ |
115 | int dm_btree_insert(struct dm_btree_info *info, dm_block_t root, | 122 | int dm_btree_insert(struct dm_btree_info *info, dm_block_t root, |
@@ -135,9 +142,10 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root, | |||
135 | uint64_t *keys, dm_block_t *new_root); | 142 | uint64_t *keys, dm_block_t *new_root); |
136 | 143 | ||
137 | /* | 144 | /* |
138 | * Removes values between 'keys' and keys2, where keys2 is keys with the | 145 | * Removes a _contiguous_ run of values starting from 'keys' and not |
139 | * final key replaced with 'end_key'. 'end_key' is the one-past-the-end | 146 | * reaching keys2 (where keys2 is keys with the final key replaced with |
140 | * value. 'keys' may be altered. | 147 | * 'end_key'). 'end_key' is the one-past-the-end value. 'keys' may be |
148 | * altered. | ||
141 | */ | 149 | */ |
142 | int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root, | 150 | int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root, |
143 | uint64_t *keys, uint64_t end_key, | 151 | uint64_t *keys, uint64_t end_key, |
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index 53091295fce9..fca6dbcf9a47 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c | |||
@@ -136,7 +136,7 @@ static int brb_push(struct bop_ring_buffer *brb, | |||
136 | return 0; | 136 | return 0; |
137 | } | 137 | } |
138 | 138 | ||
139 | static int brb_pop(struct bop_ring_buffer *brb, struct block_op *result) | 139 | static int brb_peek(struct bop_ring_buffer *brb, struct block_op *result) |
140 | { | 140 | { |
141 | struct block_op *bop; | 141 | struct block_op *bop; |
142 | 142 | ||
@@ -147,6 +147,17 @@ static int brb_pop(struct bop_ring_buffer *brb, struct block_op *result) | |||
147 | result->type = bop->type; | 147 | result->type = bop->type; |
148 | result->block = bop->block; | 148 | result->block = bop->block; |
149 | 149 | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int brb_pop(struct bop_ring_buffer *brb) | ||
154 | { | ||
155 | struct block_op *bop; | ||
156 | |||
157 | if (brb_empty(brb)) | ||
158 | return -ENODATA; | ||
159 | |||
160 | bop = brb->bops + brb->begin; | ||
150 | brb->begin = brb_next(brb, brb->begin); | 161 | brb->begin = brb_next(brb, brb->begin); |
151 | 162 | ||
152 | return 0; | 163 | return 0; |
@@ -211,7 +222,7 @@ static int apply_bops(struct sm_metadata *smm) | |||
211 | while (!brb_empty(&smm->uncommitted)) { | 222 | while (!brb_empty(&smm->uncommitted)) { |
212 | struct block_op bop; | 223 | struct block_op bop; |
213 | 224 | ||
214 | r = brb_pop(&smm->uncommitted, &bop); | 225 | r = brb_peek(&smm->uncommitted, &bop); |
215 | if (r) { | 226 | if (r) { |
216 | DMERR("bug in bop ring buffer"); | 227 | DMERR("bug in bop ring buffer"); |
217 | break; | 228 | break; |
@@ -220,6 +231,8 @@ static int apply_bops(struct sm_metadata *smm) | |||
220 | r = commit_bop(smm, &bop); | 231 | r = commit_bop(smm, &bop); |
221 | if (r) | 232 | if (r) |
222 | break; | 233 | break; |
234 | |||
235 | brb_pop(&smm->uncommitted); | ||
223 | } | 236 | } |
224 | 237 | ||
225 | return r; | 238 | return r; |
@@ -683,7 +696,6 @@ static struct dm_space_map bootstrap_ops = { | |||
683 | static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks) | 696 | static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks) |
684 | { | 697 | { |
685 | int r, i; | 698 | int r, i; |
686 | enum allocation_event ev; | ||
687 | struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); | 699 | struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); |
688 | dm_block_t old_len = smm->ll.nr_blocks; | 700 | dm_block_t old_len = smm->ll.nr_blocks; |
689 | 701 | ||
@@ -705,11 +717,12 @@ static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks) | |||
705 | * allocate any new blocks. | 717 | * allocate any new blocks. |
706 | */ | 718 | */ |
707 | do { | 719 | do { |
708 | for (i = old_len; !r && i < smm->begin; i++) { | 720 | for (i = old_len; !r && i < smm->begin; i++) |
709 | r = sm_ll_inc(&smm->ll, i, &ev); | 721 | r = add_bop(smm, BOP_INC, i); |
710 | if (r) | 722 | |
711 | goto out; | 723 | if (r) |
712 | } | 724 | goto out; |
725 | |||
713 | old_len = smm->begin; | 726 | old_len = smm->begin; |
714 | 727 | ||
715 | r = apply_bops(smm); | 728 | r = apply_bops(smm); |
@@ -754,7 +767,6 @@ int dm_sm_metadata_create(struct dm_space_map *sm, | |||
754 | { | 767 | { |
755 | int r; | 768 | int r; |
756 | dm_block_t i; | 769 | dm_block_t i; |
757 | enum allocation_event ev; | ||
758 | struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); | 770 | struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); |
759 | 771 | ||
760 | smm->begin = superblock + 1; | 772 | smm->begin = superblock + 1; |
@@ -782,7 +794,7 @@ int dm_sm_metadata_create(struct dm_space_map *sm, | |||
782 | * allocated blocks that they were built from. | 794 | * allocated blocks that they were built from. |
783 | */ | 795 | */ |
784 | for (i = superblock; !r && i < smm->begin; i++) | 796 | for (i = superblock; !r && i < smm->begin; i++) |
785 | r = sm_ll_inc(&smm->ll, i, &ev); | 797 | r = add_bop(smm, BOP_INC, i); |
786 | 798 | ||
787 | if (r) | 799 | if (r) |
788 | return r; | 800 | return r; |