Release the rmap list lock after cleaning the head entry in MmDeleteAllRmaps. This...
[reactos.git] / reactos / ntoskrnl / ke / dpc.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/dpc.c
6 * PURPOSE: Handle DPCs (Delayed Procedure Calls)
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Philip Susi (phreak@iag.net)
10 * Eric Kohl (ekohl@abo.rhein-zeitung.de)
11 * Alex Ionescu (alex@relsoft.net)
12 */
13
14 /*
15 * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
16 */
17
18 /* INCLUDES ***************************************************************/
19
20 #include <ntoskrnl.h>
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 #if defined (ALLOC_PRAGMA)
25 #pragma alloc_text(INIT, KeInitDpc)
26 #endif
27
28
29 /* TYPES *******************************************************************/
30
31 #define MAX_QUANTUM 0x7F
32
33 /* FUNCTIONS ****************************************************************/
34
35 /*
36 * FUNCTION: Initialize DPC handling
37 */
38 VOID
39 INIT_FUNCTION
40 NTAPI
41 KeInitDpc(PKPRCB Prcb)
42 {
43 InitializeListHead(&Prcb->DpcData[0].DpcListHead);
44 #if 0
45 /*
46 * FIXME:
47 * Prcb->DpcEvent is a NULL pointer.
48 */
49 KeInitializeEvent(Prcb->DpcEvent, 0, 0);
50 #endif
51 KeInitializeSpinLock(&Prcb->DpcData[0].DpcLock);
52 Prcb->MaximumDpcQueueDepth = 4;
53 Prcb->MinimumDpcRate = 3;
54 Prcb->DpcData[0].DpcQueueDepth = 0;
55 }
56
57 /*
58 * @implemented
59 */
60 VOID
61 STDCALL
62 KeInitializeThreadedDpc(PKDPC Dpc,
63 PKDEFERRED_ROUTINE DeferredRoutine,
64 PVOID DeferredContext)
65 /*
66 * FUNCTION:
67 * Initalizes a Threaded DPC and registers the DeferredRoutine for it.
68 * ARGUMENTS:
69 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
70 * DeferredRoutine = Pointer to associated DPC callback routine.
71 * DeferredContext = Parameter to be passed to the callback routine.
72 * NOTE: Callers can be running at any IRQL.
73 */
74 {
75 DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
76 Dpc->Type = ThreadedDpcObject;
77 Dpc->Number= 0;
78 Dpc->Importance= MediumImportance;
79 Dpc->DeferredRoutine = DeferredRoutine;
80 Dpc->DeferredContext = DeferredContext;
81 Dpc->DpcData = NULL;
82 }
83
84 /*
85 * @implemented
86 *
87 * FUNCTION:
88 * Initalizes a DPC and registers the DeferredRoutine for it.
89 * ARGUMENTS:
90 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
91 * DeferredRoutine = Pointer to associated DPC callback routine.
92 * DeferredContext = Parameter to be passed to the callback routine.
93 * NOTE: Callers can be running at any IRQL.
94 */
95 VOID
96 STDCALL
97 KeInitializeDpc(PKDPC Dpc,
98 PKDEFERRED_ROUTINE DeferredRoutine,
99 PVOID DeferredContext)
100 {
101 DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
102 Dpc->Type = DpcObject;
103 Dpc->Number= 0;
104 Dpc->Importance= MediumImportance;
105 Dpc->DeferredRoutine = DeferredRoutine;
106 Dpc->DeferredContext = DeferredContext;
107 Dpc->DpcData = NULL;
108 }
109
110 /*
111 * @implemented
112 *
113 * FUNCTION:
114 * Queues a DPC for execution when the IRQL of a processor
115 * drops below DISPATCH_LEVEL
116 * ARGUMENTS:
117 * Dpc = Pointed to a DPC Object Initalized by KeInitializeDpc.
118 * SystemArgument1 = Driver Determined context data
119 * SystemArgument2 = Driver Determined context data
120 * RETURNS:
121 * TRUE if the DPC object wasn't already in the queue
122 * FALSE otherwise
123 * NOTES:
124 * If there is currently a DPC active on the target processor, or a DPC
125 * interrupt has already been requested on the target processor when a
126 * DPC is queued, then no further action is necessary. The DPC will be
127 * executed on the target processor when its queue entry is processed.
128 *
129 * If there is not a DPC active on the target processor and a DPC interrupt
130 * has not been requested on the target processor, then the exact treatment
131 * of the DPC is dependent on whether the host system is a UP system or an
132 * MP system.
133 *
134 * UP system.
135 * ----------
136 * If the DPC is of medium or high importance, the current DPC queue depth
137 * is greater than the maximum target depth, or current DPC request rate is
138 * less the minimum target rate, then a DPC interrupt is requested on the
139 * host processor and the DPC will be processed when the interrupt occurs.
140 * Otherwise, no DPC interupt is requested and the DPC execution will be
141 * delayed until the DPC queue depth is greater that the target depth or the
142 * minimum DPC rate is less than the target rate.
143 *
144 * MP system.
145 * ----------
146 * If the DPC is being queued to another processor and the depth of the DPC
147 * queue on the target processor is greater than the maximum target depth or
148 * the DPC is of high importance, then a DPC interrupt is requested on the
149 * target processor and the DPC will be processed when the interrupt occurs.
150 * Otherwise, the DPC execution will be delayed on the target processor until
151 * the DPC queue depth on the target processor is greater that the maximum
152 * target depth or the minimum DPC rate on the target processor is less than
153 * the target mimimum rate.
154 *
155 * If the DPC is being queued to the current processor and the DPC is not of
156 * low importance, the current DPC queue depth is greater than the maximum
157 * target depth, or the minimum DPC rate is less than the minimum target rate,
158 * then a DPC interrupt is request on the current processor and the DPV will
159 * be processed whne the interrupt occurs. Otherwise, no DPC interupt is
160 * requested and the DPC execution will be delayed until the DPC queue depth
161 * is greater that the target depth or the minimum DPC rate is less than the
162 * target rate.
163 */
164 BOOLEAN
165 STDCALL
166 KeInsertQueueDpc(PKDPC Dpc,
167 PVOID SystemArgument1,
168 PVOID SystemArgument2)
169 {
170 KIRQL OldIrql;
171 PKPRCB Prcb;
172
173 DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
174 Dpc, SystemArgument1, SystemArgument2);
175
176 /* Check IRQL and Raise it to HIGH_LEVEL */
177 ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL);
178 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
179
180 /* Check if this is a Thread DPC, which we don't support (yet) */
181 if (Dpc->Type == ThreadedDpcObject) {
182 return FALSE;
183 KeLowerIrql(OldIrql);
184 }
185
186 #ifdef CONFIG_SMP
187 /* Get the right PCR for this CPU */
188 if (Dpc->Number >= MAXIMUM_PROCESSORS) {
189
190 ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors);
191 Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + ((Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE)))->Prcb;
192
193 } else {
194
195 ASSERT (Dpc->Number < KeNumberProcessors);
196 Prcb = KeGetCurrentPrcb();
197 Dpc->Number = KeGetCurrentProcessorNumber();
198 }
199
200 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
201 #else
202 Prcb = ((PKPCR)KPCR_BASE)->Prcb;
203 #endif
204
205 /* Get the DPC Data */
206 if (InterlockedCompareExchangeUL(&Dpc->DpcData, &Prcb->DpcData[0].DpcLock, 0)) {
207
208 DPRINT("DPC Already Inserted\n");
209 #ifdef CONFIG_SMP
210 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
211 #endif
212 KeLowerIrql(OldIrql);
213 return(FALSE);
214 }
215
216 /* Make sure the lists are free if the Queue is 0 */
217 if (Prcb->DpcData[0].DpcQueueDepth == 0) {
218
219 ASSERT(IsListEmpty(&Prcb->DpcData[0].DpcListHead));
220 } else {
221
222 ASSERT(!IsListEmpty(&Prcb->DpcData[0].DpcListHead));
223 }
224
225 /* Now we can play with the DPC safely */
226 Dpc->SystemArgument1=SystemArgument1;
227 Dpc->SystemArgument2=SystemArgument2;
228 Prcb->DpcData[0].DpcQueueDepth++;
229 Prcb->DpcData[0].DpcCount++;
230
231 /* Insert the DPC into the list. HighImportance DPCs go at the beginning */
232 if (Dpc->Importance == HighImportance) {
233
234 InsertHeadList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
235 } else {
236
237 InsertTailList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
238 }
239 DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
240
241 /* Make sure a DPC isn't executing already and respect rules outlined above. */
242 if ((!Prcb->DpcRoutineActive) && (!Prcb->DpcInterruptRequested)) {
243
244 #ifdef CONFIG_SMP
245 /* Check if this is the same CPU */
246 if (Prcb != KeGetCurrentPrcb()) {
247
248 /* Send IPI if High Importance */
249 if ((Dpc->Importance == HighImportance) ||
250 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth)) {
251
252 if (Dpc->Number >= MAXIMUM_PROCESSORS) {
253
254 KiIpiSendRequest(1 << (Dpc->Number - MAXIMUM_PROCESSORS), IPI_DPC);
255 } else {
256
257 KiIpiSendRequest(1 << Dpc->Number, IPI_DPC);
258 }
259
260 }
261 } else {
262
263 /* Request an Interrupt only if the DPC isn't low priority */
264 if ((Dpc->Importance != LowImportance) ||
265 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
266 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
267
268 /* Request Interrupt */
269 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
270 Prcb->DpcInterruptRequested = TRUE;
271 }
272 }
273 #else
274 DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Prcb->DpcData[0].DpcQueueDepth, Prcb->MaximumDpcQueueDepth, Prcb->DpcRequestRate, Prcb->MinimumDpcRate);
275
276 /* Request an Interrupt only if the DPC isn't low priority */
277 if ((Dpc->Importance != LowImportance) ||
278 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
279 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
280
281 /* Request Interrupt */
282 DPRINT("Requesting Interrupt\n");
283 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
284 Prcb->DpcInterruptRequested = TRUE;
285 }
286 #endif
287 }
288 #ifdef CONFIG_SMP
289 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
290 #endif
291 /* Lower IRQL */
292 KeLowerIrql(OldIrql);
293 return(TRUE);
294 }
295
296 /*
297 * @implemented
298 *
299 * FUNCTION:
300 * Removes DPC object from the system dpc queue
301 * ARGUMENTS:
302 * Dpc = Pointer to DPC to remove from the queue.
303 * RETURNS:
304 * TRUE if the DPC was in the queue
305 * FALSE otherwise
306 */
307 BOOLEAN
308 STDCALL
309 KeRemoveQueueDpc(PKDPC Dpc)
310 {
311 BOOLEAN WasInQueue;
312 KIRQL OldIrql;
313
314 /* Raise IRQL */
315 DPRINT("Removing DPC: %x\n", Dpc);
316 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
317 #ifdef CONFIG_SMP
318 KiAcquireSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
319 #endif
320
321 /* First make sure the DPC lock isn't being held */
322 WasInQueue = Dpc->DpcData ? TRUE : FALSE;
323 if (Dpc->DpcData) {
324
325 /* Remove the DPC */
326 ((PKDPC_DATA)Dpc->DpcData)->DpcQueueDepth--;
327 RemoveEntryList(&Dpc->DpcListEntry);
328
329 }
330 #ifdef CONFIG_SMP
331 KiReleaseSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
332 #endif
333
334 /* Return if the DPC was in the queue or not */
335 KeLowerIrql(OldIrql);
336 return WasInQueue;
337 }
338
339 /*
340 * @implemented
341 */
342 VOID
343 STDCALL
344 KeFlushQueuedDpcs(VOID)
345 /*
346 * FUNCTION:
347 * Called to Deliver DPCs if any are pending.
348 * NOTES:
349 * Called when deleting a Driver.
350 */
351 {
352 /* Request an interrupt if needed */
353 if (KeGetCurrentPrcb()->DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
354 }
355
356 /*
357 * @implemented
358 */
359 BOOLEAN
360 STDCALL
361 KeIsExecutingDpc(
362 VOID
363 )
364 {
365 /* Return if the Dpc Routine is active */
366 return KeGetCurrentPrcb()->DpcRoutineActive;
367 }
368
369 /*
370 * FUNCTION: Specifies the DPCs importance
371 * ARGUMENTS:
372 * Dpc = Initalizes DPC
373 * Importance = DPC importance
374 * RETURNS: None
375 *
376 * @implemented
377 */
378 VOID
379 STDCALL
380 KeSetImportanceDpc (IN PKDPC Dpc,
381 IN KDPC_IMPORTANCE Importance)
382 {
383 /* Set the DPC Importance */
384 Dpc->Importance = Importance;
385 }
386
387 /*
388 * @implemented
389 *
390 * FUNCTION: Specifies on which processor the DPC will run
391 * ARGUMENTS:
392 * Dpc = Initalizes DPC
393 * Number = Processor number
394 * RETURNS: None
395 */
396 VOID
397 STDCALL
398 KeSetTargetProcessorDpc(IN PKDPC Dpc,
399 IN CCHAR Number)
400 {
401 /* Check how many CPUs are on the system */
402 if (Number >= MAXIMUM_PROCESSORS) {
403
404 /* No CPU Number */
405 Dpc->Number = 0;
406
407 } else {
408
409 /* Set the Number Specified */
410 ASSERT(Number < KeNumberProcessors);
411 Dpc->Number = Number + MAXIMUM_PROCESSORS;
412 }
413 }
414
415 /*
416 * FUNCTION:
417 * Called when a quantum end occurs to check if priority should be changed
418 * and wether a new thread should be dispatched.
419 * NOTES:
420 * Called when deleting a Driver.
421 */
422 VOID
423 STDCALL
424 KiQuantumEnd(VOID)
425 {
426 PKPRCB Prcb;
427 PKTHREAD CurrentThread;
428 KIRQL OldIrql;
429 PKPROCESS Process;
430 KPRIORITY OldPriority;
431 KPRIORITY NewPriority;
432
433 /* Lock dispatcher, get current thread */
434 Prcb = KeGetCurrentPrcb();
435 CurrentThread = KeGetCurrentThread();
436 OldIrql = KeRaiseIrqlToSynchLevel();
437
438 /* Get the Thread's Process */
439 Process = CurrentThread->ApcState.Process;
440
441 /* Set DPC Event if requested */
442 if (Prcb->DpcSetEventRequest) {
443 /*
444 * FIXME:
445 * Prcb->DpcEvent is not initialized.
446 */
447 KEBUGCHECK(0);
448 KeSetEvent(Prcb->DpcEvent, 0, 0);
449 }
450
451 /* Check if Quantum expired */
452 if (CurrentThread->Quantum <= 0) {
453
454 /* Reset the new Quantum */
455 CurrentThread->Quantum = CurrentThread->QuantumReset;
456
457 /* Calculate new priority */
458 OldPriority = CurrentThread->Priority;
459 if (OldPriority < LOW_REALTIME_PRIORITY) {
460
461 /* Set the New Priority and add the Priority Decrement */
462 NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
463
464 /* Don't go out of bounds */
465 if (NewPriority < CurrentThread->BasePriority) NewPriority = CurrentThread->BasePriority;
466
467 /* Reset the priority decrement */
468 CurrentThread->PriorityDecrement = 0;
469
470 /* Set a new priority if needed */
471 if (OldPriority != NewPriority) {
472
473 /* Set new Priority */
474 BOOLEAN Dummy; /* <- This is a hack anyways... */
475 KiSetPriorityThread(CurrentThread, NewPriority, &Dummy);
476
477 } else {
478
479 /* Queue new thread if none is already */
480 if (Prcb->NextThread == NULL) {
481
482 /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
483
484 } else {
485
486 /* Make the current thread non-premeptive if a new thread is queued */
487 CurrentThread->Preempted = FALSE;
488 }
489 }
490
491
492 } else {
493 /* Set the Quantum back to Maximum */
494 //if (CurrentThread->DisableQuantum) {
495 // CurrentThread->Quantum = MAX_QUANTUM;
496 //}
497 }
498 }
499
500 /* Dispatch the Thread */
501 KeLowerIrql(DISPATCH_LEVEL);
502 KiDispatchThread(Ready);
503 }
504
505 /*
506 * @implemented
507 *
508 * FUNCTION:
509 * Called whenever a system interrupt is generated at DISPATCH_LEVEL.
510 * It delivers queued DPCs and dispatches a new thread if need be.
511 */
512 VOID
513 STDCALL
514 KiDispatchInterrupt(VOID)
515 {
516 PLIST_ENTRY DpcEntry;
517 PKDPC Dpc;
518 KIRQL OldIrql;
519 PKPRCB Prcb;
520
521 DPRINT("Dispatching Interrupts\n");
522 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
523
524 /* Set DPC Deliver to Active */
525 Prcb = KeGetCurrentPrcb();
526
527 if (Prcb->DpcData[0].DpcQueueDepth > 0) {
528 /* Raise IRQL */
529 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
530 #ifdef CONFIG_SMP
531 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
532 #endif
533 Prcb->DpcRoutineActive = TRUE;
534
535 DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead);
536 /* Loop while we have entries */
537 while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) {
538
539 ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0);
540 DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth);
541
542 /* Get the DPC call it */
543 DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead);
544 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
545 DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
546 Dpc->DpcData = NULL;
547 Prcb->DpcData[0].DpcQueueDepth--;
548 #ifdef CONFIG_SMP
549 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
550 #endif
551 /* Disable/Enabled Interrupts and Call the DPC */
552 KeLowerIrql(OldIrql);
553 DPRINT("Calling DPC: %x\n", Dpc);
554 Dpc->DeferredRoutine(Dpc,
555 Dpc->DeferredContext,
556 Dpc->SystemArgument1,
557 Dpc->SystemArgument2);
558 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
559
560 #ifdef CONFIG_SMP
561 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
562 /*
563 * If the dpc routine drops the irql below DISPATCH_LEVEL,
564 * a thread switch can occur and after the next thread switch
565 * the execution may start on an other processor.
566 */
567 if (Prcb != KeGetCurrentPrcb()) {
568
569 Prcb->DpcRoutineActive = FALSE;
570 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
571 Prcb = KeGetCurrentPrcb();
572 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
573 Prcb->DpcRoutineActive = TRUE;
574 }
575 #endif
576 }
577 /* Clear DPC Flags */
578 Prcb->DpcRoutineActive = FALSE;
579 Prcb->DpcInterruptRequested = FALSE;
580 #ifdef CONFIG_SMP
581 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
582 #endif
583
584 /* DPC Dispatching Ended, re-enable interrupts */
585 KeLowerIrql(OldIrql);
586 }
587
588 DPRINT("Checking for Quantum End\n");
589
590 /* If we have Quantum End, call the function */
591 if (Prcb->QuantumEnd) {
592
593 Prcb->QuantumEnd = FALSE;
594 KiQuantumEnd();
595 }
596 }
597
598 /* EOF */