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 #define TAG_PO_DOPE 'EPOD'
32 /* PRIVATE FUNCTIONS *********************************************************/
34 PDEVICE_OBJECT_POWER_EXTENSION
36 PopGetDope(IN PDEVICE_OBJECT DeviceObject
)
38 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
39 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
43 /* If the device already has the dope, return it */
44 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
45 if (DeviceExtension
->Dope
) goto Return
;
47 /* Allocate some dope for the device */
48 Dope
= ExAllocatePoolWithTag(NonPagedPool
,
49 sizeof(DEVICE_OBJECT_POWER_EXTENSION
),
51 if (!Dope
) goto Return
;
53 /* Initialize the initial contents of the dope */
54 RtlZeroMemory(Dope
, sizeof(DEVICE_OBJECT_POWER_EXTENSION
));
55 Dope
->DeviceObject
= DeviceObject
;
56 Dope
->State
= PowerDeviceUnspecified
;
57 InitializeListHead(&Dope
->IdleList
);
59 /* Make sure only one caller can assign dope to a device */
60 KeAcquireSpinLock(&PopDopeGlobalLock
, &OldIrql
);
62 /* Make sure the device still has no dope */
63 if (!DeviceExtension
->Dope
)
65 /* Give the local dope to this device, and remember we won the race */
66 DeviceExtension
->Dope
= (PVOID
)Dope
;
70 /* Allow other dope transactions now */
71 KeReleaseSpinLock(&PopDopeGlobalLock
, OldIrql
);
73 /* Check if someone other than us already assigned the dope, so free ours */
74 if (Dope
) ExFreePoolWithTag(Dope
, TAG_PO_DOPE
);
76 /* Return the dope to the caller */
78 return (PDEVICE_OBJECT_POWER_EXTENSION
)DeviceExtension
->Dope
;
83 PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject
)
85 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
88 /* Get dope from the device (if the device has no dope, it will receive some) */
89 Dope
= PopGetDope(DeviceObject
);
92 /* Make sure we can flush safely */
93 KeAcquireGuardedMutex(&PopVolumeLock
);
95 /* Add this volume into the list of power-manager volumes */
96 if (!Dope
->Volume
.Flink
) InsertTailList(&PopVolumeDevices
, &Dope
->Volume
);
98 /* Allow flushes to go through */
99 KeReleaseGuardedMutex(&PopVolumeLock
);
105 PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject
)
107 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
108 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
112 /* If the device already has the dope, return it */
113 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
114 if (!DeviceExtension
->Dope
)
120 /* Make sure we can flush safely */
121 KeAcquireGuardedMutex(&PopVolumeLock
);
123 /* Get dope from device */
124 Dope
= (PDEVICE_OBJECT_POWER_EXTENSION
)DeviceExtension
->Dope
;
126 if (Dope
->Volume
.Flink
)
128 /* Remove from volume from list */
129 RemoveEntryList(&Dope
->Volume
);
132 /* Allow flushes to go through */
133 KeReleaseGuardedMutex(&PopVolumeLock
);
135 /* Now remove dope from device object */
136 KeAcquireSpinLock(&PopDopeGlobalLock
, &OldIrql
);
138 /* remove from dev obj */
139 DeviceExtension
->Dope
= NULL
;
142 KeReleaseSpinLock(&PopDopeGlobalLock
, OldIrql
);
145 ExFreePoolWithTag(Dope
, TAG_PO_DOPE
);
150 PopFlushVolumeWorker(IN PVOID Context
)
152 PPOP_FLUSH_VOLUME FlushContext
= (PPOP_FLUSH_VOLUME
)Context
;
153 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
154 PLIST_ENTRY NextEntry
;
156 UCHAR Buffer
[sizeof(OBJECT_NAME_INFORMATION
) + 512];
157 POBJECT_NAME_INFORMATION NameInfo
= (PVOID
)Buffer
;
159 OBJECT_ATTRIBUTES ObjectAttributes
;
161 IO_STATUS_BLOCK IoStatusBlock
;
163 /* Acquire the flush lock since we're messing with the list */
164 KeAcquireGuardedMutex(&PopVolumeLock
);
166 /* Loop the flush list */
167 while (!IsListEmpty(&FlushContext
->List
))
169 /* Grab the next (ie: current) entry and remove it */
170 NextEntry
= FlushContext
->List
.Flink
;
171 RemoveEntryList(NextEntry
);
173 /* Add it back on the volume list */
174 InsertTailList(&PopVolumeDevices
, NextEntry
);
176 /* Done touching the volume list */
177 KeReleaseGuardedMutex(&PopVolumeLock
);
179 /* Get the dope from the volume link */
180 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
183 Status
= ObQueryNameString(Dope
->DeviceObject
,
187 if ((NT_SUCCESS(Status
)) && (NameInfo
->Name
.Buffer
))
189 /* Open the volume */
190 DPRINT("Opening: %wZ\n", &NameInfo
->Name
);
191 InitializeObjectAttributes(&ObjectAttributes
,
193 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
196 Status
= ZwCreateFile(&VolumeHandle
,
197 SYNCHRONIZE
| FILE_READ_DATA
| FILE_WRITE_DATA
,
201 GENERIC_READ
| GENERIC_WRITE
,
202 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
207 if (NT_SUCCESS(Status
))
209 /* Flush it and close it */
210 DPRINT("Sending flush to: %p\n", VolumeHandle
);
211 ZwFlushBuffersFile(VolumeHandle
, &IoStatusBlock
);
212 ZwClose(VolumeHandle
);
216 /* Acquire the flush lock again since we'll touch the list */
217 KeAcquireGuardedMutex(&PopVolumeLock
);
220 /* One more flush completed... if it was the last, signal the caller */
221 if (!--FlushContext
->Count
) KeSetEvent(&FlushContext
->Wait
, IO_NO_INCREMENT
, FALSE
);
223 /* Serialize with flushers */
224 KeReleaseGuardedMutex(&PopVolumeLock
);
229 PopFlushVolumes(IN BOOLEAN ShuttingDown
)
231 POP_FLUSH_VOLUME FlushContext
= {{0}};
233 UNICODE_STRING RegistryName
= RTL_CONSTANT_STRING(L
"\\Registry");
234 OBJECT_ATTRIBUTES ObjectAttributes
;
235 HANDLE RegistryHandle
;
236 PLIST_ENTRY NextEntry
;
237 PDEVICE_OBJECT_POWER_EXTENSION Dope
;
238 ULONG VolumeCount
= 0;
243 /* Setup the flush context */
244 InitializeListHead(&FlushContext
.List
);
245 KeInitializeEvent(&FlushContext
.Wait
, NotificationEvent
, FALSE
);
248 FlushPolicy
= ShuttingDown
? 1 | 2 : PopFlushPolicy
;
249 if ((FlushPolicy
& 1))
251 /* Registry flush requested, so open it */
252 DPRINT("Opening registry\n");
253 InitializeObjectAttributes(&ObjectAttributes
,
255 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
258 Status
= ZwOpenKey(&RegistryHandle
, KEY_READ
, &ObjectAttributes
);
259 if (NT_SUCCESS(Status
))
261 /* Flush the registry */
262 DPRINT("Flushing registry\n");
263 ZwFlushKey(RegistryHandle
);
264 ZwClose(RegistryHandle
);
268 /* Serialize with other flushes */
269 KeAcquireGuardedMutex(&PopVolumeLock
);
271 /* Scan the volume list */
272 NextEntry
= PopVolumeDevices
.Flink
;
273 while (NextEntry
!= &PopVolumeDevices
)
275 /* Get the dope from the link */
276 Dope
= CONTAINING_RECORD(NextEntry
, DEVICE_OBJECT_POWER_EXTENSION
, Volume
);
278 /* Grab the next entry now, since we'll be modifying the list */
279 NextEntry
= NextEntry
->Flink
;
281 /* Make sure the object is mounted, writable, exists, and is not a floppy */
282 if (!(Dope
->DeviceObject
->Vpb
->Flags
& VPB_MOUNTED
) ||
283 (Dope
->DeviceObject
->Characteristics
& FILE_FLOPPY_DISKETTE
) ||
284 (Dope
->DeviceObject
->Characteristics
& FILE_READ_ONLY_DEVICE
) ||
285 ((Dope
->DeviceObject
->Vpb
->RealDevice
) &&
286 (Dope
->DeviceObject
->Vpb
->RealDevice
->Characteristics
& FILE_FLOPPY_DISKETTE
)))
292 /* Remove it from the dope and add it to the flush context list */
293 RemoveEntryList(&Dope
->Volume
);
294 InsertTailList(&FlushContext
.List
, &Dope
->Volume
);
300 /* Check if we should skip non-removable devices */
301 if (!(FlushPolicy
& 2))
303 /* ReactOS only implements this routine for shutdown, which requires it */
307 /* Check if there were no volumes at all */
311 KeReleaseGuardedMutex(&PopVolumeLock
);
315 /* Allocate up to 8 flusher threads */
316 ThreadCount
= min(VolumeCount
, 8);
317 InitializeObjectAttributes(&ObjectAttributes
,
323 /* We will ourselves become a flusher thread */
324 FlushContext
.Count
= 1;
327 /* Look for any extra ones we might need */
328 while (ThreadCount
> 0)
330 /* Create a new one */
332 DPRINT("Creating flush thread\n");
333 Status
= PsCreateSystemThread(&ThreadHandle
,
338 PopFlushVolumeWorker
,
340 if (NT_SUCCESS(Status
))
342 /* One more created... */
343 FlushContext
.Count
++;
344 ZwClose(ThreadHandle
);
348 /* Allow flushes to go through */
349 KeReleaseGuardedMutex(&PopVolumeLock
);
351 /* Enter the flush work */
352 DPRINT("Local flush\n");
353 PopFlushVolumeWorker(&FlushContext
);
355 /* Wait for all flushes to be over */
356 DPRINT("Waiting for flushes\n");
357 KeWaitForSingleObject(&FlushContext
.Wait
, Executive
, KernelMode
, FALSE
, NULL
);
358 DPRINT("Flushes have completed\n");
363 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension
)
365 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
= (PVOID
)DeviceObjectExtension
;
368 /* Initialize the power flags */
369 DeviceExtension
->PowerFlags
= PowerSystemUnspecified
& 0xF;
370 DeviceExtension
->PowerFlags
|= ((PowerDeviceUnspecified
<< 4) & 0xF0);
372 /* The device object is not on drugs yet */
373 DeviceExtension
->Dope
= NULL
;
376 /* PUBLIC FUNCTIONS **********************************************************/