zap: reuse zap_leaf_t on dbuf reuse after shrink

If a shrink or truncate had recently freed a portion of the ZAP, the
dbuf could still be sitting on the dbuf cache waiting for eviction. If
it is then allocated for a new leaf before it can be evicted, the
zap_leaf_t is still attached as userdata, tripping the VERIFY.

Instead, just check for the userdata, and if we find it, reuse it.

Sponsored-by: Klara, Inc.
Sponsored-by: iXsystems, Inc.
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16157.
Closes #16204
This commit is contained in:
Rob N 2024-05-25 11:55:47 +10:00 committed by GitHub
parent 708be0f415
commit 34906f8bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 25 additions and 9 deletions

View File

@ -425,20 +425,36 @@ zap_leaf_evict_sync(void *dbu)
static zap_leaf_t *
zap_create_leaf(zap_t *zap, dmu_tx_t *tx)
{
zap_leaf_t *l = kmem_zalloc(sizeof (zap_leaf_t), KM_SLEEP);
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
rw_init(&l->l_rwlock, NULL, RW_NOLOCKDEP, NULL);
rw_enter(&l->l_rwlock, RW_WRITER);
l->l_blkid = zap_allocate_blocks(zap, 1);
l->l_dbuf = NULL;
uint64_t blkid = zap_allocate_blocks(zap, 1);
dmu_buf_t *db = NULL;
VERIFY0(dmu_buf_hold_by_dnode(zap->zap_dnode,
l->l_blkid << FZAP_BLOCK_SHIFT(zap), NULL, &l->l_dbuf,
blkid << FZAP_BLOCK_SHIFT(zap), NULL, &db,
DMU_READ_NO_PREFETCH));
dmu_buf_init_user(&l->l_dbu, zap_leaf_evict_sync, NULL, &l->l_dbuf);
VERIFY3P(NULL, ==, dmu_buf_set_user(l->l_dbuf, &l->l_dbu));
/*
* Create the leaf structure and stash it on the dbuf. If zap was
* recent shrunk or truncated, the dbuf might have been sitting in the
* cache waiting to be evicted, and so still have the old leaf attached
* to it. If so, just reuse it.
*/
zap_leaf_t *l = dmu_buf_get_user(db);
if (l == NULL) {
l = kmem_zalloc(sizeof (zap_leaf_t), KM_SLEEP);
l->l_blkid = blkid;
l->l_dbuf = db;
rw_init(&l->l_rwlock, NULL, RW_NOLOCKDEP, NULL);
dmu_buf_init_user(&l->l_dbu, zap_leaf_evict_sync, NULL,
&l->l_dbuf);
dmu_buf_set_user(l->l_dbuf, &l->l_dbu);
} else {
ASSERT3U(l->l_blkid, ==, blkid);
ASSERT3P(l->l_dbuf, ==, db);
}
rw_enter(&l->l_rwlock, RW_WRITER);
dmu_buf_will_dirty(l->l_dbuf, tx);
zap_leaf_init(l, zap->zap_normflags != 0);