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 IQS_SAME(IO_COMPLETION_BASIC_INFORMATION, 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 ICIF_PROBE_READ_WRITE, 399 IoCompletionInformation, 400 IoCompletionInformationLength, 401 ResultLength, 402 NULL, 403 PreviousMode); 404 if (!NT_SUCCESS(Status)) return Status; 405 406 /* Get the Object */ 407 Status = ObReferenceObjectByHandle(IoCompletionHandle, 408 IO_COMPLETION_QUERY_STATE, 409 IoCompletionType, 410 PreviousMode, 411 (PVOID*)&Queue, 412 NULL); 413 if (NT_SUCCESS(Status)) 414 { 415 /* Protect write in SEH */ 416 _SEH2_TRY 417 { 418 /* Return Info */ 419 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)-> 420 Depth = KeReadStateQueue(Queue); 421 422 /* Return Result Length if needed */ 423 if (ResultLength) 424 { 425 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION); 426 } 427 } 428 _SEH2_EXCEPT(ExSystemExceptionFilter()) 429 { 430 /* Get exception code */ 431 Status = _SEH2_GetExceptionCode(); 432 } 433 _SEH2_END; 434 435 /* Dereference the queue */ 436 ObDereferenceObject(Queue); 437 } 438 439 /* Return Status */ 440 return Status; 441 } 442 443 NTSTATUS 444 NTAPI 445 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle, 446 OUT PVOID *KeyContext, 447 OUT PVOID *ApcContext, 448 OUT PIO_STATUS_BLOCK IoStatusBlock, 449 IN PLARGE_INTEGER Timeout OPTIONAL) 450 { 451 LARGE_INTEGER SafeTimeout; 452 PKQUEUE Queue; 453 PIOP_MINI_COMPLETION_PACKET Packet; 454 PLIST_ENTRY ListEntry; 455 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 456 NTSTATUS Status; 457 PIRP Irp; 458 PVOID Apc, Key; 459 IO_STATUS_BLOCK IoStatus; 460 PAGED_CODE(); 461 462 /* Check if the call was from user mode */ 463 if (PreviousMode != KernelMode) 464 { 465 /* Protect probes in SEH */ 466 _SEH2_TRY 467 { 468 /* Probe the pointers */ 469 ProbeForWritePointer(KeyContext); 470 ProbeForWritePointer(ApcContext); 471 472 /* Probe the I/O Status Block */ 473 ProbeForWriteIoStatusBlock(IoStatusBlock); 474 if (Timeout) 475 { 476 /* Probe and capture the timeout */ 477 SafeTimeout = ProbeForReadLargeInteger(Timeout); 478 Timeout = &SafeTimeout; 479 } 480 } 481 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 482 { 483 /* Return the exception code */ 484 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 485 } 486 _SEH2_END; 487 } 488 489 /* Open the Object */ 490 Status = ObReferenceObjectByHandle(IoCompletionHandle, 491 IO_COMPLETION_MODIFY_STATE, 492 IoCompletionType, 493 PreviousMode, 494 (PVOID*)&Queue, 495 NULL); 496 if (NT_SUCCESS(Status)) 497 { 498 /* Remove queue */ 499 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout); 500 501 /* If we got a timeout or user_apc back, return the status */ 502 if (((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_TIMEOUT) || 503 ((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_USER_APC)) 504 { 505 /* Set this as the status */ 506 Status = (NTSTATUS)(ULONG_PTR)ListEntry; 507 } 508 else 509 { 510 /* Get the Packet Data */ 511 Packet = CONTAINING_RECORD(ListEntry, 512 IOP_MINI_COMPLETION_PACKET, 513 ListEntry); 514 515 /* Check if this is piggybacked on an IRP */ 516 if (Packet->PacketType == IopCompletionPacketIrp) 517 { 518 /* Get the IRP */ 519 Irp = CONTAINING_RECORD(ListEntry, 520 IRP, 521 Tail.Overlay.ListEntry); 522 523 /* Save values */ 524 Key = Irp->Tail.CompletionKey; 525 Apc = Irp->Overlay.AsynchronousParameters.UserApcContext; 526 IoStatus = Irp->IoStatus; 527 528 /* Free the IRP */ 529 IoFreeIrp(Irp); 530 } 531 else 532 { 533 /* Save values */ 534 Key = Packet->KeyContext; 535 Apc = Packet->ApcContext; 536 IoStatus.Status = Packet->IoStatus; 537 IoStatus.Information = Packet->IoStatusInformation; 538 539 /* Free the packet */ 540 IopFreeMiniPacket(Packet); 541 } 542 543 /* Enter SEH to write back the values */ 544 _SEH2_TRY 545 { 546 /* Write the values to caller */ 547 *ApcContext = Apc; 548 *KeyContext = Key; 549 *IoStatusBlock = IoStatus; 550 } 551 _SEH2_EXCEPT(ExSystemExceptionFilter()) 552 { 553 /* Get the exception code */ 554 Status = _SEH2_GetExceptionCode(); 555 } 556 _SEH2_END; 557 } 558 559 /* Dereference the Object */ 560 ObDereferenceObject(Queue); 561 } 562 563 /* Return status */ 564 return Status; 565 } 566 567 NTSTATUS 568 NTAPI 569 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle, 570 IN PVOID CompletionKey, 571 IN PVOID CompletionContext, 572 IN NTSTATUS CompletionStatus, 573 IN ULONG CompletionInformation) 574 { 575 NTSTATUS Status; 576 PKQUEUE Queue; 577 PAGED_CODE(); 578 579 /* Get the Object */ 580 Status = ObReferenceObjectByHandle(IoCompletionPortHandle, 581 IO_COMPLETION_MODIFY_STATE, 582 IoCompletionType, 583 ExGetPreviousMode(), 584 (PVOID*)&Queue, 585 NULL); 586 if (NT_SUCCESS(Status)) 587 { 588 /* Set the Completion */ 589 Status = IoSetIoCompletion(Queue, 590 CompletionKey, 591 CompletionContext, 592 CompletionStatus, 593 CompletionInformation, 594 TRUE); 595 596 /* Dereference the object */ 597 ObDereferenceObject(Queue); 598 } 599 600 /* Return status */ 601 return Status; 602 } 603