xref: /reactos/ntoskrnl/po/povolume.c (revision 81db5e1d)
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                                  TAG_PO_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, TAG_PO_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, TAG_PO_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             DPRINT("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                 DPRINT("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         DPRINT("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             DPRINT("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;
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         DPRINT("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     DPRINT("Local flush\n");
351     PopFlushVolumeWorker(&FlushContext);
352 
353     /* Wait for all flushes to be over */
354     DPRINT("Waiting for flushes\n");
355     KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL);
356     DPRINT("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 
376