xref: /reactos/ntoskrnl/io/iomgr/iocomp.c (revision 3e42f7b4)
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