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 #include <assert.h>
21*d0b3ecdcSMartin Matuska #include <err.h>
22*d0b3ecdcSMartin Matuska #include <libspl.h>
23*d0b3ecdcSMartin Matuska #include <stdio.h>
24*d0b3ecdcSMartin Matuska #include <stdlib.h>
25*d0b3ecdcSMartin Matuska #include <sys/abd.h>
26*d0b3ecdcSMartin Matuska #include <sys/param.h>
27*d0b3ecdcSMartin Matuska #include <sys/stdtypes.h>
28*d0b3ecdcSMartin Matuska #include <sys/zio.h>
29*d0b3ecdcSMartin Matuska #include <sys/zstd/zstd.h>
30*d0b3ecdcSMartin Matuska #include <zfs_fletcher.h>
31*d0b3ecdcSMartin Matuska
32*d0b3ecdcSMartin Matuska #include "zstream_chain.h"
33*d0b3ecdcSMartin Matuska
34*d0b3ecdcSMartin Matuska #define MAX_CHAIN_LENGTH 32
35*d0b3ecdcSMartin Matuska
36*d0b3ecdcSMartin Matuska chain_attrs_t *chain_attrs;
37*d0b3ecdcSMartin Matuska
38*d0b3ecdcSMartin Matuska static disposition_t
chain_null_step(void * item,void * context)39*d0b3ecdcSMartin Matuska chain_null_step(void *item, void *context)
40*d0b3ecdcSMartin Matuska {
41*d0b3ecdcSMartin Matuska (void) item;
42*d0b3ecdcSMartin Matuska (void) context;
43*d0b3ecdcSMartin Matuska return (D_OK);
44*d0b3ecdcSMartin Matuska }
45*d0b3ecdcSMartin Matuska
46*d0b3ecdcSMartin Matuska chain_step_t
serial_null_step(void)47*d0b3ecdcSMartin Matuska serial_null_step(void)
48*d0b3ecdcSMartin Matuska {
49*d0b3ecdcSMartin Matuska chain_step_t step = {
50*d0b3ecdcSMartin Matuska .cs_type = CS_SERIAL,
51*d0b3ecdcSMartin Matuska .cs_serial = {
52*d0b3ecdcSMartin Matuska .process = (zc_serial_process_f *)chain_null_step
53*d0b3ecdcSMartin Matuska }
54*d0b3ecdcSMartin Matuska };
55*d0b3ecdcSMartin Matuska return (step);
56*d0b3ecdcSMartin Matuska }
57*d0b3ecdcSMartin Matuska
58*d0b3ecdcSMartin Matuska chain_step_t
chain_terminator(void)59*d0b3ecdcSMartin Matuska chain_terminator(void)
60*d0b3ecdcSMartin Matuska {
61*d0b3ecdcSMartin Matuska chain_step_t step = { .cs_type = CS_TERMINATE };
62*d0b3ecdcSMartin Matuska return (step);
63*d0b3ecdcSMartin Matuska }
64*d0b3ecdcSMartin Matuska
65*d0b3ecdcSMartin Matuska static void
libraries_init(void)66*d0b3ecdcSMartin Matuska libraries_init(void)
67*d0b3ecdcSMartin Matuska {
68*d0b3ecdcSMartin Matuska zfs_refcount_init();
69*d0b3ecdcSMartin Matuska abd_init();
70*d0b3ecdcSMartin Matuska zio_init();
71*d0b3ecdcSMartin Matuska zstd_init();
72*d0b3ecdcSMartin Matuska libspl_init();
73*d0b3ecdcSMartin Matuska fletcher_4_init();
74*d0b3ecdcSMartin Matuska }
75*d0b3ecdcSMartin Matuska
76*d0b3ecdcSMartin Matuska static void
libraries_fini(void)77*d0b3ecdcSMartin Matuska libraries_fini(void)
78*d0b3ecdcSMartin Matuska {
79*d0b3ecdcSMartin Matuska fletcher_4_fini();
80*d0b3ecdcSMartin Matuska libspl_fini();
81*d0b3ecdcSMartin Matuska zio_fini();
82*d0b3ecdcSMartin Matuska zstd_fini();
83*d0b3ecdcSMartin Matuska abd_fini();
84*d0b3ecdcSMartin Matuska zfs_refcount_fini();
85*d0b3ecdcSMartin Matuska }
86*d0b3ecdcSMartin Matuska
87*d0b3ecdcSMartin Matuska /*
88*d0b3ecdcSMartin Matuska * Execute a chain of serial processing steps.
89*d0b3ecdcSMartin Matuska *
90*d0b3ecdcSMartin Matuska * For simplicity, we normalize the chain item size to that of the largest
91*d0b3ecdcSMartin Matuska * output of any step. Packets with data beyond the base drr_record_t should
92*d0b3ecdcSMartin Matuska * add their additional data to the end of the packet, and this area may be
93*d0b3ecdcSMartin Matuska * reused for different purposes as items travel down the chain.
94*d0b3ecdcSMartin Matuska *
95*d0b3ecdcSMartin Matuska * Each item traverses the entire chain before the next item is read.
96*d0b3ecdcSMartin Matuska */
97*d0b3ecdcSMartin Matuska void
zstream_chain_exec(zstream_chain_t chain,chain_attrs_t * attrs)98*d0b3ecdcSMartin Matuska zstream_chain_exec(zstream_chain_t chain, chain_attrs_t *attrs)
99*d0b3ecdcSMartin Matuska {
100*d0b3ecdcSMartin Matuska int num_steps = 0;
101*d0b3ecdcSMartin Matuska size_t packet_size = 0;
102*d0b3ecdcSMartin Matuska chain_attrs_t backup_attrs = {0};
103*d0b3ecdcSMartin Matuska
104*d0b3ecdcSMartin Matuska chain_attrs = attrs ? attrs : &backup_attrs;
105*d0b3ecdcSMartin Matuska
106*d0b3ecdcSMartin Matuska while (chain[num_steps].cs_type != CS_TERMINATE) {
107*d0b3ecdcSMartin Matuska packet_size = MAX(packet_size, chain[num_steps].cs_out_size);
108*d0b3ecdcSMartin Matuska num_steps++;
109*d0b3ecdcSMartin Matuska if (num_steps >= MAX_CHAIN_LENGTH) {
110*d0b3ecdcSMartin Matuska errx(1, "unterminated zstream_chain");
111*d0b3ecdcSMartin Matuska }
112*d0b3ecdcSMartin Matuska }
113*d0b3ecdcSMartin Matuska VERIFY3U(num_steps, >, 0);
114*d0b3ecdcSMartin Matuska
115*d0b3ecdcSMartin Matuska /*
116*d0b3ecdcSMartin Matuska * Check for consistency of input and output packet sizes in
117*d0b3ecdcSMartin Matuska * adjacent steps. A declared packet size of zero waives this check.
118*d0b3ecdcSMartin Matuska */
119*d0b3ecdcSMartin Matuska for (int i = 0; i < num_steps; i++) {
120*d0b3ecdcSMartin Matuska boolean_t mismatch = i > 0 &&
121*d0b3ecdcSMartin Matuska chain[i].cs_in_size != 0 &&
122*d0b3ecdcSMartin Matuska chain[i-1].cs_out_size != 0 &&
123*d0b3ecdcSMartin Matuska chain[i].cs_in_size != chain[i-1].cs_out_size;
124*d0b3ecdcSMartin Matuska if (mismatch) {
125*d0b3ecdcSMartin Matuska warnx("note - chain steps %d and %d have "
126*d0b3ecdcSMartin Matuska "mismatched packet sizes", i - 1, i);
127*d0b3ecdcSMartin Matuska }
128*d0b3ecdcSMartin Matuska }
129*d0b3ecdcSMartin Matuska
130*d0b3ecdcSMartin Matuska libraries_init();
131*d0b3ecdcSMartin Matuska
132*d0b3ecdcSMartin Matuska uint8_t buffer[packet_size];
133*d0b3ecdcSMartin Matuska boolean_t done = B_FALSE;
134*d0b3ecdcSMartin Matuska
135*d0b3ecdcSMartin Matuska while (!done) {
136*d0b3ecdcSMartin Matuska for (int i = 0; i < num_steps; i++) {
137*d0b3ecdcSMartin Matuska if (done) {
138*d0b3ecdcSMartin Matuska (void) chain[i].cs_serial.process(NULL,
139*d0b3ecdcSMartin Matuska chain[i].cs_context);
140*d0b3ecdcSMartin Matuska } else {
141*d0b3ecdcSMartin Matuska disposition_t dispo =
142*d0b3ecdcSMartin Matuska chain[i].cs_serial.process(buffer,
143*d0b3ecdcSMartin Matuska chain[i].cs_context);
144*d0b3ecdcSMartin Matuska if (dispo == D_EOF) {
145*d0b3ecdcSMartin Matuska VERIFY0(i);
146*d0b3ecdcSMartin Matuska done = B_TRUE;
147*d0b3ecdcSMartin Matuska } else if (dispo == D_DROP) {
148*d0b3ecdcSMartin Matuska break;
149*d0b3ecdcSMartin Matuska }
150*d0b3ecdcSMartin Matuska }
151*d0b3ecdcSMartin Matuska }
152*d0b3ecdcSMartin Matuska }
153*d0b3ecdcSMartin Matuska
154*d0b3ecdcSMartin Matuska libraries_fini();
155*d0b3ecdcSMartin Matuska }
156