xref: /reactos/ntoskrnl/po/povolume.c (revision 4019caae)
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 
332