diff options
author | Joe Thornber <ejt@redhat.com> | 2015-12-02 07:24:39 -0500 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2015-12-02 13:26:49 -0500 |
commit | 993ceab91986e2e737ce9a3e23bebc8cce649240 (patch) | |
tree | 271d69ac484060b565f25614cf25a5595b036a6c | |
parent | 30ce6e1cc5a0f781d60227e9096c86e188d2c2bd (diff) |
dm thin metadata: fix bug in dm_thin_remove_range()
dm_btree_remove_leaves() only unmaps a contiguous region so we need a
loop, in __remove_range(), to handle ranges that contain multiple
regions.
A new btree function, dm_btree_lookup_next(), is introduced which is
more efficiently able to skip over regions of the thin device which
aren't mapped. __remove_range() uses dm_btree_lookup_next() for each
iteration of __remove_range()'s loop.
Also, improve description of dm_btree_remove_leaves().
Fixes: 6550f075 ("dm thin metadata: add dm_thin_remove_range()")
Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org # 4.1+
-rw-r--r-- | drivers/md/dm-thin-metadata.c | 28 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-btree.c | 81 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-btree.h | 14 |
3 files changed, 115 insertions, 8 deletions
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 1fa45695b68a..67871e74c82b 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c | |||
@@ -1538,7 +1538,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) | 1538 | static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end) |
1539 | { | 1539 | { |
1540 | int r; | 1540 | int r; |
1541 | unsigned count; | 1541 | unsigned count, total_count = 0; |
1542 | struct dm_pool_metadata *pmd = td->pmd; | 1542 | struct dm_pool_metadata *pmd = td->pmd; |
1543 | dm_block_t keys[1] = { td->id }; | 1543 | dm_block_t keys[1] = { td->id }; |
1544 | __le64 value; | 1544 | __le64 value; |
@@ -1561,11 +1561,29 @@ static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_ | |||
1561 | if (r) | 1561 | if (r) |
1562 | return r; | 1562 | return r; |
1563 | 1563 | ||
1564 | r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count); | 1564 | /* |
1565 | if (r) | 1565 | * Remove leaves stops at the first unmapped entry, so we have to |
1566 | return r; | 1566 | * loop round finding mapped ranges. |
1567 | */ | ||
1568 | while (begin < end) { | ||
1569 | r = dm_btree_lookup_next(&pmd->bl_info, mapping_root, &begin, &begin, &value); | ||
1570 | if (r == -ENODATA) | ||
1571 | break; | ||
1572 | |||
1573 | if (r) | ||
1574 | return r; | ||
1575 | |||
1576 | if (begin >= end) | ||
1577 | break; | ||
1578 | |||
1579 | r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count); | ||
1580 | if (r) | ||
1581 | return r; | ||
1582 | |||
1583 | total_count += count; | ||
1584 | } | ||
1567 | 1585 | ||
1568 | td->mapped_blocks -= count; | 1586 | td->mapped_blocks -= total_count; |
1569 | td->changed = 1; | 1587 | td->changed = 1; |
1570 | 1588 | ||
1571 | /* | 1589 | /* |
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index 0918a7c5f809..7e5b7f12958a 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 | { |
@@ -392,6 +397,82 @@ int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root, | |||
392 | } | 397 | } |
393 | EXPORT_SYMBOL_GPL(dm_btree_lookup); | 398 | EXPORT_SYMBOL_GPL(dm_btree_lookup); |
394 | 399 | ||
400 | static int dm_btree_lookup_next_single(struct dm_btree_info *info, dm_block_t root, | ||
401 | uint64_t key, uint64_t *rkey, void *value_le) | ||
402 | { | ||
403 | int r, i; | ||
404 | uint32_t flags, nr_entries; | ||
405 | struct dm_block *node; | ||
406 | struct btree_node *n; | ||
407 | |||
408 | r = bn_read_lock(info, root, &node); | ||
409 | if (r) | ||
410 | return r; | ||
411 | |||
412 | n = dm_block_data(node); | ||
413 | flags = le32_to_cpu(n->header.flags); | ||
414 | nr_entries = le32_to_cpu(n->header.nr_entries); | ||
415 | |||
416 | if (flags & INTERNAL_NODE) { | ||
417 | i = lower_bound(n, key); | ||
418 | if (i < 0 || i >= nr_entries) { | ||
419 | r = -ENODATA; | ||
420 | goto out; | ||
421 | } | ||
422 | |||
423 | r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le); | ||
424 | if (r == -ENODATA && i < (nr_entries - 1)) { | ||
425 | i++; | ||
426 | r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le); | ||
427 | } | ||
428 | |||
429 | } else { | ||
430 | i = upper_bound(n, key); | ||
431 | if (i < 0 || i >= nr_entries) { | ||
432 | r = -ENODATA; | ||
433 | goto out; | ||
434 | } | ||
435 | |||
436 | *rkey = le64_to_cpu(n->keys[i]); | ||
437 | memcpy(value_le, value_ptr(n, i), info->value_type.size); | ||
438 | } | ||
439 | out: | ||
440 | dm_tm_unlock(info->tm, node); | ||
441 | return r; | ||
442 | } | ||
443 | |||
444 | int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root, | ||
445 | uint64_t *keys, uint64_t *rkey, void *value_le) | ||
446 | { | ||
447 | unsigned level; | ||
448 | int r = -ENODATA; | ||
449 | __le64 internal_value_le; | ||
450 | struct ro_spine spine; | ||
451 | |||
452 | init_ro_spine(&spine, info); | ||
453 | for (level = 0; level < info->levels - 1u; level++) { | ||
454 | r = btree_lookup_raw(&spine, root, keys[level], | ||
455 | lower_bound, rkey, | ||
456 | &internal_value_le, sizeof(uint64_t)); | ||
457 | if (r) | ||
458 | goto out; | ||
459 | |||
460 | if (*rkey != keys[level]) { | ||
461 | r = -ENODATA; | ||
462 | goto out; | ||
463 | } | ||
464 | |||
465 | root = le64_to_cpu(internal_value_le); | ||
466 | } | ||
467 | |||
468 | r = dm_btree_lookup_next_single(info, root, keys[level], rkey, value_le); | ||
469 | out: | ||
470 | exit_ro_spine(&spine); | ||
471 | return r; | ||
472 | } | ||
473 | |||
474 | EXPORT_SYMBOL_GPL(dm_btree_lookup_next); | ||
475 | |||
395 | /* | 476 | /* |
396 | * Splits a node by creating a sibling node and shifting half the nodes | 477 | * 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 | 478 | * contents across. Assumes there is a parent node, and it has room for |
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, |