Path: blob/main/sys/contrib/openzfs/tests/unit/mock_dmu.c
289174 views
// SPDX-License-Identifier: CDDL-1.01/*2* This file and its contents are supplied under the terms of the3* Common Development and Distribution License ("CDDL"), version 1.0.4* You may only use this file in accordance with the terms of version5* 1.0 of the CDDL.6*7* A full copy of the text of the CDDL should have accompanied this8* source. A copy of the CDDL is also available via the Internet at9* http://www.illumos.org/license/CDDL.10*/1112/*13* Copyright (c) 2026, TrueNAS.14*/1516#include <stdarg.h>17#include <stdio.h>18#include <stdlib.h>19#include <string.h>2021#include <sys/zfs_context.h>22#include <sys/dmu.h>23#include <sys/dmu_tx.h>24#include <sys/dnode.h>25#include <sys/dsl_dataset.h>26#include <sys/spa.h>27#include <sys/zfeature.h>2829#include "mock_dmu.h"30#include "unit.h"3132/*33* A mock dbuf. A real dmu_buf_t (first for casting) plus the attached user34* data pointer. Block data is stored in a separate allocation so that the35* struct address remains stable across block resizes.36*/37struct mock_dbuf {38dmu_buf_t mdb_db;39dmu_buf_user_t *mdb_user;40mock_dnode_t *mdb_owner;41void *mdb_data;42};43typedef struct mock_dbuf mock_dbuf_t;4445/*46* A mock dnode. a real dnode_t (must be first for casting) with dn_type47* and dn_object set, plus a flat array of mock_dbuf_t indexed by block id.48*/49struct mock_dnode {50dnode_t mdn_dn;51uint64_t mdn_refcount;52size_t mdn_blksize;53size_t mdn_nblocks;54mock_dbuf_t **mdn_blocks;55};5657/*58* A mock transaction. We only allocate and zero it, nothing currently uses59* any of its internals.60*/61struct mock_dmu_tx {62dmu_tx_t mtx_tx;63};6465/* Mock dnode */6667static mock_dbuf_t *68mock_dnode_block_alloc(mock_dnode_t *mdn, uint64_t blkid)69{70mock_dbuf_t *mdb = kmem_zalloc(sizeof (mock_dbuf_t), KM_SLEEP);71mdb->mdb_data = kmem_zalloc(mdn->mdn_blksize, KM_SLEEP);7273mdb->mdb_db.db_object = mdn->mdn_dn.dn_object;74mdb->mdb_db.db_offset = blkid * mdn->mdn_blksize;75mdb->mdb_db.db_size = mdn->mdn_blksize;76mdb->mdb_db.db_data = mdb->mdb_data;77mdb->mdb_owner = mdn;7879return (mdb);80}8182/* Grow the dbuf array if needed, then return (or create) the dbuf for blkid. */83static mock_dbuf_t *84mock_dnode_block_get(mock_dnode_t *mdn, uint64_t blkid)85{86if (blkid >= mdn->mdn_nblocks) {87size_t new_n = blkid + 1;88mock_dbuf_t **new_blocks =89kmem_zalloc(new_n * sizeof (mock_dbuf_t *), KM_SLEEP);90if (mdn->mdn_blocks != NULL) {91memcpy(new_blocks, mdn->mdn_blocks,92mdn->mdn_nblocks * sizeof (mock_dbuf_t *));93kmem_free(mdn->mdn_blocks,94mdn->mdn_nblocks * sizeof (mock_dbuf_t *));95}96mdn->mdn_blocks = new_blocks;97mdn->mdn_nblocks = new_n;98}99100mock_dbuf_t *mdb = mdn->mdn_blocks[blkid];101if (mdb == NULL) {102mdb = mock_dnode_block_alloc(mdn, blkid);103mdn->mdn_blocks[blkid] = mdb;104}105return (mdb);106}107108mock_dnode_t *109mock_dnode_create(size_t blksize, dmu_object_type_t type)110{111ASSERT(IS_P2ALIGNED(blksize, 512));112113mock_dnode_t *mdn = kmem_zalloc(sizeof (mock_dnode_t), KM_SLEEP);114mdn->mdn_refcount = 1;115mdn->mdn_dn.dn_type = type;116mdn->mdn_dn.dn_object = 1; /* arbitrary non-zero object number */117mdn->mdn_blksize = blksize;118119return (mdn);120}121122void123mock_dnode_destroy(mock_dnode_t *mdn)124{125for (size_t i = 0; i < mdn->mdn_nblocks; i++) {126mock_dbuf_t *mdb = mdn->mdn_blocks[i];127if (mdb == NULL)128continue;129130/*131* Call the sync evict callback if one is set, mimicking the132* real DMU when a buffer's refcount drops to zero.133*/134if (mdb->mdb_user != NULL &&135mdb->mdb_user->dbu_evict_func_sync != NULL)136mdb->mdb_user->dbu_evict_func_sync(mdb->mdb_user);137138kmem_free(mdb->mdb_data, mdb->mdb_db.db_size);139kmem_free(mdb, sizeof (mock_dbuf_t));140}141142kmem_free(mdn->mdn_blocks,143mdn->mdn_nblocks * sizeof (mock_dbuf_t *));144kmem_free(mdn, sizeof (mock_dnode_t));145}146147size_t148mock_dnode_block_count(mock_dnode_t *mdn)149{150return (mdn->mdn_nblocks);151}152153const void *154mock_dnode_block_data(mock_dnode_t *mdn, uint64_t blkid)155{156if (blkid >= mdn->mdn_nblocks)157return (NULL);158return (mdn->mdn_blocks[blkid]->mdb_db.db_data);159}160161uint64_t162mock_dnode_refcount(mock_dnode_t *mdn)163{164return (mdn->mdn_refcount);165}166167/* Mock transaction */168169mock_dmu_tx_t *170mock_tx_create(void)171{172return (kmem_zalloc(sizeof (mock_dmu_tx_t), KM_SLEEP));173}174175void176mock_tx_destroy(mock_dmu_tx_t *tx)177{178kmem_free(tx, sizeof (mock_dmu_tx_t));179}180181/* DMU stubs, either no-op or light access to mock dnode internals. */182183int184dmu_buf_hold_by_dnode(dnode_t *dn, uint64_t offset, const void *tag,185dmu_buf_t **dbp, dmu_flags_t flags)186{187(void) tag; (void) flags;188189mock_dnode_t *mdn = (mock_dnode_t *)dn;190uint64_t blkid = offset / mdn->mdn_blksize;191mock_dbuf_t *mdb = mock_dnode_block_get(mdn, blkid);192193*dbp = &mdb->mdb_db;194return (0);195}196197void198dmu_buf_rele(dmu_buf_t *db, const void *tag)199{200(void) db; (void) tag;201}202203void *204dmu_buf_get_user(dmu_buf_t *db)205{206mock_dbuf_t *mdb = (mock_dbuf_t *)db;207return (mdb->mdb_user);208}209210void *211dmu_buf_set_user(dmu_buf_t *db, dmu_buf_user_t *new_user)212{213mock_dbuf_t *mdb = (mock_dbuf_t *)db;214if (mdb->mdb_user != NULL)215return (mdb->mdb_user); /* existing user wins */216mdb->mdb_user = new_user;217return (NULL); /* new_user wins */218}219220void221dmu_buf_will_dirty(dmu_buf_t *db, dmu_tx_t *tx)222{223(void) db; (void) tx;224}225226objset_t *227dmu_buf_get_objset(dmu_buf_t *db)228{229mock_dbuf_t *mdb = (mock_dbuf_t *)db;230231/*232* We return the mock_dnode_t pointer cast to objset_t so that233* dmu_object_set_blocksize() below can recover the dnode without234* needing a separate objset structure.235*/236return ((objset_t *)mdb->mdb_owner);237}238239int240dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size,241int ibs, dmu_tx_t *tx)242{243(void) object; (void) ibs; (void) tx;244245/* os is a mock_dnode_t (see dmu_buf_get_objset() above). */246mock_dnode_t *mdn = (mock_dnode_t *)os;247248/*249* Resize block 0's data buffer in place so the struct address stays250* stable.251*/252mock_dbuf_t *mdb = mdn->mdn_blocks[0];253void *new_data = kmem_zalloc(size, KM_SLEEP);254memcpy(new_data, mdb->mdb_data,255MIN(size, (size_t)mdb->mdb_db.db_size));256kmem_free(mdb->mdb_data, mdb->mdb_db.db_size);257258mdb->mdb_data = new_data;259mdb->mdb_db.db_size = size;260mdb->mdb_db.db_data = new_data;261mdn->mdn_blksize = size;262263return (0);264}265266boolean_t267dnode_add_ref(dnode_t *dn, const void *tag)268{269(void) tag;270mock_dnode_t *mdn = (mock_dnode_t *)dn;271if (mdn->mdn_refcount == 0)272return (B_FALSE);273mdn->mdn_refcount++;274return (B_TRUE);275}276277void278dnode_rele(dnode_t *dn, const void *tag)279{280(void) tag;281mock_dnode_t *mdn = (mock_dnode_t *)dn;282unit_gt(mdn->mdn_refcount, 0);283mdn->mdn_refcount--;284}285286/*287* Misc other stubs. Not strictly DMU mocks, and might move elsewhere later,288* but for now this is all we need for our limited test set.289*/290291spa_t *292dmu_objset_spa(objset_t *os)293{294(void) os;295return (NULL);296}297298int299dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,300uint64_t size, dmu_tx_t *tx)301{302(void) os; (void) object; (void) offset; (void) size; (void) tx;303return (0);304}305306void307dmu_prefetch_by_dnode(dnode_t *dn, int64_t level, uint64_t offset,308uint64_t len, zio_priority_t pri)309{310(void) dn; (void) level; (void) offset; (void) len; (void) pri;311}312313dsl_dataset_t *314dmu_objset_ds(objset_t *os)315{316(void) os;317return (NULL);318}319320boolean_t321dsl_dataset_feature_is_active(dsl_dataset_t *ds, spa_feature_t f)322{323(void) ds; (void) f;324return (B_FALSE);325}326327void328dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx)329{330(void) ds; (void) tx;331}332333boolean_t334spa_feature_is_enabled(spa_t *spa, spa_feature_t f)335{336(void) spa; (void) f;337return (B_FALSE);338}339340int341spa_maxblocksize(spa_t *spa)342{343(void) spa;344return (SPA_OLD_MAXBLOCKSIZE);345}346347const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES];348349void350byteswap_uint64_array(void *buf, size_t size)351{352(void) buf; (void) size;353}354355/*356* Various objset+object calls; returning error, as they need to use357* _by_dnode() variants to get the mock.358*/359int360dnode_hold(objset_t *os, uint64_t object, const void *tag, dnode_t **dnp)361{362(void) os; (void) object; (void) tag; (void) dnp;363return (EIO);364}365366int367dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx)368{369(void) os; (void) object; (void) tx;370return (EIO);371}372373uint64_t374dmu_object_alloc_hold(objset_t *os, dmu_object_type_t ot,375int blocksize, int indirect_blockshift, dmu_object_type_t bonustype,376int bonuslen, int dnodesize, dnode_t **allocated_dnode,377const void *tag, dmu_tx_t *tx)378{379(void) os; (void) ot; (void) blocksize; (void) indirect_blockshift;380(void) bonustype; (void) bonuslen; (void) dnodesize;381(void) allocated_dnode; (void) tag; (void) tx;382return (EIO);383}384385int386dmu_object_claim_dnsize(objset_t *os, uint64_t object, dmu_object_type_t ot,387int blocksize, dmu_object_type_t bonus_type, int bonus_len,388int dnodesize, dmu_tx_t *tx)389{390(void) os; (void) object; (void) ot; (void) blocksize;391(void) bonus_type; (void) bonus_len; (void) dnodesize; (void) tx;392return (EIO);393}394395int396dmu_object_info(objset_t *os, uint64_t object, dmu_object_info_t *doi)397{398(void) os; (void) object; (void) doi;399return (EIO);400}401402int403dmu_prefetch_wait(objset_t *os, uint64_t object, uint64_t offset,404uint64_t len)405{406(void) os; (void) object; (void) offset; (void) len;407return (EIO);408}409410411