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 20 #define SECTOR_BLOCK 16 21 22 NTSTATUS add_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, uint32_t* csum, calc_job** pcj) { 23 calc_job* cj; 24 25 cj = ExAllocatePoolWithTag(NonPagedPool, sizeof(calc_job), ALLOC_TAG); 26 if (!cj) { 27 ERR("out of memory\n"); 28 return STATUS_INSUFFICIENT_RESOURCES; 29 } 30 31 cj->data = data; 32 cj->sectors = sectors; 33 cj->csum = csum; 34 cj->pos = 0; 35 cj->done = 0; 36 cj->refcount = 1; 37 KeInitializeEvent(&cj->event, NotificationEvent, false); 38 39 ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true); 40 41 InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry); 42 43 KeSetEvent(&Vcb->calcthreads.event, 0, false); 44 KeClearEvent(&Vcb->calcthreads.event); 45 46 ExReleaseResourceLite(&Vcb->calcthreads.lock); 47 48 *pcj = cj; 49 50 return STATUS_SUCCESS; 51 } 52 53 void free_calc_job(calc_job* cj) { 54 LONG rc = InterlockedDecrement(&cj->refcount); 55 56 if (rc == 0) 57 ExFreePool(cj); 58 } 59 60 static bool do_calc(device_extension* Vcb, calc_job* cj) { 61 LONG pos, done; 62 uint32_t* csum; 63 uint8_t* data; 64 ULONG blocksize, i; 65 66 pos = InterlockedIncrement(&cj->pos) - 1; 67 68 if ((uint32_t)pos * SECTOR_BLOCK >= cj->sectors) 69 return false; 70 71 csum = &cj->csum[pos * SECTOR_BLOCK]; 72 data = cj->data + (pos * SECTOR_BLOCK * Vcb->superblock.sector_size); 73 74 blocksize = min(SECTOR_BLOCK, cj->sectors - (pos * SECTOR_BLOCK)); 75 for (i = 0; i < blocksize; i++) { 76 *csum = ~calc_crc32c(0xffffffff, data, Vcb->superblock.sector_size); 77 csum++; 78 data += Vcb->superblock.sector_size; 79 } 80 81 done = InterlockedIncrement(&cj->done); 82 83 if ((uint32_t)done * SECTOR_BLOCK >= cj->sectors) { 84 ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true); 85 RemoveEntryList(&cj->list_entry); 86 ExReleaseResourceLite(&Vcb->calcthreads.lock); 87 88 KeSetEvent(&cj->event, 0, false); 89 } 90 91 return true; 92 } 93 94 _Function_class_(KSTART_ROUTINE) 95 void __stdcall calc_thread(void* context) { 96 drv_calc_thread* thread = context; 97 device_extension* Vcb = thread->DeviceObject->DeviceExtension; 98 99 ObReferenceObject(thread->DeviceObject); 100 101 while (true) { 102 KeWaitForSingleObject(&Vcb->calcthreads.event, Executive, KernelMode, false, NULL); 103 104 while (true) { 105 calc_job* cj; 106 bool b; 107 108 ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true); 109 110 if (IsListEmpty(&Vcb->calcthreads.job_list)) { 111 ExReleaseResourceLite(&Vcb->calcthreads.lock); 112 break; 113 } 114 115 cj = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry); 116 cj->refcount++; 117 118 ExReleaseResourceLite(&Vcb->calcthreads.lock); 119 120 b = do_calc(Vcb, cj); 121 122 free_calc_job(cj); 123 124 if (!b) 125 break; 126 } 127 128 if (thread->quit) 129 break; 130 } 131 132 ObDereferenceObject(thread->DeviceObject); 133 134 KeSetEvent(&thread->finished, 0, false); 135 136 PsTerminateSystemThread(STATUS_SUCCESS); 137 } 138