2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/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)
10 /* INCLUDES *****************************************************************/
16 POBJECT_TYPE IoCompletionType
;
18 GENERAL_LOOKASIDE IoCompletionPacketLookaside
;
20 GENERIC_MAPPING IopCompletionMapping
=
22 STANDARD_RIGHTS_READ
| IO_COMPLETION_QUERY_STATE
,
23 STANDARD_RIGHTS_WRITE
| IO_COMPLETION_MODIFY_STATE
,
24 STANDARD_RIGHTS_EXECUTE
| SYNCHRONIZE
| IO_COMPLETION_QUERY_STATE
,
25 IO_COMPLETION_ALL_ACCESS
28 static const INFORMATION_CLASS_INFO IoCompletionInfoClass
[] =
30 /* IoCompletionBasicInformation */
31 ICI_SQ_SAME(sizeof(IO_COMPLETION_BASIC_INFORMATION
), sizeof(ULONG
), ICIF_QUERY
),
34 /* PRIVATE FUNCTIONS *********************************************************/
38 IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject
,
43 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnsafeContext
= Context
;
45 /* Reference the device object */
46 ObReferenceObject(UnsafeContext
->DeviceObject
);
48 /* Call the completion routine */
49 Status
= UnsafeContext
->CompletionRoutine(DeviceObject
,
51 UnsafeContext
->Context
);
53 /* Dereference the device object */
54 ObDereferenceObject(UnsafeContext
->DeviceObject
);
56 /* Free our context */
57 ExFreePool(UnsafeContext
);
63 IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet
)
65 PKPRCB Prcb
= KeGetCurrentPrcb();
66 PNPAGED_LOOKASIDE_LIST List
;
69 List
= (PNPAGED_LOOKASIDE_LIST
)Prcb
->
70 PPLookasideList
[LookasideCompletionList
].P
;
73 /* Check if the Free was within the Depth or not */
74 if (ExQueryDepthSList(&List
->L
.ListHead
) >= List
->L
.Depth
)
76 /* Let the balancer know */
80 List
= (PNPAGED_LOOKASIDE_LIST
)Prcb
->
81 PPLookasideList
[LookasideCompletionList
].L
;
84 /* Check if the Free was within the Depth or not */
85 if (ExQueryDepthSList(&List
->L
.ListHead
) >= List
->L
.Depth
)
87 /* All lists failed, use the pool */
94 /* The free was within dhe Depth */
95 InterlockedPushEntrySList(&List
->L
.ListHead
, (PSLIST_ENTRY
)Packet
);
100 IopDeleteIoCompletion(PVOID ObjectBody
)
102 PKQUEUE Queue
= ObjectBody
;
103 PLIST_ENTRY FirstEntry
;
104 PLIST_ENTRY CurrentEntry
;
106 PIOP_MINI_COMPLETION_PACKET Packet
;
108 /* Rundown the Queue */
109 FirstEntry
= KeRundownQueue(Queue
);
112 /* Loop the packets */
113 CurrentEntry
= FirstEntry
;
117 Packet
= CONTAINING_RECORD(CurrentEntry
,
118 IOP_MINI_COMPLETION_PACKET
,
121 /* Go to next Entry */
122 CurrentEntry
= CurrentEntry
->Flink
;
124 /* Check if it's part of an IRP, or a separate packet */
125 if (Packet
->PacketType
== IopCompletionPacketIrp
)
127 /* Get the IRP and free it */
128 Irp
= CONTAINING_RECORD(Packet
, IRP
, Tail
.Overlay
.ListEntry
);
133 /* Use common routine */
134 IopFreeMiniPacket(Packet
);
136 } while (FirstEntry
!= CurrentEntry
);
140 /* PUBLIC FUNCTIONS **********************************************************/
147 IoSetIoCompletion(IN PVOID IoCompletion
,
150 IN NTSTATUS IoStatus
,
151 IN ULONG_PTR IoStatusInformation
,
154 PKQUEUE Queue
= (PKQUEUE
)IoCompletion
;
155 PNPAGED_LOOKASIDE_LIST List
;
156 PKPRCB Prcb
= KeGetCurrentPrcb();
157 PIOP_MINI_COMPLETION_PACKET Packet
;
160 List
= (PNPAGED_LOOKASIDE_LIST
)Prcb
->
161 PPLookasideList
[LookasideCompletionList
].P
;
163 /* Try to allocate the Packet */
164 List
->L
.TotalAllocates
++;
165 Packet
= (PVOID
)InterlockedPopEntrySList(&List
->L
.ListHead
);
167 /* Check if that failed, use the L list if it did */
170 /* Let the balancer know */
171 List
->L
.AllocateMisses
++;
174 List
= (PNPAGED_LOOKASIDE_LIST
)Prcb
->
175 PPLookasideList
[LookasideCompletionList
].L
;
177 /* Try to allocate the Packet */
178 List
->L
.TotalAllocates
++;
179 Packet
= (PVOID
)InterlockedPopEntrySList(&List
->L
.ListHead
);
182 /* Still failed, use pool */
185 /* Let the balancer know */
186 List
->L
.AllocateMisses
++;
188 /* Allocate from Nonpaged Pool */
189 Packet
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*Packet
), IOC_TAG
);
192 /* Make sure we have one by now... */
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
;
202 /* Insert the Queue */
203 KeInsertQueue(Queue
, &Packet
->ListEntry
);
207 /* Out of memory, fail */
208 return STATUS_INSUFFICIENT_RESOURCES
;
212 return STATUS_SUCCESS
;
220 IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject
,
222 IN PIO_COMPLETION_ROUTINE CompletionRoutine
,
224 IN BOOLEAN InvokeOnSuccess
,
225 IN BOOLEAN InvokeOnError
,
226 IN BOOLEAN InvokeOnCancel
)
228 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnloadContext
;
230 /* Allocate the context */
231 UnloadContext
= ExAllocatePoolWithTag(NonPagedPool
,
232 sizeof(*UnloadContext
),
234 if (!UnloadContext
) return STATUS_INSUFFICIENT_RESOURCES
;
236 /* Set up the context */
237 UnloadContext
->DeviceObject
= DeviceObject
;
238 UnloadContext
->Context
= Context
;
239 UnloadContext
->CompletionRoutine
= CompletionRoutine
;
241 /* Now set the completion routine */
242 IoSetCompletionRoutine(Irp
,
243 IopUnloadSafeCompletion
,
248 return STATUS_SUCCESS
;
253 NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle
,
254 IN ACCESS_MASK DesiredAccess
,
255 IN POBJECT_ATTRIBUTES ObjectAttributes
,
256 IN ULONG NumberOfConcurrentThreads
)
259 HANDLE hIoCompletionHandle
;
260 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
264 /* Check if this was a user-mode call */
265 if (PreviousMode
!= KernelMode
)
267 /* Wrap probing in SEH */
270 /* Probe the handle */
271 ProbeForWriteHandle(IoCompletionHandle
);
273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
275 /* Return the exception code */
276 _SEH2_YIELD(return _SEH2_GetExceptionCode());
281 /* Create the Object */
282 Status
= ObCreateObject(PreviousMode
,
291 if (NT_SUCCESS(Status
))
293 /* Initialize the Queue */
294 KeInitializeQueue(Queue
, NumberOfConcurrentThreads
);
297 Status
= ObInsertObject(Queue
,
302 &hIoCompletionHandle
);
303 if (NT_SUCCESS(Status
))
305 /* Protect writing the handle in SEH */
308 /* Write the handle back */
309 *IoCompletionHandle
= hIoCompletionHandle
;
311 _SEH2_EXCEPT(ExSystemExceptionFilter())
313 /* Get the exception code */
314 Status
= _SEH2_GetExceptionCode();
326 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle
,
327 IN ACCESS_MASK DesiredAccess
,
328 IN POBJECT_ATTRIBUTES ObjectAttributes
)
330 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
331 HANDLE hIoCompletionHandle
;
335 /* Check if this was a user-mode call */
336 if (PreviousMode
!= KernelMode
)
338 /* Wrap probing in SEH */
341 /* Probe the handle */
342 ProbeForWriteHandle(IoCompletionHandle
);
344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
346 /* Return the exception code */
347 _SEH2_YIELD(return _SEH2_GetExceptionCode());
352 /* Open the Object */
353 Status
= ObOpenObjectByName(ObjectAttributes
,
359 &hIoCompletionHandle
);
360 if (NT_SUCCESS(Status
))
362 /* Protect writing the handle in SEH */
365 /* Write the handle back */
366 *IoCompletionHandle
= hIoCompletionHandle
;
368 _SEH2_EXCEPT(ExSystemExceptionFilter())
370 /* Get the exception code */
371 Status
= _SEH2_GetExceptionCode();
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
)
389 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
393 /* Check buffers and parameters */
394 Status
= DefaultQueryInfoBufferCheck(IoCompletionInformationClass
,
395 IoCompletionInfoClass
,
396 sizeof(IoCompletionInfoClass
) /
397 sizeof(IoCompletionInfoClass
[0]),
398 IoCompletionInformation
,
399 IoCompletionInformationLength
,
403 if (!NT_SUCCESS(Status
)) return Status
;
406 Status
= ObReferenceObjectByHandle(IoCompletionHandle
,
407 IO_COMPLETION_QUERY_STATE
,
412 if (NT_SUCCESS(Status
))
414 /* Protect write in SEH */
418 ((PIO_COMPLETION_BASIC_INFORMATION
)IoCompletionInformation
)->
419 Depth
= KeReadStateQueue(Queue
);
421 /* Return Result Length if needed */
424 *ResultLength
= sizeof(IO_COMPLETION_BASIC_INFORMATION
);
427 _SEH2_EXCEPT(ExSystemExceptionFilter())
429 /* Get exception code */
430 Status
= _SEH2_GetExceptionCode();
434 /* Dereference the queue */
435 ObDereferenceObject(Queue
);
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
)
450 LARGE_INTEGER SafeTimeout
;
452 PIOP_MINI_COMPLETION_PACKET Packet
;
453 PLIST_ENTRY ListEntry
;
454 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
458 IO_STATUS_BLOCK IoStatus
;
461 /* Check if the call was from user mode */
462 if (PreviousMode
!= KernelMode
)
464 /* Protect probes in SEH */
467 /* Probe the pointers */
468 ProbeForWritePointer(KeyContext
);
469 ProbeForWritePointer(ApcContext
);
471 /* Probe the I/O Status Block */
472 ProbeForWriteIoStatusBlock(IoStatusBlock
);
475 /* Probe and capture the timeout */
476 SafeTimeout
= ProbeForReadLargeInteger(Timeout
);
477 Timeout
= &SafeTimeout
;
480 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
482 /* Return the exception code */
483 _SEH2_YIELD(return _SEH2_GetExceptionCode());
488 /* Open the Object */
489 Status
= ObReferenceObjectByHandle(IoCompletionHandle
,
490 IO_COMPLETION_MODIFY_STATE
,
495 if (NT_SUCCESS(Status
))
498 ListEntry
= KeRemoveQueue(Queue
, PreviousMode
, Timeout
);
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
))
504 /* Set this as the status */
505 Status
= (NTSTATUS
)(ULONG_PTR
)ListEntry
;
509 /* Get the Packet Data */
510 Packet
= CONTAINING_RECORD(ListEntry
,
511 IOP_MINI_COMPLETION_PACKET
,
514 /* Check if this is piggybacked on an IRP */
515 if (Packet
->PacketType
== IopCompletionPacketIrp
)
518 Irp
= CONTAINING_RECORD(ListEntry
,
520 Tail
.Overlay
.ListEntry
);
523 Key
= Irp
->Tail
.CompletionKey
;
524 Apc
= Irp
->Overlay
.AsynchronousParameters
.UserApcContext
;
525 IoStatus
= Irp
->IoStatus
;
533 Key
= Packet
->KeyContext
;
534 Apc
= Packet
->ApcContext
;
535 IoStatus
.Status
= Packet
->IoStatus
;
536 IoStatus
.Information
= Packet
->IoStatusInformation
;
538 /* Free the packet */
539 IopFreeMiniPacket(Packet
);
542 /* Enter SEH to write back the values */
545 /* Write the values to caller */
548 *IoStatusBlock
= IoStatus
;
550 _SEH2_EXCEPT(ExSystemExceptionFilter())
552 /* Get the exception code */
553 Status
= _SEH2_GetExceptionCode();
558 /* Dereference the Object */
559 ObDereferenceObject(Queue
);
568 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle
,
569 IN PVOID CompletionKey
,
570 IN PVOID CompletionContext
,
571 IN NTSTATUS CompletionStatus
,
572 IN ULONG CompletionInformation
)
579 Status
= ObReferenceObjectByHandle(IoCompletionPortHandle
,
580 IO_COMPLETION_MODIFY_STATE
,
585 if (NT_SUCCESS(Status
))
587 /* Set the Completion */
588 Status
= IoSetIoCompletion(Queue
,
592 CompletionInformation
,
595 /* Dereference the object */
596 ObDereferenceObject(Queue
);