1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19 #include "xxhash.h"
20 #include "crc32c.h"
21
calc_thread_main(device_extension * Vcb,calc_job * cj)22 void calc_thread_main(device_extension* Vcb, calc_job* cj) {
23 while (true) {
24 KIRQL irql;
25 calc_job* cj2;
26 uint8_t* src;
27 void* dest;
28 bool last_one = false;
29
30 KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
31
32 if (cj && cj->not_started == 0) {
33 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
34 break;
35 }
36
37 if (cj)
38 cj2 = cj;
39 else {
40 if (IsListEmpty(&Vcb->calcthreads.job_list)) {
41 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
42 break;
43 }
44
45 cj2 = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry);
46 }
47
48 src = cj2->in;
49 dest = cj2->out;
50
51 switch (cj2->type) {
52 case calc_thread_crc32c:
53 case calc_thread_xxhash:
54 case calc_thread_sha256:
55 case calc_thread_blake2:
56 cj2->in = (uint8_t*)cj2->in + Vcb->superblock.sector_size;
57 cj2->out = (uint8_t*)cj2->out + Vcb->csum_size;
58 break;
59
60 default:
61 break;
62 }
63
64 cj2->not_started--;
65
66 if (cj2->not_started == 0) {
67 RemoveEntryList(&cj2->list_entry);
68 last_one = true;
69 }
70
71 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
72
73 switch (cj2->type) {
74 case calc_thread_crc32c:
75 *(uint32_t*)dest = ~calc_crc32c(0xffffffff, src, Vcb->superblock.sector_size);
76 break;
77
78 case calc_thread_xxhash:
79 *(uint64_t*)dest = XXH64(src, Vcb->superblock.sector_size, 0);
80 break;
81
82 case calc_thread_sha256:
83 calc_sha256(dest, src, Vcb->superblock.sector_size);
84 break;
85
86 case calc_thread_blake2:
87 blake2b(dest, BLAKE2_HASH_SIZE, src, Vcb->superblock.sector_size);
88 break;
89
90 case calc_thread_decomp_zlib:
91 cj2->Status = zlib_decompress(src, cj2->inlen, dest, cj2->outlen);
92
93 if (!NT_SUCCESS(cj2->Status))
94 ERR("zlib_decompress returned %08lx\n", cj2->Status);
95 break;
96
97 case calc_thread_decomp_lzo:
98 cj2->Status = lzo_decompress(src, cj2->inlen, dest, cj2->outlen, cj2->off);
99
100 if (!NT_SUCCESS(cj2->Status))
101 ERR("lzo_decompress returned %08lx\n", cj2->Status);
102 break;
103
104 case calc_thread_decomp_zstd:
105 cj2->Status = zstd_decompress(src, cj2->inlen, dest, cj2->outlen);
106
107 if (!NT_SUCCESS(cj2->Status))
108 ERR("zstd_decompress returned %08lx\n", cj2->Status);
109 break;
110
111 case calc_thread_comp_zlib:
112 cj2->Status = zlib_compress(src, cj2->inlen, dest, cj2->outlen, Vcb->options.zlib_level, &cj2->space_left);
113
114 if (!NT_SUCCESS(cj2->Status))
115 ERR("zlib_compress returned %08lx\n", cj2->Status);
116 break;
117
118 case calc_thread_comp_lzo:
119 cj2->Status = lzo_compress(src, cj2->inlen, dest, cj2->outlen, &cj2->space_left);
120
121 if (!NT_SUCCESS(cj2->Status))
122 ERR("lzo_compress returned %08lx\n", cj2->Status);
123 break;
124
125 case calc_thread_comp_zstd:
126 cj2->Status = zstd_compress(src, cj2->inlen, dest, cj2->outlen, Vcb->options.zstd_level, &cj2->space_left);
127
128 if (!NT_SUCCESS(cj2->Status))
129 ERR("zstd_compress returned %08lx\n", cj2->Status);
130 break;
131 }
132
133 if (InterlockedDecrement(&cj2->left) == 0)
134 KeSetEvent(&cj2->event, 0, false);
135
136 if (last_one)
137 break;
138 }
139 }
140
do_calc_job(device_extension * Vcb,uint8_t * data,uint32_t sectors,void * csum)141 void do_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, void* csum) {
142 KIRQL irql;
143 calc_job cj;
144
145 cj.in = data;
146 cj.out = csum;
147 cj.left = cj.not_started = sectors;
148
149 switch (Vcb->superblock.csum_type) {
150 case CSUM_TYPE_CRC32C:
151 cj.type = calc_thread_crc32c;
152 break;
153
154 case CSUM_TYPE_XXHASH:
155 cj.type = calc_thread_xxhash;
156 break;
157
158 case CSUM_TYPE_SHA256:
159 cj.type = calc_thread_sha256;
160 break;
161
162 case CSUM_TYPE_BLAKE2:
163 cj.type = calc_thread_blake2;
164 break;
165 }
166
167 KeInitializeEvent(&cj.event, NotificationEvent, false);
168
169 KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
170
171 InsertTailList(&Vcb->calcthreads.job_list, &cj.list_entry);
172
173 KeSetEvent(&Vcb->calcthreads.event, 0, false);
174 KeClearEvent(&Vcb->calcthreads.event);
175
176 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
177
178 calc_thread_main(Vcb, &cj);
179
180 KeWaitForSingleObject(&cj.event, Executive, KernelMode, false, NULL);
181 }
182
add_calc_job_decomp(device_extension * Vcb,uint8_t compression,void * in,unsigned int inlen,void * out,unsigned int outlen,unsigned int off,calc_job ** pcj)183 NTSTATUS add_calc_job_decomp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
184 void* out, unsigned int outlen, unsigned int off, calc_job** pcj) {
185 calc_job* cj;
186 KIRQL irql;
187
188 cj = ExAllocatePoolWithTag(NonPagedPool, sizeof(calc_job), ALLOC_TAG);
189 if (!cj) {
190 ERR("out of memory\n");
191 return STATUS_INSUFFICIENT_RESOURCES;
192 }
193
194 cj->in = in;
195 cj->inlen = inlen;
196 cj->out = out;
197 cj->outlen = outlen;
198 cj->off = off;
199 cj->left = cj->not_started = 1;
200 cj->Status = STATUS_SUCCESS;
201
202 switch (compression) {
203 case BTRFS_COMPRESSION_ZLIB:
204 cj->type = calc_thread_decomp_zlib;
205 break;
206
207 case BTRFS_COMPRESSION_LZO:
208 cj->type = calc_thread_decomp_lzo;
209 break;
210
211 case BTRFS_COMPRESSION_ZSTD:
212 cj->type = calc_thread_decomp_zstd;
213 break;
214
215 default:
216 ERR("unexpected compression type %x\n", compression);
217 ExFreePool(cj);
218 return STATUS_NOT_SUPPORTED;
219 }
220
221 KeInitializeEvent(&cj->event, NotificationEvent, false);
222
223 KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
224
225 InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry);
226
227 KeSetEvent(&Vcb->calcthreads.event, 0, false);
228 KeClearEvent(&Vcb->calcthreads.event);
229
230 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
231
232 *pcj = cj;
233
234 return STATUS_SUCCESS;
235 }
236
add_calc_job_comp(device_extension * Vcb,uint8_t compression,void * in,unsigned int inlen,void * out,unsigned int outlen,calc_job ** pcj)237 NTSTATUS add_calc_job_comp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
238 void* out, unsigned int outlen, calc_job** pcj) {
239 calc_job* cj;
240 KIRQL irql;
241
242 cj = ExAllocatePoolWithTag(NonPagedPool, sizeof(calc_job), ALLOC_TAG);
243 if (!cj) {
244 ERR("out of memory\n");
245 return STATUS_INSUFFICIENT_RESOURCES;
246 }
247
248 cj->in = in;
249 cj->inlen = inlen;
250 cj->out = out;
251 cj->outlen = outlen;
252 cj->left = cj->not_started = 1;
253 cj->Status = STATUS_SUCCESS;
254
255 switch (compression) {
256 case BTRFS_COMPRESSION_ZLIB:
257 cj->type = calc_thread_comp_zlib;
258 break;
259
260 case BTRFS_COMPRESSION_LZO:
261 cj->type = calc_thread_comp_lzo;
262 break;
263
264 case BTRFS_COMPRESSION_ZSTD:
265 cj->type = calc_thread_comp_zstd;
266 break;
267
268 default:
269 ERR("unexpected compression type %x\n", compression);
270 ExFreePool(cj);
271 return STATUS_NOT_SUPPORTED;
272 }
273
274 KeInitializeEvent(&cj->event, NotificationEvent, false);
275
276 KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
277
278 InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry);
279
280 KeSetEvent(&Vcb->calcthreads.event, 0, false);
281 KeClearEvent(&Vcb->calcthreads.event);
282
283 KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
284
285 *pcj = cj;
286
287 return STATUS_SUCCESS;
288 }
289
_Function_class_(KSTART_ROUTINE)290 _Function_class_(KSTART_ROUTINE)
291 void __stdcall calc_thread(void* context) {
292 drv_calc_thread* thread = context;
293 device_extension* Vcb = thread->DeviceObject->DeviceExtension;
294
295 ObReferenceObject(thread->DeviceObject);
296
297 KeSetSystemAffinityThread((KAFFINITY)(1 << thread->number));
298
299 while (true) {
300 KeWaitForSingleObject(&Vcb->calcthreads.event, Executive, KernelMode, false, NULL);
301
302 calc_thread_main(Vcb, NULL);
303
304 if (thread->quit)
305 break;
306 }
307
308 ObDereferenceObject(thread->DeviceObject);
309
310 KeSetEvent(&thread->finished, 0, false);
311
312 PsTerminateSystemThread(STATUS_SUCCESS);
313 }
314