Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/tests/unit/mock_dmu.c
289174 views
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 *
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 *
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 *
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
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
149
mock_dnode_block_count(mock_dnode_t *mdn)
150
{
151
return (mdn->mdn_nblocks);
152
}
153
154
const void *
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
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 *
171
mock_tx_create(void)
172
{
173
return (kmem_zalloc(sizeof (mock_dmu_tx_t), KM_SLEEP));
174
}
175
176
void
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
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
199
dmu_buf_rele(dmu_buf_t *db, const void *tag)
200
{
201
(void) db; (void) tag;
202
}
203
204
void *
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 *
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
222
dmu_buf_will_dirty(dmu_buf_t *db, dmu_tx_t *tx)
223
{
224
(void) db; (void) tx;
225
}
226
227
objset_t *
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
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
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
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 *
293
dmu_objset_spa(objset_t *os)
294
{
295
(void) os;
296
return (NULL);
297
}
298
299
int
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
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 *
315
dmu_objset_ds(objset_t *os)
316
{
317
(void) os;
318
return (NULL);
319
}
320
321
boolean_t
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
329
dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx)
330
{
331
(void) ds; (void) tx;
332
}
333
334
boolean_t
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
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
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
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
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
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
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
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
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
411