]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add zfs_refcount_transfer_ownership_many()
authorBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 8 Oct 2018 21:58:21 +0000 (14:58 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 9 Oct 2018 17:05:48 +0000 (10:05 -0700)
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

include/sys/refcount.h
module/zfs/arc.c
module/zfs/refcount.c

index 0059a245eea7022ddde8a1c676b35f487cbddb7c..e982faeba0f29031b2f5ad035da40374bd25f783 100644 (file)
@@ -76,6 +76,8 @@ int64_t zfs_refcount_add_many(zfs_refcount_t *, uint64_t, void *);
 int64_t zfs_refcount_remove_many(zfs_refcount_t *, uint64_t, void *);
 void zfs_refcount_transfer(zfs_refcount_t *, zfs_refcount_t *);
 void zfs_refcount_transfer_ownership(zfs_refcount_t *, void *, void *);
+void zfs_refcount_transfer_ownership_many(zfs_refcount_t *, uint64_t,
+    void *, void *);
 boolean_t zfs_refcount_held(zfs_refcount_t *, void *);
 boolean_t zfs_refcount_not_held(zfs_refcount_t *, void *);
 
@@ -106,8 +108,9 @@ typedef struct refcount {
        atomic_add_64(&(src)->rc_count, -__tmp); \
        atomic_add_64(&(dst)->rc_count, __tmp); \
 }
-#define        zfs_refcount_transfer_ownership(rc, current_holder, new_holder) (void)0
-#define        zfs_refcount_held(rc, holder)           ((rc)->rc_count > 0)
+#define        zfs_refcount_transfer_ownership(rc, ch, nh)             ((void)0)
+#define        zfs_refcount_transfer_ownership_many(rc, nr, ch, nh)    ((void)0)
+#define        zfs_refcount_held(rc, holder)                   ((rc)->rc_count > 0)
 #define        zfs_refcount_not_held(rc, holder)               (B_TRUE)
 
 #define        zfs_refcount_init()
index ae912ee6eb116cc707f7da131110a45c27fb3f50..e5d88fe5d7ef69505ae9cd93e86fa77a29de647a 100644 (file)
@@ -3081,8 +3081,8 @@ arc_share_buf(arc_buf_hdr_t *hdr, arc_buf_t *buf)
         * refcount ownership to the hdr since it always owns
         * the refcount whenever an arc_buf_t is shared.
         */
-       zfs_refcount_transfer_ownership(&hdr->b_l1hdr.b_state->arcs_size,
-           buf, hdr);
+       zfs_refcount_transfer_ownership_many(&hdr->b_l1hdr.b_state->arcs_size,
+           arc_hdr_size(hdr), buf, hdr);
        hdr->b_l1hdr.b_pabd = abd_get_from_buf(buf->b_data, arc_buf_size(buf));
        abd_take_ownership_of_buf(hdr->b_l1hdr.b_pabd,
            HDR_ISTYPE_METADATA(hdr));
@@ -3110,8 +3110,8 @@ arc_unshare_buf(arc_buf_hdr_t *hdr, arc_buf_t *buf)
         * We are no longer sharing this buffer so we need
         * to transfer its ownership to the rightful owner.
         */
-       zfs_refcount_transfer_ownership(&hdr->b_l1hdr.b_state->arcs_size,
-           hdr, buf);
+       zfs_refcount_transfer_ownership_many(&hdr->b_l1hdr.b_state->arcs_size,
+           arc_hdr_size(hdr), hdr, buf);
        arc_hdr_clear_flags(hdr, ARC_FLAG_SHARED_DATA);
        abd_release_ownership_of_buf(hdr->b_l1hdr.b_pabd);
        abd_put(hdr->b_l1hdr.b_pabd);
index 0a93aafb1358db4df62c3a1de63a424d515d3d08..bcaa6d387539eabb648c5229b7acaefba7b8bdc9 100644 (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