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* data, UINT32 sectors, UINT32* 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     InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry);
41     ExReleaseResourceLite(&Vcb->calcthreads.lock);
42 
43     KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
44     KeClearEvent(&Vcb->calcthreads.event);
45 
46     *pcj = cj;
47 
48     return STATUS_SUCCESS;
49 }
50 
51 void free_calc_job(calc_job* cj) {
52     LONG rc = InterlockedDecrement(&cj->refcount);
53 
54     if (rc == 0)
55         ExFreePool(cj);
56 }
57 
58 static BOOL do_calc(device_extension* Vcb, calc_job* cj) {
59     LONG pos, done;
60     UINT32* csum;
61     UINT8* data;
62     ULONG blocksize, i;
63 
64     pos = InterlockedIncrement(&cj->pos) - 1;
65 
66     if ((UINT32)pos * SECTOR_BLOCK >= cj->sectors)
67         return FALSE;
68 
69     csum = &cj->csum[pos * SECTOR_BLOCK];
70     data = cj->data + (pos * SECTOR_BLOCK * Vcb->superblock.sector_size);
71 
72     blocksize = min(SECTOR_BLOCK, cj->sectors - (pos * SECTOR_BLOCK));
73     for (i = 0; i < blocksize; i++) {
74         *csum = ~calc_crc32c(0xffffffff, data, Vcb->superblock.sector_size);
75         csum++;
76         data += Vcb->superblock.sector_size;
77     }
78 
79     done = InterlockedIncrement(&cj->done);
80 
81     if ((UINT32)done * SECTOR_BLOCK >= cj->sectors) {
82         ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, TRUE);
83         RemoveEntryList(&cj->list_entry);
84         ExReleaseResourceLite(&Vcb->calcthreads.lock);
85 
86         KeSetEvent(&cj->event, 0, FALSE);
87     }
88 
89     return TRUE;
90 }
91 
92 _Function_class_(KSTART_ROUTINE)
93 #ifdef __REACTOS__
94 void NTAPI calc_thread(void* context) {
95 #else
96 void calc_thread(void* context) {
97 #endif
98     drv_calc_thread* thread = context;
99     device_extension* Vcb = thread->DeviceObject->DeviceExtension;
100 
101     ObReferenceObject(thread->DeviceObject);
102 
103     while (TRUE) {
104         KeWaitForSingleObject(&Vcb->calcthreads.event, Executive, KernelMode, FALSE, NULL);
105 
106         while (TRUE) {
107             calc_job* cj;
108             BOOL b;
109 
110             ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, TRUE);
111 
112             if (IsListEmpty(&Vcb->calcthreads.job_list)) {
113                 ExReleaseResourceLite(&Vcb->calcthreads.lock);
114                 break;
115             }
116 
117             cj = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry);
118             cj->refcount++;
119 
120             ExReleaseResourceLite(&Vcb->calcthreads.lock);
121 
122             b = do_calc(Vcb, cj);
123 
124             free_calc_job(cj);
125 
126             if (!b)
127                 break;
128         }
129 
130         if (thread->quit)
131             break;
132     }
133 
134     ObDereferenceObject(thread->DeviceObject);
135 
136     KeSetEvent(&thread->finished, 0, FALSE);
137 
138     PsTerminateSystemThread(STATUS_SUCCESS);
139 }
140