Clarify error message when a range-tree double-add occurs

In various other pieces of logic have resulted in situations where 
we double-free space in ZFS. This in turn results in a double-add 
to the range trees. These issues have been much more difficult to 
diagnose than they should have been, because the error handling 
around this case is much weaker than around the double remove case.

Reviewed-by: Matt Ahrens <matt@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: George Wilson <gwilson@delphix.com>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #10654
This commit is contained in:
Paul Dagnelie 2020-08-07 14:13:13 -07:00 committed by GitHub
parent d4e6e9597d
commit 12045d0278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 22 additions and 8 deletions

View File

@ -251,9 +251,19 @@ range_tree_destroy(range_tree_t *rt)
void void
range_tree_adjust_fill(range_tree_t *rt, range_seg_t *rs, int64_t delta) range_tree_adjust_fill(range_tree_t *rt, range_seg_t *rs, int64_t delta)
{ {
ASSERT3U(rs_get_fill(rs, rt) + delta, !=, 0); if (delta < 0 && delta * -1 >= rs_get_fill(rs, rt)) {
ASSERT3U(rs_get_fill(rs, rt) + delta, <=, rs_get_end(rs, rt) - zfs_panic_recover("zfs: attempting to decrease fill to or "
rs_get_start(rs, rt)); "below 0; probable double remove in segment [%llx:%llx]",
(longlong_t)rs_get_start(rs, rt),
(longlong_t)rs_get_end(rs, rt));
}
if (rs_get_fill(rs, rt) + delta > rs_get_end(rs, rt) -
rs_get_start(rs, rt)) {
zfs_panic_recover("zfs: attempting to increase fill beyond "
"max; probable double add in segment [%llx:%llx]",
(longlong_t)rs_get_start(rs, rt),
(longlong_t)rs_get_end(rs, rt));
}
if (rt->rt_ops != NULL && rt->rt_ops->rtop_remove != NULL) if (rt->rt_ops != NULL && rt->rt_ops->rtop_remove != NULL)
rt->rt_ops->rtop_remove(rt, rs, rt->rt_arg); rt->rt_ops->rtop_remove(rt, rs, rt->rt_arg);
@ -290,10 +300,14 @@ range_tree_add_impl(void *arg, uint64_t start, uint64_t size, uint64_t fill)
* the normal code paths. * the normal code paths.
*/ */
if (rs != NULL) { if (rs != NULL) {
ASSERT3U(rt->rt_gap, !=, 0); if (gap == 0) {
zfs_panic_recover("zfs: adding existent segment to "
"range tree (offset=%llx size=%llx)",
(longlong_t)start, (longlong_t)size);
return;
}
uint64_t rstart = rs_get_start(rs, rt); uint64_t rstart = rs_get_start(rs, rt);
uint64_t rend = rs_get_end(rs, rt); uint64_t rend = rs_get_end(rs, rt);
ASSERT3U(gap, !=, 0);
if (rstart <= start && rend >= end) { if (rstart <= start && rend >= end) {
range_tree_adjust_fill(rt, rs, fill); range_tree_adjust_fill(rt, rs, fill);
return; return;
@ -431,7 +445,7 @@ range_tree_remove_impl(range_tree_t *rt, uint64_t start, uint64_t size,
/* Make sure we completely overlap with someone */ /* Make sure we completely overlap with someone */
if (rs == NULL) { if (rs == NULL) {
zfs_panic_recover("zfs: removing nonexistent segment from " zfs_panic_recover("zfs: removing nonexistent segment from "
"range tree (offset=%llu size=%llu)", "range tree (offset=%llx size=%llx)",
(longlong_t)start, (longlong_t)size); (longlong_t)start, (longlong_t)size);
return; return;
} }
@ -455,8 +469,8 @@ range_tree_remove_impl(range_tree_t *rt, uint64_t start, uint64_t size,
} else if (rs_get_start(rs, rt) != start || } else if (rs_get_start(rs, rt) != start ||
rs_get_end(rs, rt) != end) { rs_get_end(rs, rt) != end) {
zfs_panic_recover("zfs: freeing partial segment of " zfs_panic_recover("zfs: freeing partial segment of "
"gap tree (offset=%llu size=%llu) of " "gap tree (offset=%llx size=%llx) of "
"(offset=%llu size=%llu)", "(offset=%llx size=%llx)",
(longlong_t)start, (longlong_t)size, (longlong_t)start, (longlong_t)size,
(longlong_t)rs_get_start(rs, rt), (longlong_t)rs_get_start(rs, rt),
(longlong_t)rs_get_end(rs, rt) - rs_get_start(rs, (longlong_t)rs_get_end(rs, rt) - rs_get_start(rs,