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