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