1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/io/iomgr/iocomp.c 5 * PURPOSE: I/O Wrappers (called Completion Ports) for Kernel Queues 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Thomas Weidenmueller (w3seek@reactos.org) 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 POBJECT_TYPE IoCompletionType; 17 18 GENERAL_LOOKASIDE IoCompletionPacketLookaside; 19 20 GENERIC_MAPPING IopCompletionMapping = 21 { 22 STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE, 23 STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE, 24 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, 25 IO_COMPLETION_ALL_ACCESS 26 }; 27 28 static const INFORMATION_CLASS_INFO IoCompletionInfoClass[] = 29 { 30 /* IoCompletionBasicInformation */ 31 ICI_SQ_SAME(sizeof(IO_COMPLETION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY), 32 }; 33 34 /* PRIVATE FUNCTIONS *********************************************************/ 35 36 NTSTATUS 37 NTAPI 38 IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject, 39 IN PIRP Irp, 40 IN PVOID Context) 41 { 42 NTSTATUS Status; 43 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnsafeContext = Context; 44 45 /* Reference the device object */ 46 ObReferenceObject(UnsafeContext->DeviceObject); 47 48 /* Call the completion routine */ 49 Status= UnsafeContext->CompletionRoutine(DeviceObject, 50 Irp, 51 UnsafeContext->Context); 52 53 /* Dereference the device object */ 54 ObDereferenceObject(UnsafeContext->DeviceObject); 55 56 /* Free our context */ 57 ExFreePool(UnsafeContext); 58 return Status; 59 } 60 61 VOID 62 NTAPI 63 IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet) 64 { 65 PKPRCB Prcb = KeGetCurrentPrcb(); 66 PNPAGED_LOOKASIDE_LIST List; 67 68 /* Use the P List */ 69 List = (PNPAGED_LOOKASIDE_LIST)Prcb-> 70 PPLookasideList[LookasideCompletionList].P; 71 List->L.TotalFrees++; 72 73 /* Check if the Free was within the Depth or not */ 74 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth) 75 { 76 /* Let the balancer know */ 77 List->L.FreeMisses++; 78 79 /* Use the L List */ 80 List = (PNPAGED_LOOKASIDE_LIST)Prcb-> 81 PPLookasideList[LookasideCompletionList].L; 82 List->L.TotalFrees++; 83 84 /* Check if the Free was within the Depth or not */ 85 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth) 86 { 87 /* All lists failed, use the pool */ 88 List->L.FreeMisses++; 89 ExFreePool(Packet); 90 return; 91 } 92 } 93 94 /* The free was within dhe Depth */ 95 InterlockedPushEntrySList(&List->L.ListHead, (PSLIST_ENTRY)Packet); 96 } 97 98 VOID 99 NTAPI 100 IopDeleteIoCompletion(PVOID ObjectBody) 101 { 102 PKQUEUE Queue = ObjectBody; 103 PLIST_ENTRY FirstEntry; 104 PLIST_ENTRY CurrentEntry; 105 PIRP Irp; 106 PIOP_MINI_COMPLETION_PACKET Packet; 107 108 /* Rundown the Queue */ 109 FirstEntry = KeRundownQueue(Queue); 110 if (FirstEntry) 111 { 112 /* Loop the packets */ 113 CurrentEntry = FirstEntry; 114 do 115 { 116 /* Get the Packet */ 117 Packet = CONTAINING_RECORD(CurrentEntry, 118 IOP_MINI_COMPLETION_PACKET, 119 ListEntry); 120 121 /* Go to next Entry */ 122 CurrentEntry = CurrentEntry->Flink; 123 124 /* Check if it's part of an IRP, or a separate packet */ 125 if (Packet->PacketType == IopCompletionPacketIrp) 126 { 127 /* Get the IRP and free it */ 128 Irp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.ListEntry); 129 IoFreeIrp(Irp); 130 } 131 else 132 { 133 /* Use common routine */ 134 IopFreeMiniPacket(Packet); 135 } 136 } while (FirstEntry != CurrentEntry); 137 } 138 } 139 140 /* PUBLIC FUNCTIONS **********************************************************/ 141 142 /* 143 * @implemented 144 */ 145 NTSTATUS 146 NTAPI 147 IoSetIoCompletion(IN PVOID IoCompletion, 148 IN PVOID KeyContext, 149 IN PVOID ApcContext, 150 IN NTSTATUS IoStatus, 151 IN ULONG_PTR IoStatusInformation, 152 IN BOOLEAN Quota) 153 { 154 PKQUEUE Queue = (PKQUEUE)IoCompletion; 155 PNPAGED_LOOKASIDE_LIST List; 156 PKPRCB Prcb = KeGetCurrentPrcb(); 157 PIOP_MINI_COMPLETION_PACKET Packet; 158 159 /* Get the P List */ 160 List = (PNPAGED_LOOKASIDE_LIST)Prcb-> 161 PPLookasideList[LookasideCompletionList].P; 162 163 /* Try to allocate the Packet */ 164 List->L.TotalAllocates++; 165 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead); 166 167 /* Check if that failed, use the L list if it did */ 168 if (!Packet) 169 { 170 /* Let the balancer know */ 171 List->L.AllocateMisses++; 172 173 /* Get L List */ 174 List = (PNPAGED_LOOKASIDE_LIST)Prcb-> 175 PPLookasideList[LookasideCompletionList].L; 176 177 /* Try to allocate the Packet */ 178 List->L.TotalAllocates++; 179 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead); 180 } 181 182 /* Still failed, use pool */ 183 if (!Packet) 184 { 185 /* Let the balancer know */ 186 List->L.AllocateMisses++; 187 188 /* Allocate from Nonpaged Pool */ 189 Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG); 190 } 191 192 /* Make sure we have one by now... */ 193 if (Packet) 194 { 195 /* Set up the Packet */ 196 Packet->PacketType = IopCompletionPacketMini; 197 Packet->KeyContext = KeyContext; 198 Packet->ApcContext = ApcContext; 199 Packet->IoStatus = IoStatus; 200 Packet->IoStatusInformation = IoStatusInformation; 201 202 /* Insert the Queue */ 203 KeInsertQueue(Queue, &Packet->ListEntry); 204 } 205 else 206 { 207 /* Out of memory, fail */ 208 return STATUS_INSUFFICIENT_RESOURCES; 209 } 210 211 /* Return Success */ 212 return STATUS_SUCCESS; 213 } 214 215 /* 216 * @implemented 217 */ 218 NTSTATUS 219 NTAPI 220 IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject, 221 IN PIRP Irp, 222 IN PIO_COMPLETION_ROUTINE CompletionRoutine, 223 IN PVOID Context, 224 IN BOOLEAN InvokeOnSuccess, 225 IN BOOLEAN InvokeOnError, 226 IN BOOLEAN InvokeOnCancel) 227 { 228 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnloadContext; 229 230 /* Allocate the context */ 231 UnloadContext = ExAllocatePoolWithTag(NonPagedPool, 232 sizeof(*UnloadContext), 233 'sUoI'); 234 if (!UnloadContext) return STATUS_INSUFFICIENT_RESOURCES; 235 236 /* Set up the context */ 237 UnloadContext->DeviceObject = DeviceObject; 238 UnloadContext->Context = Context; 239 UnloadContext->CompletionRoutine = CompletionRoutine; 240 241 /* Now set the completion routine */ 242 IoSetCompletionRoutine(Irp, 243 IopUnloadSafeCompletion, 244 UnloadContext, 245 InvokeOnSuccess, 246 InvokeOnError, 247 InvokeOnCancel); 248 return STATUS_SUCCESS; 249 } 250 251 NTSTATUS 252 NTAPI 253 NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle, 254 IN ACCESS_MASK DesiredAccess, 255 IN POBJECT_ATTRIBUTES ObjectAttributes, 256 IN ULONG NumberOfConcurrentThreads) 257 { 258 PKQUEUE Queue; 259 HANDLE hIoCompletionHandle; 260 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 261 NTSTATUS Status; 262 PAGED_CODE(); 263 264 /* Check if this was a user-mode call */ 265 if (PreviousMode != KernelMode) 266 { 267 /* Wrap probing in SEH */ 268 _SEH2_TRY 269 { 270 /* Probe the handle */ 271 ProbeForWriteHandle(IoCompletionHandle); 272 } 273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 274 { 275 /* Return the exception code */ 276 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 277 } 278 _SEH2_END; 279 } 280 281 /* Create the Object */ 282 Status = ObCreateObject(PreviousMode, 283 IoCompletionType, 284 ObjectAttributes, 285 PreviousMode, 286 NULL, 287 sizeof(KQUEUE), 288 0, 289 0, 290 (PVOID*)&Queue); 291 if (NT_SUCCESS(Status)) 292 { 293 /* Initialize the Queue */ 294 KeInitializeQueue(Queue, NumberOfConcurrentThreads); 295 296 /* Insert it */ 297 Status = ObInsertObject(Queue, 298 NULL, 299 DesiredAccess, 300 0, 301 NULL, 302 &hIoCompletionHandle); 303 if (NT_SUCCESS(Status)) 304 { 305 /* Protect writing the handle in SEH */ 306 _SEH2_TRY 307 { 308 /* Write the handle back */ 309 *IoCompletionHandle = hIoCompletionHandle; 310 } 311 _SEH2_EXCEPT(ExSystemExceptionFilter()) 312 { 313 /* Get the exception code */ 314 Status = _SEH2_GetExceptionCode(); 315 } 316 _SEH2_END; 317 } 318 } 319 320 /* Return Status */ 321 return Status; 322 } 323 324 NTSTATUS 325 NTAPI 326 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle, 327 IN ACCESS_MASK DesiredAccess, 328 IN POBJECT_ATTRIBUTES ObjectAttributes) 329 { 330 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 331 HANDLE hIoCompletionHandle; 332 NTSTATUS Status; 333 PAGED_CODE(); 334 335 /* Check if this was a user-mode call */ 336 if (PreviousMode != KernelMode) 337 { 338 /* Wrap probing in SEH */ 339 _SEH2_TRY 340 { 341 /* Probe the handle */ 342 ProbeForWriteHandle(IoCompletionHandle); 343 } 344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 345 { 346 /* Return the exception code */ 347 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 348 } 349 _SEH2_END; 350 } 351 352 /* Open the Object */ 353 Status = ObOpenObjectByName(ObjectAttributes, 354 IoCompletionType, 355 PreviousMode, 356 NULL, 357 DesiredAccess, 358 NULL, 359 &hIoCompletionHandle); 360 if (NT_SUCCESS(Status)) 361 { 362 /* Protect writing the handle in SEH */ 363 _SEH2_TRY 364 { 365 /* Write the handle back */ 366 *IoCompletionHandle = hIoCompletionHandle; 367 } 368 _SEH2_EXCEPT(ExSystemExceptionFilter()) 369 { 370 /* Get the exception code */ 371 Status = _SEH2_GetExceptionCode(); 372 } 373 _SEH2_END; 374 } 375 376 /* Return Status */ 377 return Status; 378 } 379 380 NTSTATUS 381 NTAPI 382 NtQueryIoCompletion(IN HANDLE IoCompletionHandle, 383 IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, 384 OUT PVOID IoCompletionInformation, 385 IN ULONG IoCompletionInformationLength, 386 OUT PULONG ResultLength OPTIONAL) 387 { 388 PKQUEUE Queue; 389 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 390 NTSTATUS Status; 391 PAGED_CODE(); 392 393 /* Check buffers and parameters */ 394 Status = DefaultQueryInfoBufferCheck(IoCompletionInformationClass, 395 IoCompletionInfoClass, 396 sizeof(IoCompletionInfoClass) / 397 sizeof(IoCompletionInfoClass[0]), 398 IoCompletionInformation, 399 IoCompletionInformationLength, 400 ResultLength, 401 NULL, 402 PreviousMode); 403 if (!NT_SUCCESS(Status)) return Status; 404 405 /* Get the Object */ 406 Status = ObReferenceObjectByHandle(IoCompletionHandle, 407 IO_COMPLETION_QUERY_STATE, 408 IoCompletionType, 409 PreviousMode, 410 (PVOID*)&Queue, 411 NULL); 412 if (NT_SUCCESS(Status)) 413 { 414 /* Protect write in SEH */ 415 _SEH2_TRY 416 { 417 /* Return Info */ 418 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)-> 419 Depth = KeReadStateQueue(Queue); 420 421 /* Return Result Length if needed */ 422 if (ResultLength) 423 { 424 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION); 425 } 426 } 427 _SEH2_EXCEPT(ExSystemExceptionFilter()) 428 { 429 /* Get exception code */ 430 Status = _SEH2_GetExceptionCode(); 431 } 432 _SEH2_END; 433 434 /* Dereference the queue */ 435 ObDereferenceObject(Queue); 436 } 437 438 /* Return Status */ 439 return Status; 440 } 441 442 NTSTATUS 443 NTAPI 444 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle, 445 OUT PVOID *KeyContext, 446 OUT PVOID *ApcContext, 447 OUT PIO_STATUS_BLOCK IoStatusBlock, 448 IN PLARGE_INTEGER Timeout OPTIONAL) 449 { 450 LARGE_INTEGER SafeTimeout; 451 PKQUEUE Queue; 452 PIOP_MINI_COMPLETION_PACKET Packet; 453 PLIST_ENTRY ListEntry; 454 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 455 NTSTATUS Status; 456 PIRP Irp; 457 PVOID Apc, Key; 458 IO_STATUS_BLOCK IoStatus; 459 PAGED_CODE(); 460 461 /* Check if the call was from user mode */ 462 if (PreviousMode != KernelMode) 463 { 464 /* Protect probes in SEH */ 465 _SEH2_TRY 466 { 467 /* Probe the pointers */ 468 ProbeForWritePointer(KeyContext); 469 ProbeForWritePointer(ApcContext); 470 471 /* Probe the I/O Status Block */ 472 ProbeForWriteIoStatusBlock(IoStatusBlock); 473 if (Timeout) 474 { 475 /* Probe and capture the timeout */ 476 SafeTimeout = ProbeForReadLargeInteger(Timeout); 477 Timeout = &SafeTimeout; 478 } 479 } 480 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 481 { 482 /* Return the exception code */ 483 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 484 } 485 _SEH2_END; 486 } 487 488 /* Open the Object */ 489 Status = ObReferenceObjectByHandle(IoCompletionHandle, 490 IO_COMPLETION_MODIFY_STATE, 491 IoCompletionType, 492 PreviousMode, 493 (PVOID*)&Queue, 494 NULL); 495 if (NT_SUCCESS(Status)) 496 { 497 /* Remove queue */ 498 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout); 499 500 /* If we got a timeout or user_apc back, return the status */ 501 if (((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_TIMEOUT) || 502 ((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_USER_APC)) 503 { 504 /* Set this as the status */ 505 Status = (NTSTATUS)(ULONG_PTR)ListEntry; 506 } 507 else 508 { 509 /* Get the Packet Data */ 510 Packet = CONTAINING_RECORD(ListEntry, 511 IOP_MINI_COMPLETION_PACKET, 512 ListEntry); 513 514 /* Check if this is piggybacked on an IRP */ 515 if (Packet->PacketType == IopCompletionPacketIrp) 516 { 517 /* Get the IRP */ 518 Irp = CONTAINING_RECORD(ListEntry, 519 IRP, 520 Tail.Overlay.ListEntry); 521 522 /* Save values */ 523 Key = Irp->Tail.CompletionKey; 524 Apc = Irp->Overlay.AsynchronousParameters.UserApcContext; 525 IoStatus = Irp->IoStatus; 526 527 /* Free the IRP */ 528 IoFreeIrp(Irp); 529 } 530 else 531 { 532 /* Save values */ 533 Key = Packet->KeyContext; 534 Apc = Packet->ApcContext; 535 IoStatus.Status = Packet->IoStatus; 536 IoStatus.Information = Packet->IoStatusInformation; 537 538 /* Free the packet */ 539 IopFreeMiniPacket(Packet); 540 } 541 542 /* Enter SEH to write back the values */ 543 _SEH2_TRY 544 { 545 /* Write the values to caller */ 546 *ApcContext = Apc; 547 *KeyContext = Key; 548 *IoStatusBlock = IoStatus; 549 } 550 _SEH2_EXCEPT(ExSystemExceptionFilter()) 551 { 552 /* Get the exception code */ 553 Status = _SEH2_GetExceptionCode(); 554 } 555 _SEH2_END; 556 } 557 558 /* Dereference the Object */ 559 ObDereferenceObject(Queue); 560 } 561 562 /* Return status */ 563 return Status; 564 } 565 566 NTSTATUS 567 NTAPI 568 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle, 569 IN PVOID CompletionKey, 570 IN PVOID CompletionContext, 571 IN NTSTATUS CompletionStatus, 572 IN ULONG CompletionInformation) 573 { 574 NTSTATUS Status; 575 PKQUEUE Queue; 576 PAGED_CODE(); 577 578 /* Get the Object */ 579 Status = ObReferenceObjectByHandle(IoCompletionPortHandle, 580 IO_COMPLETION_MODIFY_STATE, 581 IoCompletionType, 582 ExGetPreviousMode(), 583 (PVOID*)&Queue, 584 NULL); 585 if (NT_SUCCESS(Status)) 586 { 587 /* Set the Completion */ 588 Status = IoSetIoCompletion(Queue, 589 CompletionKey, 590 CompletionContext, 591 CompletionStatus, 592 CompletionInformation, 593 TRUE); 594 595 /* Dereference the object */ 596 ObDereferenceObject(Queue); 597 } 598 599 /* Return status */ 600 return Status; 601 } 602