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