[CMAKE]
[reactos.git] / ntoskrnl / po / povolume.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/po/povolume.c
5 * PURPOSE: Power Manager DOPE and Volume Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 typedef struct _POP_FLUSH_VOLUME
18 {
19 LIST_ENTRY List;
20 LONG Count;
21 KEVENT Wait;
22 } POP_FLUSH_VOLUME, *PPOP_FLUSH_VOLUME;
23
24 ULONG PopFlushPolicy = 0;
25
26 KGUARDED_MUTEX PopVolumeLock;
27 LIST_ENTRY PopVolumeDevices;
28 KSPIN_LOCK PopDopeGlobalLock;
29
30 /* PRIVATE FUNCTIONS *********************************************************/
31
32 PDEVICE_OBJECT_POWER_EXTENSION
33 NTAPI
34 PopGetDope(IN PDEVICE_OBJECT DeviceObject)
35 {
36 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
37 PDEVICE_OBJECT_POWER_EXTENSION Dope;
38 KIRQL OldIrql;
39 PAGED_CODE();
40
41 /* If the device already has the dope, return it */
42 DeviceExtension = IoGetDevObjExtension(DeviceObject);
43 if (DeviceExtension->Dope) goto Return;
44
45 /* Allocate some dope for the device */
46 Dope = ExAllocatePoolWithTag(NonPagedPool,
47 sizeof(DEVICE_OBJECT_POWER_EXTENSION),
48 'Dope');
49 if (!Dope) goto Return;
50
51 /* Initialize the initial contents of the dope */
52 RtlZeroMemory(Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION));
53 Dope->DeviceObject = DeviceObject;
54 Dope->State = PowerDeviceUnspecified;
55 InitializeListHead(&Dope->IdleList);
56
57 /* Make sure only one caller can assign dope to a device */
58 KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql);
59
60 /* Make sure the device still has no dope */
61 if (!DeviceExtension->Dope)
62 {
63 /* Give the local dope to this device, and remember we won the race */
64 DeviceExtension->Dope = (PVOID)Dope;
65 Dope = NULL;
66 }
67
68 /* Allow other dope transactions now */
69 KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql);
70
71 /* Check if someone other than us already assigned the dope, so free ours */
72 if (Dope) ExFreePoolWithTag(Dope, 'Dope');
73
74 /* Return the dope to the caller */
75 Return:
76 return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope;
77 }
78
79 VOID
80 NTAPI
81 PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject)
82 {
83 PDEVICE_OBJECT_POWER_EXTENSION Dope;
84 PAGED_CODE();
85
86 /* Get dope from the device (if the device has no dope, it will receive some) */
87 Dope = PopGetDope(DeviceObject);
88 if (Dope)
89 {
90 /* Make sure we can flush safely */
91 KeAcquireGuardedMutex(&PopVolumeLock);
92
93 /* Add this volume into the list of power-manager volumes */
94 if (!Dope->Volume.Flink) InsertTailList(&PopVolumeDevices, &Dope->Volume);
95
96 /* Allow flushes to go through */
97 KeReleaseGuardedMutex(&PopVolumeLock);
98 }
99 }
100
101 VOID
102 NTAPI
103 PopFlushVolumeWorker(IN PVOID Context)
104 {
105 PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context;
106 PDEVICE_OBJECT_POWER_EXTENSION Dope;
107 PLIST_ENTRY NextEntry;
108 NTSTATUS Status;
109 UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512];
110 POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer;
111 ULONG Length;
112 OBJECT_ATTRIBUTES ObjectAttributes;
113 HANDLE VolumeHandle;
114 IO_STATUS_BLOCK IoStatusBlock;
115
116 /* Acquire the flush lock since we're messing with the list */
117 KeAcquireGuardedMutex(&PopVolumeLock);
118
119 /* Loop the flush list */
120 while (!IsListEmpty(&FlushContext->List))
121 {
122 /* Grab the next (ie: current) entry and remove it */
123 NextEntry = FlushContext->List.Flink;
124 RemoveEntryList(NextEntry);
125
126 /* Add it back on the volume list */
127 InsertTailList(&PopVolumeDevices, NextEntry);
128
129 /* Done touching the volume list */
130 KeReleaseGuardedMutex(&PopVolumeLock);
131
132 /* Get the dope from the volume link */
133 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
134
135 /* Get the name */
136 Status = ObQueryNameString(Dope->DeviceObject,
137 NameInfo,
138 sizeof(Buffer),
139 &Length);
140 if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer))
141 {
142 /* Open the volume */
143 DPRINT1("Opening: %wZ\n", &NameInfo->Name);
144 InitializeObjectAttributes(&ObjectAttributes,
145 &NameInfo->Name,
146 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
147 0,
148 0);
149 Status = ZwCreateFile(&VolumeHandle,
150 SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
151 &ObjectAttributes,
152 &IoStatusBlock,
153 NULL,
154 GENERIC_READ | GENERIC_WRITE,
155 FILE_SHARE_READ | FILE_SHARE_WRITE,
156 FILE_OPEN,
157 0,
158 NULL,
159 0);
160 if (NT_SUCCESS(Status))
161 {
162 /* Flush it and close it */
163 DPRINT1("Sending flush to: %lx\n", VolumeHandle);
164 ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock);
165 ZwClose(VolumeHandle);
166 }
167 }
168
169 /* Acquire the flush lock again since we'll touch the list */
170 KeAcquireGuardedMutex(&PopVolumeLock);
171 }
172
173 /* One more flush completed... if it was the last, signal the caller */
174 if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE);
175
176 /* Serialize with flushers */
177 KeReleaseGuardedMutex(&PopVolumeLock);
178 }
179
180 VOID
181 NTAPI
182 PopFlushVolumes(IN BOOLEAN ShuttingDown)
183 {
184 POP_FLUSH_VOLUME FlushContext = {{0}};
185 ULONG FlushPolicy;
186 UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry");
187 OBJECT_ATTRIBUTES ObjectAttributes;
188 HANDLE RegistryHandle;
189 PLIST_ENTRY NextEntry;
190 PDEVICE_OBJECT_POWER_EXTENSION Dope;
191 ULONG VolumeCount = 0;
192 NTSTATUS Status;
193 HANDLE ThreadHandle;
194 ULONG ThreadCount;
195
196 /* Setup the flush context */
197 InitializeListHead(&FlushContext.List);
198 KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE);
199
200 /* What to flush */
201 FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy;
202 if ((FlushPolicy & 1))
203 {
204 /* Registry flush requested, so open it */
205 DPRINT1("Opening registry\n");
206 InitializeObjectAttributes(&ObjectAttributes,
207 &RegistryName,
208 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
209 NULL,
210 NULL);
211 Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes);
212 if (NT_SUCCESS(Status))
213 {
214 /* Flush the registry */
215 DPRINT1("Flushing registry\n");
216 ZwFlushKey(RegistryHandle);
217 ZwClose(RegistryHandle);
218 }
219 }
220
221 /* Serialize with other flushes */
222 KeAcquireGuardedMutex(&PopVolumeLock);
223
224 /* Scan the volume list */
225 NextEntry = PopVolumeDevices.Flink;
226 while (NextEntry != &PopVolumeDevices)
227 {
228 /* Get the dope from the link */
229 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
230
231 /* Grab the next entry now, since we'll be modifying the list */
232 NextEntry = NextEntry->Flink;
233
234 /* Make sure the object is mounted, writable, exists, and is not a floppy */
235 if (!(Dope->DeviceObject->Vpb->Flags & VPB_MOUNTED) ||
236 (Dope->DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) ||
237 (Dope->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) ||
238 ((Dope->DeviceObject->Vpb->RealDevice) &&
239 (Dope->DeviceObject->Vpb->RealDevice->Characteristics & FILE_FLOPPY_DISKETTE)))
240 {
241 /* Not flushable */
242 continue;
243 }
244
245 /* Remove it from the dope and add it to the flush context list */
246 RemoveEntryList(&Dope->Volume);
247 InsertTailList(&FlushContext.List, &Dope->Volume);
248
249 /* Next */
250 VolumeCount++;
251 }
252
253 /* Check if we should skip non-removable devices */
254 if (!(FlushPolicy & 2))
255 {
256 /* ReactOS only implements this routine for shutdown, which requires it */
257 UNIMPLEMENTED;
258 while (TRUE);
259 }
260
261 /* Check if there were no volumes at all */
262 if (!VolumeCount)
263 {
264 /* Nothing to do */
265 KeReleaseGuardedMutex(&PopVolumeLock);
266 return;
267 }
268
269 /* Allocate up to 8 flusher threads */
270 ThreadCount = min(VolumeCount, 8);
271 InitializeObjectAttributes(&ObjectAttributes,
272 NULL,
273 OBJ_KERNEL_HANDLE,
274 NULL,
275 NULL);
276
277 /* We will ourselves become a flusher thread */
278 FlushContext.Count = 1;
279 ThreadCount--;
280
281 /* Look for any extra ones we might need */
282 while (ThreadCount > 0)
283 {
284 /* Create a new one */
285 ThreadCount--;
286 DPRINT1("Creating flush thread\n");
287 Status = PsCreateSystemThread(&ThreadHandle,
288 THREAD_ALL_ACCESS,
289 &ObjectAttributes,
290 0L,
291 NULL,
292 PopFlushVolumeWorker,
293 &FlushContext);
294 if (NT_SUCCESS(Status))
295 {
296 /* One more created... */
297 FlushContext.Count++;
298 ZwClose(ThreadHandle);
299 }
300 }
301
302 /* Allow flushes to go through */
303 KeReleaseGuardedMutex(&PopVolumeLock);
304
305 /* Enter the flush work */
306 DPRINT1("Local flush\n");
307 PopFlushVolumeWorker(&FlushContext);
308
309 /* Wait for all flushes to be over */
310 DPRINT1("Waiting for flushes\n");
311 KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL);
312 DPRINT1("Flushes have completed\n");
313 }
314
315 VOID
316 NTAPI
317 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension)
318 {
319 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension;
320 PAGED_CODE();
321
322 /* Initialize the power flags */
323 DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF;
324 DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0);
325
326 /* The device object is not on drugs yet */
327 DeviceExtension->Dope = NULL;
328 }
329
330 /* PUBLIC FUNCTIONS **********************************************************/
331