unit/zap: test that cursors correctly release all dnode holds
Cursors defer taking holds until they're needed, so if a cursor is created but not used, it may still hold resources that it would have cleaned up along the way, but never got chance to. (this really happened in the first version of zap_cursor_init_by_dnode(), so not a contrived case!) Sponsored-by: TrueNAS Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com> Signed-off-by: Rob Norris <rob.norris@truenas.com> Closes #18603
This commit is contained in:
committed by
Brian Behlendorf
parent
49b71917a6
commit
bfe4a8bb9d
@@ -725,6 +725,113 @@ test_cursor_serialize(const MunitParameter params[], void *data)
|
|||||||
return (MUNIT_OK);
|
return (MUNIT_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following tests confirm that the cursor is properly cleaning up dnode
|
||||||
|
* holds taken (or not) across the lifetime of the cursor. The test is not
|
||||||
|
* about how or when it takes holds, only that the dnode refcount is the
|
||||||
|
* same before zap_cursor_init() as after zap_cursor_fini().
|
||||||
|
*/
|
||||||
|
static MunitResult
|
||||||
|
test_cursor_release_unused(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
dnode_t *dn = mock_zap_create_params(params, "type");
|
||||||
|
|
||||||
|
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
|
||||||
|
|
||||||
|
zap_cursor_t zc;
|
||||||
|
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
|
||||||
|
zap_cursor_fini(&zc);
|
||||||
|
|
||||||
|
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
|
||||||
|
|
||||||
|
unit_true(mock_zap_is_params(dn, params, "type"));
|
||||||
|
mock_zap_destroy(dn);
|
||||||
|
|
||||||
|
return (MUNIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_cursor_release_advance(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
dnode_t *dn = mock_zap_create_params(params, "type");
|
||||||
|
|
||||||
|
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
|
||||||
|
|
||||||
|
zap_cursor_t zc;
|
||||||
|
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
|
||||||
|
zap_cursor_advance(&zc);
|
||||||
|
zap_cursor_fini(&zc);
|
||||||
|
|
||||||
|
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
|
||||||
|
|
||||||
|
unit_true(mock_zap_is_params(dn, params, "type"));
|
||||||
|
mock_zap_destroy(dn);
|
||||||
|
|
||||||
|
return (MUNIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_cursor_release_empty(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
dnode_t *dn = mock_zap_create_params(params, "type");
|
||||||
|
|
||||||
|
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
|
||||||
|
|
||||||
|
zap_cursor_t zc;
|
||||||
|
zap_attribute_t *za = zap_attribute_alloc();
|
||||||
|
|
||||||
|
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
|
||||||
|
unit_err(zap_cursor_retrieve(&zc, za), ENOENT);
|
||||||
|
|
||||||
|
zap_attribute_free(za);
|
||||||
|
zap_cursor_fini(&zc);
|
||||||
|
|
||||||
|
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
|
||||||
|
|
||||||
|
unit_true(mock_zap_is_params(dn, params, "type"));
|
||||||
|
mock_zap_destroy(dn);
|
||||||
|
|
||||||
|
return (MUNIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_cursor_release_one(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
dnode_t *dn = mock_zap_create_params(params, "type");
|
||||||
|
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
|
||||||
|
|
||||||
|
uint64_t v = 1;
|
||||||
|
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
|
||||||
|
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &v, tx));
|
||||||
|
|
||||||
|
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
|
||||||
|
|
||||||
|
zap_cursor_t zc;
|
||||||
|
zap_attribute_t *za = zap_attribute_alloc();
|
||||||
|
|
||||||
|
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
|
||||||
|
unit_ok(zap_cursor_retrieve(&zc, za));
|
||||||
|
|
||||||
|
zap_attribute_free(za);
|
||||||
|
zap_cursor_fini(&zc);
|
||||||
|
|
||||||
|
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
|
||||||
|
|
||||||
|
mock_tx_destroy((mock_dmu_tx_t *)tx);
|
||||||
|
unit_true(mock_zap_is_params(dn, params, "type"));
|
||||||
|
mock_zap_destroy(dn);
|
||||||
|
|
||||||
|
return (MUNIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========== */
|
/* ========== */
|
||||||
|
|
||||||
/* Test suite definition and boilerplate. */
|
/* Test suite definition and boilerplate. */
|
||||||
@@ -759,6 +866,15 @@ static const MunitTest zap_tests[] = {
|
|||||||
UNIT_TEST_ZAP_TYPES("cursor", test_cursor),
|
UNIT_TEST_ZAP_TYPES("cursor", test_cursor),
|
||||||
UNIT_TEST_ZAP_TYPES("cursor_serialize", test_cursor_serialize),
|
UNIT_TEST_ZAP_TYPES("cursor_serialize", test_cursor_serialize),
|
||||||
|
|
||||||
|
UNIT_TEST_ZAP_TYPES(
|
||||||
|
"cursor_release_unused", test_cursor_release_unused),
|
||||||
|
UNIT_TEST_ZAP_TYPES(
|
||||||
|
"cursor_release_advance", test_cursor_release_advance),
|
||||||
|
UNIT_TEST_ZAP_TYPES(
|
||||||
|
"cursor_release_empty", test_cursor_release_empty),
|
||||||
|
UNIT_TEST_ZAP_TYPES(
|
||||||
|
"cursor_release_one", test_cursor_release_one),
|
||||||
|
|
||||||
{ 0 },
|
{ 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user