From 4b99eaf765ca78545ad43246231e4cc90dd772f5 Mon Sep 17 00:00:00 2001 From: Rich Ercolani Date: Sat, 2 Oct 2021 10:07:43 -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. Signed-off-by: Rich Ercolani --- 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 1e6449472e38..2f59ebb32b07 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 a26b0d739921..043344a1375f 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)