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 
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* csum;
63     UINT8* data;
64     ULONG blocksize, i;
65 
66     pos = InterlockedIncrement(&cj->pos) - 1;
67 
68     if ((UINT32)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)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 #ifdef __REACTOS__
96 void NTAPI calc_thread(void* context) {
97 #else
98 void calc_thread(void* context) {
99 #endif
100     drv_calc_thread* thread = context;
101     device_extension* Vcb = thread->DeviceObject->DeviceExtension;
102 
103     ObReferenceObject(thread->DeviceObject);
104 
105     while (TRUE) {
106         KeWaitForSingleObject(&Vcb->calcthreads.event, Executive, KernelMode, FALSE, NULL);
107 
108         while (TRUE) {
109             calc_job* cj;
110             BOOL b;
111 
112             ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, TRUE);
113 
114             if (IsListEmpty(&Vcb->calcthreads.job_list)) {
115                 ExReleaseResourceLite(&Vcb->calcthreads.lock);
116                 break;
117             }
118 
119             cj = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry);
120             cj->refcount++;
121 
122             ExReleaseResourceLite(&Vcb->calcthreads.lock);
123 
124             b = do_calc(Vcb, cj);
125 
126             free_calc_job(cj);
127 
128             if (!b)
129                 break;
130         }
131 
132         if (thread->quit)
133             break;
134     }
135 
136     ObDereferenceObject(thread->DeviceObject);
137 
138     KeSetEvent(&thread->finished, 0, FALSE);
139 
140     PsTerminateSystemThread(STATUS_SUCCESS);
141 }
142