scoutfs: fix leaking alloc bits in merge

In a merge where the input and source trees are the same, the input
block can be an initial pre-cow version of the dirty source block.
Dirtying blocks in the change will clear allocations in the dirty source
block but they will remain in the pre-cow input block.  The merge can
then set these blocks in the dst, even though they were also used by
allocation, because they're still set in the pre-cow input block.

This fix is clumsy, but minimal and specific to this problem.  A more
thorough fix is being worked on which introduces more staging more
allocator trees and should stop calls that are modifying the current
active avail or free trees.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2020-08-26 14:39:12 -07:00
committed by Zach Brown
parent 289caeb353
commit d1e62a43c9
2 changed files with 16 additions and 0 deletions
+1
View File
@@ -135,6 +135,7 @@
EXPAND_COUNTER(radix_inconsistent_eio) \
EXPAND_COUNTER(radix_inconsistent_ref) \
EXPAND_COUNTER(radix_merge) \
EXPAND_COUNTER(radix_merge_bad_clean_input) \
EXPAND_COUNTER(radix_merge_empty) \
EXPAND_COUNTER(radix_undo_ref) \
EXPAND_COUNTER(radix_walk) \
+15
View File
@@ -1392,6 +1392,21 @@ wrapped:
goto out;
src_rdx = src_bl->data;
/*
* If we're searching the avail allocator tree then we
* must be sure that we copy leaves after change
* allocations have been applied. If we had a read-only
* copy of the allocator leaf before it was cowed we
* could merge bits that were used for dirty block
* allocations by the change. By not resetting the
* change the repeated lookup will find the current
* dirty leaf block.
*/
if (src == inp && inp_bl != src_bl) {
scoutfs_inc_counter(sb, radix_merge_bad_clean_input);
goto wrapped;
}
ret = get_leaf(sb, alloc, wri, &chg, dst, GLF_DIRTY, bit,
&leaf_bit, &dst_bl);
if (ret < 0)