started moving tags to a private internal header
[reactos.git] / reactos / ntoskrnl / io / iocomp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/io/iocomp.c
5 * PURPOSE: No purpose listed.
6 *
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 POBJECT_TYPE IoCompletionType;
17
18 NPAGED_LOOKASIDE_LIST IoCompletionPacketLookaside;
19
20 static 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 /* FUNCTIONS *****************************************************************/
35
36 VOID
37 STDCALL
38 IopFreeIoCompletionPacket(PIO_COMPLETION_PACKET Packet)
39 {
40 PKPRCB Prcb = KeGetCurrentPrcb();
41 PNPAGED_LOOKASIDE_LIST List;
42
43 /* Use the P List */
44 List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[LookasideCompletionList].P;
45 List->L.TotalFrees++;
46
47 /* Check if the Free was within the Depth or not */
48 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
49 {
50 /* Let the balancer know */
51 List->L.FreeMisses++;
52
53 /* Use the L List */
54 List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[LookasideCompletionList].L;
55 List->L.TotalFrees++;
56
57 /* Check if the Free was within the Depth or not */
58 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
59 {
60 /* All lists failed, use the pool */
61 List->L.FreeMisses++;
62 ExFreePool(Packet);
63 }
64 }
65
66 /* The free was within dhe Depth */
67 InterlockedPushEntrySList(&List->L.ListHead, (PSINGLE_LIST_ENTRY)Packet);
68 }
69
70 VOID
71 STDCALL
72 IopDeleteIoCompletion(PVOID ObjectBody)
73 {
74 PKQUEUE Queue = ObjectBody;
75 PLIST_ENTRY FirstEntry;
76 PLIST_ENTRY CurrentEntry;
77 PIRP Irp;
78 PIO_COMPLETION_PACKET Packet;
79
80 DPRINT("IopDeleteIoCompletion()\n");
81
82 /* Rundown the Queue */
83 FirstEntry = KeRundownQueue(Queue);
84
85 /* Clean up the IRPs */
86 if (FirstEntry) {
87
88 CurrentEntry = FirstEntry;
89 do {
90
91 /* Get the Packet */
92 Packet = CONTAINING_RECORD(CurrentEntry, IO_COMPLETION_PACKET, ListEntry);
93
94 /* Go to next Entry */
95 CurrentEntry = CurrentEntry->Flink;
96
97 /* Check if it's part of an IRP, or a separate packet */
98 if (Packet->PacketType == IrpCompletionPacket)
99 {
100 /* Get the IRP and free it */
101 Irp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.ListEntry);
102 IoFreeIrp(Irp);
103 }
104 else
105 {
106 /* Use common routine */
107 IopFreeIoCompletionPacket(Packet);
108 }
109 } while (FirstEntry != CurrentEntry);
110 }
111 }
112
113 /*
114 * @implemented
115 */
116 NTSTATUS
117 STDCALL
118 IoSetIoCompletion(IN PVOID IoCompletion,
119 IN PVOID KeyContext,
120 IN PVOID ApcContext,
121 IN NTSTATUS IoStatus,
122 IN ULONG_PTR IoStatusInformation,
123 IN BOOLEAN Quota)
124 {
125 PKQUEUE Queue = (PKQUEUE)IoCompletion;
126 PNPAGED_LOOKASIDE_LIST List;
127 PKPRCB Prcb = KeGetCurrentPrcb();
128 PIO_COMPLETION_PACKET Packet;
129
130 /* Get the P List */
131 List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[LookasideCompletionList].P;
132
133 /* Try to allocate the Packet */
134 List->L.TotalAllocates++;
135 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
136
137 /* Check if that failed, use the L list if it did */
138 if (!Packet)
139 {
140 /* Let the balancer know */
141 List->L.AllocateMisses++;
142
143 /* Get L List */
144 List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[LookasideCompletionList].L;
145
146 /* Try to allocate the Packet */
147 List->L.TotalAllocates++;
148 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
149 }
150
151 /* Still failed, use pool */
152 if (!Packet)
153 {
154 /* Let the balancer know */
155 List->L.AllocateMisses++;
156
157 /* Allocate from Nonpaged Pool */
158 Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG);
159 }
160
161 /* Make sure we have one by now... */
162 if (Packet)
163 {
164 /* Set up the Packet */
165 Packet->PacketType = IrpMiniCompletionPacket;
166 Packet->Key = KeyContext;
167 Packet->Context = ApcContext;
168 Packet->IoStatus.Status = IoStatus;
169 Packet->IoStatus.Information = IoStatusInformation;
170
171 /* Insert the Queue */
172 KeInsertQueue(Queue, &Packet->ListEntry);
173 }
174 else
175 {
176 return STATUS_INSUFFICIENT_RESOURCES;
177 }
178
179 /* Return Success */
180 return STATUS_SUCCESS;
181 }
182
183 /*
184 * @unimplemented
185 */
186 NTSTATUS
187 STDCALL
188 IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,
189 IN PIRP Irp,
190 IN PIO_COMPLETION_ROUTINE CompletionRoutine,
191 IN PVOID Context,
192 IN BOOLEAN InvokeOnSuccess,
193 IN BOOLEAN InvokeOnError,
194 IN BOOLEAN InvokeOnCancel)
195 {
196 UNIMPLEMENTED;
197 return STATUS_NOT_IMPLEMENTED;
198 }
199
200 VOID
201 FASTCALL
202 IopInitIoCompletionImplementation(VOID)
203 {
204 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
205 UNICODE_STRING Name;
206
207 DPRINT1("Creating IoCompletion Object Type\n");
208
209 /* Initialize the Driver object type */
210 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
211 RtlInitUnicodeString(&Name, L"IoCompletion");
212 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
213 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KQUEUE);
214 ObjectTypeInitializer.PoolType = NonPagedPool;
215 ObjectTypeInitializer.ValidAccessMask = IO_COMPLETION_ALL_ACCESS;
216 ObjectTypeInitializer.UseDefaultObject = TRUE;
217 ObjectTypeInitializer.GenericMapping = IopCompletionMapping;
218 ObjectTypeInitializer.DeleteProcedure = IopDeleteIoCompletion;
219 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &IoCompletionType);
220 }
221
222 NTSTATUS
223 STDCALL
224 NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,
225 IN ACCESS_MASK DesiredAccess,
226 IN POBJECT_ATTRIBUTES ObjectAttributes,
227 IN ULONG NumberOfConcurrentThreads)
228 {
229 PKQUEUE Queue;
230 HANDLE hIoCompletionHandle;
231 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
232 NTSTATUS Status = STATUS_SUCCESS;
233
234 PAGED_CODE();
235
236 if (PreviousMode != KernelMode) {
237
238 _SEH_TRY {
239
240 ProbeForWrite(IoCompletionHandle,
241 sizeof(HANDLE),
242 sizeof(ULONG));
243 } _SEH_HANDLE {
244
245 Status = _SEH_GetExceptionCode();
246 } _SEH_END;
247
248 if (!NT_SUCCESS(Status)) {
249
250 return Status;
251 }
252 }
253
254 /* Create the Object */
255 Status = ObCreateObject(PreviousMode,
256 IoCompletionType,
257 ObjectAttributes,
258 PreviousMode,
259 NULL,
260 sizeof(KQUEUE),
261 0,
262 0,
263 (PVOID*)&Queue);
264
265 /* Check for success */
266 if (NT_SUCCESS(Status)) {
267
268 /* Initialize the Queue */
269 KeInitializeQueue(Queue, NumberOfConcurrentThreads);
270
271 /* Insert it */
272 Status = ObInsertObject(Queue,
273 NULL,
274 DesiredAccess,
275 0,
276 NULL,
277 &hIoCompletionHandle);
278 ObDereferenceObject(Queue);
279
280 if (NT_SUCCESS(Status)) {
281
282 _SEH_TRY {
283
284 *IoCompletionHandle = hIoCompletionHandle;
285 } _SEH_HANDLE {
286
287 Status = _SEH_GetExceptionCode();
288 } _SEH_END;
289 }
290 }
291
292 /* Return Status */
293 return Status;
294 }
295
296 NTSTATUS
297 STDCALL
298 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
299 IN ACCESS_MASK DesiredAccess,
300 IN POBJECT_ATTRIBUTES ObjectAttributes)
301 {
302 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
303 HANDLE hIoCompletionHandle;
304 NTSTATUS Status = STATUS_SUCCESS;
305
306 PAGED_CODE();
307
308 if(PreviousMode != KernelMode) {
309
310 _SEH_TRY {
311
312 ProbeForWrite(IoCompletionHandle,
313 sizeof(HANDLE),
314 sizeof(ULONG));
315 } _SEH_HANDLE {
316
317 Status = _SEH_GetExceptionCode();
318 } _SEH_END;
319
320 if(!NT_SUCCESS(Status)) {
321
322 return Status;
323 }
324 }
325
326 /* Open the Object */
327 Status = ObOpenObjectByName(ObjectAttributes,
328 IoCompletionType,
329 NULL,
330 PreviousMode,
331 DesiredAccess,
332 NULL,
333 &hIoCompletionHandle);
334
335 if (NT_SUCCESS(Status)) {
336
337 _SEH_TRY {
338
339 *IoCompletionHandle = hIoCompletionHandle;
340 } _SEH_HANDLE {
341
342 Status = _SEH_GetExceptionCode();
343 } _SEH_END;
344 }
345
346 /* Return Status */
347 return Status;
348 }
349
350
351 NTSTATUS
352 STDCALL
353 NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
354 IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
355 OUT PVOID IoCompletionInformation,
356 IN ULONG IoCompletionInformationLength,
357 OUT PULONG ResultLength OPTIONAL)
358 {
359 PKQUEUE Queue;
360 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
361 NTSTATUS Status = STATUS_SUCCESS;
362
363 PAGED_CODE();
364
365 /* Check buffers and parameters */
366 DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
367 IoCompletionInfoClass,
368 IoCompletionInformation,
369 IoCompletionInformationLength,
370 ResultLength,
371 PreviousMode,
372 &Status);
373 if(!NT_SUCCESS(Status)) {
374
375 DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status);
376 return Status;
377 }
378
379 /* Get the Object */
380 Status = ObReferenceObjectByHandle(IoCompletionHandle,
381 IO_COMPLETION_QUERY_STATE,
382 IoCompletionType,
383 PreviousMode,
384 (PVOID*)&Queue,
385 NULL);
386
387 /* Check for Success */
388 if (NT_SUCCESS(Status)) {
389
390 _SEH_TRY {
391
392 /* Return Info */
393 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = KeReadStateQueue(Queue);
394 ObDereferenceObject(Queue);
395
396 /* Return Result Length if needed */
397 if (ResultLength) {
398
399 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
400 }
401 } _SEH_HANDLE {
402
403 Status = _SEH_GetExceptionCode();
404 } _SEH_END;
405 }
406
407 /* Return Status */
408 return Status;
409 }
410
411 /*
412 * Dequeues an I/O completion message from an I/O completion object
413 */
414 NTSTATUS
415 STDCALL
416 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
417 OUT PVOID *CompletionKey,
418 OUT PVOID *CompletionContext,
419 OUT PIO_STATUS_BLOCK IoStatusBlock,
420 IN PLARGE_INTEGER Timeout OPTIONAL)
421 {
422 LARGE_INTEGER SafeTimeout;
423 PKQUEUE Queue;
424 PIO_COMPLETION_PACKET Packet;
425 PLIST_ENTRY ListEntry;
426 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
427 NTSTATUS Status = STATUS_SUCCESS;
428
429 PAGED_CODE();
430
431 if (PreviousMode != KernelMode) {
432
433 _SEH_TRY {
434
435 ProbeForWrite(CompletionKey,
436 sizeof(PVOID),
437 sizeof(ULONG));
438 ProbeForWrite(CompletionContext,
439 sizeof(PVOID),
440 sizeof(ULONG));
441 ProbeForWrite(IoStatusBlock,
442 sizeof(IO_STATUS_BLOCK),
443 sizeof(ULONG));
444 if (Timeout != NULL) {
445
446 ProbeForRead(Timeout,
447 sizeof(LARGE_INTEGER),
448 sizeof(ULONG));
449 SafeTimeout = *Timeout;
450 Timeout = &SafeTimeout;
451 }
452 } _SEH_HANDLE {
453
454 Status = _SEH_GetExceptionCode();
455 } _SEH_END;
456
457 if (!NT_SUCCESS(Status)) {
458
459 return Status;
460 }
461 }
462
463 /* Open the Object */
464 Status = ObReferenceObjectByHandle(IoCompletionHandle,
465 IO_COMPLETION_MODIFY_STATE,
466 IoCompletionType,
467 PreviousMode,
468 (PVOID*)&Queue,
469 NULL);
470
471 /* Check for success */
472 if (NT_SUCCESS(Status)) {
473
474 /* Remove queue */
475 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
476
477 /* If we got a timeout or user_apc back, return the status */
478 if ((NTSTATUS)ListEntry == STATUS_TIMEOUT || (NTSTATUS)ListEntry == STATUS_USER_APC) {
479
480 Status = (NTSTATUS)ListEntry;
481
482 } else {
483
484 /* Get the Packet Data */
485 Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry);
486
487 _SEH_TRY {
488
489 /* Check if this is piggybacked on an IRP */
490 if (Packet->PacketType == IrpCompletionPacket)
491 {
492 /* Get the IRP */
493 PIRP Irp = NULL;
494 Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
495
496 /* Return values to user */
497 *CompletionKey = Irp->Tail.CompletionKey;
498 *CompletionContext = Irp->Overlay.AsynchronousParameters.UserApcContext;
499 *IoStatusBlock = Packet->IoStatus;
500 IoFreeIrp(Irp);
501 }
502 else
503 {
504 /* This is a user-mode generated or API generated mini-packet */
505 *CompletionKey = Packet->Key;
506 *CompletionContext = Packet->Context;
507 *IoStatusBlock = Packet->IoStatus;
508 IopFreeIoCompletionPacket(Packet);
509 }
510
511 } _SEH_HANDLE {
512
513 Status = _SEH_GetExceptionCode();
514 } _SEH_END;
515 }
516
517 /* Dereference the Object */
518 ObDereferenceObject(Queue);
519 }
520
521 /* Return status */
522 return Status;
523 }
524
525 /*
526 * Queues an I/O completion message to an I/O completion object
527 */
528 NTSTATUS
529 STDCALL
530 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
531 IN PVOID CompletionKey,
532 IN PVOID CompletionContext,
533 IN NTSTATUS CompletionStatus,
534 IN ULONG CompletionInformation)
535 {
536 NTSTATUS Status;
537 PKQUEUE Queue;
538
539 PAGED_CODE();
540
541 /* Get the Object */
542 Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
543 IO_COMPLETION_MODIFY_STATE,
544 IoCompletionType,
545 ExGetPreviousMode(),
546 (PVOID*)&Queue,
547 NULL);
548
549 /* Check for Success */
550 if (NT_SUCCESS(Status)) {
551
552 /* Set the Completion */
553 Status = IoSetIoCompletion(Queue,
554 CompletionKey,
555 CompletionContext,
556 CompletionStatus,
557 CompletionInformation,
558 TRUE);
559 ObDereferenceObject(Queue);
560 }
561
562 /* Return status */
563 return Status;
564 }