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 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 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 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 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 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