xref: /reactos/ntoskrnl/io/iomgr/iocomp.c (revision d0d86ab5)
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
IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)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
IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet)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
IopDeleteIoCompletion(PVOID ObjectBody)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
IoSetIoCompletion(IN PVOID IoCompletion,IN PVOID KeyContext,IN PVOID ApcContext,IN NTSTATUS IoStatus,IN ULONG_PTR IoStatusInformation,IN BOOLEAN Quota)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
IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PIO_COMPLETION_ROUTINE CompletionRoutine,IN PVOID Context,IN BOOLEAN InvokeOnSuccess,IN BOOLEAN InvokeOnError,IN BOOLEAN InvokeOnCancel)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
NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG NumberOfConcurrentThreads)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
NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes)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
NtQueryIoCompletion(IN HANDLE IoCompletionHandle,IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,OUT PVOID IoCompletionInformation,IN ULONG IoCompletionInformationLength,OUT PULONG ResultLength OPTIONAL)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
NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,OUT PVOID * KeyContext,OUT PVOID * ApcContext,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER Timeout OPTIONAL)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
NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,IN PVOID CompletionKey,IN PVOID CompletionContext,IN NTSTATUS CompletionStatus,IN ULONG CompletionInformation)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