Sync with trunk revision 63128.
[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 PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject)
104 {
105 PDEVICE_OBJECT_POWER_EXTENSION Dope;
106 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
107 KIRQL OldIrql;
108 PAGED_CODE();
109
110 /* If the device already has the dope, return it */
111 DeviceExtension = IoGetDevObjExtension(DeviceObject);
112 if (!DeviceExtension->Dope)
113 {
114 /* no dope */
115 return;
116 }
117
118 /* Make sure we can flush safely */
119 KeAcquireGuardedMutex(&PopVolumeLock);
120
121 /* Get dope from device */
122 Dope = (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope;
123
124 if (Dope->Volume.Flink)
125 {
126 /* Remove from volume from list */
127 RemoveEntryList(&Dope->Volume);
128 }
129
130 /* Allow flushes to go through */
131 KeReleaseGuardedMutex(&PopVolumeLock);
132
133 /* Now remove dope from device object */
134 KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql);
135
136 /* remove from dev obj */
137 DeviceExtension->Dope = NULL;
138
139 /* Release lock */
140 KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql);
141
142 /* Free dope */
143 ExFreePoolWithTag(Dope, 'Dope');
144 }
145
146 VOID
147 NTAPI
148 PopFlushVolumeWorker(IN PVOID Context)
149 {
150 PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context;
151 PDEVICE_OBJECT_POWER_EXTENSION Dope;
152 PLIST_ENTRY NextEntry;
153 NTSTATUS Status;
154 UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512];
155 POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer;
156 ULONG Length;
157 OBJECT_ATTRIBUTES ObjectAttributes;
158 HANDLE VolumeHandle;
159 IO_STATUS_BLOCK IoStatusBlock;
160
161 /* Acquire the flush lock since we're messing with the list */
162 KeAcquireGuardedMutex(&PopVolumeLock);
163
164 /* Loop the flush list */
165 while (!IsListEmpty(&FlushContext->List))
166 {
167 /* Grab the next (ie: current) entry and remove it */
168 NextEntry = FlushContext->List.Flink;
169 RemoveEntryList(NextEntry);
170
171 /* Add it back on the volume list */
172 InsertTailList(&PopVolumeDevices, NextEntry);
173
174 /* Done touching the volume list */
175 KeReleaseGuardedMutex(&PopVolumeLock);
176
177 /* Get the dope from the volume link */
178 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
179
180 /* Get the name */
181 Status = ObQueryNameString(Dope->DeviceObject,
182 NameInfo,
183 sizeof(Buffer),
184 &Length);
185 if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer))
186 {
187 /* Open the volume */
188 DPRINT1("Opening: %wZ\n", &NameInfo->Name);
189 InitializeObjectAttributes(&ObjectAttributes,
190 &NameInfo->Name,
191 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
192 0,
193 0);
194 Status = ZwCreateFile(&VolumeHandle,
195 SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
196 &ObjectAttributes,
197 &IoStatusBlock,
198 NULL,
199 GENERIC_READ | GENERIC_WRITE,
200 FILE_SHARE_READ | FILE_SHARE_WRITE,
201 FILE_OPEN,
202 0,
203 NULL,
204 0);
205 if (NT_SUCCESS(Status))
206 {
207 /* Flush it and close it */
208 DPRINT1("Sending flush to: %p\n", VolumeHandle);
209 ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock);
210 ZwClose(VolumeHandle);
211 }
212 }
213
214 /* Acquire the flush lock again since we'll touch the list */
215 KeAcquireGuardedMutex(&PopVolumeLock);
216 }
217
218 /* One more flush completed... if it was the last, signal the caller */
219 if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE);
220
221 /* Serialize with flushers */
222 KeReleaseGuardedMutex(&PopVolumeLock);
223 }
224
225 VOID
226 NTAPI
227 PopFlushVolumes(IN BOOLEAN ShuttingDown)
228 {
229 POP_FLUSH_VOLUME FlushContext = {{0}};
230 ULONG FlushPolicy;
231 UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry");
232 OBJECT_ATTRIBUTES ObjectAttributes;
233 HANDLE RegistryHandle;
234 PLIST_ENTRY NextEntry;
235 PDEVICE_OBJECT_POWER_EXTENSION Dope;
236 ULONG VolumeCount = 0;
237 NTSTATUS Status;
238 HANDLE ThreadHandle;
239 ULONG ThreadCount;
240
241 /* Setup the flush context */
242 InitializeListHead(&FlushContext.List);
243 KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE);
244
245 /* What to flush */
246 FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy;
247 if ((FlushPolicy & 1))
248 {
249 /* Registry flush requested, so open it */
250 DPRINT1("Opening registry\n");
251 InitializeObjectAttributes(&ObjectAttributes,
252 &RegistryName,
253 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
254 NULL,
255 NULL);
256 Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes);
257 if (NT_SUCCESS(Status))
258 {
259 /* Flush the registry */
260 DPRINT1("Flushing registry\n");
261 ZwFlushKey(RegistryHandle);
262 ZwClose(RegistryHandle);
263 }
264 }
265
266 /* Serialize with other flushes */
267 KeAcquireGuardedMutex(&PopVolumeLock);
268
269 /* Scan the volume list */
270 NextEntry = PopVolumeDevices.Flink;
271 while (NextEntry != &PopVolumeDevices)
272 {
273 /* Get the dope from the link */
274 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
275
276 /* Grab the next entry now, since we'll be modifying the list */
277 NextEntry = NextEntry->Flink;
278
279 /* Make sure the object is mounted, writable, exists, and is not a floppy */
280 if (!(Dope->DeviceObject->Vpb->Flags & VPB_MOUNTED) ||
281 (Dope->DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) ||
282 (Dope->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) ||
283 ((Dope->DeviceObject->Vpb->RealDevice) &&
284 (Dope->DeviceObject->Vpb->RealDevice->Characteristics & FILE_FLOPPY_DISKETTE)))
285 {
286 /* Not flushable */
287 continue;
288 }
289
290 /* Remove it from the dope and add it to the flush context list */
291 RemoveEntryList(&Dope->Volume);
292 InsertTailList(&FlushContext.List, &Dope->Volume);
293
294 /* Next */
295 VolumeCount++;
296 }
297
298 /* Check if we should skip non-removable devices */
299 if (!(FlushPolicy & 2))
300 {
301 /* ReactOS only implements this routine for shutdown, which requires it */
302 UNIMPLEMENTED_DBGBREAK();
303 }
304
305 /* Check if there were no volumes at all */
306 if (!VolumeCount)
307 {
308 /* Nothing to do */
309 KeReleaseGuardedMutex(&PopVolumeLock);
310 return;
311 }
312
313 /* Allocate up to 8 flusher threads */
314 ThreadCount = min(VolumeCount, 8);
315 InitializeObjectAttributes(&ObjectAttributes,
316 NULL,
317 OBJ_KERNEL_HANDLE,
318 NULL,
319 NULL);
320
321 /* We will ourselves become a flusher thread */
322 FlushContext.Count = 1;
323 ThreadCount--;
324
325 /* Look for any extra ones we might need */
326 while (ThreadCount > 0)
327 {
328 /* Create a new one */
329 ThreadCount--;
330 DPRINT1("Creating flush thread\n");
331 Status = PsCreateSystemThread(&ThreadHandle,
332 THREAD_ALL_ACCESS,
333 &ObjectAttributes,
334 0L,
335 NULL,
336 PopFlushVolumeWorker,
337 &FlushContext);
338 if (NT_SUCCESS(Status))
339 {
340 /* One more created... */
341 FlushContext.Count++;
342 ZwClose(ThreadHandle);
343 }
344 }
345
346 /* Allow flushes to go through */
347 KeReleaseGuardedMutex(&PopVolumeLock);
348
349 /* Enter the flush work */
350 DPRINT1("Local flush\n");
351 PopFlushVolumeWorker(&FlushContext);
352
353 /* Wait for all flushes to be over */
354 DPRINT1("Waiting for flushes\n");
355 KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL);
356 DPRINT1("Flushes have completed\n");
357 }
358
359 VOID
360 NTAPI
361 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension)
362 {
363 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension;
364 PAGED_CODE();
365
366 /* Initialize the power flags */
367 DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF;
368 DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0);
369
370 /* The device object is not on drugs yet */
371 DeviceExtension->Dope = NULL;
372 }
373
374 /* PUBLIC FUNCTIONS **********************************************************/
375