xref: /xnu-10002.61.3/osfmk/kdp/output_stages/out_lz4.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1 /*
2  * Copyright (c) 2021 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #if defined(CONFIG_KDP_INTERACTIVE_DEBUGGING)
30 
31 #include <mach/kern_return.h>
32 #include <os/base.h>
33 #include <kern/assert.h>
34 #include <kern/kalloc.h>
35 #include <kern/misc_protos.h>
36 #include <kdp/kdp_core.h>
37 #include <kdp/kdp_out_stage.h>
38 #include <kdp/processor_core.h>
39 #include <kdp/output_stages/output_stages.h>
40 #include <libkern/compression/compression.h>
41 
42 // One page seems to be a sweet-spot for both buffers; we tend to dump a page or less at a time so
43 // smaller sizes start incurring overhead for the loop, and larger sizes waste more memory without
44 // a noticable increase in throughput.
45 //
46 // Having a buffer full of zeroes wastes a bit of memory, but we need some way to pass zeroes to
47 // Compression when we are asked to pad the stream.
48 #define DST_BUF_SIZE PAGE_SIZE
49 #define ZERO_BUF_SIZE PAGE_SIZE
50 
51 struct lz4_stage_data {
52 	compression_stream_t stream;
53 	compression_algorithm_t algorithm;
54 	uint8_t *dst_buf;
55 	const uint8_t *zero_buf;
56 	bool reset_failed;
57 };
58 
59 static void
lz4_stage_reset(struct kdp_output_stage * stage)60 lz4_stage_reset(struct kdp_output_stage *stage)
61 {
62 	struct lz4_stage_data *data;
63 	compression_status_t status;
64 
65 	assert(compression_ki_ptr);
66 
67 	assert(stage);
68 	assert(stage->kos_initialized);
69 
70 	data = stage->kos_data;
71 	assert(data);
72 
73 	status = compression_ki_ptr->compression_stream_reinit(&data->stream,
74 	    COMPRESSION_STREAM_ENCODE, data->algorithm);
75 	if (COMPRESSION_STATUS_OK != status) {
76 		data->reset_failed = true;
77 	}
78 
79 	data->stream.dst_ptr = data->dst_buf;
80 	data->stream.dst_size = DST_BUF_SIZE;
81 
82 	stage->kos_bypass = false;
83 	stage->kos_bytes_written = 0;
84 }
85 
86 static kern_return_t
lz4_stage_stream(struct lz4_stage_data * data,struct kdp_output_stage * next_stage,char * corename,const uint8_t * src_buf,size_t src_buf_size,uint64_t * written)87 lz4_stage_stream(struct lz4_stage_data *data, struct kdp_output_stage *next_stage, char *corename,
88     const uint8_t *src_buf, size_t src_buf_size, uint64_t *written)
89 {
90 	bool finalize;
91 	compression_status_t status;
92 	kern_return_t ret;
93 	size_t produced;
94 
95 	assert(compression_ki_ptr);
96 
97 	assert(data);
98 	assert(next_stage);
99 	assert(written);
100 
101 	finalize = !src_buf && !src_buf_size;
102 	assert((src_buf && src_buf_size) || finalize);
103 
104 	data->stream.src_ptr = src_buf;
105 	data->stream.src_size = src_buf_size;
106 
107 	// Every time we process more bytes, the stream source and destination pointers
108 	// and sizes are adjusted.
109 	do {
110 		status = compression_ki_ptr->compression_stream_process(&data->stream,
111 		    finalize ? COMPRESSION_STREAM_FINALIZE : 0);
112 		if (COMPRESSION_STATUS_ERROR == status) {
113 			return KERN_FAILURE;
114 		}
115 
116 		// The difference between the original destination buffer size and the size of the
117 		// remaining space tells us how many bytes were produced.
118 		produced = DST_BUF_SIZE - data->stream.dst_size;
119 		// Pass along those bytes to the next stage so that we empty the destination buffer.
120 		ret = next_stage->kos_funcs.kosf_outproc(next_stage, KDP_DATA, corename,
121 		    produced, data->dst_buf);
122 		if (KERN_SUCCESS != ret) {
123 			return ret;
124 		}
125 		*written += produced;
126 		data->stream.dst_ptr = data->dst_buf;
127 		data->stream.dst_size = DST_BUF_SIZE;
128 		// Continue processing while the source buffer is non-empty, or we are finalizing
129 		// and there are still bytes being produced in the destination buffer.
130 	} while (data->stream.src_size || (finalize && COMPRESSION_STATUS_END != status));
131 
132 	if (finalize) {
133 		return next_stage->kos_funcs.kosf_outproc(next_stage, KDP_DATA, corename, 0, NULL);
134 	}
135 
136 	return KERN_SUCCESS;
137 }
138 
139 static kern_return_t
lz4_stage_outproc(struct kdp_output_stage * stage,unsigned int request,char * corename,uint64_t length,void * panic_data)140 lz4_stage_outproc(struct kdp_output_stage *stage, unsigned int request,
141     char *corename, uint64_t length, void *panic_data)
142 {
143 	struct lz4_stage_data *data;
144 	struct kdp_output_stage *next_stage;
145 	kern_return_t ret;
146 	size_t pad_length;
147 	size_t zero_size;
148 
149 	assert(stage);
150 	assert(stage->kos_initialized);
151 
152 	data = stage->kos_data;
153 	assert(data);
154 
155 	next_stage = STAILQ_NEXT(stage, kos_next);
156 	assert(next_stage);
157 
158 	if (data->reset_failed) {
159 		return KERN_FAILURE;
160 	}
161 
162 	if (KDP_SEEK == request) {
163 		stage->kos_bypass = true;
164 	}
165 
166 	if (stage->kos_bypass || KDP_DATA != request) {
167 		return next_stage->kos_funcs.kosf_outproc(next_stage, request, corename, length,
168 		           panic_data);
169 	}
170 
171 	if (panic_data) {
172 		// Write panic data to the stream.
173 		return lz4_stage_stream(data, next_stage, corename, panic_data, (size_t)length,
174 		           &stage->kos_bytes_written);
175 	} else {
176 		if (length) {
177 			// Pad the stream with zeroes.
178 			pad_length = (size_t)length;
179 			do {
180 				zero_size = MIN(pad_length, ZERO_BUF_SIZE);
181 				ret = lz4_stage_stream(data, next_stage, corename, data->zero_buf,
182 				    zero_size, &stage->kos_bytes_written);
183 				if (KERN_SUCCESS != ret) {
184 					return ret;
185 				}
186 				pad_length -= zero_size;
187 			} while (pad_length);
188 			return KERN_SUCCESS;
189 		} else {
190 			// Finalize the stream.
191 			return lz4_stage_stream(data, next_stage, corename, NULL, 0, &stage->kos_bytes_written);
192 		}
193 	}
194 }
195 
196 static void
lz4_stage_free(struct kdp_output_stage * stage)197 lz4_stage_free(struct kdp_output_stage *stage)
198 {
199 	struct lz4_stage_data *data;
200 
201 	assert(compression_ki_ptr);
202 
203 	assert(stage);
204 	assert(stage->kos_initialized);
205 
206 	data = stage->kos_data;
207 	assert(data);
208 
209 	kfree_data(data->dst_buf, DST_BUF_SIZE);
210 
211 	compression_ki_ptr->compression_stream_destroy(&data->stream);
212 
213 	kfree_type(typeof(*data), data);
214 
215 	stage->kos_initialized = false;
216 }
217 
218 kern_return_t
lz4_stage_initialize(struct kdp_output_stage * stage)219 lz4_stage_initialize(struct kdp_output_stage *stage)
220 {
221 	struct lz4_stage_data *data;
222 	compression_status_t status;
223 
224 	assert(compression_ki_ptr);
225 
226 	assert(stage);
227 	assert(!stage->kos_initialized);
228 	assert(!stage->kos_data);
229 
230 	data = kalloc_type(typeof(*data), Z_WAITOK);
231 	assert(data);
232 
233 	data->algorithm = COMPRESSION_LZ4;
234 
235 	data->dst_buf = kalloc_data(DST_BUF_SIZE, Z_WAITOK);
236 	assert(data->dst_buf);
237 
238 	data->zero_buf = kalloc_data(ZERO_BUF_SIZE, Z_WAITOK | Z_ZERO);
239 	assert(data->zero_buf);
240 
241 	data->reset_failed = false;
242 
243 	status = compression_ki_ptr->compression_stream_init(&data->stream, COMPRESSION_STREAM_ENCODE,
244 	    data->algorithm);
245 	if (COMPRESSION_STATUS_ERROR == status) {
246 		return KERN_FAILURE;
247 	}
248 	data->stream.dst_ptr = data->dst_buf;
249 	data->stream.dst_size = DST_BUF_SIZE;
250 
251 	stage->kos_data = data;
252 	stage->kos_data_size = sizeof(*data);
253 
254 	stage->kos_funcs.kosf_reset = lz4_stage_reset;
255 	stage->kos_funcs.kosf_outproc = lz4_stage_outproc;
256 	stage->kos_funcs.kosf_free = lz4_stage_free;
257 
258 	stage->kos_initialized = true;
259 
260 	return KERN_SUCCESS;
261 }
262 
263 static void
lz4_stage_registration_callback(void)264 lz4_stage_registration_callback(void)
265 {
266 	kern_return_t ret = kdp_core_handle_lz4_available();
267 	if (KERN_SUCCESS != ret) {
268 		printf("(%s) Failed to handle availability of LZ4. Error 0x%x\n", __func__, ret);
269 	}
270 }
271 
272 void
lz4_stage_monitor_availability(void)273 lz4_stage_monitor_availability(void)
274 {
275 	compression_interface_set_registration_callback(lz4_stage_registration_callback);
276 }
277 
278 #endif /* defined(CONFIG_KDP_INTERACTIVE_DEBUGGING) */
279