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