- use inlined probing macros for basic types
[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 DPRINT("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 ProbeForWriteHandle(IoCompletionHandle);
241 } _SEH_HANDLE {
242
243 Status = _SEH_GetExceptionCode();
244 } _SEH_END;
245
246 if (!NT_SUCCESS(Status)) {
247
248 return Status;
249 }
250 }
251
252 /* Create the Object */
253 Status = ObCreateObject(PreviousMode,
254 IoCompletionType,
255 ObjectAttributes,
256 PreviousMode,
257 NULL,
258 sizeof(KQUEUE),
259 0,
260 0,
261 (PVOID*)&Queue);
262
263 /* Check for success */
264 if (NT_SUCCESS(Status)) {
265
266 /* Initialize the Queue */
267 KeInitializeQueue(Queue, NumberOfConcurrentThreads);
268
269 /* Insert it */
270 Status = ObInsertObject(Queue,
271 NULL,
272 DesiredAccess,
273 0,
274 NULL,
275 &hIoCompletionHandle);
276 ObDereferenceObject(Queue);
277
278 if (NT_SUCCESS(Status)) {
279
280 _SEH_TRY {
281
282 *IoCompletionHandle = hIoCompletionHandle;
283 } _SEH_HANDLE {
284
285 Status = _SEH_GetExceptionCode();
286 } _SEH_END;
287 }
288 }
289
290 /* Return Status */
291 return Status;
292 }
293
294 NTSTATUS
295 STDCALL
296 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
297 IN ACCESS_MASK DesiredAccess,
298 IN POBJECT_ATTRIBUTES ObjectAttributes)
299 {
300 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
301 HANDLE hIoCompletionHandle;
302 NTSTATUS Status = STATUS_SUCCESS;
303
304 PAGED_CODE();
305
306 if(PreviousMode != KernelMode) {
307
308 _SEH_TRY {
309
310 ProbeForWriteHandle(IoCompletionHandle);
311 } _SEH_HANDLE {
312
313 Status = _SEH_GetExceptionCode();
314 } _SEH_END;
315
316 if(!NT_SUCCESS(Status)) {
317
318 return Status;
319 }
320 }
321
322 /* Open the Object */
323 Status = ObOpenObjectByName(ObjectAttributes,
324 IoCompletionType,
325 NULL,
326 PreviousMode,
327 DesiredAccess,
328 NULL,
329 &hIoCompletionHandle);
330
331 if (NT_SUCCESS(Status)) {
332
333 _SEH_TRY {
334
335 *IoCompletionHandle = hIoCompletionHandle;
336 } _SEH_HANDLE {
337
338 Status = _SEH_GetExceptionCode();
339 } _SEH_END;
340 }
341
342 /* Return Status */
343 return Status;
344 }
345
346
347 NTSTATUS
348 STDCALL
349 NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
350 IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
351 OUT PVOID IoCompletionInformation,
352 IN ULONG IoCompletionInformationLength,
353 OUT PULONG ResultLength OPTIONAL)
354 {
355 PKQUEUE Queue;
356 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
357 NTSTATUS Status = STATUS_SUCCESS;
358
359 PAGED_CODE();
360
361 /* Check buffers and parameters */
362 DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
363 IoCompletionInfoClass,
364 IoCompletionInformation,
365 IoCompletionInformationLength,
366 ResultLength,
367 PreviousMode,
368 &Status);
369 if(!NT_SUCCESS(Status)) {
370
371 DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status);
372 return Status;
373 }
374
375 /* Get the Object */
376 Status = ObReferenceObjectByHandle(IoCompletionHandle,
377 IO_COMPLETION_QUERY_STATE,
378 IoCompletionType,
379 PreviousMode,
380 (PVOID*)&Queue,
381 NULL);
382
383 /* Check for Success */
384 if (NT_SUCCESS(Status)) {
385
386 _SEH_TRY {
387
388 /* Return Info */
389 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = KeReadStateQueue(Queue);
390 ObDereferenceObject(Queue);
391
392 /* Return Result Length if needed */
393 if (ResultLength) {
394
395 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
396 }
397 } _SEH_HANDLE {
398
399 Status = _SEH_GetExceptionCode();
400 } _SEH_END;
401 }
402
403 /* Return Status */
404 return Status;
405 }
406
407 /*
408 * Dequeues an I/O completion message from an I/O completion object
409 */
410 NTSTATUS
411 STDCALL
412 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
413 OUT PVOID *CompletionKey,
414 OUT PVOID *CompletionContext,
415 OUT PIO_STATUS_BLOCK IoStatusBlock,
416 IN PLARGE_INTEGER Timeout OPTIONAL)
417 {
418 LARGE_INTEGER SafeTimeout;
419 PKQUEUE Queue;
420 PIO_COMPLETION_PACKET Packet;
421 PLIST_ENTRY ListEntry;
422 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
423 NTSTATUS Status = STATUS_SUCCESS;
424
425 PAGED_CODE();
426
427 if (PreviousMode != KernelMode) {
428
429 _SEH_TRY {
430
431 ProbeForWritePointer(CompletionKey);
432 ProbeForWritePointer(CompletionContext);
433 ProbeForWrite(IoStatusBlock,
434 sizeof(IO_STATUS_BLOCK),
435 sizeof(ULONG));
436 if (Timeout != NULL) {
437
438 SafeTimeout = ProbeForReadLargeInteger(Timeout);
439 Timeout = &SafeTimeout;
440 }
441 } _SEH_HANDLE {
442
443 Status = _SEH_GetExceptionCode();
444 } _SEH_END;
445
446 if (!NT_SUCCESS(Status)) {
447
448 return Status;
449 }
450 }
451
452 /* Open the Object */
453 Status = ObReferenceObjectByHandle(IoCompletionHandle,
454 IO_COMPLETION_MODIFY_STATE,
455 IoCompletionType,
456 PreviousMode,
457 (PVOID*)&Queue,
458 NULL);
459
460 /* Check for success */
461 if (NT_SUCCESS(Status)) {
462
463 /* Remove queue */
464 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
465
466 /* If we got a timeout or user_apc back, return the status */
467 if ((NTSTATUS)ListEntry == STATUS_TIMEOUT || (NTSTATUS)ListEntry == STATUS_USER_APC) {
468
469 Status = (NTSTATUS)ListEntry;
470
471 } else {
472
473 /* Get the Packet Data */
474 Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry);
475
476 _SEH_TRY {
477
478 /* Check if this is piggybacked on an IRP */
479 if (Packet->PacketType == IrpCompletionPacket)
480 {
481 /* Get the IRP */
482 PIRP Irp = NULL;
483 Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
484
485 /* Return values to user */
486 *CompletionKey = Irp->Tail.CompletionKey;
487 *CompletionContext = Irp->Overlay.AsynchronousParameters.UserApcContext;
488 *IoStatusBlock = Packet->IoStatus;
489 IoFreeIrp(Irp);
490 }
491 else
492 {
493 /* This is a user-mode generated or API generated mini-packet */
494 *CompletionKey = Packet->Key;
495 *CompletionContext = Packet->Context;
496 *IoStatusBlock = Packet->IoStatus;
497 IopFreeIoCompletionPacket(Packet);
498 }
499
500 } _SEH_HANDLE {
501
502 Status = _SEH_GetExceptionCode();
503 } _SEH_END;
504 }
505
506 /* Dereference the Object */
507 ObDereferenceObject(Queue);
508 }
509
510 /* Return status */
511 return Status;
512 }
513
514 /*
515 * Queues an I/O completion message to an I/O completion object
516 */
517 NTSTATUS
518 STDCALL
519 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
520 IN PVOID CompletionKey,
521 IN PVOID CompletionContext,
522 IN NTSTATUS CompletionStatus,
523 IN ULONG CompletionInformation)
524 {
525 NTSTATUS Status;
526 PKQUEUE Queue;
527
528 PAGED_CODE();
529
530 /* Get the Object */
531 Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
532 IO_COMPLETION_MODIFY_STATE,
533 IoCompletionType,
534 ExGetPreviousMode(),
535 (PVOID*)&Queue,
536 NULL);
537
538 /* Check for Success */
539 if (NT_SUCCESS(Status)) {
540
541 /* Set the Completion */
542 Status = IoSetIoCompletion(Queue,
543 CompletionKey,
544 CompletionContext,
545 CompletionStatus,
546 CompletionInformation,
547 TRUE);
548 ObDereferenceObject(Queue);
549 }
550
551 /* Return status */
552 return Status;
553 }