Merge 36852, 37322, 37333, 37334, 43428, 43451, 44259, 46404 from amd64 branch.
[reactos.git] / reactos / ntoskrnl / io / iomgr / iocomp.c
1 /*
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)
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 | IO_COMPLETION_QUERY_STATE,
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 }