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