1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/dpc.c 5 * PURPOSE: Deferred Procedure Call (DPC) Support 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Philip Susi (phreak@iag.net) 8 * Eric Kohl 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS *******************************************************************/ 18 19 ULONG KiMaximumDpcQueueDepth = 4; 20 ULONG KiMinimumDpcRate = 3; 21 ULONG KiAdjustDpcThreshold = 20; 22 ULONG KiIdealDpcRate = 20; 23 BOOLEAN KeThreadDpcEnable; 24 FAST_MUTEX KiGenericCallDpcMutex; 25 KDPC KiTimerExpireDpc; 26 ULONG KiTimeLimitIsrMicroseconds; 27 ULONG KiDPCTimeout = 110; 28 29 /* PRIVATE FUNCTIONS *********************************************************/ 30 31 VOID 32 NTAPI 33 KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime) 34 { 35 #if DBG 36 ULONG i = 0; 37 PLIST_ENTRY ListHead, NextEntry; 38 KIRQL OldIrql; 39 PKTIMER Timer; 40 41 /* Raise IRQL to high and loop timers */ 42 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 43 do 44 { 45 /* Loop the current list */ 46 ListHead = &KiTimerTableListHead[i].Entry; 47 NextEntry = ListHead->Flink; 48 while (NextEntry != ListHead) 49 { 50 /* Get the timer and move to the next one */ 51 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 52 NextEntry = NextEntry->Flink; 53 54 /* Check if it expired */ 55 if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) 56 { 57 /* Check if the DPC was queued, but didn't run */ 58 if (!(KeGetCurrentPrcb()->TimerRequest) && 59 !(*((volatile PULONG*)(&KiTimerExpireDpc.DpcData)))) 60 { 61 /* This is bad, breakpoint! */ 62 DPRINT1("Invalid timer state!\n"); 63 DbgBreakPoint(); 64 } 65 } 66 } 67 68 /* Move to the next timer */ 69 i++; 70 } while(i < TIMER_TABLE_SIZE); 71 72 /* Lower IRQL and return */ 73 KeLowerIrql(OldIrql); 74 #endif 75 } 76 77 VOID 78 NTAPI 79 KiTimerExpiration(IN PKDPC Dpc, 80 IN PVOID DeferredContext, 81 IN PVOID SystemArgument1, 82 IN PVOID SystemArgument2) 83 { 84 ULARGE_INTEGER SystemTime, InterruptTime; 85 LARGE_INTEGER Interval; 86 LONG Limit, Index, i; 87 ULONG Timers, ActiveTimers, DpcCalls; 88 PLIST_ENTRY ListHead, NextEntry; 89 KIRQL OldIrql; 90 PKTIMER Timer; 91 PKDPC TimerDpc; 92 ULONG Period; 93 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; 94 PKSPIN_LOCK_QUEUE LockQueue; 95 PKPRCB Prcb = KeGetCurrentPrcb(); 96 97 /* Disable interrupts */ 98 _disable(); 99 100 /* Query system and interrupt time */ 101 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); 102 InterruptTime.QuadPart = KeQueryInterruptTime(); 103 Limit = KeTickCount.LowPart; 104 105 /* Bring interrupts back */ 106 _enable(); 107 108 /* Get the index of the timer and normalize it */ 109 Index = PtrToLong(SystemArgument1); 110 if ((Limit - Index) >= TIMER_TABLE_SIZE) 111 { 112 /* Normalize it */ 113 Limit = Index + TIMER_TABLE_SIZE - 1; 114 } 115 116 /* Setup index and actual limit */ 117 Index--; 118 Limit &= (TIMER_TABLE_SIZE - 1); 119 120 /* Setup accounting data */ 121 DpcCalls = 0; 122 Timers = 24; 123 ActiveTimers = 4; 124 125 /* Lock the Database and Raise IRQL */ 126 OldIrql = KiAcquireDispatcherLock(); 127 128 /* Start expiration loop */ 129 do 130 { 131 /* Get the current index */ 132 Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); 133 134 /* Get list pointers and loop the list */ 135 ListHead = &KiTimerTableListHead[Index].Entry; 136 while (ListHead != ListHead->Flink) 137 { 138 /* Lock the timer and go to the next entry */ 139 LockQueue = KiAcquireTimerLock(Index); 140 NextEntry = ListHead->Flink; 141 142 /* Get the current timer and check its due time */ 143 Timers--; 144 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 145 if ((NextEntry != ListHead) && 146 (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) 147 { 148 /* It's expired, remove it */ 149 ActiveTimers--; 150 KiRemoveEntryTimer(Timer); 151 152 /* Make it non-inserted, unlock it, and signal it */ 153 Timer->Header.Inserted = FALSE; 154 KiReleaseTimerLock(LockQueue); 155 Timer->Header.SignalState = 1; 156 157 /* Get the DPC and period */ 158 TimerDpc = Timer->Dpc; 159 Period = Timer->Period; 160 161 /* Check if there's any waiters */ 162 if (!IsListEmpty(&Timer->Header.WaitListHead)) 163 { 164 /* Check the type of event */ 165 if (Timer->Header.Type == TimerNotificationObject) 166 { 167 /* Unwait the thread */ 168 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); 169 } 170 else 171 { 172 /* Otherwise unwait the thread and signal the timer */ 173 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); 174 } 175 } 176 177 /* Check if we have a period */ 178 if (Period) 179 { 180 /* Calculate the interval and insert the timer */ 181 Interval.QuadPart = Int32x32To64(Period, -10000); 182 while (!KiInsertTreeTimer(Timer, Interval)); 183 } 184 185 /* Check if we have a DPC */ 186 if (TimerDpc) 187 { 188 #ifdef CONFIG_SMP 189 /* 190 * If the DPC is targeted to another processor, 191 * then insert it into that processor's DPC queue 192 * instead of delivering it now. 193 * If the DPC is a threaded DPC, and the current CPU 194 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), 195 * then also insert it into the DPC queue for threaded delivery, 196 * instead of doing it here. 197 */ 198 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && 199 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || 200 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) 201 { 202 /* Queue it */ 203 KeInsertQueueDpc(TimerDpc, 204 UlongToPtr(SystemTime.LowPart), 205 UlongToPtr(SystemTime.HighPart)); 206 } 207 else 208 #endif 209 { 210 /* Setup the DPC Entry */ 211 DpcEntry[DpcCalls].Dpc = TimerDpc; 212 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; 213 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; 214 DpcCalls++; 215 ASSERT(DpcCalls < MAX_TIMER_DPCS); 216 } 217 } 218 219 /* Check if we're done processing */ 220 if (!(ActiveTimers) || !(Timers)) 221 { 222 /* Release the dispatcher while doing DPCs */ 223 KiReleaseDispatcherLock(DISPATCH_LEVEL); 224 225 /* Start looping all DPC Entries */ 226 for (i = 0; DpcCalls; DpcCalls--, i++) 227 { 228 #if DBG 229 /* Clear DPC Time */ 230 Prcb->DebugDpcTime = 0; 231 #endif 232 233 /* Call the DPC */ 234 DpcEntry[i].Routine(DpcEntry[i].Dpc, 235 DpcEntry[i].Context, 236 UlongToPtr(SystemTime.LowPart), 237 UlongToPtr(SystemTime.HighPart)); 238 } 239 240 /* Reset accounting */ 241 Timers = 24; 242 ActiveTimers = 4; 243 244 /* Lock the dispatcher database */ 245 KiAcquireDispatcherLock(); 246 } 247 } 248 else 249 { 250 /* Check if the timer list is empty */ 251 if (NextEntry != ListHead) 252 { 253 /* Sanity check */ 254 ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= 255 Timer->DueTime.QuadPart); 256 257 /* Update the time */ 258 _disable(); 259 KiTimerTableListHead[Index].Time.QuadPart = 260 Timer->DueTime.QuadPart; 261 _enable(); 262 } 263 264 /* Release the lock */ 265 KiReleaseTimerLock(LockQueue); 266 267 /* Check if we've scanned all the timers we could */ 268 if (!Timers) 269 { 270 /* Release the dispatcher while doing DPCs */ 271 KiReleaseDispatcherLock(DISPATCH_LEVEL); 272 273 /* Start looping all DPC Entries */ 274 for (i = 0; DpcCalls; DpcCalls--, i++) 275 { 276 #if DBG 277 /* Clear DPC Time */ 278 Prcb->DebugDpcTime = 0; 279 #endif 280 281 /* Call the DPC */ 282 DpcEntry[i].Routine(DpcEntry[i].Dpc, 283 DpcEntry[i].Context, 284 UlongToPtr(SystemTime.LowPart), 285 UlongToPtr(SystemTime.HighPart)); 286 } 287 288 /* Reset accounting */ 289 Timers = 24; 290 ActiveTimers = 4; 291 292 /* Lock the dispatcher database */ 293 KiAcquireDispatcherLock(); 294 } 295 296 /* Done looping */ 297 break; 298 } 299 } 300 } while (Index != Limit); 301 302 /* Verify the timer table, on debug builds */ 303 if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime); 304 305 /* Check if we still have DPC entries */ 306 if (DpcCalls) 307 { 308 /* Release the dispatcher while doing DPCs */ 309 KiReleaseDispatcherLock(DISPATCH_LEVEL); 310 311 /* Start looping all DPC Entries */ 312 for (i = 0; DpcCalls; DpcCalls--, i++) 313 { 314 #if DBG 315 /* Clear DPC Time */ 316 Prcb->DebugDpcTime = 0; 317 #endif 318 319 /* Call the DPC */ 320 DpcEntry[i].Routine(DpcEntry[i].Dpc, 321 DpcEntry[i].Context, 322 UlongToPtr(SystemTime.LowPart), 323 UlongToPtr(SystemTime.HighPart)); 324 } 325 326 /* Lower IRQL if we need to */ 327 if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); 328 } 329 else 330 { 331 /* Unlock the dispatcher */ 332 KiReleaseDispatcherLock(OldIrql); 333 } 334 } 335 336 VOID 337 FASTCALL 338 KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead, 339 IN KIRQL OldIrql) 340 { 341 ULARGE_INTEGER SystemTime; 342 LARGE_INTEGER Interval; 343 LONG i; 344 ULONG DpcCalls = 0; 345 PKTIMER Timer; 346 PKDPC TimerDpc; 347 ULONG Period; 348 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; 349 PKPRCB Prcb = KeGetCurrentPrcb(); 350 351 /* Query system */ 352 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); 353 354 /* Loop expired list */ 355 while (ExpiredListHead->Flink != ExpiredListHead) 356 { 357 /* Get the current timer */ 358 Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); 359 360 /* Remove it */ 361 RemoveEntryList(&Timer->TimerListEntry); 362 363 /* Not inserted */ 364 Timer->Header.Inserted = FALSE; 365 366 /* Signal it */ 367 Timer->Header.SignalState = 1; 368 369 /* Get the DPC and period */ 370 TimerDpc = Timer->Dpc; 371 Period = Timer->Period; 372 373 /* Check if there's any waiters */ 374 if (!IsListEmpty(&Timer->Header.WaitListHead)) 375 { 376 /* Check the type of event */ 377 if (Timer->Header.Type == TimerNotificationObject) 378 { 379 /* Unwait the thread */ 380 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); 381 } 382 else 383 { 384 /* Otherwise unwait the thread and signal the timer */ 385 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); 386 } 387 } 388 389 /* Check if we have a period */ 390 if (Period) 391 { 392 /* Calculate the interval and insert the timer */ 393 Interval.QuadPart = Int32x32To64(Period, -10000); 394 while (!KiInsertTreeTimer(Timer, Interval)); 395 } 396 397 /* Check if we have a DPC */ 398 if (TimerDpc) 399 { 400 #ifdef CONFIG_SMP 401 /* 402 * If the DPC is targeted to another processor, 403 * then insert it into that processor's DPC queue 404 * instead of delivering it now. 405 * If the DPC is a threaded DPC, and the current CPU 406 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), 407 * then also insert it into the DPC queue for threaded delivery, 408 * instead of doing it here. 409 */ 410 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && 411 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || 412 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) 413 { 414 /* Queue it */ 415 KeInsertQueueDpc(TimerDpc, 416 UlongToPtr(SystemTime.LowPart), 417 UlongToPtr(SystemTime.HighPart)); 418 } 419 else 420 #endif 421 { 422 /* Setup the DPC Entry */ 423 DpcEntry[DpcCalls].Dpc = TimerDpc; 424 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; 425 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; 426 DpcCalls++; 427 ASSERT(DpcCalls < MAX_TIMER_DPCS); 428 } 429 } 430 } 431 432 /* Check if we still have DPC entries */ 433 if (DpcCalls) 434 { 435 /* Release the dispatcher while doing DPCs */ 436 KiReleaseDispatcherLock(DISPATCH_LEVEL); 437 438 /* Start looping all DPC Entries */ 439 for (i = 0; DpcCalls; DpcCalls--, i++) 440 { 441 #if DBG 442 /* Clear DPC Time */ 443 Prcb->DebugDpcTime = 0; 444 #endif 445 446 /* Call the DPC */ 447 DpcEntry[i].Routine(DpcEntry[i].Dpc, 448 DpcEntry[i].Context, 449 UlongToPtr(SystemTime.LowPart), 450 UlongToPtr(SystemTime.HighPart)); 451 } 452 453 /* Lower IRQL */ 454 KeLowerIrql(OldIrql); 455 } 456 else 457 { 458 /* Unlock the dispatcher */ 459 KiReleaseDispatcherLock(OldIrql); 460 } 461 } 462 463 _Requires_lock_not_held_(Prcb->PrcbLock) 464 VOID 465 NTAPI 466 KiQuantumEnd(VOID) 467 { 468 PKPRCB Prcb = KeGetCurrentPrcb(); 469 PKTHREAD NextThread, Thread = Prcb->CurrentThread; 470 471 /* Check if a DPC Event was requested to be signaled */ 472 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0)) 473 { 474 /* Signal it */ 475 KeSetEvent(&Prcb->DpcEvent, 0, 0); 476 } 477 478 /* Raise to synchronization level and lock the PRCB and thread */ 479 KeRaiseIrqlToSynchLevel(); 480 KiAcquireThreadLock(Thread); 481 KiAcquirePrcbLock(Prcb); 482 483 /* Check if Quantum expired */ 484 if (Thread->Quantum <= 0) 485 { 486 /* Check if we're real-time and with quantums disabled */ 487 if ((Thread->Priority >= LOW_REALTIME_PRIORITY) && 488 (Thread->ApcState.Process->DisableQuantum)) 489 { 490 /* Otherwise, set maximum quantum */ 491 Thread->Quantum = MAX_QUANTUM; 492 } 493 else 494 { 495 /* Reset the new Quantum */ 496 Thread->Quantum = Thread->QuantumReset; 497 498 /* Calculate new priority */ 499 Thread->Priority = KiComputeNewPriority(Thread, 1); 500 501 /* Check if a new thread is scheduled */ 502 if (!Prcb->NextThread) 503 { 504 /* Get a new ready thread */ 505 NextThread = KiSelectReadyThread(Thread->Priority, Prcb); 506 if (NextThread) 507 { 508 /* Found one, set it on standby */ 509 NextThread->State = Standby; 510 Prcb->NextThread = NextThread; 511 } 512 } 513 else 514 { 515 /* Otherwise, make sure that this thread doesn't get preempted */ 516 Thread->Preempted = FALSE; 517 } 518 } 519 } 520 521 /* Release the thread lock */ 522 KiReleaseThreadLock(Thread); 523 524 /* Check if there's no thread scheduled */ 525 if (!Prcb->NextThread) 526 { 527 /* Just leave now */ 528 KiReleasePrcbLock(Prcb); 529 KeLowerIrql(DISPATCH_LEVEL); 530 return; 531 } 532 533 /* Get the next thread now */ 534 NextThread = Prcb->NextThread; 535 536 /* Set current thread's swap busy to true */ 537 KiSetThreadSwapBusy(Thread); 538 539 /* Switch threads in PRCB */ 540 Prcb->NextThread = NULL; 541 Prcb->CurrentThread = NextThread; 542 543 /* Set thread to running and the switch reason to Quantum End */ 544 NextThread->State = Running; 545 Thread->WaitReason = WrQuantumEnd; 546 547 /* Queue it on the ready lists */ 548 KxQueueReadyThread(Thread, Prcb); 549 550 /* Set wait IRQL to APC_LEVEL */ 551 Thread->WaitIrql = APC_LEVEL; 552 553 /* Swap threads */ 554 KiSwapContext(APC_LEVEL, Thread); 555 556 /* Lower IRQL back to DISPATCH_LEVEL */ 557 KeLowerIrql(DISPATCH_LEVEL); 558 } 559 560 VOID 561 FASTCALL 562 KiRetireDpcList(IN PKPRCB Prcb) 563 { 564 PKDPC_DATA DpcData; 565 PLIST_ENTRY ListHead, DpcEntry; 566 PKDPC Dpc; 567 PKDEFERRED_ROUTINE DeferredRoutine; 568 PVOID DeferredContext, SystemArgument1, SystemArgument2; 569 ULONG_PTR TimerHand; 570 #ifdef CONFIG_SMP 571 KIRQL OldIrql; 572 #endif 573 574 /* Get data and list variables before starting anything else */ 575 DpcData = &Prcb->DpcData[DPC_NORMAL]; 576 ListHead = &DpcData->DpcListHead; 577 578 /* Main outer loop */ 579 do 580 { 581 /* Set us as active */ 582 Prcb->DpcRoutineActive = TRUE; 583 584 /* Check if this is a timer expiration request */ 585 if (Prcb->TimerRequest) 586 { 587 /* It is, get the timer hand and disable timer request */ 588 TimerHand = Prcb->TimerHand; 589 Prcb->TimerRequest = 0; 590 591 /* Expire timers with interrupts enabled */ 592 _enable(); 593 KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL); 594 _disable(); 595 } 596 597 /* Loop while we have entries in the queue */ 598 while (DpcData->DpcQueueDepth != 0) 599 { 600 /* Lock the DPC data and get the DPC entry*/ 601 KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock); 602 DpcEntry = ListHead->Flink; 603 604 /* Make sure we have an entry */ 605 if (DpcEntry != ListHead) 606 { 607 /* Remove the DPC from the list */ 608 RemoveEntryList(DpcEntry); 609 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry); 610 611 /* Clear its DPC data and save its parameters */ 612 Dpc->DpcData = NULL; 613 DeferredRoutine = Dpc->DeferredRoutine; 614 DeferredContext = Dpc->DeferredContext; 615 SystemArgument1 = Dpc->SystemArgument1; 616 SystemArgument2 = Dpc->SystemArgument2; 617 618 /* Decrease the queue depth */ 619 DpcData->DpcQueueDepth--; 620 621 #if DBG 622 /* Clear DPC Time */ 623 Prcb->DebugDpcTime = 0; 624 #endif 625 626 /* Release the lock */ 627 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 628 629 /* Re-enable interrupts */ 630 _enable(); 631 632 /* Call the DPC */ 633 DeferredRoutine(Dpc, 634 DeferredContext, 635 SystemArgument1, 636 SystemArgument2); 637 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 638 639 /* Disable interrupts and keep looping */ 640 _disable(); 641 } 642 else 643 { 644 /* The queue should be flushed now */ 645 ASSERT(DpcData->DpcQueueDepth == 0); 646 647 /* Release DPC Lock */ 648 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 649 } 650 } 651 652 /* Clear DPC Flags */ 653 Prcb->DpcRoutineActive = FALSE; 654 Prcb->DpcInterruptRequested = FALSE; 655 656 #ifdef CONFIG_SMP 657 /* Check if we have deferred threads */ 658 if (Prcb->DeferredReadyListHead.Next) 659 { 660 661 /* Re-enable interrupts and raise to synch */ 662 _enable(); 663 OldIrql = KeRaiseIrqlToSynchLevel(); 664 665 /* Process deferred threads */ 666 KiProcessDeferredReadyList(Prcb); 667 668 /* Lower IRQL back and disable interrupts */ 669 KeLowerIrql(OldIrql); 670 _disable(); 671 } 672 #endif 673 } while (DpcData->DpcQueueDepth != 0); 674 } 675 676 VOID 677 NTAPI 678 KiInitializeDpc(IN PKDPC Dpc, 679 IN PKDEFERRED_ROUTINE DeferredRoutine, 680 IN PVOID DeferredContext, 681 IN KOBJECTS Type) 682 { 683 /* Setup the DPC Object */ 684 Dpc->Type = Type; 685 Dpc->Number = 0; 686 Dpc->Importance= MediumImportance; 687 Dpc->DeferredRoutine = DeferredRoutine; 688 Dpc->DeferredContext = DeferredContext; 689 Dpc->DpcData = NULL; 690 } 691 692 /* PUBLIC FUNCTIONS **********************************************************/ 693 694 /* 695 * @implemented 696 */ 697 VOID 698 NTAPI 699 KeInitializeThreadedDpc(IN PKDPC Dpc, 700 IN PKDEFERRED_ROUTINE DeferredRoutine, 701 IN PVOID DeferredContext) 702 { 703 /* Call the internal routine */ 704 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject); 705 } 706 707 /* 708 * @implemented 709 */ 710 VOID 711 NTAPI 712 KeInitializeDpc(IN PKDPC Dpc, 713 IN PKDEFERRED_ROUTINE DeferredRoutine, 714 IN PVOID DeferredContext) 715 { 716 /* Call the internal routine */ 717 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject); 718 } 719 720 /* 721 * @implemented 722 */ 723 BOOLEAN 724 NTAPI 725 KeInsertQueueDpc(IN PKDPC Dpc, 726 IN PVOID SystemArgument1, 727 IN PVOID SystemArgument2) 728 { 729 KIRQL OldIrql; 730 PKPRCB Prcb, CurrentPrcb; 731 ULONG Cpu; 732 PKDPC_DATA DpcData; 733 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE; 734 ASSERT_DPC(Dpc); 735 736 /* Check IRQL and Raise it to HIGH_LEVEL */ 737 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 738 CurrentPrcb = KeGetCurrentPrcb(); 739 740 /* Check if the DPC has more then the maximum number of CPUs */ 741 if (Dpc->Number >= MAXIMUM_PROCESSORS) 742 { 743 /* Then substract the maximum and get that PRCB. */ 744 Cpu = Dpc->Number - MAXIMUM_PROCESSORS; 745 Prcb = KiProcessorBlock[Cpu]; 746 } 747 else 748 { 749 /* Use the current one */ 750 Prcb = CurrentPrcb; 751 Cpu = Prcb->Number; 752 } 753 754 /* ROS Sanity Check */ 755 ASSERT(Prcb == CurrentPrcb); 756 757 /* Check if this is a threaded DPC and threaded DPCs are enabled */ 758 if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)) 759 { 760 /* Then use the threaded data */ 761 DpcData = &Prcb->DpcData[DPC_THREADED]; 762 } 763 else 764 { 765 /* Otherwise, use the regular data */ 766 DpcData = &Prcb->DpcData[DPC_NORMAL]; 767 } 768 769 /* Acquire the DPC lock */ 770 KiAcquireSpinLock(&DpcData->DpcLock); 771 772 /* Get the DPC Data */ 773 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL)) 774 { 775 /* Now we can play with the DPC safely */ 776 Dpc->SystemArgument1 = SystemArgument1; 777 Dpc->SystemArgument2 = SystemArgument2; 778 DpcData->DpcQueueDepth++; 779 DpcData->DpcCount++; 780 DpcConfigured = TRUE; 781 782 /* Check if this is a high importance DPC */ 783 if (Dpc->Importance == HighImportance) 784 { 785 /* Pre-empty other DPCs */ 786 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 787 } 788 else 789 { 790 /* Add it at the end */ 791 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 792 } 793 794 /* Check if this is the DPC on the threaded list */ 795 if (&Prcb->DpcData[DPC_THREADED] == DpcData) 796 { 797 /* Make sure a threaded DPC isn't already active */ 798 if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested)) 799 { 800 /* FIXME: Setup Threaded DPC */ 801 UNIMPLEMENTED_FATAL("Threaded DPC not supported\n"); 802 } 803 } 804 else 805 { 806 /* Make sure a DPC isn't executing already */ 807 if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested)) 808 { 809 /* Check if this is the same CPU */ 810 if (Prcb != CurrentPrcb) 811 { 812 /* 813 * Check if the DPC is of high importance or above the 814 * maximum depth. If it is, then make sure that the CPU 815 * isn't idle, or that it's sleeping. 816 */ 817 if (((Dpc->Importance == HighImportance) || 818 (DpcData->DpcQueueDepth >= 819 Prcb->MaximumDpcQueueDepth)) && 820 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) || 821 (Prcb->Sleeping))) 822 { 823 /* Set interrupt requested */ 824 Prcb->DpcInterruptRequested = TRUE; 825 826 /* Set DPC inserted */ 827 DpcInserted = TRUE; 828 } 829 } 830 else 831 { 832 /* Check if the DPC is of anything but low importance */ 833 if ((Dpc->Importance != LowImportance) || 834 (DpcData->DpcQueueDepth >= 835 Prcb->MaximumDpcQueueDepth) || 836 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) 837 { 838 /* Set interrupt requested */ 839 Prcb->DpcInterruptRequested = TRUE; 840 841 /* Set DPC inserted */ 842 DpcInserted = TRUE; 843 } 844 } 845 } 846 } 847 } 848 849 /* Release the lock */ 850 KiReleaseSpinLock(&DpcData->DpcLock); 851 852 /* Check if the DPC was inserted */ 853 if (DpcInserted) 854 { 855 /* Check if this was SMP */ 856 if (Prcb != CurrentPrcb) 857 { 858 /* It was, request and IPI */ 859 KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC); 860 } 861 else 862 { 863 /* It wasn't, request an interrupt from HAL */ 864 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 865 } 866 } 867 868 /* Lower IRQL */ 869 KeLowerIrql(OldIrql); 870 return DpcConfigured; 871 } 872 873 /* 874 * @implemented 875 */ 876 BOOLEAN 877 NTAPI 878 KeRemoveQueueDpc(IN PKDPC Dpc) 879 { 880 PKDPC_DATA DpcData; 881 BOOLEAN Enable; 882 ASSERT_DPC(Dpc); 883 884 /* Disable interrupts */ 885 Enable = KeDisableInterrupts(); 886 887 /* Get DPC data */ 888 DpcData = Dpc->DpcData; 889 if (DpcData) 890 { 891 /* Acquire the DPC lock */ 892 KiAcquireSpinLock(&DpcData->DpcLock); 893 894 /* Make sure that the data didn't change */ 895 if (DpcData == Dpc->DpcData) 896 { 897 /* Remove the DPC */ 898 DpcData->DpcQueueDepth--; 899 RemoveEntryList(&Dpc->DpcListEntry); 900 Dpc->DpcData = NULL; 901 } 902 903 /* Release the lock */ 904 KiReleaseSpinLock(&DpcData->DpcLock); 905 } 906 907 /* Re-enable interrupts */ 908 if (Enable) _enable(); 909 910 /* Return if the DPC was in the queue or not */ 911 return DpcData ? TRUE : FALSE; 912 } 913 914 /* 915 * @implemented 916 */ 917 _IRQL_requires_max_(APC_LEVEL) 918 VOID 919 NTAPI 920 KeFlushQueuedDpcs(VOID) 921 { 922 ULONG ProcessorIndex; 923 PKPRCB TargetPrcb; 924 925 PAGED_CODE(); 926 ASSERT(KeGetCurrentThread()->SystemAffinityActive == FALSE); 927 928 /* Loop all processors */ 929 for (ProcessorIndex = 0; ProcessorIndex < KeNumberProcessors; ProcessorIndex++) 930 { 931 /* Get the target processor's PRCB */ 932 TargetPrcb = KiProcessorBlock[ProcessorIndex]; 933 934 /* Check if there are DPCs on either queues */ 935 if ((TargetPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) || 936 (TargetPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0)) 937 { 938 /* Check if this is the current processor */ 939 if (TargetPrcb == KeGetCurrentPrcb()) 940 { 941 /* Request a DPC interrupt */ 942 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 943 } 944 else 945 { 946 /* Attach to the target processor. This will cause a DPC 947 interrupt on the target processor and flush all DPCs. */ 948 KeSetSystemAffinityThread(TargetPrcb->SetMember); 949 } 950 } 951 } 952 953 /* Revert back to user affinity */ 954 if (KeGetCurrentThread()->SystemAffinityActive) 955 { 956 KeRevertToUserAffinityThread(); 957 } 958 } 959 960 /* 961 * @implemented 962 */ 963 BOOLEAN 964 NTAPI 965 KeIsExecutingDpc(VOID) 966 { 967 /* Return if the Dpc Routine is active */ 968 return KeGetCurrentPrcb()->DpcRoutineActive; 969 } 970 971 /* 972 * @implemented 973 */ 974 VOID 975 NTAPI 976 KeSetImportanceDpc (IN PKDPC Dpc, 977 IN KDPC_IMPORTANCE Importance) 978 { 979 /* Set the DPC Importance */ 980 ASSERT_DPC(Dpc); 981 Dpc->Importance = Importance; 982 } 983 984 /* 985 * @implemented 986 */ 987 VOID 988 NTAPI 989 KeSetTargetProcessorDpc(IN PKDPC Dpc, 990 IN CCHAR Number) 991 { 992 /* Set a target CPU */ 993 ASSERT_DPC(Dpc); 994 Dpc->Number = Number + MAXIMUM_PROCESSORS; 995 } 996 997 /* 998 * @implemented 999 */ 1000 VOID 1001 NTAPI 1002 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine, 1003 IN PVOID Context) 1004 { 1005 ULONG Barrier = KeNumberProcessors; 1006 KIRQL OldIrql; 1007 DEFERRED_REVERSE_BARRIER ReverseBarrier; 1008 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL); 1009 1010 // 1011 // The barrier is the number of processors, each processor will decrement it 1012 // by one, so when all processors have run the DPC, the barrier reaches zero 1013 // 1014 ReverseBarrier.Barrier = Barrier; 1015 ReverseBarrier.TotalProcessors = Barrier; 1016 1017 // 1018 // But we don't need the barrier on UP, since we can simply call the routine 1019 // directly while at DISPATCH_LEVEL and not worry about anything else 1020 // 1021 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 1022 Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier); 1023 KeLowerIrql(OldIrql); 1024 } 1025 1026 /* 1027 * @implemented 1028 */ 1029 VOID 1030 NTAPI 1031 KeSignalCallDpcDone(IN PVOID SystemArgument1) 1032 { 1033 // 1034 // Decrement the barrier, which is actually the processor count 1035 // 1036 InterlockedDecrement((PLONG)SystemArgument1); 1037 } 1038 1039 /* 1040 * @implemented 1041 */ 1042 BOOLEAN 1043 NTAPI 1044 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2) 1045 { 1046 // 1047 // There is nothing to do on UP systems -- the processor calling this wins 1048 // 1049 UNREFERENCED_PARAMETER(SystemArgument2); 1050 return TRUE; 1051 } 1052 1053 /* EOF */ 1054