832a6324b8de228f25d5acc9ce3f1360298e8516
[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, (PSINGLE_LIST_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 TAG('I', 'o', 'U', 's'));
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 = STATUS_SUCCESS;
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 /* Get the exception code */
276 Status = _SEH2_GetExceptionCode();
277 }
278 _SEH2_END;
279
280 /* Fail on exception */
281 if (!NT_SUCCESS(Status)) return Status;
282 }
283
284 /* Create the Object */
285 Status = ObCreateObject(PreviousMode,
286 IoCompletionType,
287 ObjectAttributes,
288 PreviousMode,
289 NULL,
290 sizeof(KQUEUE),
291 0,
292 0,
293 (PVOID*)&Queue);
294 if (NT_SUCCESS(Status))
295 {
296 /* Initialize the Queue */
297 KeInitializeQueue(Queue, NumberOfConcurrentThreads);
298
299 /* Insert it */
300 Status = ObInsertObject(Queue,
301 NULL,
302 DesiredAccess,
303 0,
304 NULL,
305 &hIoCompletionHandle);
306 if (NT_SUCCESS(Status))
307 {
308 /* Protect writing the handle in SEH */
309 _SEH2_TRY
310 {
311 /* Write the handle back */
312 *IoCompletionHandle = hIoCompletionHandle;
313 }
314 _SEH2_EXCEPT(ExSystemExceptionFilter())
315 {
316 /* Get the exception code */
317 Status = _SEH2_GetExceptionCode();
318 }
319 _SEH2_END;
320 }
321 }
322
323 /* Return Status */
324 return Status;
325 }
326
327 NTSTATUS
328 NTAPI
329 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
330 IN ACCESS_MASK DesiredAccess,
331 IN POBJECT_ATTRIBUTES ObjectAttributes)
332 {
333 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
334 HANDLE hIoCompletionHandle;
335 NTSTATUS Status = STATUS_SUCCESS;
336 PAGED_CODE();
337
338 /* Check if this was a user-mode call */
339 if (PreviousMode != KernelMode)
340 {
341 /* Wrap probing in SEH */
342 _SEH2_TRY
343 {
344 /* Probe the handle */
345 ProbeForWriteHandle(IoCompletionHandle);
346 }
347 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
348 {
349 /* Get the exception code */
350 Status = _SEH2_GetExceptionCode();
351 }
352 _SEH2_END;
353
354 /* Fail on exception */
355 if (!NT_SUCCESS(Status)) return Status;
356 }
357
358 /* Open the Object */
359 Status = ObOpenObjectByName(ObjectAttributes,
360 IoCompletionType,
361 PreviousMode,
362 NULL,
363 DesiredAccess,
364 NULL,
365 &hIoCompletionHandle);
366 if (NT_SUCCESS(Status))
367 {
368 /* Protect writing the handle in SEH */
369 _SEH2_TRY
370 {
371 /* Write the handle back */
372 *IoCompletionHandle = hIoCompletionHandle;
373 }
374 _SEH2_EXCEPT(ExSystemExceptionFilter())
375 {
376 /* Get the exception code */
377 Status = _SEH2_GetExceptionCode();
378 }
379 _SEH2_END;
380 }
381
382 /* Return Status */
383 return Status;
384 }
385
386 NTSTATUS
387 NTAPI
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)
393 {
394 PKQUEUE Queue;
395 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
396 NTSTATUS Status = STATUS_SUCCESS;
397 PAGED_CODE();
398
399 /* Check buffers and parameters */
400 Status = DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
401 IoCompletionInfoClass,
402 sizeof(IoCompletionInfoClass) /
403 sizeof(IoCompletionInfoClass[0]),
404 IoCompletionInformation,
405 IoCompletionInformationLength,
406 ResultLength,
407 PreviousMode);
408 if (!NT_SUCCESS(Status)) return Status;
409
410 /* Get the Object */
411 Status = ObReferenceObjectByHandle(IoCompletionHandle,
412 IO_COMPLETION_QUERY_STATE,
413 IoCompletionType,
414 PreviousMode,
415 (PVOID*)&Queue,
416 NULL);
417 if (NT_SUCCESS(Status))
418 {
419 /* Protect write in SEH */
420 _SEH2_TRY
421 {
422 /* Return Info */
423 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->
424 Depth = KeReadStateQueue(Queue);
425
426 /* Return Result Length if needed */
427 if (ResultLength)
428 {
429 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
430 }
431 }
432 _SEH2_EXCEPT(ExSystemExceptionFilter())
433 {
434 /* Get exception code */
435 Status = _SEH2_GetExceptionCode();
436 }
437 _SEH2_END;
438
439 /* Dereference the queue */
440 ObDereferenceObject(Queue);
441 }
442
443 /* Return Status */
444 return Status;
445 }
446
447 NTSTATUS
448 NTAPI
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)
454 {
455 LARGE_INTEGER SafeTimeout;
456 PKQUEUE Queue;
457 PIOP_MINI_COMPLETION_PACKET Packet;
458 PLIST_ENTRY ListEntry;
459 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
460 NTSTATUS Status = STATUS_SUCCESS;
461 PIRP Irp;
462 PVOID Apc, Key;
463 IO_STATUS_BLOCK IoStatus;
464 PAGED_CODE();
465
466 /* Check if the call was from user mode */
467 if (PreviousMode != KernelMode)
468 {
469 /* Protect probes in SEH */
470 _SEH2_TRY
471 {
472 /* Probe the pointers */
473 ProbeForWritePointer(KeyContext);
474 ProbeForWritePointer(ApcContext);
475
476 /* Probe the I/O Status Block */
477 ProbeForWriteIoStatusBlock(IoStatusBlock);
478 if (Timeout)
479 {
480 /* Probe and capture the timeout */
481 SafeTimeout = ProbeForReadLargeInteger(Timeout);
482 Timeout = &SafeTimeout;
483 }
484 }
485 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
486 {
487 /* Get the exception code */
488 Status = _SEH2_GetExceptionCode();
489 }
490 _SEH2_END;
491
492 /* Fail on exception */
493 if (!NT_SUCCESS(Status)) return Status;
494 }
495
496 /* Open the Object */
497 Status = ObReferenceObjectByHandle(IoCompletionHandle,
498 IO_COMPLETION_MODIFY_STATE,
499 IoCompletionType,
500 PreviousMode,
501 (PVOID*)&Queue,
502 NULL);
503 if (NT_SUCCESS(Status))
504 {
505 /* Remove queue */
506 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
507
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))
511 {
512 /* Set this as the status */
513 Status = (NTSTATUS)ListEntry;
514 }
515 else
516 {
517 /* Get the Packet Data */
518 Packet = CONTAINING_RECORD(ListEntry,
519 IOP_MINI_COMPLETION_PACKET,
520 ListEntry);
521
522 /* Check if this is piggybacked on an IRP */
523 if (Packet->PacketType == IopCompletionPacketIrp)
524 {
525 /* Get the IRP */
526 Irp = CONTAINING_RECORD(ListEntry,
527 IRP,
528 Tail.Overlay.ListEntry);
529
530 /* Save values */
531 Key = Irp->Tail.CompletionKey;
532 Apc = Irp->Overlay.AsynchronousParameters.UserApcContext;
533 IoStatus = Irp->IoStatus;
534
535 /* Free the IRP */
536 IoFreeIrp(Irp);
537 }
538 else
539 {
540 /* Save values */
541 Key = Packet->KeyContext;
542 Apc = Packet->ApcContext;
543 IoStatus.Status = Packet->IoStatus;
544 IoStatus.Information = Packet->IoStatusInformation;
545
546 /* Free the packet */
547 IopFreeMiniPacket(Packet);
548 }
549
550 /* Enter SEH to write back the values */
551 _SEH2_TRY
552 {
553 /* Write the values to caller */
554 *ApcContext = Apc;
555 *KeyContext = Key;
556 *IoStatusBlock = IoStatus;
557 }
558 _SEH2_EXCEPT(ExSystemExceptionFilter())
559 {
560 /* Get the exception code */
561 Status = _SEH2_GetExceptionCode();
562 }
563 _SEH2_END;
564 }
565
566 /* Dereference the Object */
567 ObDereferenceObject(Queue);
568 }
569
570 /* Return status */
571 return Status;
572 }
573
574 NTSTATUS
575 NTAPI
576 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
577 IN PVOID CompletionKey,
578 IN PVOID CompletionContext,
579 IN NTSTATUS CompletionStatus,
580 IN ULONG CompletionInformation)
581 {
582 NTSTATUS Status;
583 PKQUEUE Queue;
584 PAGED_CODE();
585
586 /* Get the Object */
587 Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
588 IO_COMPLETION_MODIFY_STATE,
589 IoCompletionType,
590 ExGetPreviousMode(),
591 (PVOID*)&Queue,
592 NULL);
593 if (NT_SUCCESS(Status))
594 {
595 /* Set the Completion */
596 Status = IoSetIoCompletion(Queue,
597 CompletionKey,
598 CompletionContext,
599 CompletionStatus,
600 CompletionInformation,
601 TRUE);
602
603 /* Dereference the object */
604 ObDereferenceObject(Queue);
605 }
606
607 /* Return status */
608 return Status;
609 }