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 #define TAG_PO_DOPE 'EPOD' 31 32 /* PRIVATE FUNCTIONS *********************************************************/ 33 34 PDEVICE_OBJECT_POWER_EXTENSION 35 NTAPI 36 PopGetDope(IN PDEVICE_OBJECT DeviceObject) 37 { 38 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; 39 PDEVICE_OBJECT_POWER_EXTENSION Dope; 40 KIRQL OldIrql; 41 PAGED_CODE(); 42 43 /* If the device already has the dope, return it */ 44 DeviceExtension = IoGetDevObjExtension(DeviceObject); 45 if (DeviceExtension->Dope) goto Return; 46 47 /* Allocate some dope for the device */ 48 Dope = ExAllocatePoolWithTag(NonPagedPool, 49 sizeof(DEVICE_OBJECT_POWER_EXTENSION), 50 TAG_PO_DOPE); 51 if (!Dope) goto Return; 52 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); 58 59 /* Make sure only one caller can assign dope to a device */ 60 KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql); 61 62 /* Make sure the device still has no dope */ 63 if (!DeviceExtension->Dope) 64 { 65 /* Give the local dope to this device, and remember we won the race */ 66 DeviceExtension->Dope = (PVOID)Dope; 67 Dope = NULL; 68 } 69 70 /* Allow other dope transactions now */ 71 KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql); 72 73 /* Check if someone other than us already assigned the dope, so free ours */ 74 if (Dope) ExFreePoolWithTag(Dope, TAG_PO_DOPE); 75 76 /* Return the dope to the caller */ 77 Return: 78 return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; 79 } 80 81 VOID 82 NTAPI 83 PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject) 84 { 85 PDEVICE_OBJECT_POWER_EXTENSION Dope; 86 PAGED_CODE(); 87 88 /* Get dope from the device (if the device has no dope, it will receive some) */ 89 Dope = PopGetDope(DeviceObject); 90 if (Dope) 91 { 92 /* Make sure we can flush safely */ 93 KeAcquireGuardedMutex(&PopVolumeLock); 94 95 /* Add this volume into the list of power-manager volumes */ 96 if (!Dope->Volume.Flink) InsertTailList(&PopVolumeDevices, &Dope->Volume); 97 98 /* Allow flushes to go through */ 99 KeReleaseGuardedMutex(&PopVolumeLock); 100 } 101 } 102 103 VOID 104 NTAPI 105 PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject) 106 { 107 PDEVICE_OBJECT_POWER_EXTENSION Dope; 108 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; 109 KIRQL OldIrql; 110 PAGED_CODE(); 111 112 /* If the device already has the dope, return it */ 113 DeviceExtension = IoGetDevObjExtension(DeviceObject); 114 if (!DeviceExtension->Dope) 115 { 116 /* no dope */ 117 return; 118 } 119 120 /* Make sure we can flush safely */ 121 KeAcquireGuardedMutex(&PopVolumeLock); 122 123 /* Get dope from device */ 124 Dope = (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; 125 126 if (Dope->Volume.Flink) 127 { 128 /* Remove from volume from list */ 129 RemoveEntryList(&Dope->Volume); 130 } 131 132 /* Allow flushes to go through */ 133 KeReleaseGuardedMutex(&PopVolumeLock); 134 135 /* Now remove dope from device object */ 136 KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql); 137 138 /* remove from dev obj */ 139 DeviceExtension->Dope = NULL; 140 141 /* Release lock */ 142 KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql); 143 144 /* Free dope */ 145 ExFreePoolWithTag(Dope, TAG_PO_DOPE); 146 } 147 148 VOID 149 NTAPI 150 PopFlushVolumeWorker(IN PVOID Context) 151 { 152 PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context; 153 PDEVICE_OBJECT_POWER_EXTENSION Dope; 154 PLIST_ENTRY NextEntry; 155 NTSTATUS Status; 156 UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512]; 157 POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer; 158 ULONG Length; 159 OBJECT_ATTRIBUTES ObjectAttributes; 160 HANDLE VolumeHandle; 161 IO_STATUS_BLOCK IoStatusBlock; 162 163 /* Acquire the flush lock since we're messing with the list */ 164 KeAcquireGuardedMutex(&PopVolumeLock); 165 166 /* Loop the flush list */ 167 while (!IsListEmpty(&FlushContext->List)) 168 { 169 /* Grab the next (ie: current) entry and remove it */ 170 NextEntry = FlushContext->List.Flink; 171 RemoveEntryList(NextEntry); 172 173 /* Add it back on the volume list */ 174 InsertTailList(&PopVolumeDevices, NextEntry); 175 176 /* Done touching the volume list */ 177 KeReleaseGuardedMutex(&PopVolumeLock); 178 179 /* Get the dope from the volume link */ 180 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); 181 182 /* Get the name */ 183 Status = ObQueryNameString(Dope->DeviceObject, 184 NameInfo, 185 sizeof(Buffer), 186 &Length); 187 if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer)) 188 { 189 /* Open the volume */ 190 DPRINT("Opening: %wZ\n", &NameInfo->Name); 191 InitializeObjectAttributes(&ObjectAttributes, 192 &NameInfo->Name, 193 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 194 0, 195 0); 196 Status = ZwCreateFile(&VolumeHandle, 197 SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, 198 &ObjectAttributes, 199 &IoStatusBlock, 200 NULL, 201 GENERIC_READ | GENERIC_WRITE, 202 FILE_SHARE_READ | FILE_SHARE_WRITE, 203 FILE_OPEN, 204 0, 205 NULL, 206 0); 207 if (NT_SUCCESS(Status)) 208 { 209 /* Flush it and close it */ 210 DPRINT("Sending flush to: %p\n", VolumeHandle); 211 ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock); 212 ZwClose(VolumeHandle); 213 } 214 } 215 216 /* Acquire the flush lock again since we'll touch the list */ 217 KeAcquireGuardedMutex(&PopVolumeLock); 218 } 219 220 /* One more flush completed... if it was the last, signal the caller */ 221 if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE); 222 223 /* Serialize with flushers */ 224 KeReleaseGuardedMutex(&PopVolumeLock); 225 } 226 227 VOID 228 NTAPI 229 PopFlushVolumes(IN BOOLEAN ShuttingDown) 230 { 231 POP_FLUSH_VOLUME FlushContext = {{0}}; 232 ULONG FlushPolicy; 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; 239 NTSTATUS Status; 240 HANDLE ThreadHandle; 241 ULONG ThreadCount; 242 243 /* Setup the flush context */ 244 InitializeListHead(&FlushContext.List); 245 KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE); 246 247 /* What to flush */ 248 FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy; 249 if ((FlushPolicy & 1)) 250 { 251 /* Registry flush requested, so open it */ 252 DPRINT("Opening registry\n"); 253 InitializeObjectAttributes(&ObjectAttributes, 254 &RegistryName, 255 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 256 NULL, 257 NULL); 258 Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes); 259 if (NT_SUCCESS(Status)) 260 { 261 /* Flush the registry */ 262 DPRINT("Flushing registry\n"); 263 ZwFlushKey(RegistryHandle); 264 ZwClose(RegistryHandle); 265 } 266 } 267 268 /* Serialize with other flushes */ 269 KeAcquireGuardedMutex(&PopVolumeLock); 270 271 /* Scan the volume list */ 272 NextEntry = PopVolumeDevices.Flink; 273 while (NextEntry != &PopVolumeDevices) 274 { 275 /* Get the dope from the link */ 276 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); 277 278 /* Grab the next entry now, since we'll be modifying the list */ 279 NextEntry = NextEntry->Flink; 280 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))) 287 { 288 /* Not flushable */ 289 continue; 290 } 291 292 /* Remove it from the dope and add it to the flush context list */ 293 RemoveEntryList(&Dope->Volume); 294 InsertTailList(&FlushContext.List, &Dope->Volume); 295 296 /* Next */ 297 VolumeCount++; 298 } 299 300 /* Check if we should skip non-removable devices */ 301 if (!(FlushPolicy & 2)) 302 { 303 /* ReactOS only implements this routine for shutdown, which requires it */ 304 UNIMPLEMENTED; 305 } 306 307 /* Check if there were no volumes at all */ 308 if (!VolumeCount) 309 { 310 /* Nothing to do */ 311 KeReleaseGuardedMutex(&PopVolumeLock); 312 return; 313 } 314 315 /* Allocate up to 8 flusher threads */ 316 ThreadCount = min(VolumeCount, 8); 317 InitializeObjectAttributes(&ObjectAttributes, 318 NULL, 319 OBJ_KERNEL_HANDLE, 320 NULL, 321 NULL); 322 323 /* We will ourselves become a flusher thread */ 324 FlushContext.Count = 1; 325 ThreadCount--; 326 327 /* Look for any extra ones we might need */ 328 while (ThreadCount > 0) 329 { 330 /* Create a new one */ 331 ThreadCount--; 332 DPRINT("Creating flush thread\n"); 333 Status = PsCreateSystemThread(&ThreadHandle, 334 THREAD_ALL_ACCESS, 335 &ObjectAttributes, 336 0L, 337 NULL, 338 PopFlushVolumeWorker, 339 &FlushContext); 340 if (NT_SUCCESS(Status)) 341 { 342 /* One more created... */ 343 FlushContext.Count++; 344 ZwClose(ThreadHandle); 345 } 346 } 347 348 /* Allow flushes to go through */ 349 KeReleaseGuardedMutex(&PopVolumeLock); 350 351 /* Enter the flush work */ 352 DPRINT("Local flush\n"); 353 PopFlushVolumeWorker(&FlushContext); 354 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"); 359 } 360 361 VOID 362 NTAPI 363 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension) 364 { 365 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension; 366 PAGED_CODE(); 367 368 /* Initialize the power flags */ 369 DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF; 370 DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0); 371 372 /* The device object is not on drugs yet */ 373 DeviceExtension->Dope = NULL; 374 } 375 376 /* PUBLIC FUNCTIONS **********************************************************/ 377 378