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