From 9d1407e8f24825d74ba7cf228de545c0d1a0ce8f Mon Sep 17 00:00:00 2001 From: Rich Ercolani <214141+rincebrain@users.noreply.github.com> Date: Fri, 8 Oct 2021 14:10:34 -0400 Subject: [PATCH] Correct refcount_add in dmu_zfetch refcount_add_many(foo,N) is not the same as for (i=0; i < N; i++) { refcount_add(foo); } Unfortunately, this is only actually true with debug kernels and reference_tracking_enable=1. Reviewed-by: Brian Behlendorf Signed-off-by: Rich Ercolani Closes #12589 Closes #12602 --- include/sys/zfs_refcount.h | 8 ++++++++ module/zfs/dmu_zfetch.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/sys/zfs_refcount.h b/include/sys/zfs_refcount.h index 1e6449472e..2f59ebb32b 100644 --- a/include/sys/zfs_refcount.h +++ b/include/sys/zfs_refcount.h @@ -72,6 +72,14 @@ int zfs_refcount_is_zero(zfs_refcount_t *); int64_t zfs_refcount_count(zfs_refcount_t *); int64_t zfs_refcount_add(zfs_refcount_t *, const void *); int64_t zfs_refcount_remove(zfs_refcount_t *, const void *); +/* + * Note that (add|remove)_many add/remove one reference with "number" N, + * _not_ make N references with "number" 1, which is what vanilla + * zfs_refcount_(add|remove) would do if called N times. + * + * Attempting to remove a reference with number N when none exists is a + * panic on debug kernels with reference_tracking enabled. + */ int64_t zfs_refcount_add_many(zfs_refcount_t *, uint64_t, const void *); int64_t zfs_refcount_remove_many(zfs_refcount_t *, uint64_t, const void *); void zfs_refcount_transfer(zfs_refcount_t *, zfs_refcount_t *); diff --git a/module/zfs/dmu_zfetch.c b/module/zfs/dmu_zfetch.c index a26b0d7399..043344a137 100644 --- a/module/zfs/dmu_zfetch.c +++ b/module/zfs/dmu_zfetch.c @@ -488,7 +488,8 @@ dmu_zfetch_run(zstream_t *zs, boolean_t missed, boolean_t have_lock) issued = pf_end - pf_start + ipf_end - ipf_start; if (issued > 1) { /* More references on top of taken in dmu_zfetch_prepare(). */ - zfs_refcount_add_many(&zs->zs_refs, issued - 1, NULL); + for (int i = 0; i < issued - 1; i++) + zfs_refcount_add(&zs->zs_refs, NULL); } else if (issued == 0) { /* Some other thread has done our work, so drop the ref. */ if (zfs_refcount_remove(&zs->zs_refs, NULL) == 0)