Skip to content

Commit fabdd50

Browse files
robnixhamza
authored andcommitted
zinject: count matches and injections for each handler
When building tests with zinject, it can be quite difficult to work out if you're producing the right kind of IO to match the rules you've set up. So, here we extend injection records to count the number of times a handler matched the operation, and how often an error was actually injected (ie after frequency and other exclusions are applied). Then, display those counts in the `zinject` output. Reviewed-by: Tony Hutter <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: Rob Norris <[email protected]> Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Closes openzfs#16938
1 parent 675b49d commit fabdd50

File tree

6 files changed

+236
-39
lines changed

6 files changed

+236
-39
lines changed

cmd/zinject/zinject.c

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
2323
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
2424
* Copyright (c) 2017, Intel Corporation.
25-
* Copyright (c) 2023-2024, Klara Inc.
25+
* Copyright (c) 2023-2025, Klara, Inc.
2626
*/
2727

2828
/*
@@ -404,27 +404,30 @@ print_data_handler(int id, const char *pool, zinject_record_t *record,
404404

405405
if (*count == 0) {
406406
(void) printf("%3s %-15s %-6s %-6s %-8s %3s %-4s "
407-
"%-15s\n", "ID", "POOL", "OBJSET", "OBJECT", "TYPE",
408-
"LVL", "DVAs", "RANGE");
407+
"%-15s %-6s %-15s\n", "ID", "POOL", "OBJSET", "OBJECT",
408+
"TYPE", "LVL", "DVAs", "RANGE", "MATCH", "INJECT");
409409
(void) printf("--- --------------- ------ "
410-
"------ -------- --- ---- ---------------\n");
410+
"------ -------- --- ---- --------------- "
411+
"------ ------\n");
411412
}
412413

413414
*count += 1;
414415

415-
(void) printf("%3d %-15s %-6llu %-6llu %-8s %-3d 0x%02x ",
416-
id, pool, (u_longlong_t)record->zi_objset,
417-
(u_longlong_t)record->zi_object, type_to_name(record->zi_type),
418-
record->zi_level, record->zi_dvas);
419-
420-
421-
if (record->zi_start == 0 &&
422-
record->zi_end == -1ULL)
423-
(void) printf("all\n");
416+
char rangebuf[32];
417+
if (record->zi_start == 0 && record->zi_end == -1ULL)
418+
snprintf(rangebuf, sizeof (rangebuf), "all");
424419
else
425-
(void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
420+
snprintf(rangebuf, sizeof (rangebuf), "[%llu, %llu]",
421+
(u_longlong_t)record->zi_start,
426422
(u_longlong_t)record->zi_end);
427423

424+
425+
(void) printf("%3d %-15s %-6llu %-6llu %-8s %-3d 0x%02x %-15s "
426+
"%6lu %6lu\n", id, pool, (u_longlong_t)record->zi_objset,
427+
(u_longlong_t)record->zi_object, type_to_name(record->zi_type),
428+
record->zi_level, record->zi_dvas, rangebuf,
429+
record->zi_match_count, record->zi_inject_count);
430+
428431
return (0);
429432
}
430433

@@ -445,21 +448,25 @@ print_device_handler(int id, const char *pool, zinject_record_t *record,
445448
return (0);
446449

447450
if (*count == 0) {
448-
(void) printf("%3s %-15s %-16s %-5s %-10s %-9s\n",
449-
"ID", "POOL", "GUID", "TYPE", "ERROR", "FREQ");
451+
(void) printf("%3s %-15s %-16s %-5s %-10s %-9s "
452+
"%-6s %-6s\n",
453+
"ID", "POOL", "GUID", "TYPE", "ERROR", "FREQ",
454+
"MATCH", "INJECT");
450455
(void) printf(
451456
"--- --------------- ---------------- "
452-
"----- ---------- ---------\n");
457+
"----- ---------- --------- "
458+
"------ ------\n");
453459
}
454460

455461
*count += 1;
456462

457463
double freq = record->zi_freq == 0 ? 100.0f :
458464
(((double)record->zi_freq) / ZI_PERCENTAGE_MAX) * 100.0f;
459465

460-
(void) printf("%3d %-15s %llx %-5s %-10s %8.4f%%\n", id, pool,
461-
(u_longlong_t)record->zi_guid, iotypestr[record->zi_iotype],
462-
err_to_str(record->zi_error), freq);
466+
(void) printf("%3d %-15s %llx %-5s %-10s %8.4f%% "
467+
"%6lu %6lu\n", id, pool, (u_longlong_t)record->zi_guid,
468+
iotypestr[record->zi_iotype], err_to_str(record->zi_error),
469+
freq, record->zi_match_count, record->zi_inject_count);
463470

464471
return (0);
465472
}
@@ -477,18 +484,25 @@ print_delay_handler(int id, const char *pool, zinject_record_t *record,
477484
return (0);
478485

479486
if (*count == 0) {
480-
(void) printf("%3s %-15s %-15s %-15s %s\n",
481-
"ID", "POOL", "DELAY (ms)", "LANES", "GUID");
482-
(void) printf("--- --------------- --------------- "
483-
"--------------- ----------------\n");
487+
(void) printf("%3s %-15s %-16s %-10s %-5s %-9s "
488+
"%-6s %-6s\n",
489+
"ID", "POOL", "GUID", "DELAY (ms)", "LANES", "FREQ",
490+
"MATCH", "INJECT");
491+
(void) printf("--- --------------- ---------------- "
492+
"---------- ----- --------- "
493+
"------ ------\n");
484494
}
485495

486496
*count += 1;
487497

488-
(void) printf("%3d %-15s %-15llu %-15llu %llx\n", id, pool,
498+
double freq = record->zi_freq == 0 ? 100.0f :
499+
(((double)record->zi_freq) / ZI_PERCENTAGE_MAX) * 100.0f;
500+
501+
(void) printf("%3d %-15s %llx %10llu %5llu %8.4f%% "
502+
"%6lu %6lu\n", id, pool, (u_longlong_t)record->zi_guid,
489503
(u_longlong_t)NSEC2MSEC(record->zi_timer),
490504
(u_longlong_t)record->zi_nlanes,
491-
(u_longlong_t)record->zi_guid);
505+
freq, record->zi_match_count, record->zi_inject_count);
492506

493507
return (0);
494508
}

include/sys/zfs_ioctl.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
* Copyright (c) 2012, 2024 by Delphix. All rights reserved.
2424
* Copyright 2016 RackTop Systems.
2525
* Copyright (c) 2017, Intel Corporation.
26-
* Copyright (c) 2024, Klara, Inc.
26+
* Copyright (c) 2024-2025, Klara, Inc.
2727
*/
2828

2929
#ifndef _SYS_ZFS_IOCTL_H
@@ -421,6 +421,8 @@ typedef struct zinject_record {
421421
uint64_t zi_nlanes;
422422
uint32_t zi_cmd;
423423
uint32_t zi_dvas;
424+
uint64_t zi_match_count; /* count of times matched */
425+
uint64_t zi_inject_count; /* count of times injected */
424426
} zinject_record_t;
425427

426428
#define ZINJECT_NULL 0x1

module/zfs/zio_inject.c

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
2323
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
2424
* Copyright (c) 2017, Intel Corporation.
25-
* Copyright (c) 2024, Klara Inc.
25+
* Copyright (c) 2024-2025, Klara, Inc.
2626
*/
2727

2828
/*
@@ -129,6 +129,9 @@ static boolean_t
129129
zio_match_handler(const zbookmark_phys_t *zb, uint64_t type, int dva,
130130
zinject_record_t *record, int error)
131131
{
132+
boolean_t matched = B_FALSE;
133+
boolean_t injected = B_FALSE;
134+
132135
/*
133136
* Check for a match against the MOS, which is based on type
134137
*/
@@ -137,9 +140,8 @@ zio_match_handler(const zbookmark_phys_t *zb, uint64_t type, int dva,
137140
record->zi_object == DMU_META_DNODE_OBJECT) {
138141
if (record->zi_type == DMU_OT_NONE ||
139142
type == record->zi_type)
140-
return (freq_triggered(record->zi_freq));
141-
else
142-
return (B_FALSE);
143+
matched = B_TRUE;
144+
goto done;
143145
}
144146

145147
/*
@@ -153,10 +155,20 @@ zio_match_handler(const zbookmark_phys_t *zb, uint64_t type, int dva,
153155
(record->zi_dvas == 0 ||
154156
(dva != ZI_NO_DVA && (record->zi_dvas & (1ULL << dva)))) &&
155157
error == record->zi_error) {
156-
return (freq_triggered(record->zi_freq));
158+
matched = B_TRUE;
159+
goto done;
157160
}
158161

159-
return (B_FALSE);
162+
done:
163+
if (matched) {
164+
record->zi_match_count++;
165+
injected = freq_triggered(record->zi_freq);
166+
}
167+
168+
if (injected)
169+
record->zi_inject_count++;
170+
171+
return (injected);
160172
}
161173

162174
/*
@@ -177,8 +189,11 @@ zio_handle_panic_injection(spa_t *spa, const char *tag, uint64_t type)
177189
continue;
178190

179191
if (handler->zi_record.zi_type == type &&
180-
strcmp(tag, handler->zi_record.zi_func) == 0)
192+
strcmp(tag, handler->zi_record.zi_func) == 0) {
193+
handler->zi_record.zi_match_count++;
194+
handler->zi_record.zi_inject_count++;
181195
panic("Panic requested in function %s\n", tag);
196+
}
182197
}
183198

184199
rw_exit(&inject_lock);
@@ -336,6 +351,8 @@ zio_handle_label_injection(zio_t *zio, int error)
336351

337352
if (zio->io_vd->vdev_guid == handler->zi_record.zi_guid &&
338353
(offset >= start && offset <= end)) {
354+
handler->zi_record.zi_match_count++;
355+
handler->zi_record.zi_inject_count++;
339356
ret = error;
340357
break;
341358
}
@@ -400,12 +417,16 @@ zio_handle_device_injection_impl(vdev_t *vd, zio_t *zio, int err1, int err2)
400417

401418
if (handler->zi_record.zi_error == err1 ||
402419
handler->zi_record.zi_error == err2) {
420+
handler->zi_record.zi_match_count++;
421+
403422
/*
404423
* limit error injection if requested
405424
*/
406425
if (!freq_triggered(handler->zi_record.zi_freq))
407426
continue;
408427

428+
handler->zi_record.zi_inject_count++;
429+
409430
/*
410431
* For a failed open, pretend like the device
411432
* has gone away.
@@ -441,6 +462,8 @@ zio_handle_device_injection_impl(vdev_t *vd, zio_t *zio, int err1, int err2)
441462
break;
442463
}
443464
if (handler->zi_record.zi_error == ENXIO) {
465+
handler->zi_record.zi_match_count++;
466+
handler->zi_record.zi_inject_count++;
444467
ret = SET_ERROR(EIO);
445468
break;
446469
}
@@ -483,6 +506,8 @@ zio_handle_ignored_writes(zio_t *zio)
483506
handler->zi_record.zi_cmd != ZINJECT_IGNORED_WRITES)
484507
continue;
485508

509+
handler->zi_record.zi_match_count++;
510+
486511
/*
487512
* Positive duration implies # of seconds, negative
488513
* a number of txgs
@@ -495,8 +520,10 @@ zio_handle_ignored_writes(zio_t *zio)
495520
}
496521

497522
/* Have a "problem" writing 60% of the time */
498-
if (random_in_range(100) < 60)
523+
if (random_in_range(100) < 60) {
524+
handler->zi_record.zi_inject_count++;
499525
zio->io_pipeline &= ~ZIO_VDEV_IO_STAGES;
526+
}
500527
break;
501528
}
502529

@@ -520,6 +547,9 @@ spa_handle_ignored_writes(spa_t *spa)
520547
handler->zi_record.zi_cmd != ZINJECT_IGNORED_WRITES)
521548
continue;
522549

550+
handler->zi_record.zi_match_count++;
551+
handler->zi_record.zi_inject_count++;
552+
523553
if (handler->zi_record.zi_duration > 0) {
524554
VERIFY(handler->zi_record.zi_timer == 0 ||
525555
ddi_time_after64(
@@ -601,9 +631,6 @@ zio_handle_io_delay(zio_t *zio)
601631
if (handler->zi_record.zi_cmd != ZINJECT_DELAY_IO)
602632
continue;
603633

604-
if (!freq_triggered(handler->zi_record.zi_freq))
605-
continue;
606-
607634
if (vd->vdev_guid != handler->zi_record.zi_guid)
608635
continue;
609636

@@ -628,6 +655,12 @@ zio_handle_io_delay(zio_t *zio)
628655
ASSERT3U(handler->zi_record.zi_nlanes, >,
629656
handler->zi_next_lane);
630657

658+
handler->zi_record.zi_match_count++;
659+
660+
/* Limit the use of this handler if requested */
661+
if (!freq_triggered(handler->zi_record.zi_freq))
662+
continue;
663+
631664
/*
632665
* We want to issue this IO to the lane that will become
633666
* idle the soonest, so we compare the soonest this
@@ -699,6 +732,9 @@ zio_handle_io_delay(zio_t *zio)
699732
*/
700733
min_handler->zi_next_lane = (min_handler->zi_next_lane + 1) %
701734
min_handler->zi_record.zi_nlanes;
735+
736+
min_handler->zi_record.zi_inject_count++;
737+
702738
}
703739

704740
mutex_exit(&inject_delay_mtx);
@@ -721,9 +757,11 @@ zio_handle_pool_delay(spa_t *spa, hrtime_t elapsed, zinject_type_t command)
721757
handler = list_next(&inject_handlers, handler)) {
722758
ASSERT3P(handler->zi_spa_name, !=, NULL);
723759
if (strcmp(spa_name(spa), handler->zi_spa_name) == 0) {
760+
handler->zi_record.zi_match_count++;
724761
uint64_t pause =
725762
SEC2NSEC(handler->zi_record.zi_duration);
726763
if (pause > elapsed) {
764+
handler->zi_record.zi_inject_count++;
727765
delay = pause - elapsed;
728766
}
729767
id = handler->zi_id;

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ tests = ['json_sanity']
159159
tags = ['functional', 'cli_root', 'json']
160160

161161
[tests/functional/cli_root/zinject]
162-
tests = ['zinject_args']
162+
tests = ['zinject_args', 'zinject_counts']
163163
pre =
164164
post =
165165
tags = ['functional', 'cli_root', 'zinject']

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
615615
functional/cli_root/json/setup.ksh \
616616
functional/cli_root/json/json_sanity.ksh \
617617
functional/cli_root/zinject/zinject_args.ksh \
618+
functional/cli_root/zinject/zinject_counts.ksh \
618619
functional/cli_root/zdb/zdb_002_pos.ksh \
619620
functional/cli_root/zdb/zdb_003_pos.ksh \
620621
functional/cli_root/zdb/zdb_004_pos.ksh \

0 commit comments

Comments
 (0)