xref: /freebsd/sys/contrib/openzfs/tests/unit/mock_dmu.c (revision d9497217456002b0ddad3cd319570d0b098daa29)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * http://www.illumos.org/license/CDDL.
11  */
12 
13 /*
14  * Copyright (c) 2026, TrueNAS.
15  */
16 
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <sys/zfs_context.h>
23 #include <sys/dmu.h>
24 #include <sys/dmu_tx.h>
25 #include <sys/dnode.h>
26 #include <sys/dsl_dataset.h>
27 #include <sys/spa.h>
28 #include <sys/zfeature.h>
29 
30 #include "mock_dmu.h"
31 #include "unit.h"
32 
33 /*
34  * A mock dbuf. A real dmu_buf_t (first for casting) plus the attached user
35  * data pointer. Block data is stored in a separate allocation so that the
36  * struct address remains stable across block resizes.
37  */
38 struct mock_dbuf {
39 	dmu_buf_t		mdb_db;
40 	dmu_buf_user_t		*mdb_user;
41 	mock_dnode_t		*mdb_owner;
42 	void			*mdb_data;
43 };
44 typedef struct mock_dbuf mock_dbuf_t;
45 
46 /*
47  * A mock dnode. a real dnode_t (must be first for casting) with dn_type
48  * and dn_object set, plus a flat array of mock_dbuf_t indexed by block id.
49  */
50 struct mock_dnode {
51 	dnode_t			mdn_dn;
52 	uint64_t		mdn_refcount;
53 	size_t			mdn_blksize;
54 	size_t			mdn_nblocks;
55 	mock_dbuf_t		**mdn_blocks;
56 };
57 
58 /*
59  * A mock transaction. We only allocate and zero it, nothing currently uses
60  * any of its internals.
61  */
62 struct mock_dmu_tx {
63 	dmu_tx_t		mtx_tx;
64 };
65 
66 /* Mock dnode */
67 
68 static mock_dbuf_t *
mock_dnode_block_alloc(mock_dnode_t * mdn,uint64_t blkid)69 mock_dnode_block_alloc(mock_dnode_t *mdn, uint64_t blkid)
70 {
71 	mock_dbuf_t *mdb = kmem_zalloc(sizeof (mock_dbuf_t), KM_SLEEP);
72 	mdb->mdb_data = kmem_zalloc(mdn->mdn_blksize, KM_SLEEP);
73 
74 	mdb->mdb_db.db_object = mdn->mdn_dn.dn_object;
75 	mdb->mdb_db.db_offset = blkid * mdn->mdn_blksize;
76 	mdb->mdb_db.db_size   = mdn->mdn_blksize;
77 	mdb->mdb_db.db_data   = mdb->mdb_data;
78 	mdb->mdb_owner = mdn;
79 
80 	return (mdb);
81 }
82 
83 /* Grow the dbuf array if needed, then return (or create) the dbuf for blkid. */
84 static mock_dbuf_t *
mock_dnode_block_get(mock_dnode_t * mdn,uint64_t blkid)85 mock_dnode_block_get(mock_dnode_t *mdn, uint64_t blkid)
86 {
87 	if (blkid >= mdn->mdn_nblocks) {
88 		size_t new_n = blkid + 1;
89 		mock_dbuf_t **new_blocks =
90 		    kmem_zalloc(new_n * sizeof (mock_dbuf_t *), KM_SLEEP);
91 		if (mdn->mdn_blocks != NULL) {
92 			memcpy(new_blocks, mdn->mdn_blocks,
93 			    mdn->mdn_nblocks * sizeof (mock_dbuf_t *));
94 			kmem_free(mdn->mdn_blocks,
95 			    mdn->mdn_nblocks * sizeof (mock_dbuf_t *));
96 		}
97 		mdn->mdn_blocks = new_blocks;
98 		mdn->mdn_nblocks = new_n;
99 	}
100 
101 	mock_dbuf_t *mdb = mdn->mdn_blocks[blkid];
102 	if (mdb == NULL) {
103 		mdb = mock_dnode_block_alloc(mdn, blkid);
104 		mdn->mdn_blocks[blkid] = mdb;
105 	}
106 	return (mdb);
107 }
108 
109 mock_dnode_t *
mock_dnode_create(size_t blksize,dmu_object_type_t type)110 mock_dnode_create(size_t blksize, dmu_object_type_t type)
111 {
112 	ASSERT(IS_P2ALIGNED(blksize, 512));
113 
114 	mock_dnode_t *mdn = kmem_zalloc(sizeof (mock_dnode_t), KM_SLEEP);
115 	mdn->mdn_refcount = 1;
116 	mdn->mdn_dn.dn_type = type;
117 	mdn->mdn_dn.dn_object = 1;	/* arbitrary non-zero object number */
118 	mdn->mdn_blksize = blksize;
119 
120 	return (mdn);
121 }
122 
123 void
mock_dnode_destroy(mock_dnode_t * mdn)124 mock_dnode_destroy(mock_dnode_t *mdn)
125 {
126 	for (size_t i = 0; i < mdn->mdn_nblocks; i++) {
127 		mock_dbuf_t *mdb = mdn->mdn_blocks[i];
128 		if (mdb == NULL)
129 			continue;
130 
131 		/*
132 		 * Call the sync evict callback if one is set, mimicking the
133 		 * real DMU when a buffer's refcount drops to zero.
134 		 */
135 		if (mdb->mdb_user != NULL &&
136 		    mdb->mdb_user->dbu_evict_func_sync != NULL)
137 			mdb->mdb_user->dbu_evict_func_sync(mdb->mdb_user);
138 
139 		kmem_free(mdb->mdb_data, mdb->mdb_db.db_size);
140 		kmem_free(mdb, sizeof (mock_dbuf_t));
141 	}
142 
143 	kmem_free(mdn->mdn_blocks,
144 	    mdn->mdn_nblocks * sizeof (mock_dbuf_t *));
145 	kmem_free(mdn, sizeof (mock_dnode_t));
146 }
147 
148 size_t
mock_dnode_block_count(mock_dnode_t * mdn)149 mock_dnode_block_count(mock_dnode_t *mdn)
150 {
151 	return (mdn->mdn_nblocks);
152 }
153 
154 const void *
mock_dnode_block_data(mock_dnode_t * mdn,uint64_t blkid)155 mock_dnode_block_data(mock_dnode_t *mdn, uint64_t blkid)
156 {
157 	if (blkid >= mdn->mdn_nblocks)
158 		return (NULL);
159 	return (mdn->mdn_blocks[blkid]->mdb_db.db_data);
160 }
161 
162 uint64_t
mock_dnode_refcount(mock_dnode_t * mdn)163 mock_dnode_refcount(mock_dnode_t *mdn)
164 {
165 	return (mdn->mdn_refcount);
166 }
167 
168 /* Mock transaction */
169 
170 mock_dmu_tx_t *
mock_tx_create(void)171 mock_tx_create(void)
172 {
173 	return (kmem_zalloc(sizeof (mock_dmu_tx_t), KM_SLEEP));
174 }
175 
176 void
mock_tx_destroy(mock_dmu_tx_t * tx)177 mock_tx_destroy(mock_dmu_tx_t *tx)
178 {
179 	kmem_free(tx, sizeof (mock_dmu_tx_t));
180 }
181 
182 /* DMU stubs, either no-op or light access to mock dnode internals. */
183 
184 int
dmu_buf_hold_by_dnode(dnode_t * dn,uint64_t offset,const void * tag,dmu_buf_t ** dbp,dmu_flags_t flags)185 dmu_buf_hold_by_dnode(dnode_t *dn, uint64_t offset, const void *tag,
186     dmu_buf_t **dbp, dmu_flags_t flags)
187 {
188 	(void) tag; (void) flags;
189 
190 	mock_dnode_t *mdn = (mock_dnode_t *)dn;
191 	uint64_t blkid = offset / mdn->mdn_blksize;
192 	mock_dbuf_t *mdb = mock_dnode_block_get(mdn, blkid);
193 
194 	*dbp = &mdb->mdb_db;
195 	return (0);
196 }
197 
198 void
dmu_buf_rele(dmu_buf_t * db,const void * tag)199 dmu_buf_rele(dmu_buf_t *db, const void *tag)
200 {
201 	(void) db; (void) tag;
202 }
203 
204 void *
dmu_buf_get_user(dmu_buf_t * db)205 dmu_buf_get_user(dmu_buf_t *db)
206 {
207 	mock_dbuf_t *mdb = (mock_dbuf_t *)db;
208 	return (mdb->mdb_user);
209 }
210 
211 void *
dmu_buf_set_user(dmu_buf_t * db,dmu_buf_user_t * new_user)212 dmu_buf_set_user(dmu_buf_t *db, dmu_buf_user_t *new_user)
213 {
214 	mock_dbuf_t *mdb = (mock_dbuf_t *)db;
215 	if (mdb->mdb_user != NULL)
216 		return (mdb->mdb_user);	/* existing user wins */
217 	mdb->mdb_user = new_user;
218 	return (NULL);			/* new_user wins */
219 }
220 
221 void
dmu_buf_will_dirty(dmu_buf_t * db,dmu_tx_t * tx)222 dmu_buf_will_dirty(dmu_buf_t *db, dmu_tx_t *tx)
223 {
224 	(void) db; (void) tx;
225 }
226 
227 objset_t *
dmu_buf_get_objset(dmu_buf_t * db)228 dmu_buf_get_objset(dmu_buf_t *db)
229 {
230 	mock_dbuf_t *mdb = (mock_dbuf_t *)db;
231 
232 	/*
233 	 * We return the mock_dnode_t pointer cast to objset_t so that
234 	 * dmu_object_set_blocksize() below can recover the dnode without
235 	 * needing a separate objset structure.
236 	 */
237 	return ((objset_t *)mdb->mdb_owner);
238 }
239 
240 int
dmu_object_set_blocksize(objset_t * os,uint64_t object,uint64_t size,int ibs,dmu_tx_t * tx)241 dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size,
242     int ibs, dmu_tx_t *tx)
243 {
244 	(void) object; (void) ibs; (void) tx;
245 
246 	/* os is a mock_dnode_t (see dmu_buf_get_objset() above). */
247 	mock_dnode_t *mdn = (mock_dnode_t *)os;
248 
249 	/*
250 	 * Resize block 0's data buffer in place so the struct address stays
251 	 * stable.
252 	 */
253 	mock_dbuf_t *mdb = mdn->mdn_blocks[0];
254 	void *new_data = kmem_zalloc(size, KM_SLEEP);
255 	memcpy(new_data, mdb->mdb_data,
256 	    MIN(size, (size_t)mdb->mdb_db.db_size));
257 	kmem_free(mdb->mdb_data, mdb->mdb_db.db_size);
258 
259 	mdb->mdb_data = new_data;
260 	mdb->mdb_db.db_size = size;
261 	mdb->mdb_db.db_data = new_data;
262 	mdn->mdn_blksize = size;
263 
264 	return (0);
265 }
266 
267 boolean_t
dnode_add_ref(dnode_t * dn,const void * tag)268 dnode_add_ref(dnode_t *dn, const void *tag)
269 {
270 	(void) tag;
271 	mock_dnode_t *mdn = (mock_dnode_t *)dn;
272 	if (mdn->mdn_refcount == 0)
273 		return (B_FALSE);
274 	mdn->mdn_refcount++;
275 	return (B_TRUE);
276 }
277 
278 void
dnode_rele(dnode_t * dn,const void * tag)279 dnode_rele(dnode_t *dn, const void *tag)
280 {
281 	(void) tag;
282 	mock_dnode_t *mdn = (mock_dnode_t *)dn;
283 	unit_gt(mdn->mdn_refcount, 0);
284 	mdn->mdn_refcount--;
285 }
286 
287 /*
288  * Misc other stubs. Not strictly DMU mocks, and might move elsewhere later,
289  * but for now this is all we need for our limited test set.
290  */
291 
292 spa_t *
dmu_objset_spa(objset_t * os)293 dmu_objset_spa(objset_t *os)
294 {
295 	(void) os;
296 	return (NULL);
297 }
298 
299 int
dmu_free_range(objset_t * os,uint64_t object,uint64_t offset,uint64_t size,dmu_tx_t * tx)300 dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,
301     uint64_t size, dmu_tx_t *tx)
302 {
303 	(void) os; (void) object; (void) offset; (void) size; (void) tx;
304 	return (0);
305 }
306 
307 void
dmu_prefetch_by_dnode(dnode_t * dn,int64_t level,uint64_t offset,uint64_t len,zio_priority_t pri)308 dmu_prefetch_by_dnode(dnode_t *dn, int64_t level, uint64_t offset,
309     uint64_t len, zio_priority_t pri)
310 {
311 	(void) dn; (void) level; (void) offset; (void) len; (void) pri;
312 }
313 
314 dsl_dataset_t *
dmu_objset_ds(objset_t * os)315 dmu_objset_ds(objset_t *os)
316 {
317 	(void) os;
318 	return (NULL);
319 }
320 
321 boolean_t
dsl_dataset_feature_is_active(dsl_dataset_t * ds,spa_feature_t f)322 dsl_dataset_feature_is_active(dsl_dataset_t *ds, spa_feature_t f)
323 {
324 	(void) ds; (void) f;
325 	return (B_FALSE);
326 }
327 
328 void
dsl_dataset_dirty(dsl_dataset_t * ds,dmu_tx_t * tx)329 dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx)
330 {
331 	(void) ds; (void) tx;
332 }
333 
334 boolean_t
spa_feature_is_enabled(spa_t * spa,spa_feature_t f)335 spa_feature_is_enabled(spa_t *spa, spa_feature_t f)
336 {
337 	(void) spa; (void) f;
338 	return (B_FALSE);
339 }
340 
341 int
spa_maxblocksize(spa_t * spa)342 spa_maxblocksize(spa_t *spa)
343 {
344 	(void) spa;
345 	return (SPA_OLD_MAXBLOCKSIZE);
346 }
347 
348 const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES];
349 
350 void
byteswap_uint64_array(void * buf,size_t size)351 byteswap_uint64_array(void *buf, size_t size)
352 {
353 	(void) buf; (void) size;
354 }
355 
356 /*
357  * Various objset+object calls; returning error, as they need to use
358  * _by_dnode() variants to get the mock.
359  */
360 int
dnode_hold(objset_t * os,uint64_t object,const void * tag,dnode_t ** dnp)361 dnode_hold(objset_t *os, uint64_t object, const void *tag, dnode_t **dnp)
362 {
363 	(void) os; (void) object; (void) tag; (void) dnp;
364 	return (EIO);
365 }
366 
367 int
dmu_object_free(objset_t * os,uint64_t object,dmu_tx_t * tx)368 dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx)
369 {
370 	(void) os; (void) object; (void) tx;
371 	return (EIO);
372 }
373 
374 uint64_t
dmu_object_alloc_hold(objset_t * os,dmu_object_type_t ot,int blocksize,int indirect_blockshift,dmu_object_type_t bonustype,int bonuslen,int dnodesize,dnode_t ** allocated_dnode,const void * tag,dmu_tx_t * tx)375 dmu_object_alloc_hold(objset_t *os, dmu_object_type_t ot,
376     int blocksize, int indirect_blockshift, dmu_object_type_t bonustype,
377     int bonuslen, int dnodesize, dnode_t **allocated_dnode,
378     const void *tag, dmu_tx_t *tx)
379 {
380 	(void) os; (void) ot; (void) blocksize; (void) indirect_blockshift;
381 	(void) bonustype; (void) bonuslen; (void) dnodesize;
382 	(void) allocated_dnode; (void) tag; (void) tx;
383 	return (EIO);
384 }
385 
386 int
dmu_object_claim_dnsize(objset_t * os,uint64_t object,dmu_object_type_t ot,int blocksize,dmu_object_type_t bonus_type,int bonus_len,int dnodesize,dmu_tx_t * tx)387 dmu_object_claim_dnsize(objset_t *os, uint64_t object, dmu_object_type_t ot,
388     int blocksize, dmu_object_type_t bonus_type, int bonus_len,
389     int dnodesize, dmu_tx_t *tx)
390 {
391 	(void) os; (void) object; (void) ot; (void) blocksize;
392 	(void) bonus_type; (void) bonus_len; (void) dnodesize; (void) tx;
393 	return (EIO);
394 }
395 
396 int
dmu_object_info(objset_t * os,uint64_t object,dmu_object_info_t * doi)397 dmu_object_info(objset_t *os, uint64_t object, dmu_object_info_t *doi)
398 {
399 	(void) os; (void) object; (void) doi;
400 	return (EIO);
401 }
402 
403 int
dmu_prefetch_wait(objset_t * os,uint64_t object,uint64_t offset,uint64_t len)404 dmu_prefetch_wait(objset_t *os, uint64_t object, uint64_t offset,
405     uint64_t len)
406 {
407 	(void) os; (void) object; (void) offset; (void) len;
408 	return (EIO);
409 }
410