1*d0b3ecdcSMartin Matuska // SPDX-License-Identifier: CDDL-1.0 2*d0b3ecdcSMartin Matuska /* 3*d0b3ecdcSMartin Matuska * CDDL HEADER START 4*d0b3ecdcSMartin Matuska * 5*d0b3ecdcSMartin Matuska * This file and its contents are supplied under the terms of the Common 6*d0b3ecdcSMartin Matuska * Development and Distribution License ("CDDL"), version 1.0. You may only use 7*d0b3ecdcSMartin Matuska * this file in accordance with the terms of version 1.0 of the CDDL. 8*d0b3ecdcSMartin Matuska * 9*d0b3ecdcSMartin Matuska * A full copy of the text of the CDDL should have accompanied this source. A 10*d0b3ecdcSMartin Matuska * copy of the CDDL is also available via the Internet at 11*d0b3ecdcSMartin Matuska * http://www.illumos.org/license/CDDL. 12*d0b3ecdcSMartin Matuska * 13*d0b3ecdcSMartin Matuska * CDDL HEADER END 14*d0b3ecdcSMartin Matuska */ 15*d0b3ecdcSMartin Matuska 16*d0b3ecdcSMartin Matuska /* 17*d0b3ecdcSMartin Matuska * Copyright (c) 2026 by Garth Snyder. All rights reserved. 18*d0b3ecdcSMartin Matuska */ 19*d0b3ecdcSMartin Matuska 20*d0b3ecdcSMartin Matuska #ifndef _ZSTREAM_CHAIN_H 21*d0b3ecdcSMartin Matuska #define _ZSTREAM_CHAIN_H 22*d0b3ecdcSMartin Matuska 23*d0b3ecdcSMartin Matuska #ifdef __cplusplus 24*d0b3ecdcSMartin Matuska extern "C" { 25*d0b3ecdcSMartin Matuska #endif 26*d0b3ecdcSMartin Matuska 27*d0b3ecdcSMartin Matuska #include <stddef.h> 28*d0b3ecdcSMartin Matuska #include <stdint.h> 29*d0b3ecdcSMartin Matuska #include <sys/zfs_ioctl.h> 30*d0b3ecdcSMartin Matuska 31*d0b3ecdcSMartin Matuska /* 32*d0b3ecdcSMartin Matuska * A chain is a linear series of steps that process packets of data. It's 33*d0b3ecdcSMartin Matuska * designed to modularize common functionality, reduce code duplication, and 34*d0b3ecdcSMartin Matuska * separate processing structure from implementation. 35*d0b3ecdcSMartin Matuska * 36*d0b3ecdcSMartin Matuska * Some terms: 37*d0b3ecdcSMartin Matuska * 38*d0b3ecdcSMartin Matuska * **STEP** - A chain_step_t struct that represents a packet-processing 39*d0b3ecdcSMartin Matuska * module and any arguments or context that it needs. Chain modules 40*d0b3ecdcSMartin Matuska * generally define a function named serial_* that produces a chain_step_t 41*d0b3ecdcSMartin Matuska * that can be incorporated directly into a chain. 42*d0b3ecdcSMartin Matuska * 43*d0b3ecdcSMartin Matuska * **CHAIN** - An array of chain_step_t's. It's just data, so you can create 44*d0b3ecdcSMartin Matuska * the array however you like. But normally you'd just declare the whole 45*d0b3ecdcSMartin Matuska * thing: 46*d0b3ecdcSMartin Matuska * 47*d0b3ecdcSMartin Matuska * zstream_chain_t dump_chain = { 48*d0b3ecdcSMartin Matuska * serial_read_stream(infile), 49*d0b3ecdcSMartin Matuska * serial_validate_fletcher4(), 50*d0b3ecdcSMartin Matuska * serial_byteswap(BS_INCOMING), 51*d0b3ecdcSMartin Matuska * serial_validate_records(), 52*d0b3ecdcSMartin Matuska * serial_dump_records(&dump_args), 53*d0b3ecdcSMartin Matuska * serial_null_output(), 54*d0b3ecdcSMartin Matuska * chain_terminator() 55*d0b3ecdcSMartin Matuska * } 56*d0b3ecdcSMartin Matuska * 57*d0b3ecdcSMartin Matuska * Or more succinctly: 58*d0b3ecdcSMartin Matuska * 59*d0b3ecdcSMartin Matuska * zstream_chain_t dump_chain = { 60*d0b3ecdcSMartin Matuska * STANDARD_INPUT_STACK(infile), 61*d0b3ecdcSMartin Matuska * serial_dump_records(&dump_args), 62*d0b3ecdcSMartin Matuska * NULL_OUTPUT_STACK() 63*d0b3ecdcSMartin Matuska * }; 64*d0b3ecdcSMartin Matuska * 65*d0b3ecdcSMartin Matuska * Chains must be terminated by a step of type CS_TERMINATE. 66*d0b3ecdcSMartin Matuska * 67*d0b3ecdcSMartin Matuska * **ITEMS** - The data packets that flow through a chain. Each step accepts 68*d0b3ecdcSMartin Matuska * items of one size and emits items of another size, which may be smaller, 69*d0b3ecdcSMartin Matuska * larger, or the same size. Items will generally be structs that start with 70*d0b3ecdcSMartin Matuska * a drr_packet_t (defined in zstream_io.h) and may include additional 71*d0b3ecdcSMartin Matuska * module-specific fields. 72*d0b3ecdcSMartin Matuska * 73*d0b3ecdcSMartin Matuska * **PROCESSING FUNCTION** - Each step names a processing function that does 74*d0b3ecdcSMartin Matuska * the actual work of transforming an input buffer into an output buffer. 75*d0b3ecdcSMartin Matuska * The transformation happens in place, in a single buffer provided by the 76*d0b3ecdcSMartin Matuska * chain. 77*d0b3ecdcSMartin Matuska * 78*d0b3ecdcSMartin Matuska * The processing function should return a disposition_t, normally D_OK. A 79*d0b3ecdcSMartin Matuska * function can return D_DROP to remove an item from the stream entirely. It 80*d0b3ecdcSMartin Matuska * can also return D_EOF to indicate that no more data will be forthcoming. 81*d0b3ecdcSMartin Matuska * However, only the first step in the chain should ever return D_EOF. 82*d0b3ecdcSMartin Matuska * 83*d0b3ecdcSMartin Matuska * Functions are called with a NULL packet pointer when the end of the 84*d0b3ecdcSMartin Matuska * stream passes by them. 85*d0b3ecdcSMartin Matuska * 86*d0b3ecdcSMartin Matuska * **CONTEXT** - An arbitrary void * that the chain passes along to the 87*d0b3ecdcSMartin Matuska * processing function as an argument. 88*d0b3ecdcSMartin Matuska * 89*d0b3ecdcSMartin Matuska * **CHAIN ATTRIBUTES** - A global set of flags available to all steps. 90*d0b3ecdcSMartin Matuska */ 91*d0b3ecdcSMartin Matuska 92*d0b3ecdcSMartin Matuska #define CA_BYTESWAPPED (1ULL << 0) /* ca_attrs */ 93*d0b3ecdcSMartin Matuska #define CA_BIG_ENDIAN_INPUT (1ULL << 1) 94*d0b3ecdcSMartin Matuska #define CA_LITTLE_ENDIAN_INPUT (1ULL << 2) 95*d0b3ecdcSMartin Matuska 96*d0b3ecdcSMartin Matuska #define CA_VERBOSE (1ULL << 0) /* ca_command_opts */ 97*d0b3ecdcSMartin Matuska #define CA_VERY_VERBOSE (1ULL << 1) 98*d0b3ecdcSMartin Matuska #define CA_DUMP_DATA (1ULL << 2) 99*d0b3ecdcSMartin Matuska #define CA_IGNORE_CKSUMS (1ULL << 3) 100*d0b3ecdcSMartin Matuska #define CA_DO_NOT_VALIDATE (1ULL << 4) 101*d0b3ecdcSMartin Matuska #define CA_FORBID_DEDUP (1ULL << 5) 102*d0b3ecdcSMartin Matuska #define CA_REQUIRE_DEDUP (1ULL << 6) 103*d0b3ecdcSMartin Matuska #define CA_REQUIRE_NATIVE_ENDIAN (1ULL << 7) 104*d0b3ecdcSMartin Matuska #define CA_BYTESWAP_ON_OUTPUT (1ULL << 8) 105*d0b3ecdcSMartin Matuska #define CA_BIG_ENDIAN_OUT (1ULL << 9) 106*d0b3ecdcSMartin Matuska #define CA_LITTLE_ENDIAN_OUT (1ULL << 10) 107*d0b3ecdcSMartin Matuska #define CA_OPPOSITE_ENDIAN_OUT (1ULL << 11) 108*d0b3ecdcSMartin Matuska 109*d0b3ecdcSMartin Matuska #define OPTION_ENABLED(option) (!!(chain_attrs->ca_command_opts & (option))) 110*d0b3ecdcSMartin Matuska #define STREAM_HAS_FEATURE(feat) (!!(chain_attrs->ca_feature_flags & (feat))) 111*d0b3ecdcSMartin Matuska #define ATTR_IS_SET(attr) (!!(chain_attrs->ca_attrs & (attr))) 112*d0b3ecdcSMartin Matuska 113*d0b3ecdcSMartin Matuska #define ENABLE_OPTION(attrs, opt) ((attrs)->ca_command_opts |= (opt)) 114*d0b3ecdcSMartin Matuska #define SET_ATTR(attr) (chain_attrs->ca_attrs |= (attr)) 115*d0b3ecdcSMartin Matuska 116*d0b3ecdcSMartin Matuska typedef struct { 117*d0b3ecdcSMartin Matuska uint64_t rs_num_records; 118*d0b3ecdcSMartin Matuska uint64_t rs_total_header_bytes; 119*d0b3ecdcSMartin Matuska uint64_t rs_total_payload_bytes; 120*d0b3ecdcSMartin Matuska } record_stats_t; 121*d0b3ecdcSMartin Matuska 122*d0b3ecdcSMartin Matuska /* 123*d0b3ecdcSMartin Matuska * Chain attribute flags that describe the stream. Statistics are maintained 124*d0b3ecdcSMartin Matuska * by the zstream_io modules. 125*d0b3ecdcSMartin Matuska */ 126*d0b3ecdcSMartin Matuska typedef struct { 127*d0b3ecdcSMartin Matuska uint64_t ca_feature_flags; /* From drr_versioninfo */ 128*d0b3ecdcSMartin Matuska uint64_t ca_attrs; /* Discovered attributes */ 129*d0b3ecdcSMartin Matuska uint64_t ca_command_opts; /* Command line options */ 130*d0b3ecdcSMartin Matuska record_stats_t ca_totals_in; 131*d0b3ecdcSMartin Matuska record_stats_t ca_totals_out; 132*d0b3ecdcSMartin Matuska record_stats_t ca_stats_in[DRR_NUMTYPES]; 133*d0b3ecdcSMartin Matuska record_stats_t ca_stats_out[DRR_NUMTYPES]; 134*d0b3ecdcSMartin Matuska } chain_attrs_t; 135*d0b3ecdcSMartin Matuska 136*d0b3ecdcSMartin Matuska typedef enum { CS_SERIAL, CS_TERMINATE } step_type_t; 137*d0b3ecdcSMartin Matuska typedef enum { D_OK, D_EOF, D_DROP } disposition_t; 138*d0b3ecdcSMartin Matuska 139*d0b3ecdcSMartin Matuska typedef disposition_t 140*d0b3ecdcSMartin Matuska zc_serial_process_f(void *item, void *context); 141*d0b3ecdcSMartin Matuska 142*d0b3ecdcSMartin Matuska typedef struct chain_step 143*d0b3ecdcSMartin Matuska { 144*d0b3ecdcSMartin Matuska step_type_t cs_type; 145*d0b3ecdcSMartin Matuska size_t cs_in_size; 146*d0b3ecdcSMartin Matuska size_t cs_out_size; 147*d0b3ecdcSMartin Matuska void *cs_context; 148*d0b3ecdcSMartin Matuska struct { 149*d0b3ecdcSMartin Matuska zc_serial_process_f *process; 150*d0b3ecdcSMartin Matuska } cs_serial; 151*d0b3ecdcSMartin Matuska } chain_step_t; 152*d0b3ecdcSMartin Matuska 153*d0b3ecdcSMartin Matuska typedef chain_step_t zstream_chain_t[]; 154*d0b3ecdcSMartin Matuska 155*d0b3ecdcSMartin Matuska /* 156*d0b3ecdcSMartin Matuska * Chain attributes accessible to any step on the chain. In theory this 157*d0b3ecdcSMartin Matuska * could cause a race condition between reading and setting, but all 158*d0b3ecdcSMartin Matuska * attributes are typically set by the time the first record has been read. 159*d0b3ecdcSMartin Matuska * Ergo, nobody else will be executing while that first chain_read() runs. 160*d0b3ecdcSMartin Matuska */ 161*d0b3ecdcSMartin Matuska extern chain_attrs_t *chain_attrs; 162*d0b3ecdcSMartin Matuska 163*d0b3ecdcSMartin Matuska /* 164*d0b3ecdcSMartin Matuska * Execute a chain. Returns once execution is complete. You can pass NULL 165*d0b3ecdcSMartin Matuska * for the attrs if you're not interested in preserving them after the chain 166*d0b3ecdcSMartin Matuska * has run. 167*d0b3ecdcSMartin Matuska */ 168*d0b3ecdcSMartin Matuska void 169*d0b3ecdcSMartin Matuska zstream_chain_exec(zstream_chain_t chain, chain_attrs_t *attrs); 170*d0b3ecdcSMartin Matuska 171*d0b3ecdcSMartin Matuska chain_step_t 172*d0b3ecdcSMartin Matuska serial_null_step(void); 173*d0b3ecdcSMartin Matuska 174*d0b3ecdcSMartin Matuska chain_step_t 175*d0b3ecdcSMartin Matuska chain_terminator(void); 176*d0b3ecdcSMartin Matuska 177*d0b3ecdcSMartin Matuska #ifdef __cplusplus 178*d0b3ecdcSMartin Matuska } 179*d0b3ecdcSMartin Matuska #endif 180*d0b3ecdcSMartin Matuska 181*d0b3ecdcSMartin Matuska #endif /* _ZSTREAM_CHAIN_H */ 182