Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/tests/unit/test_zap.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 <stdbool.h>
18
19
#include <sys/zap.h>
20
#include <sys/btree.h>
21
typedef struct spa spa_t; /* forward decl for zap_impl.h */
22
#include <sys/zap_impl.h>
23
24
#include "mock_dmu.h"
25
#include "unit.h"
26
27
/* ========== */
28
29
/*
30
* Normally defined and initialised in arc.c. We define and initialise it
31
* ourselves here so this mock can be linked without arc.c.
32
*/
33
uint64_t zfs_crc64_table[256];
34
35
static void
36
mock_crc64_init(void)
37
{
38
for (int i = 0; i < 256; i++) {
39
uint64_t ct = i;
40
for (int j = 8; j > 0; j--)
41
ct = (ct >> 1) ^ (-(ct & 1) & ZFS_CRC64_POLY);
42
zfs_crc64_table[i] = ct;
43
}
44
}
45
46
/* Misc utility functions. */
47
48
#define rd64(ptr, off) (*(uint64_t *)((const char *)(ptr) + (off)))
49
50
/* ========== */
51
52
/* ZAP-specific mocks and other test helpers. */
53
54
/* Create a microzap backed by a mock dnode. */
55
static dnode_t *
56
mock_zap_create_microzap(void) {
57
/*
58
* We use DMU_OTN_ZAP_DATA so that DMU_OT_BYTESWAP() returns
59
* DMU_BSWAP_ZAP without consulting dmu_ot[], which is not currently
60
* provided in the mock.
61
*/
62
mock_dnode_t *mdn = mock_dnode_create(512, DMU_OTN_ZAP_DATA);
63
dnode_t *dn = (dnode_t *)mdn;
64
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
65
mzap_create_impl(dn, 0, 0, tx);
66
mock_tx_destroy((mock_dmu_tx_t *)tx);
67
return (dn);
68
}
69
70
/* Create a fatzap backed by a mock dnode. */
71
static dnode_t *
72
mock_zap_create_fatzap(void)
73
{
74
/*
75
* We can only create microzaps directly. They only take u64s as a
76
* value, so we add a u16 to trigger an upgrade to fatzap.
77
*/
78
dnode_t *dn = mock_zap_create_microzap();
79
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
80
uint16_t upgrade = 0;
81
zap_add_by_dnode(dn, "_upgrade", sizeof (uint16_t), 1, &upgrade, tx);
82
zap_remove_by_dnode(dn, "_upgrade", tx);
83
mock_tx_destroy((mock_dmu_tx_t *)tx);
84
return (dn);
85
}
86
87
static bool
88
mock_zap_is_microzap(dnode_t *dn)
89
{
90
/* check block 0 has a microzap header */
91
const void *blk = mock_dnode_block_data((mock_dnode_t *)dn, 0);
92
return (rd64(blk, 0) == ZBT_MICRO);
93
}
94
95
static bool
96
mock_zap_is_fatzap(dnode_t *dn)
97
{
98
/* check block 0 has a fatzap header */
99
const void *blk = mock_dnode_block_data((mock_dnode_t *)dn, 0);
100
return (rd64(blk, 0) == ZBT_HEADER && rd64(blk, 8) == ZAP_MAGIC);
101
}
102
103
static void
104
mock_zap_destroy(dnode_t *dn)
105
{
106
mock_dnode_t *mdn = (mock_dnode_t *)dn;
107
unit_eq(mock_dnode_refcount(mdn), 1);
108
mock_dnode_destroy(mdn);
109
}
110
111
/* Create a ZAP of the type named in the given test params. */
112
static dnode_t *
113
mock_zap_create_params(const MunitParameter params[], const char *key) {
114
const char *type = munit_parameters_get(params, key);
115
if (type == NULL)
116
munit_error("mock_zap_create_params: missing type param");
117
else if (strcmp(type, "micro") == 0)
118
return (mock_zap_create_microzap());
119
else if (strcmp(type, "fat") == 0)
120
return (mock_zap_create_fatzap());
121
else
122
munit_errorf("mock_zap_create_params: invalid type '%s'", type);
123
__builtin_unreachable();
124
}
125
126
/*
127
* Confirm the stored ZAP is of the type named in the given test params. This
128
* is useful for sanity checks within tests that a ZAP wasn't unexpectedly
129
* upgraded during the test.
130
*/
131
static bool
132
mock_zap_is_params(dnode_t *dn, const MunitParameter params[],
133
const char *key)
134
{
135
const char *type = munit_parameters_get(params, key);
136
if (type == NULL)
137
munit_error("mock_zap_is_params: missing type param");
138
else if (strcmp(type, "micro") == 0)
139
return (mock_zap_is_microzap(dn));
140
else if (strcmp(type, "fat") == 0)
141
return (mock_zap_is_fatzap(dn));
142
else
143
munit_errorf("mock_zap_is_params: invalid type '%s'", type);
144
__builtin_unreachable();
145
}
146
147
/* ========== */
148
149
/*
150
* Sanity checks for mock ZAPs. Ensures that the mock_zap_create_* functions
151
* really do create the right kind of ZAPs, since many of the tests need to
152
* run against both kinds to confirm that they all work the same way.
153
*/
154
static MunitResult
155
test_mock_microzap_sanity(const MunitParameter params[], void *data)
156
{
157
(void) params, (void) data;
158
159
dnode_t *dn = mock_zap_create_microzap();
160
unit_true(mock_zap_is_microzap(dn));
161
mock_zap_destroy(dn);
162
163
return (MUNIT_OK);
164
}
165
166
static MunitResult
167
test_mock_fatzap_sanity(const MunitParameter params[], void *data)
168
{
169
(void) params, (void) data;
170
171
dnode_t *dn = mock_zap_create_fatzap();
172
unit_true(mock_zap_is_fatzap(dn));
173
mock_zap_destroy(dn);
174
175
return (MUNIT_OK);
176
}
177
178
/* ========== */
179
180
/*
181
* A simple add, lookup and remove test. Confirms basic operation. These are
182
* tested together simply because all other tests rely on these primitives.
183
*/
184
static MunitResult
185
test_zap_basic(const MunitParameter params[], void *data)
186
{
187
(void) data;
188
189
dnode_t *dn = mock_zap_create_params(params, "type");
190
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
191
192
/* Insert a few entries. */
193
uint64_t val42 = 42;
194
uint64_t val99 = 99;
195
uint64_t val0 = 0;
196
197
unit_ok(zap_add_by_dnode(dn, "hello",
198
sizeof (uint64_t), 1, &val42, tx));
199
unit_ok(zap_add_by_dnode(dn, "world",
200
sizeof (uint64_t), 1, &val99, tx));
201
unit_ok(zap_add_by_dnode(dn, "zero",
202
sizeof (uint64_t), 1, &val0, tx));
203
204
/* Lookup each entry. */
205
uint64_t result = 0;
206
unit_ok(zap_lookup_by_dnode(dn, "hello",
207
sizeof (uint64_t), 1, &result));
208
unit_eq(result, 42);
209
210
unit_ok(zap_lookup_by_dnode(dn, "world",
211
sizeof (uint64_t), 1, &result));
212
unit_eq(result, 99);
213
214
unit_ok(zap_lookup_by_dnode(dn, "zero",
215
sizeof (uint64_t), 1, &result));
216
unit_eq(result, 0);
217
218
/* Non-existent key should return ENOENT. */
219
unit_err(zap_lookup_by_dnode(dn, "nope",
220
sizeof (uint64_t), 1, &result), ENOENT);
221
222
/* Removing an entry should make it impossible to look up. */
223
unit_ok(zap_remove_by_dnode(dn, "world", tx));
224
unit_err(zap_lookup_by_dnode(dn, "world",
225
sizeof (uint64_t), 1, &result), ENOENT);
226
227
mock_tx_destroy((mock_dmu_tx_t *)tx);
228
unit_true(mock_zap_is_params(dn, params, "type"));
229
mock_zap_destroy(dn);
230
231
return (MUNIT_OK);
232
}
233
234
/* ========== */
235
236
/*
237
* "Core" ZAP API tests. Covers the most basic functionality upon which which
238
* everything else is built.
239
*
240
* Note that to avoid microzap upgrade here, we only short keys and
241
* single-uint64 values.
242
*/
243
244
/* zap_add: add new items. */
245
static MunitResult
246
test_zap_add(const MunitParameter params[], void *data)
247
{
248
(void) data;
249
250
dnode_t *dn = mock_zap_create_params(params, "type");
251
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
252
253
/* A key added can be found by that name. */
254
uint64_t va = 1, var = 0;
255
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &va, tx));
256
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var));
257
unit_eq(var, 1);
258
259
/* Another key added can be found by that name. */
260
uint64_t vb = 2, vbr = 0;
261
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &vb, tx));
262
unit_ok(zap_lookup_by_dnode(dn, "b", sizeof (uint64_t), 1, &vbr));
263
unit_eq(vbr, 2);
264
265
/* The first key is still findable with the right value. */
266
var = 0;
267
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var));
268
unit_eq(var, 1);
269
270
/* Adding the key again fails. */
271
unit_err(zap_add_by_dnode(dn, "a",
272
sizeof (uint64_t), 1, &va, tx), EEXIST);
273
274
/* Adding the key with a different value still fails. */
275
va = 2;
276
unit_err(zap_add_by_dnode(dn, "a",
277
sizeof (uint64_t), 1, &va, tx), EEXIST);
278
279
/* And is still findable with the original value. */
280
var = 0;
281
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var));
282
unit_eq(var, 1);
283
284
mock_tx_destroy((mock_dmu_tx_t *)tx);
285
unit_true(mock_zap_is_params(dn, params, "type"));
286
mock_zap_destroy(dn);
287
288
return (MUNIT_OK);
289
}
290
291
/* zap_update: add new or replace existing items. */
292
static MunitResult
293
test_zap_update(const MunitParameter params[], void *data)
294
{
295
(void) data;
296
297
dnode_t *dn = mock_zap_create_params(params, "type");
298
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
299
300
/* Update on a non-existent key inserts it. */
301
uint64_t va = 1, var = 0;
302
unit_ok(zap_update_by_dnode(dn, "a", sizeof (uint64_t), 1, &va, tx));
303
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var));
304
unit_eq(var, 1);
305
306
/* Update on an existing key replaces it without error. */
307
va = 2;
308
unit_ok(zap_update_by_dnode(dn, "a", sizeof (uint64_t), 1, &va, tx));
309
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var));
310
unit_eq(var, 2);
311
312
/* Count should still be 1 (no duplicate was created). */
313
uint64_t count = 0;
314
unit_ok(zap_count_by_dnode(dn, &count));
315
unit_eq(count, 1);
316
317
mock_tx_destroy((mock_dmu_tx_t *)tx);
318
unit_true(mock_zap_is_params(dn, params, "type"));
319
mock_zap_destroy(dn);
320
321
return (MUNIT_OK);
322
}
323
324
/* zap_remove: remove existing items. */
325
static MunitResult
326
test_zap_remove(const MunitParameter params[], void *data)
327
{
328
(void) data;
329
330
dnode_t *dn = mock_zap_create_params(params, "type");
331
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
332
333
/* Removing a non-existing key fails. */
334
unit_err(zap_remove_by_dnode(dn, "a", tx), ENOENT);
335
336
/* Adding two keys. */
337
uint64_t va = 1, vb = 2;
338
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &va, tx));
339
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &vb, tx));
340
341
/* Remove an existing key succeeds. */
342
unit_ok(zap_remove_by_dnode(dn, "a", tx));
343
344
/* After removing, looking up removed key fails. */
345
uint64_t var = 0;
346
unit_err(
347
zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &var), ENOENT);
348
349
/* Looking up the other key succeeds, and has the correct value. */
350
uint64_t vbr = 0;
351
unit_ok(zap_lookup_by_dnode(dn, "b", sizeof (uint64_t), 1, &vbr));
352
unit_eq(vbr, 2);
353
354
mock_tx_destroy((mock_dmu_tx_t *)tx);
355
unit_true(mock_zap_is_params(dn, params, "type"));
356
mock_zap_destroy(dn);
357
358
return (MUNIT_OK);
359
}
360
361
/* zap_count: number of entries, typically without lookup or traversal. */
362
static MunitResult
363
test_zap_count(const MunitParameter params[], void *data)
364
{
365
(void) data;
366
367
dnode_t *dn = mock_zap_create_params(params, "type");
368
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
369
370
/* A new ZAP has zero entries. */
371
uint64_t count = 0;
372
unit_ok(zap_count_by_dnode(dn, &count));
373
unit_eq(count, 0);
374
375
/* Adding two keys bumps the count to 2. */
376
uint64_t v = 1;
377
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
378
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &v, tx));
379
unit_ok(zap_count_by_dnode(dn, &count));
380
unit_eq(count, 2);
381
382
/* Removing a key reduces the count. */
383
unit_ok(zap_remove_by_dnode(dn, "a", tx));
384
unit_ok(zap_count_by_dnode(dn, &count));
385
unit_eq(count, 1);
386
387
mock_tx_destroy((mock_dmu_tx_t *)tx);
388
unit_true(mock_zap_is_params(dn, params, "type"));
389
mock_zap_destroy(dn);
390
391
return (MUNIT_OK);
392
}
393
394
/* zap_contains: existence check without reading the value. */
395
static MunitResult
396
test_zap_contains(const MunitParameter params[], void *data)
397
{
398
(void) data;
399
400
dnode_t *dn = mock_zap_create_params(params, "type");
401
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
402
403
uint64_t v = 1;
404
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
405
unit_ok(zap_contains_by_dnode(dn, "a"));
406
unit_err(zap_contains_by_dnode(dn, "b"), ENOENT);
407
408
mock_tx_destroy((mock_dmu_tx_t *)tx);
409
unit_true(mock_zap_is_params(dn, params, "type"));
410
mock_zap_destroy(dn);
411
412
return (MUNIT_OK);
413
}
414
415
/* zap_length: item metadata without reading the value. */
416
static MunitResult
417
test_zap_length(const MunitParameter params[], void *data)
418
{
419
(void) data;
420
421
dnode_t *dn = mock_zap_create_params(params, "type");
422
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
423
424
/* uint64: integer_size=8, num_integers=1. */
425
uint64_t v = 42;
426
unit_ok(zap_add_by_dnode(dn, "u64",
427
sizeof (uint64_t), 1, &v, tx));
428
429
uint64_t isz = 0, nint = 0;
430
unit_ok(zap_length_by_dnode(dn, "u64", &isz, &nint));
431
unit_eq(isz, 8);
432
unit_eq(nint, 1);
433
434
/* Missing key returns ENOENT. */
435
unit_err(zap_length_by_dnode(dn, "nope", &isz, &nint), ENOENT);
436
437
/* Either output pointer may be NULL. */
438
isz = 0; nint = 0;
439
unit_ok(zap_length_by_dnode(dn, "u64", NULL, &nint));
440
unit_ok(zap_length_by_dnode(dn, "u64", &isz, NULL));
441
unit_eq(isz, 8);
442
unit_eq(nint, 1);
443
444
mock_tx_destroy((mock_dmu_tx_t *)tx);
445
unit_true(mock_zap_is_params(dn, params, "type"));
446
mock_zap_destroy(dn);
447
448
return (MUNIT_OK);
449
}
450
451
/* zap_increment: add integer value to existing integer */
452
static MunitResult
453
test_zap_increment(const MunitParameter params[], void *data)
454
{
455
(void) data;
456
457
dnode_t *dn = mock_zap_create_params(params, "type");
458
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
459
460
uint64_t r = 0;
461
462
/* Increment a missing key creates it with that value. */
463
unit_ok(zap_increment_by_dnode(dn, "a", 5, tx));
464
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &r));
465
unit_eq(r, 5);
466
467
/* Further increments accumulate. */
468
unit_ok(zap_increment_by_dnode(dn, "a", 3, tx));
469
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &r));
470
unit_eq(r, 8);
471
472
/* Decrement works. */
473
unit_ok(zap_increment_by_dnode(dn, "a", -2, tx));
474
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &r));
475
unit_eq(r, 6);
476
477
/* Zero delta leaves it unchanged. */
478
r = 0;
479
unit_ok(zap_increment_by_dnode(dn, "a", 0, tx));
480
unit_ok(zap_lookup_by_dnode(dn, "a", sizeof (uint64_t), 1, &r));
481
unit_eq(r, 6);
482
483
/* Decrementing to zero removes the entry. */
484
unit_ok(zap_increment_by_dnode(dn, "a", -6, tx));
485
unit_err(zap_lookup_by_dnode(dn, "a",
486
sizeof (uint64_t), 1, &r), ENOENT);
487
488
/* Delta of zero is a no-op even for a missing key. */
489
unit_ok(zap_increment_by_dnode(dn, "a", 0, tx));
490
unit_err(zap_lookup_by_dnode(dn, "a",
491
sizeof (uint64_t), 1, &r), ENOENT);
492
493
mock_tx_destroy((mock_dmu_tx_t *)tx);
494
unit_true(mock_zap_is_params(dn, params, "type"));
495
mock_zap_destroy(dn);
496
497
return (MUNIT_OK);
498
}
499
500
/* ========== */
501
502
/*
503
* zap_add_int/zap_remove_int/zap_lookup_int: single uint64_t value,
504
* stringified to form the key.
505
*/
506
static MunitResult
507
test_zap_int(const MunitParameter params[], void *data)
508
{
509
(void) data;
510
511
dnode_t *dn = mock_zap_create_params(params, "type");
512
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
513
514
/* Add some ints. */
515
unit_ok(zap_add_int_by_dnode(dn, 5, tx));
516
unit_ok(zap_add_int_by_dnode(dn, 17, tx));
517
518
/* Confirm they're there. */
519
unit_ok(zap_lookup_int_by_dnode(dn, 17));
520
unit_ok(zap_lookup_int_by_dnode(dn, 5));
521
522
/* But not something we didn't add. */
523
unit_err(zap_lookup_int_by_dnode(dn, 23), ENOENT);
524
525
/* Adding something that already exists fails. */
526
unit_err(zap_add_int_by_dnode(dn, 17, tx), EEXIST);
527
528
/* Removing it works, and then it can't be found. */
529
unit_ok(zap_remove_int_by_dnode(dn, 17, tx));
530
unit_err(zap_lookup_int_by_dnode(dn, 17), ENOENT);
531
532
/* Add it can be added back. */
533
unit_ok(zap_add_int_by_dnode(dn, 17, tx));
534
unit_ok(zap_lookup_int_by_dnode(dn, 17));
535
536
mock_tx_destroy((mock_dmu_tx_t *)tx);
537
unit_true(mock_zap_is_params(dn, params, "type"));
538
mock_zap_destroy(dn);
539
540
return (MUNIT_OK);
541
}
542
543
/* zap_*_int_key: like zap_*_int, but with separate value. */
544
static MunitResult
545
test_zap_int_keys(const MunitParameter params[], void *data)
546
{
547
(void) data;
548
549
dnode_t *dn = mock_zap_create_params(params, "type");
550
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
551
552
/* Add some ints. */
553
unit_ok(zap_add_int_key_by_dnode(dn, 5, 17, tx));
554
unit_ok(zap_add_int_key_by_dnode(dn, 23, 35, tx));
555
556
/* Confirm they're there. */
557
uint64_t r = 0;
558
unit_ok(zap_lookup_int_key_by_dnode(dn, 5, &r));
559
unit_eq(r, 17);
560
unit_ok(zap_lookup_int_key_by_dnode(dn, 23, &r));
561
unit_eq(r, 35);
562
563
/* But not something we didn't add. */
564
unit_err(zap_lookup_int_key_by_dnode(dn, 79, &r), ENOENT);
565
566
/* Adding something that already exists fails. */
567
unit_err(zap_add_int_key_by_dnode(dn, 23, 51, tx), EEXIST);
568
569
/* Updating it works though. */
570
unit_ok(zap_update_int_key_by_dnode(dn, 23, 51, tx));
571
572
/* Removing it works, and then it can't be found. */
573
unit_ok(zap_remove_int_by_dnode(dn, 23, tx));
574
unit_err(zap_lookup_int_key_by_dnode(dn, 23, &r), ENOENT);
575
576
/* Add it can be added back. */
577
unit_ok(zap_add_int_key_by_dnode(dn, 23, 11, tx));
578
unit_ok(zap_lookup_int_key_by_dnode(dn, 23, &r));
579
unit_eq(r, 11);
580
581
mock_tx_destroy((mock_dmu_tx_t *)tx);
582
unit_true(mock_zap_is_params(dn, params, "type"));
583
mock_zap_destroy(dn);
584
585
return (MUNIT_OK);
586
}
587
588
/* ========== */
589
590
/*
591
* Separate stats tests for each ZAP type, since they are about internals and
592
* so can and will produce different results.
593
*/
594
595
static MunitResult
596
test_microzap_stats(const MunitParameter params[], void *data)
597
{
598
(void) params; (void) data;
599
600
dnode_t *dn = mock_zap_create_microzap();
601
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
602
603
zap_stats_t zs;
604
uint64_t v = 1;
605
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
606
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &v, tx));
607
unit_ok(zap_get_stats_by_dnode(dn, &zs));
608
609
/* We added two entries. */
610
unit_eq(zs.zs_num_entries, 2);
611
612
/* MicroZAP is always a single block. */
613
unit_eq(zs.zs_num_blocks, 1);
614
615
/* Blocksize matches what we passed to mock_dnode_create(). */
616
unit_eq(zs.zs_blocksize, 512);
617
618
mock_tx_destroy((mock_dmu_tx_t *)tx);
619
unit_true(mock_zap_is_microzap(dn));
620
mock_zap_destroy(dn);
621
622
return (MUNIT_OK);
623
}
624
625
static MunitResult
626
test_fatzap_stats(const MunitParameter params[], void *data)
627
{
628
(void) params; (void) data;
629
630
dnode_t *dn = mock_zap_create_fatzap();
631
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
632
633
zap_stats_t zs;
634
uint64_t v = 1;
635
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
636
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &v, tx));
637
unit_ok(zap_get_stats_by_dnode(dn, &zs));
638
639
/* We added two entries. */
640
unit_eq(zs.zs_num_entries, 2);
641
642
/* One header block, one leaf block. */
643
unit_eq(zs.zs_num_blocks, 2);
644
645
/* FatZAP block size set by tuneable. */
646
unit_eq(zs.zs_blocksize, 1 << fzap_default_block_shift);
647
648
mock_tx_destroy((mock_dmu_tx_t *)tx);
649
unit_true(mock_zap_is_fatzap(dn));
650
mock_zap_destroy(dn);
651
652
return (MUNIT_OK);
653
}
654
655
/* ========== */
656
657
/* Cursor tests. */
658
659
/*
660
* Basic cursor test. Add a bunch of keys+values to a ZAP, read them back
661
* via cursor, confirm they're all there and nothing else is.
662
*/
663
static MunitResult
664
test_cursor(const MunitParameter params[], void *data)
665
{
666
(void) data;
667
668
dnode_t *dn = mock_zap_create_params(params, "type");
669
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
670
671
/* For each ASCII letter as key, add a unique value to the ZAP. */
672
for (int i = 0; i < 26; i++) {
673
char c = (char)i + 'a';
674
char k[2] = { c, '\0' };
675
uint64_t v = (uint64_t)c * 11;
676
unit_ok(zap_add_by_dnode(dn, k, sizeof (uint64_t), 1, &v, tx));
677
}
678
679
/* Sanity check; confirm they're all there by count. */
680
uint64_t count = 0;
681
unit_ok(zap_count_by_dnode(dn, &count));
682
unit_eq(count, 26);
683
684
zap_cursor_t zc;
685
zap_attribute_t *za = zap_attribute_alloc();
686
687
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
688
689
/*
690
* Cursors don't guarantee an order, so we run over them them all,
691
* confirm the key matches the value, and then set a bit for each
692
* one we've seen. By the end, we should have seen them all.
693
*/
694
uint64_t seen = 0;
695
for (int i = 0; i < 26; i++) {
696
unit_ok(zap_cursor_retrieve(&zc, za));
697
698
/* Confirm attribute has the right details for the value. */
699
unit_eq(za->za_integer_length, sizeof (uint64_t));
700
unit_eq(za->za_num_integers, 1);
701
702
/*
703
* And the right key in za_name. Note that we don't check
704
* za_name_len, which is the length of a buffer that can
705
* definitely hold the key, not the key length itself.
706
*/
707
char c = za->za_name[0];
708
unit_true(c >= 'a' && c <= 'z');
709
unit_zero(za->za_name[1]);
710
711
/* Check the value in the attribute. */
712
uint64_t v = (uint64_t)c * 11;
713
unit_eq(za->za_first_integer, v);
714
715
/*
716
* Also do a direct lookup and confirm the value matches
717
* the value from the attribute.
718
*/
719
char k[2] = { c, '\0' };
720
uint64_t result = 0;
721
unit_ok(zap_lookup_by_dnode(dn, k,
722
sizeof (uint64_t), 1, &result));
723
unit_eq(result, v);
724
725
/* This one is good, set the bit to remember this fact. */
726
seen |= 1 << (c-'a');
727
728
zap_cursor_advance(&zc);
729
}
730
731
/* There should be no more keys in the ZAP. */
732
unit_err(zap_cursor_retrieve(&zc, za), ENOENT);
733
734
/* Bits 0-25 should be set if we've seen them all. */
735
unit_eq(seen, (1 << 26) - 1);
736
737
zap_attribute_free(za);
738
zap_cursor_fini(&zc);
739
740
mock_tx_destroy((mock_dmu_tx_t *)tx);
741
unit_true(mock_zap_is_params(dn, params, "type"));
742
mock_zap_destroy(dn);
743
744
return (MUNIT_OK);
745
}
746
747
/*
748
* Cursor serialize test. Add a bunch of items, use the cursor to read half of
749
* them back, then serialize the cursor. Reload the cursor from the serialized
750
* state and confirm that we pick up where we left off. Then do it again to
751
* ensure it doesn't rely on any internal state.
752
*/
753
static MunitResult
754
test_cursor_serialize(const MunitParameter params[], void *data)
755
{
756
(void) data;
757
758
dnode_t *dn = mock_zap_create_params(params, "type");
759
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
760
761
/* For each ASCII letter as key, add a unique value to the ZAP. */
762
for (int i = 0; i < 26; i++) {
763
char c = (char)i + 'a';
764
char k[2] = { c, '\0' };
765
uint64_t v = (uint64_t)c * 11;
766
unit_ok(zap_add_by_dnode(dn, k, sizeof (uint64_t), 1, &v, tx));
767
}
768
769
/* Sanity check; confirm they're all there by count. */
770
uint64_t count = 0;
771
unit_ok(zap_count_by_dnode(dn, &count));
772
unit_eq(count, 26);
773
774
/*
775
* Like test_cursor above, we'll walk over the ZAP and set bits
776
* for each key we see.
777
*/
778
zap_cursor_t zc;
779
zap_attribute_t *za = zap_attribute_alloc();
780
uint64_t seen = 0;
781
782
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
783
for (int i = 0; i < 13; i++) {
784
unit_ok(zap_cursor_retrieve(&zc, za));
785
786
char c = za->za_name[0];
787
unit_true(c >= 'a' && c <= 'z');
788
789
/* This one is good, set the bit to remember this fact. */
790
seen |= 1 << (c-'a');
791
792
zap_cursor_advance(&zc);
793
}
794
795
/* Serialise the and terminate the cursor. */
796
uint64_t cookie = zap_cursor_serialize(&zc);
797
zap_cursor_fini(&zc);
798
799
/*
800
* Record the bits we saw in the first iteration; we'll use this
801
* when we reload the cursor a second time below.
802
*/
803
uint64_t orig_seen = seen;
804
805
/* Reinitialise the cursor from the cookie. */
806
unit_ok(zap_cursor_init_serialized_by_dnode(&zc, dn, cookie));
807
808
/* Loop over the remaining entries and track them. */
809
for (int i = 0; i < 13; i++) {
810
unit_ok(zap_cursor_retrieve(&zc, za));
811
812
char c = za->za_name[0];
813
unit_true(c >= 'a' && c <= 'z');
814
815
/* This one is good, set the bit to remember this fact. */
816
seen |= 1 << (c-'a');
817
818
zap_cursor_advance(&zc);
819
}
820
821
/* There should be no more keys in the ZAP. */
822
unit_err(zap_cursor_retrieve(&zc, za), ENOENT);
823
824
/* Bits 0-25 should be set if we've seen them all. */
825
unit_eq(seen, (1 << 26) - 1);
826
827
/* Cursor done. */
828
zap_cursor_fini(&zc);
829
830
/*
831
* Restore the seen state to before when we reinitialised the saved
832
* cursor.
833
*/
834
seen = orig_seen;
835
836
/*
837
* Do it all again a second time. This is making sure that the saved
838
* cursor is usable even after the its been "used".
839
*/
840
unit_ok(zap_cursor_init_serialized_by_dnode(&zc, dn, cookie));
841
for (int i = 0; i < 13; i++) {
842
unit_ok(zap_cursor_retrieve(&zc, za));
843
844
char c = za->za_name[0];
845
unit_true(c >= 'a' && c <= 'z');
846
847
seen |= 1 << (c-'a');
848
849
zap_cursor_advance(&zc);
850
}
851
852
unit_err(zap_cursor_retrieve(&zc, za), ENOENT);
853
unit_eq(seen, (1 << 26) - 1);
854
855
zap_attribute_free(za);
856
zap_cursor_fini(&zc);
857
858
mock_tx_destroy((mock_dmu_tx_t *)tx);
859
unit_true(mock_zap_is_params(dn, params, "type"));
860
mock_zap_destroy(dn);
861
862
return (MUNIT_OK);
863
}
864
865
/*
866
* The following tests confirm that the cursor is properly cleaning up dnode
867
* holds taken (or not) across the lifetime of the cursor. The test is not
868
* about how or when it takes holds, only that the dnode refcount is the
869
* same before zap_cursor_init() as after zap_cursor_fini().
870
*/
871
static MunitResult
872
test_cursor_release_unused(const MunitParameter params[], void *data)
873
{
874
(void) data;
875
876
dnode_t *dn = mock_zap_create_params(params, "type");
877
878
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
879
880
zap_cursor_t zc;
881
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
882
zap_cursor_fini(&zc);
883
884
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
885
886
unit_true(mock_zap_is_params(dn, params, "type"));
887
mock_zap_destroy(dn);
888
889
return (MUNIT_OK);
890
}
891
892
static MunitResult
893
test_cursor_release_advance(const MunitParameter params[], void *data)
894
{
895
(void) data;
896
897
dnode_t *dn = mock_zap_create_params(params, "type");
898
899
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
900
901
zap_cursor_t zc;
902
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
903
zap_cursor_advance(&zc);
904
zap_cursor_fini(&zc);
905
906
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
907
908
unit_true(mock_zap_is_params(dn, params, "type"));
909
mock_zap_destroy(dn);
910
911
return (MUNIT_OK);
912
}
913
914
static MunitResult
915
test_cursor_release_empty(const MunitParameter params[], void *data)
916
{
917
(void) data;
918
919
dnode_t *dn = mock_zap_create_params(params, "type");
920
921
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
922
923
zap_cursor_t zc;
924
zap_attribute_t *za = zap_attribute_alloc();
925
926
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
927
unit_err(zap_cursor_retrieve(&zc, za), ENOENT);
928
929
zap_attribute_free(za);
930
zap_cursor_fini(&zc);
931
932
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
933
934
unit_true(mock_zap_is_params(dn, params, "type"));
935
mock_zap_destroy(dn);
936
937
return (MUNIT_OK);
938
}
939
940
static MunitResult
941
test_cursor_release_one(const MunitParameter params[], void *data)
942
{
943
(void) data;
944
945
dnode_t *dn = mock_zap_create_params(params, "type");
946
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
947
948
uint64_t v = 1;
949
unit_ok(zap_add_by_dnode(dn, "a", sizeof (uint64_t), 1, &v, tx));
950
unit_ok(zap_add_by_dnode(dn, "b", sizeof (uint64_t), 1, &v, tx));
951
952
uint64_t refcount = mock_dnode_refcount((mock_dnode_t *)dn);
953
954
zap_cursor_t zc;
955
zap_attribute_t *za = zap_attribute_alloc();
956
957
unit_ok(zap_cursor_init_by_dnode(&zc, dn));
958
unit_ok(zap_cursor_retrieve(&zc, za));
959
960
zap_attribute_free(za);
961
zap_cursor_fini(&zc);
962
963
unit_eq(refcount, mock_dnode_refcount((mock_dnode_t *)dn));
964
965
mock_tx_destroy((mock_dmu_tx_t *)tx);
966
unit_true(mock_zap_is_params(dn, params, "type"));
967
mock_zap_destroy(dn);
968
969
return (MUNIT_OK);
970
}
971
972
/* ========== */
973
974
/* zap_value_search: find key with given uint64 value. */
975
static MunitResult
976
test_zap_value_search(const MunitParameter params[], void *data)
977
{
978
(void) data;
979
980
dnode_t *dn = mock_zap_create_params(params, "type");
981
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
982
983
/* Add some items. */
984
uint64_t v1 = 1, v2 = 2, v3 = 3;
985
unit_ok(zap_add_by_dnode(dn, "one", sizeof (uint64_t), 1, &v1, tx));
986
unit_ok(zap_add_by_dnode(dn, "two", sizeof (uint64_t), 1, &v2, tx));
987
unit_ok(zap_add_by_dnode(dn, "three", sizeof (uint64_t), 1, &v3, tx));
988
989
char name[ZAP_MAXNAMELEN];
990
991
/* Find one of them. */
992
unit_ok(zap_value_search_by_dnode(dn, 2, 0, name, sizeof (name)));
993
unit_str_eq(name, "two");
994
995
/* Nonexistent value. */
996
unit_err(zap_value_search_by_dnode(dn, 10, 0,
997
name, sizeof (name)), ENOENT);
998
999
/* Buffer too small for the key. */
1000
unit_err(zap_value_search_by_dnode(dn, 3, 0, name, 2), ENAMETOOLONG);
1001
1002
mock_tx_destroy((mock_dmu_tx_t *)tx);
1003
unit_true(mock_zap_is_params(dn, params, "type"));
1004
mock_zap_destroy(dn);
1005
1006
return (MUNIT_OK);
1007
}
1008
1009
/* zap_value_search: value masks */
1010
static MunitResult
1011
test_zap_value_search_mask(const MunitParameter params[], void *data)
1012
{
1013
(void) data;
1014
1015
dnode_t *dn = mock_zap_create_params(params, "type");
1016
dmu_tx_t *tx = (dmu_tx_t *)mock_tx_create();
1017
1018
/*
1019
* Add a set of values. These all have the same bottom 16 bits, with
1020
* different upper 48 bits, segmented so we can mask them in different
1021
* and interesting ways.
1022
*/
1023
uint64_t v1 = 0x000000000000f0f0ull;
1024
uint64_t v2 = 0x00000000fffff0f0ull;
1025
uint64_t v3 = 0x0000ffff0000f0f0ull;
1026
uint64_t v4 = 0xffff00000000f0f0ull;
1027
1028
/*
1029
* Generate four random keys. We do this because zap_value_search() is
1030
* implemented with a simple cursor walk, so will always return the
1031
* first match in hash order, which with fixed keys will always give
1032
* exactly the same results. Using random keys ensures the test values
1033
* are encountered in different orders between test runs, giving us
1034
* better coverage when there are multiple matches.
1035
*/
1036
1037
char k1[9], k2[9], k3[9], k4[9];
1038
unit_rand_str(k1, sizeof (k1));
1039
unit_rand_str(k2, sizeof (k2));
1040
unit_rand_str(k3, sizeof (k3));
1041
unit_rand_str(k4, sizeof (k4));
1042
1043
unit_ok(zap_add_by_dnode(dn, k1, sizeof (uint64_t), 1, &v1, tx));
1044
unit_ok(zap_add_by_dnode(dn, k2, sizeof (uint64_t), 1, &v2, tx));
1045
unit_ok(zap_add_by_dnode(dn, k3, sizeof (uint64_t), 1, &v3, tx));
1046
unit_ok(zap_add_by_dnode(dn, k4, sizeof (uint64_t), 1, &v4, tx));
1047
1048
char name[ZAP_MAXNAMELEN];
1049
1050
/* 0 mask is equivalent to all bits set in mask ie exact match. */
1051
unit_ok(zap_value_search_by_dnode(dn,
1052
0xf0f0, 0, name, sizeof (name)));
1053
unit_str_eq(name, k1);
1054
unit_ok(zap_value_search_by_dnode(dn,
1055
0xf0f0, 0xffffffffffffffffull, name, sizeof (name)));
1056
unit_str_eq(name, k1);
1057
1058
/* Low 16 bits could match any. */
1059
unit_ok(zap_value_search_by_dnode(dn,
1060
0xf0f0, 0xffff, name, sizeof (name)));
1061
1062
/* Low 32 bits, 3/1 matches. */
1063
unit_ok(zap_value_search_by_dnode(dn,
1064
0x0000f0f0, 0xffffffff, name, sizeof (name)));
1065
unit_true(strcmp(name, k1) == 0 || strcmp(name, k3) == 0 ||
1066
strcmp(name, k4) == 0);
1067
unit_ok(zap_value_search_by_dnode(dn,
1068
0xfffff0f0, 0xffffffff, name, sizeof (name)));
1069
unit_str_eq(name, k2);
1070
1071
/* Low 48 bits, 2/1/1 matches */
1072
unit_ok(zap_value_search_by_dnode(dn,
1073
0x00000000f0f0ull, 0xffffffffffffull, name, sizeof (name)));
1074
unit_true(strcmp(name, k1) == 0 || strcmp(name, k4) == 0);
1075
unit_ok(zap_value_search_by_dnode(dn,
1076
0x0000fffff0f0ull, 0xffffffffffffull, name, sizeof (name)));
1077
unit_str_eq(name, k2);
1078
unit_ok(zap_value_search_by_dnode(dn,
1079
0xffff0000f0f0ull, 0xffffffffffffull, name, sizeof (name)));
1080
unit_str_eq(name, k3);
1081
1082
/* Value doesn't exist directly, but matches when mask applied. */
1083
unit_ok(zap_value_search_by_dnode(dn,
1084
0xffffffff, 0xffff0000, name, sizeof (name)));
1085
unit_str_eq(name, k2);
1086
1087
mock_tx_destroy((mock_dmu_tx_t *)tx);
1088
unit_true(mock_zap_is_params(dn, params, "type"));
1089
mock_zap_destroy(dn);
1090
1091
return (MUNIT_OK);
1092
}
1093
1094
/* ========== */
1095
1096
/* Test suite definition and boilerplate. */
1097
1098
#define UNIT_PARAM_ZAP_TYPES(p) \
1099
UNIT_PARAM((p), "micro", "fat")
1100
1101
static const MunitParameterEnum zap_type_params[] = {
1102
UNIT_PARAM_ZAP_TYPES("type"),
1103
{ 0 },
1104
};
1105
1106
#define UNIT_TEST_ZAP_TYPES(name, func) \
1107
UNIT_TEST(name, func, zap_type_params)
1108
1109
static const MunitTest zap_tests[] = {
1110
UNIT_TEST("mock_microzap_sanity", test_mock_microzap_sanity),
1111
UNIT_TEST("mock_fatzap_sanity", test_mock_fatzap_sanity),
1112
1113
UNIT_TEST_ZAP_TYPES("zap_basic", test_zap_basic),
1114
1115
UNIT_TEST_ZAP_TYPES("zap_add", test_zap_add),
1116
UNIT_TEST_ZAP_TYPES("zap_update", test_zap_update),
1117
UNIT_TEST_ZAP_TYPES("zap_remove", test_zap_remove),
1118
UNIT_TEST_ZAP_TYPES("zap_count", test_zap_count),
1119
UNIT_TEST_ZAP_TYPES("zap_contains", test_zap_contains),
1120
UNIT_TEST_ZAP_TYPES("zap_length", test_zap_length),
1121
1122
UNIT_TEST_ZAP_TYPES("zap_increment", test_zap_increment),
1123
1124
UNIT_TEST_ZAP_TYPES("zap_int", test_zap_int),
1125
UNIT_TEST_ZAP_TYPES("zap_int_keys", test_zap_int_keys),
1126
1127
UNIT_TEST("microzap_stats", test_microzap_stats),
1128
UNIT_TEST("fatzap_stats", test_fatzap_stats),
1129
1130
UNIT_TEST_ZAP_TYPES("cursor", test_cursor),
1131
UNIT_TEST_ZAP_TYPES("cursor_serialize", test_cursor_serialize),
1132
1133
UNIT_TEST_ZAP_TYPES(
1134
"cursor_release_unused", test_cursor_release_unused),
1135
UNIT_TEST_ZAP_TYPES(
1136
"cursor_release_advance", test_cursor_release_advance),
1137
UNIT_TEST_ZAP_TYPES(
1138
"cursor_release_empty", test_cursor_release_empty),
1139
UNIT_TEST_ZAP_TYPES(
1140
"cursor_release_one", test_cursor_release_one),
1141
1142
UNIT_TEST_ZAP_TYPES(
1143
"zap_value_search", test_zap_value_search),
1144
UNIT_TEST_ZAP_TYPES(
1145
"zap_value_search_mask", test_zap_value_search_mask),
1146
1147
{ 0 },
1148
};
1149
1150
static const MunitSuite zap_test_suite = {
1151
"zap.",
1152
zap_tests,
1153
NULL,
1154
1,
1155
MUNIT_SUITE_OPTION_NONE,
1156
};
1157
1158
int
1159
main(int argc, char **argv)
1160
{
1161
mock_crc64_init();
1162
1163
zap_init();
1164
1165
int rc = munit_suite_main(&zap_test_suite, NULL, argc, argv);
1166
1167
zap_fini();
1168
1169
return (rc);
1170
}
1171
1172