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
, (PSINGLE_LIST_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
),
233 TAG('I', 'o', 'U', 's'));
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();
261 NTSTATUS Status
= STATUS_SUCCESS
;
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 /* Get the exception code */
276 Status
= _SEH2_GetExceptionCode();
280 /* Fail on exception */
281 if (!NT_SUCCESS(Status
)) return Status
;
284 /* Create the Object */
285 Status
= ObCreateObject(PreviousMode
,
294 if (NT_SUCCESS(Status
))
296 /* Initialize the Queue */
297 KeInitializeQueue(Queue
, NumberOfConcurrentThreads
);
300 Status
= ObInsertObject(Queue
,
305 &hIoCompletionHandle
);
306 if (NT_SUCCESS(Status
))
308 /* Protect writing the handle in SEH */
311 /* Write the handle back */
312 *IoCompletionHandle
= hIoCompletionHandle
;
314 _SEH2_EXCEPT(ExSystemExceptionFilter())
316 /* Get the exception code */
317 Status
= _SEH2_GetExceptionCode();
329 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle
,
330 IN ACCESS_MASK DesiredAccess
,
331 IN POBJECT_ATTRIBUTES ObjectAttributes
)
333 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
334 HANDLE hIoCompletionHandle
;
335 NTSTATUS Status
= STATUS_SUCCESS
;
338 /* Check if this was a user-mode call */
339 if (PreviousMode
!= KernelMode
)
341 /* Wrap probing in SEH */
344 /* Probe the handle */
345 ProbeForWriteHandle(IoCompletionHandle
);
347 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
349 /* Get the exception code */
350 Status
= _SEH2_GetExceptionCode();
354 /* Fail on exception */
355 if (!NT_SUCCESS(Status
)) return Status
;
358 /* Open the Object */
359 Status
= ObOpenObjectByName(ObjectAttributes
,
365 &hIoCompletionHandle
);
366 if (NT_SUCCESS(Status
))
368 /* Protect writing the handle in SEH */
371 /* Write the handle back */
372 *IoCompletionHandle
= hIoCompletionHandle
;
374 _SEH2_EXCEPT(ExSystemExceptionFilter())
376 /* Get the exception code */
377 Status
= _SEH2_GetExceptionCode();
388 NtQueryIoCompletion(IN HANDLE IoCompletionHandle
,
389 IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass
,
390 OUT PVOID IoCompletionInformation
,
391 IN ULONG IoCompletionInformationLength
,
392 OUT PULONG ResultLength OPTIONAL
)
395 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
396 NTSTATUS Status
= STATUS_SUCCESS
;
399 /* Check buffers and parameters */
400 Status
= DefaultQueryInfoBufferCheck(IoCompletionInformationClass
,
401 IoCompletionInfoClass
,
402 sizeof(IoCompletionInfoClass
) /
403 sizeof(IoCompletionInfoClass
[0]),
404 IoCompletionInformation
,
405 IoCompletionInformationLength
,
408 if (!NT_SUCCESS(Status
)) return Status
;
411 Status
= ObReferenceObjectByHandle(IoCompletionHandle
,
412 IO_COMPLETION_QUERY_STATE
,
417 if (NT_SUCCESS(Status
))
419 /* Protect write in SEH */
423 ((PIO_COMPLETION_BASIC_INFORMATION
)IoCompletionInformation
)->
424 Depth
= KeReadStateQueue(Queue
);
426 /* Return Result Length if needed */
429 *ResultLength
= sizeof(IO_COMPLETION_BASIC_INFORMATION
);
432 _SEH2_EXCEPT(ExSystemExceptionFilter())
434 /* Get exception code */
435 Status
= _SEH2_GetExceptionCode();
439 /* Dereference the queue */
440 ObDereferenceObject(Queue
);
449 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle
,
450 OUT PVOID
*KeyContext
,
451 OUT PVOID
*ApcContext
,
452 OUT PIO_STATUS_BLOCK IoStatusBlock
,
453 IN PLARGE_INTEGER Timeout OPTIONAL
)
455 LARGE_INTEGER SafeTimeout
;
457 PIOP_MINI_COMPLETION_PACKET Packet
;
458 PLIST_ENTRY ListEntry
;
459 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
460 NTSTATUS Status
= STATUS_SUCCESS
;
463 IO_STATUS_BLOCK IoStatus
;
466 /* Check if the call was from user mode */
467 if (PreviousMode
!= KernelMode
)
469 /* Protect probes in SEH */
472 /* Probe the pointers */
473 ProbeForWritePointer(KeyContext
);
474 ProbeForWritePointer(ApcContext
);
476 /* Probe the I/O Status Block */
477 ProbeForWriteIoStatusBlock(IoStatusBlock
);
480 /* Probe and capture the timeout */
481 SafeTimeout
= ProbeForReadLargeInteger(Timeout
);
482 Timeout
= &SafeTimeout
;
485 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
487 /* Get the exception code */
488 Status
= _SEH2_GetExceptionCode();
492 /* Fail on exception */
493 if (!NT_SUCCESS(Status
)) return Status
;
496 /* Open the Object */
497 Status
= ObReferenceObjectByHandle(IoCompletionHandle
,
498 IO_COMPLETION_MODIFY_STATE
,
503 if (NT_SUCCESS(Status
))
506 ListEntry
= KeRemoveQueue(Queue
, PreviousMode
, Timeout
);
508 /* If we got a timeout or user_apc back, return the status */
509 if (((NTSTATUS
)ListEntry
== STATUS_TIMEOUT
) ||
510 ((NTSTATUS
)ListEntry
== STATUS_USER_APC
))
512 /* Set this as the status */
513 Status
= (NTSTATUS
)ListEntry
;
517 /* Get the Packet Data */
518 Packet
= CONTAINING_RECORD(ListEntry
,
519 IOP_MINI_COMPLETION_PACKET
,
522 /* Check if this is piggybacked on an IRP */
523 if (Packet
->PacketType
== IopCompletionPacketIrp
)
526 Irp
= CONTAINING_RECORD(ListEntry
,
528 Tail
.Overlay
.ListEntry
);
531 Key
= Irp
->Tail
.CompletionKey
;
532 Apc
= Irp
->Overlay
.AsynchronousParameters
.UserApcContext
;
533 IoStatus
= Irp
->IoStatus
;
541 Key
= Packet
->KeyContext
;
542 Apc
= Packet
->ApcContext
;
543 IoStatus
.Status
= Packet
->IoStatus
;
544 IoStatus
.Information
= Packet
->IoStatusInformation
;
546 /* Free the packet */
547 IopFreeMiniPacket(Packet
);
550 /* Enter SEH to write back the values */
553 /* Write the values to caller */
556 *IoStatusBlock
= IoStatus
;
558 _SEH2_EXCEPT(ExSystemExceptionFilter())
560 /* Get the exception code */
561 Status
= _SEH2_GetExceptionCode();
566 /* Dereference the Object */
567 ObDereferenceObject(Queue
);
576 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle
,
577 IN PVOID CompletionKey
,
578 IN PVOID CompletionContext
,
579 IN NTSTATUS CompletionStatus
,
580 IN ULONG CompletionInformation
)
587 Status
= ObReferenceObjectByHandle(IoCompletionPortHandle
,
588 IO_COMPLETION_MODIFY_STATE
,
593 if (NT_SUCCESS(Status
))
595 /* Set the Completion */
596 Status
= IoSetIoCompletion(Queue
,
600 CompletionInformation
,
603 /* Dereference the object */
604 ObDereferenceObject(Queue
);