aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Thornber <ejt@redhat.com>2015-12-02 07:24:39 -0500
committerMike Snitzer <snitzer@redhat.com>2015-12-02 13:26:49 -0500
commit993ceab91986e2e737ce9a3e23bebc8cce649240 (patch)
tree271d69ac484060b565f25614cf25a5595b036a6c
parent30ce6e1cc5a0f781d60227e9096c86e188d2c2bd (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.c28
-rw-r--r--drivers/md/persistent-data/dm-btree.c81
-rw-r--r--drivers/md/persistent-data/dm-btree.h14
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)
1538static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end) 1538static 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
66static int upper_bound(struct btree_node *n, uint64_t key)
67{
68 return bsearch(n, key, 1);
69}
70
66void inc_children(struct dm_transaction_manager *tm, struct btree_node *n, 71void 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}
393EXPORT_SYMBOL_GPL(dm_btree_lookup); 398EXPORT_SYMBOL_GPL(dm_btree_lookup);
394 399
400static 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 }
439out:
440 dm_tm_unlock(info->tm, node);
441 return r;
442}
443
444int 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);
469out:
470 exit_ro_spine(&spine);
471 return r;
472}
473
474EXPORT_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 */
116int 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 */
115int dm_btree_insert(struct dm_btree_info *info, dm_block_t root, 122int 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 */
142int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root, 150int 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,