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