Add zfs_refcount_transfer_ownership_many()

When debugging is enabled and a zfs_refcount_t contains multiple holders
using the same key, but different ref_counts, the wrong reference_t may
be transferred.  Add a zfs_refcount_transfer_ownership_many() function,
like the existing zfs_refcount_*_many() functions, to match and transfer
the correct refcount_t;

This issue may occur when using encryption with refcount debugging
enabled.  An arc_buf_hdr_t can have references for both the
hdr->b_l1hdr.b_pabd and hdr->b_crypt_hdr.b_rabd both of which use
the hdr as the reference holder.  When unsharing the buffer the
p_abd should be transferred.

This issue does not impact production builds because refcount holders
are not tracked.

Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #7219
Closes #8000
This commit is contained in:
Brian Behlendorf
2018-10-08 14:58:21 -07:00
parent 4cbde2ecbf
commit d7e4b30a67
3 changed files with 21 additions and 9 deletions
+12 -3
View File
@@ -234,8 +234,8 @@ zfs_refcount_transfer(zfs_refcount_t *dst, zfs_refcount_t *src)
}
void
zfs_refcount_transfer_ownership(zfs_refcount_t *rc, void *current_holder,
void *new_holder)
zfs_refcount_transfer_ownership_many(zfs_refcount_t *rc, uint64_t number,
void *current_holder, void *new_holder)
{
reference_t *ref;
boolean_t found = B_FALSE;
@@ -248,7 +248,8 @@ zfs_refcount_transfer_ownership(zfs_refcount_t *rc, void *current_holder,
for (ref = list_head(&rc->rc_list); ref;
ref = list_next(&rc->rc_list, ref)) {
if (ref->ref_holder == current_holder) {
if (ref->ref_holder == current_holder &&
ref->ref_number == number) {
ref->ref_holder = new_holder;
found = B_TRUE;
break;
@@ -258,6 +259,14 @@ zfs_refcount_transfer_ownership(zfs_refcount_t *rc, void *current_holder,
mutex_exit(&rc->rc_mtx);
}
void
zfs_refcount_transfer_ownership(zfs_refcount_t *rc, void *current_holder,
void *new_holder)
{
return (zfs_refcount_transfer_ownership_many(rc, 1, current_holder,
new_holder));
}
/*
* If tracking is enabled, return true if a reference exists that matches
* the "holder" tag. If tracking is disabled, then return true if a reference