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
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 typedef struct _POP_FLUSH_VOLUME
22 } POP_FLUSH_VOLUME
, *PPOP_FLUSH_VOLUME
;
24 ULONG PopFlushPolicy
= 0;
26 KGUARDED_MUTEX PopVolumeLock
;
27 LIST_ENTRY PopVolumeDevices
;
28 KSPIN_LOCK PopDopeGlobalLock
;
30 /* PRIVATE FUNCTIONS *********************************************************/
32 PDEVICE_OBJECT_POWER_EXTENSION
34 PopGetDope(IN PDEVICE_OBJECT DeviceObject
)
36 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
37 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
41 /* If the device already has the dope, return it */
42 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
43 if (DeviceExtension
->Dope
) goto Return
;
45 /* Allocate some dope for the device */
46 Dope
= ExAllocatePoolWithTag(NonPagedPool
,
47 sizeof(DEVICE_OBJECT_POWER_EXTENSION
),
49 if (!Dope
) goto Return
;
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
);
57 /* Make sure only one caller can assign dope to a device */
58 KeAcquireSpinLock(&PopDopeGlobalLock
, &OldIrql
);
60 /* Make sure the device still has no dope */
61 if (!DeviceExtension
->Dope
)
63 /* Give the local dope to this device, and remember we won the race */
64 DeviceExtension
->Dope
= (PVOID
)Dope
;
68 /* Allow other dope transactions now */
69 KeReleaseSpinLock(&PopDopeGlobalLock
, OldIrql
);
71 /* Check if someone other than us already assigned the dope, so free ours */
72 if (Dope
) ExFreePoolWithTag(Dope
, 'Dope');
74 /* Return the dope to the caller */
76 return (PDEVICE_OBJECT_POWER_EXTENSION
)DeviceExtension
->Dope
;
81 PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject
)
83 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
86 /* Get dope from the device (if the device has no dope, it will receive some) */
87 Dope
= PopGetDope(DeviceObject
);
90 /* Make sure we can flush safely */
91 KeAcquireGuardedMutex(&PopVolumeLock
);
93 /* Add this volume into the list of power-manager volumes */
94 if (!Dope
->Volume
.Flink
) InsertTailList(&PopVolumeDevices
, &Dope
->Volume
);
96 /* Allow flushes to go through */
97 KeReleaseGuardedMutex(&PopVolumeLock
);
103 PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject
)
105 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
106 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
110 /* If the device already has the dope, return it */
111 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
112 if (!DeviceExtension
->Dope
)
118 /* Make sure we can flush safely */
119 KeAcquireGuardedMutex(&PopVolumeLock
);
121 /* Get dope from device */
122 Dope
= (PDEVICE_OBJECT_POWER_EXTENSION
)DeviceExtension
->Dope
;
124 if (Dope
->Volume
.Flink
)
126 /* Remove from volume from list */
127 RemoveEntryList(&Dope
->Volume
);
130 /* Allow flushes to go through */
131 KeReleaseGuardedMutex(&PopVolumeLock
);
133 /* Now remove dope from device object */
134 KeAcquireSpinLock(&PopDopeGlobalLock
, &OldIrql
);
136 /* remove from dev obj */
137 DeviceExtension
->Dope
= NULL
;
140 KeReleaseSpinLock(&PopDopeGlobalLock
, OldIrql
);
143 ExFreePoolWithTag(Dope
, 'Dope');
148 PopFlushVolumeWorker(IN PVOID Context
)
150 PPOP_FLUSH_VOLUME FlushContext
= (PPOP_FLUSH_VOLUME
)Context
;
151 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
152 PLIST_ENTRY NextEntry
;
154 UCHAR Buffer
[sizeof(OBJECT_NAME_INFORMATION
) + 512];
155 POBJECT_NAME_INFORMATION NameInfo
= (PVOID
)Buffer
;
157 OBJECT_ATTRIBUTES ObjectAttributes
;
159 IO_STATUS_BLOCK IoStatusBlock
;
161 /* Acquire the flush lock since we're messing with the list */
162 KeAcquireGuardedMutex(&PopVolumeLock
);
164 /* Loop the flush list */
165 while (!IsListEmpty(&FlushContext
->List
))
167 /* Grab the next (ie: current) entry and remove it */
168 NextEntry
= FlushContext
->List
.Flink
;
169 RemoveEntryList(NextEntry
);
171 /* Add it back on the volume list */
172 InsertTailList(&PopVolumeDevices
, NextEntry
);
174 /* Done touching the volume list */
175 KeReleaseGuardedMutex(&PopVolumeLock
);
177 /* Get the dope from the volume link */
178 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
181 Status
= ObQueryNameString(Dope
->DeviceObject
,
185 if ((NT_SUCCESS(Status
)) && (NameInfo
->Name
.Buffer
))
187 /* Open the volume */
188 DPRINT("Opening: %wZ\n", &NameInfo
->Name
);
189 InitializeObjectAttributes(&ObjectAttributes
,
191 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
194 Status
= ZwCreateFile(&VolumeHandle
,
195 SYNCHRONIZE
| FILE_READ_DATA
| FILE_WRITE_DATA
,
199 GENERIC_READ
| GENERIC_WRITE
,
200 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
205 if (NT_SUCCESS(Status
))
207 /* Flush it and close it */
208 DPRINT("Sending flush to: %p\n", VolumeHandle
);
209 ZwFlushBuffersFile(VolumeHandle
, &IoStatusBlock
);
210 ZwClose(VolumeHandle
);
214 /* Acquire the flush lock again since we'll touch the list */
215 KeAcquireGuardedMutex(&PopVolumeLock
);
218 /* One more flush completed... if it was the last, signal the caller */
219 if (!--FlushContext
->Count
) KeSetEvent(&FlushContext
->Wait
, IO_NO_INCREMENT
, FALSE
);
221 /* Serialize with flushers */
222 KeReleaseGuardedMutex(&PopVolumeLock
);
227 PopFlushVolumes(IN BOOLEAN ShuttingDown
)
229 POP_FLUSH_VOLUME FlushContext
= {{0}};
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;
241 /* Setup the flush context */
242 InitializeListHead(&FlushContext
.List
);
243 KeInitializeEvent(&FlushContext
.Wait
, NotificationEvent
, FALSE
);
246 FlushPolicy
= ShuttingDown
? 1 | 2 : PopFlushPolicy
;
247 if ((FlushPolicy
& 1))
249 /* Registry flush requested, so open it */
250 DPRINT("Opening registry\n");
251 InitializeObjectAttributes(&ObjectAttributes
,
253 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
256 Status
= ZwOpenKey(&RegistryHandle
, KEY_READ
, &ObjectAttributes
);
257 if (NT_SUCCESS(Status
))
259 /* Flush the registry */
260 DPRINT("Flushing registry\n");
261 ZwFlushKey(RegistryHandle
);
262 ZwClose(RegistryHandle
);
266 /* Serialize with other flushes */
267 KeAcquireGuardedMutex(&PopVolumeLock
);
269 /* Scan the volume list */
270 NextEntry
= PopVolumeDevices
.Flink
;
271 while (NextEntry
!= &PopVolumeDevices
)
273 /* Get the dope from the link */
274 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
276 /* Grab the next entry now, since we'll be modifying the list */
277 NextEntry
= NextEntry
->Flink
;
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
)))
290 /* Remove it from the dope and add it to the flush context list */
291 RemoveEntryList(&Dope
->Volume
);
292 InsertTailList(&FlushContext
.List
, &Dope
->Volume
);
298 /* Check if we should skip non-removable devices */
299 if (!(FlushPolicy
& 2))
301 /* ReactOS only implements this routine for shutdown, which requires it */
305 /* Check if there were no volumes at all */
309 KeReleaseGuardedMutex(&PopVolumeLock
);
313 /* Allocate up to 8 flusher threads */
314 ThreadCount
= min(VolumeCount
, 8);
315 InitializeObjectAttributes(&ObjectAttributes
,
321 /* We will ourselves become a flusher thread */
322 FlushContext
.Count
= 1;
325 /* Look for any extra ones we might need */
326 while (ThreadCount
> 0)
328 /* Create a new one */
330 DPRINT("Creating flush thread\n");
331 Status
= PsCreateSystemThread(&ThreadHandle
,
336 PopFlushVolumeWorker
,
338 if (NT_SUCCESS(Status
))
340 /* One more created... */
341 FlushContext
.Count
++;
342 ZwClose(ThreadHandle
);
346 /* Allow flushes to go through */
347 KeReleaseGuardedMutex(&PopVolumeLock
);
349 /* Enter the flush work */
350 DPRINT("Local flush\n");
351 PopFlushVolumeWorker(&FlushContext
);
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");
361 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension
)
363 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
= (PVOID
)DeviceObjectExtension
;
366 /* Initialize the power flags */
367 DeviceExtension
->PowerFlags
= PowerSystemUnspecified
& 0xF;
368 DeviceExtension
->PowerFlags
|= ((PowerDeviceUnspecified
<< 4) & 0xF0);
370 /* The device object is not on drugs yet */
371 DeviceExtension
->Dope
= NULL
;
374 /* PUBLIC FUNCTIONS **********************************************************/