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 PopFlushVolumeWorker(IN PVOID Context
)
105 PPOP_FLUSH_VOLUME FlushContext
= (PPOP_FLUSH_VOLUME
)Context
;
106 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
107 PLIST_ENTRY NextEntry
;
109 UCHAR Buffer
[sizeof(OBJECT_NAME_INFORMATION
) + 512];
110 POBJECT_NAME_INFORMATION NameInfo
= (PVOID
)Buffer
;
112 OBJECT_ATTRIBUTES ObjectAttributes
;
114 IO_STATUS_BLOCK IoStatusBlock
;
116 /* Acquire the flush lock since we're messing with the list */
117 KeAcquireGuardedMutex(&PopVolumeLock
);
119 /* Loop the flush list */
120 while (!IsListEmpty(&FlushContext
->List
))
122 /* Grab the next (ie: current) entry and remove it */
123 NextEntry
= FlushContext
->List
.Flink
;
124 RemoveEntryList(NextEntry
);
126 /* Add it back on the volume list */
127 InsertTailList(&PopVolumeDevices
, NextEntry
);
129 /* Done touching the volume list */
130 KeReleaseGuardedMutex(&PopVolumeLock
);
132 /* Get the dope from the volume link */
133 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
136 Status
= ObQueryNameString(Dope
->DeviceObject
,
140 if ((NT_SUCCESS(Status
)) && (NameInfo
->Name
.Buffer
))
142 /* Open the volume */
143 DPRINT1("Opening: %wZ\n", &NameInfo
->Name
);
144 InitializeObjectAttributes(&ObjectAttributes
,
146 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
149 Status
= ZwCreateFile(&VolumeHandle
,
150 SYNCHRONIZE
| FILE_READ_DATA
| FILE_WRITE_DATA
,
154 GENERIC_READ
| GENERIC_WRITE
,
155 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
160 if (NT_SUCCESS(Status
))
162 /* Flush it and close it */
163 DPRINT1("Sending flush to: %lx\n", VolumeHandle
);
164 ZwFlushBuffersFile(VolumeHandle
, &IoStatusBlock
);
165 ZwClose(VolumeHandle
);
169 /* Acquire the flush lock again since we'll touch the list */
170 KeAcquireGuardedMutex(&PopVolumeLock
);
173 /* One more flush completed... if it was the last, signal the caller */
174 if (!--FlushContext
->Count
) KeSetEvent(&FlushContext
->Wait
, IO_NO_INCREMENT
, FALSE
);
176 /* Serialize with flushers */
177 KeReleaseGuardedMutex(&PopVolumeLock
);
182 PopFlushVolumes(IN BOOLEAN ShuttingDown
)
184 POP_FLUSH_VOLUME FlushContext
= {{0}};
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;
196 /* Setup the flush context */
197 InitializeListHead(&FlushContext
.List
);
198 KeInitializeEvent(&FlushContext
.Wait
, NotificationEvent
, FALSE
);
201 FlushPolicy
= ShuttingDown
? 1 | 2 : PopFlushPolicy
;
202 if ((FlushPolicy
& 1))
204 /* Registry flush requested, so open it */
205 DPRINT1("Opening registry\n");
206 InitializeObjectAttributes(&ObjectAttributes
,
208 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
211 Status
= ZwOpenKey(&RegistryHandle
, KEY_READ
, &ObjectAttributes
);
212 if (NT_SUCCESS(Status
))
214 /* Flush the registry */
215 DPRINT1("Flushing registry\n");
216 ZwFlushKey(RegistryHandle
);
217 ZwClose(RegistryHandle
);
221 /* Serialize with other flushes */
222 KeAcquireGuardedMutex(&PopVolumeLock
);
224 /* Scan the volume list */
225 NextEntry
= PopVolumeDevices
.Flink
;
226 while (NextEntry
!= &PopVolumeDevices
)
228 /* Get the dope from the link */
229 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
231 /* Grab the next entry now, since we'll be modifying the list */
232 NextEntry
= NextEntry
->Flink
;
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
)))
245 /* Remove it from the dope and add it to the flush context list */
246 RemoveEntryList(&Dope
->Volume
);
247 InsertTailList(&FlushContext
.List
, &Dope
->Volume
);
253 /* Check if we should skip non-removable devices */
254 if (!(FlushPolicy
& 2))
256 /* ReactOS only implements this routine for shutdown, which requires it */
261 /* Check if there were no volumes at all */
265 KeReleaseGuardedMutex(&PopVolumeLock
);
269 /* Allocate up to 8 flusher threads */
270 ThreadCount
= min(VolumeCount
, 8);
271 InitializeObjectAttributes(&ObjectAttributes
,
277 /* We will ourselves become a flusher thread */
278 FlushContext
.Count
= 1;
281 /* Look for any extra ones we might need */
282 while (ThreadCount
> 0)
284 /* Create a new one */
286 DPRINT1("Creating flush thread\n");
287 Status
= PsCreateSystemThread(&ThreadHandle
,
292 PopFlushVolumeWorker
,
294 if (NT_SUCCESS(Status
))
296 /* One more created... */
297 FlushContext
.Count
++;
298 ZwClose(ThreadHandle
);
302 /* Allow flushes to go through */
303 KeReleaseGuardedMutex(&PopVolumeLock
);
305 /* Enter the flush work */
306 DPRINT1("Local flush\n");
307 PopFlushVolumeWorker(&FlushContext
);
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");
317 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension
)
319 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
= (PVOID
)DeviceObjectExtension
;
322 /* Initialize the power flags */
323 DeviceExtension
->PowerFlags
= PowerSystemUnspecified
& 0xF;
324 DeviceExtension
->PowerFlags
|= ((PowerDeviceUnspecified
<< 4) & 0xF0);
326 /* The device object is not on drugs yet */
327 DeviceExtension
->Dope
= NULL
;
330 /* PUBLIC FUNCTIONS **********************************************************/