From 8f933f53e23372edab2d2e9a550b89ee9188618b Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Fri, 29 May 2026 14:38:48 +1000 Subject: [PATCH] unit/mock_dmu: track dnode refcount changes The thing under test will be taking and releasing dnode refs/holds. By counting them and exposing the current count, we can assert in test cleanup that we haven't missed releasing any, especially in cases where the hold is held across multiple test steps. Sponsored-by: TrueNAS Reviewed-by: Brian Behlendorf Reviewed-by: Alexander Motin Signed-off-by: Rob Norris Closes #18603 --- tests/unit/mock_dmu.c | 20 ++++++++++++++++++-- tests/unit/mock_dmu.h | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/unit/mock_dmu.c b/tests/unit/mock_dmu.c index 65c38c1fd9f..ae035498da6 100644 --- a/tests/unit/mock_dmu.c +++ b/tests/unit/mock_dmu.c @@ -28,6 +28,7 @@ #include #include "mock_dmu.h" +#include "unit.h" /* * A mock dbuf. A real dmu_buf_t (first for casting) plus the attached user @@ -48,6 +49,7 @@ typedef struct mock_dbuf mock_dbuf_t; */ struct mock_dnode { dnode_t mdn_dn; + uint64_t mdn_refcount; size_t mdn_blksize; size_t mdn_nblocks; mock_dbuf_t **mdn_blocks; @@ -110,6 +112,7 @@ mock_dnode_create(size_t blksize, dmu_object_type_t type) ASSERT(IS_P2ALIGNED(blksize, 512)); mock_dnode_t *mdn = kmem_zalloc(sizeof (mock_dnode_t), KM_SLEEP); + mdn->mdn_refcount = 1; mdn->mdn_dn.dn_type = type; mdn->mdn_dn.dn_object = 1; /* arbitrary non-zero object number */ mdn->mdn_blksize = blksize; @@ -156,6 +159,12 @@ mock_dnode_block_data(mock_dnode_t *mdn, uint64_t blkid) return (mdn->mdn_blocks[blkid]->mdb_db.db_data); } +uint64_t +mock_dnode_refcount(mock_dnode_t *mdn) +{ + return (mdn->mdn_refcount); +} + /* Mock transaction */ mock_dmu_tx_t * @@ -258,14 +267,21 @@ dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size, boolean_t dnode_add_ref(dnode_t *dn, const void *tag) { - (void) dn; (void) tag; + (void) tag; + mock_dnode_t *mdn = (mock_dnode_t *)dn; + if (mdn->mdn_refcount == 0) + return (B_FALSE); + mdn->mdn_refcount++; return (B_TRUE); } void dnode_rele(dnode_t *dn, const void *tag) { - (void) dn; (void) tag; + (void) tag; + mock_dnode_t *mdn = (mock_dnode_t *)dn; + unit_gt(mdn->mdn_refcount, 0); + mdn->mdn_refcount--; } /* diff --git a/tests/unit/mock_dmu.h b/tests/unit/mock_dmu.h index a46454c779f..2ac82c18b7a 100644 --- a/tests/unit/mock_dmu.h +++ b/tests/unit/mock_dmu.h @@ -40,6 +40,9 @@ size_t mock_dnode_block_count(mock_dnode_t *mdn); /* Returns a pointer to the data under the given block id. */ const void *mock_dnode_block_data(mock_dnode_t *mdn, uint64_t blkid); +/* Returns the current dnode ref (hold) count. */ +uint64_t mock_dnode_refcount(mock_dnode_t *mdn); + /* Create/destroy a mock transaction handle. */ mock_dmu_tx_t *mock_tx_create(void); void mock_tx_destroy(mock_dmu_tx_t *tx);