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 VOID 464 NTAPI 465 KiQuantumEnd(VOID) 466 { 467 PKPRCB Prcb = KeGetCurrentPrcb(); 468 PKTHREAD NextThread, Thread = Prcb->CurrentThread; 469 470 /* Check if a DPC Event was requested to be signaled */ 471 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0)) 472 { 473 /* Signal it */ 474 KeSetEvent(&Prcb->DpcEvent, 0, 0); 475 } 476 477 /* Raise to synchronization level and lock the PRCB and thread */ 478 KeRaiseIrqlToSynchLevel(); 479 KiAcquireThreadLock(Thread); 480 KiAcquirePrcbLock(Prcb); 481 482 /* Check if Quantum expired */ 483 if (Thread->Quantum <= 0) 484 { 485 /* Check if we're real-time and with quantums disabled */ 486 if ((Thread->Priority >= LOW_REALTIME_PRIORITY) && 487 (Thread->ApcState.Process->DisableQuantum)) 488 { 489 /* Otherwise, set maximum quantum */ 490 Thread->Quantum = MAX_QUANTUM; 491 } 492 else 493 { 494 /* Reset the new Quantum */ 495 Thread->Quantum = Thread->QuantumReset; 496 497 /* Calculate new priority */ 498 Thread->Priority = KiComputeNewPriority(Thread, 1); 499 500 /* Check if a new thread is scheduled */ 501 if (!Prcb->NextThread) 502 { 503 /* Get a new ready thread */ 504 NextThread = KiSelectReadyThread(Thread->Priority, Prcb); 505 if (NextThread) 506 { 507 /* Found one, set it on standby */ 508 NextThread->State = Standby; 509 Prcb->NextThread = NextThread; 510 } 511 } 512 else 513 { 514 /* Otherwise, make sure that this thread doesn't get preempted */ 515 Thread->Preempted = FALSE; 516 } 517 } 518 } 519 520 /* Release the thread lock */ 521 KiReleaseThreadLock(Thread); 522 523 /* Check if there's no thread scheduled */ 524 if (!Prcb->NextThread) 525 { 526 /* Just leave now */ 527 KiReleasePrcbLock(Prcb); 528 KeLowerIrql(DISPATCH_LEVEL); 529 return; 530 } 531 532 /* Get the next thread now */ 533 NextThread = Prcb->NextThread; 534 535 /* Set current thread's swap busy to true */ 536 KiSetThreadSwapBusy(Thread); 537 538 /* Switch threads in PRCB */ 539 Prcb->NextThread = NULL; 540 Prcb->CurrentThread = NextThread; 541 542 /* Set thread to running and the switch reason to Quantum End */ 543 NextThread->State = Running; 544 Thread->WaitReason = WrQuantumEnd; 545 546 /* Queue it on the ready lists */ 547 KxQueueReadyThread(Thread, Prcb); 548 549 /* Set wait IRQL to APC_LEVEL */ 550 Thread->WaitIrql = APC_LEVEL; 551 552 /* Swap threads */ 553 KiSwapContext(APC_LEVEL, Thread); 554 555 /* Lower IRQL back to DISPATCH_LEVEL */ 556 KeLowerIrql(DISPATCH_LEVEL); 557 } 558 559 VOID 560 FASTCALL 561 KiRetireDpcList(IN PKPRCB Prcb) 562 { 563 PKDPC_DATA DpcData; 564 PLIST_ENTRY ListHead, DpcEntry; 565 PKDPC Dpc; 566 PKDEFERRED_ROUTINE DeferredRoutine; 567 PVOID DeferredContext, SystemArgument1, SystemArgument2; 568 ULONG_PTR TimerHand; 569 #ifdef CONFIG_SMP 570 KIRQL OldIrql; 571 #endif 572 573 /* Get data and list variables before starting anything else */ 574 DpcData = &Prcb->DpcData[DPC_NORMAL]; 575 ListHead = &DpcData->DpcListHead; 576 577 /* Main outer loop */ 578 do 579 { 580 /* Set us as active */ 581 Prcb->DpcRoutineActive = TRUE; 582 583 /* Check if this is a timer expiration request */ 584 if (Prcb->TimerRequest) 585 { 586 /* It is, get the timer hand and disable timer request */ 587 TimerHand = Prcb->TimerHand; 588 Prcb->TimerRequest = 0; 589 590 /* Expire timers with interrups enabled */ 591 _enable(); 592 KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL); 593 _disable(); 594 } 595 596 /* Loop while we have entries in the queue */ 597 while (DpcData->DpcQueueDepth != 0) 598 { 599 /* Lock the DPC data and get the DPC entry*/ 600 KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock); 601 DpcEntry = ListHead->Flink; 602 603 /* Make sure we have an entry */ 604 if (DpcEntry != ListHead) 605 { 606 /* Remove the DPC from the list */ 607 RemoveEntryList(DpcEntry); 608 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry); 609 610 /* Clear its DPC data and save its parameters */ 611 Dpc->DpcData = NULL; 612 DeferredRoutine = Dpc->DeferredRoutine; 613 DeferredContext = Dpc->DeferredContext; 614 SystemArgument1 = Dpc->SystemArgument1; 615 SystemArgument2 = Dpc->SystemArgument2; 616 617 /* Decrease the queue depth */ 618 DpcData->DpcQueueDepth--; 619 620 #if DBG 621 /* Clear DPC Time */ 622 Prcb->DebugDpcTime = 0; 623 #endif 624 625 /* Release the lock */ 626 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 627 628 /* Re-enable interrupts */ 629 _enable(); 630 631 /* Call the DPC */ 632 DeferredRoutine(Dpc, 633 DeferredContext, 634 SystemArgument1, 635 SystemArgument2); 636 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 637 638 /* Disable interrupts and keep looping */ 639 _disable(); 640 } 641 else 642 { 643 /* The queue should be flushed now */ 644 ASSERT(DpcData->DpcQueueDepth == 0); 645 646 /* Release DPC Lock */ 647 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 648 } 649 } 650 651 /* Clear DPC Flags */ 652 Prcb->DpcRoutineActive = FALSE; 653 Prcb->DpcInterruptRequested = FALSE; 654 655 #ifdef CONFIG_SMP 656 /* Check if we have deferred threads */ 657 if (Prcb->DeferredReadyListHead.Next) 658 { 659 660 /* Re-enable interrupts and raise to synch */ 661 _enable(); 662 OldIrql = KeRaiseIrqlToSynchLevel(); 663 664 /* Process deferred threads */ 665 KiProcessDeferredReadyList(Prcb); 666 667 /* Lower IRQL back and disable interrupts */ 668 KeLowerIrql(OldIrql); 669 _disable(); 670 } 671 #endif 672 } while (DpcData->DpcQueueDepth != 0); 673 } 674 675 VOID 676 NTAPI 677 KiInitializeDpc(IN PKDPC Dpc, 678 IN PKDEFERRED_ROUTINE DeferredRoutine, 679 IN PVOID DeferredContext, 680 IN KOBJECTS Type) 681 { 682 /* Setup the DPC Object */ 683 Dpc->Type = Type; 684 Dpc->Number = 0; 685 Dpc->Importance= MediumImportance; 686 Dpc->DeferredRoutine = DeferredRoutine; 687 Dpc->DeferredContext = DeferredContext; 688 Dpc->DpcData = NULL; 689 } 690 691 /* PUBLIC FUNCTIONS **********************************************************/ 692 693 /* 694 * @implemented 695 */ 696 VOID 697 NTAPI 698 KeInitializeThreadedDpc(IN PKDPC Dpc, 699 IN PKDEFERRED_ROUTINE DeferredRoutine, 700 IN PVOID DeferredContext) 701 { 702 /* Call the internal routine */ 703 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject); 704 } 705 706 /* 707 * @implemented 708 */ 709 VOID 710 NTAPI 711 KeInitializeDpc(IN PKDPC Dpc, 712 IN PKDEFERRED_ROUTINE DeferredRoutine, 713 IN PVOID DeferredContext) 714 { 715 /* Call the internal routine */ 716 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject); 717 } 718 719 /* 720 * @implemented 721 */ 722 BOOLEAN 723 NTAPI 724 KeInsertQueueDpc(IN PKDPC Dpc, 725 IN PVOID SystemArgument1, 726 IN PVOID SystemArgument2) 727 { 728 KIRQL OldIrql; 729 PKPRCB Prcb, CurrentPrcb; 730 ULONG Cpu; 731 PKDPC_DATA DpcData; 732 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE; 733 ASSERT_DPC(Dpc); 734 735 /* Check IRQL and Raise it to HIGH_LEVEL */ 736 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 737 CurrentPrcb = KeGetCurrentPrcb(); 738 739 /* Check if the DPC has more then the maximum number of CPUs */ 740 if (Dpc->Number >= MAXIMUM_PROCESSORS) 741 { 742 /* Then substract the maximum and get that PRCB. */ 743 Cpu = Dpc->Number - MAXIMUM_PROCESSORS; 744 Prcb = KiProcessorBlock[Cpu]; 745 } 746 else 747 { 748 /* Use the current one */ 749 Prcb = CurrentPrcb; 750 Cpu = Prcb->Number; 751 } 752 753 /* ROS Sanity Check */ 754 ASSERT(Prcb == CurrentPrcb); 755 756 /* Check if this is a threaded DPC and threaded DPCs are enabled */ 757 if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)) 758 { 759 /* Then use the threaded data */ 760 DpcData = &Prcb->DpcData[DPC_THREADED]; 761 } 762 else 763 { 764 /* Otherwise, use the regular data */ 765 DpcData = &Prcb->DpcData[DPC_NORMAL]; 766 } 767 768 /* Acquire the DPC lock */ 769 KiAcquireSpinLock(&DpcData->DpcLock); 770 771 /* Get the DPC Data */ 772 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL)) 773 { 774 /* Now we can play with the DPC safely */ 775 Dpc->SystemArgument1 = SystemArgument1; 776 Dpc->SystemArgument2 = SystemArgument2; 777 DpcData->DpcQueueDepth++; 778 DpcData->DpcCount++; 779 DpcConfigured = TRUE; 780 781 /* Check if this is a high importance DPC */ 782 if (Dpc->Importance == HighImportance) 783 { 784 /* Pre-empty other DPCs */ 785 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 786 } 787 else 788 { 789 /* Add it at the end */ 790 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 791 } 792 793 /* Check if this is the DPC on the threaded list */ 794 if (&Prcb->DpcData[DPC_THREADED] == DpcData) 795 { 796 /* Make sure a threaded DPC isn't already active */ 797 if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested)) 798 { 799 /* FIXME: Setup Threaded DPC */ 800 UNIMPLEMENTED_FATAL("Threaded DPC not supported\n"); 801 } 802 } 803 else 804 { 805 /* Make sure a DPC isn't executing already */ 806 if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested)) 807 { 808 /* Check if this is the same CPU */ 809 if (Prcb != CurrentPrcb) 810 { 811 /* 812 * Check if the DPC is of high importance or above the 813 * maximum depth. If it is, then make sure that the CPU 814 * isn't idle, or that it's sleeping. 815 */ 816 if (((Dpc->Importance == HighImportance) || 817 (DpcData->DpcQueueDepth >= 818 Prcb->MaximumDpcQueueDepth)) && 819 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) || 820 (Prcb->Sleeping))) 821 { 822 /* Set interrupt requested */ 823 Prcb->DpcInterruptRequested = TRUE; 824 825 /* Set DPC inserted */ 826 DpcInserted = TRUE; 827 } 828 } 829 else 830 { 831 /* Check if the DPC is of anything but low importance */ 832 if ((Dpc->Importance != LowImportance) || 833 (DpcData->DpcQueueDepth >= 834 Prcb->MaximumDpcQueueDepth) || 835 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) 836 { 837 /* Set interrupt requested */ 838 Prcb->DpcInterruptRequested = TRUE; 839 840 /* Set DPC inserted */ 841 DpcInserted = TRUE; 842 } 843 } 844 } 845 } 846 } 847 848 /* Release the lock */ 849 KiReleaseSpinLock(&DpcData->DpcLock); 850 851 /* Check if the DPC was inserted */ 852 if (DpcInserted) 853 { 854 /* Check if this was SMP */ 855 if (Prcb != CurrentPrcb) 856 { 857 /* It was, request and IPI */ 858 KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC); 859 } 860 else 861 { 862 /* It wasn't, request an interrupt from HAL */ 863 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 864 } 865 } 866 867 /* Lower IRQL */ 868 KeLowerIrql(OldIrql); 869 return DpcConfigured; 870 } 871 872 /* 873 * @implemented 874 */ 875 BOOLEAN 876 NTAPI 877 KeRemoveQueueDpc(IN PKDPC Dpc) 878 { 879 PKDPC_DATA DpcData; 880 BOOLEAN Enable; 881 ASSERT_DPC(Dpc); 882 883 /* Disable interrupts */ 884 Enable = KeDisableInterrupts(); 885 886 /* Get DPC data */ 887 DpcData = Dpc->DpcData; 888 if (DpcData) 889 { 890 /* Acquire the DPC lock */ 891 KiAcquireSpinLock(&DpcData->DpcLock); 892 893 /* Make sure that the data didn't change */ 894 if (DpcData == Dpc->DpcData) 895 { 896 /* Remove the DPC */ 897 DpcData->DpcQueueDepth--; 898 RemoveEntryList(&Dpc->DpcListEntry); 899 Dpc->DpcData = NULL; 900 } 901 902 /* Release the lock */ 903 KiReleaseSpinLock(&DpcData->DpcLock); 904 } 905 906 /* Re-enable interrupts */ 907 if (Enable) _enable(); 908 909 /* Return if the DPC was in the queue or not */ 910 return DpcData ? TRUE : FALSE; 911 } 912 913 /* 914 * @implemented 915 */ 916 VOID 917 NTAPI 918 KeFlushQueuedDpcs(VOID) 919 { 920 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 921 PAGED_CODE(); 922 923 /* Check if this is an UP machine */ 924 if (KeActiveProcessors == 1) 925 { 926 /* Check if there are DPCs on either queues */ 927 if ((CurrentPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) || 928 (CurrentPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0)) 929 { 930 /* Request an interrupt */ 931 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 932 } 933 } 934 else 935 { 936 /* FIXME: SMP support required */ 937 ASSERT(FALSE); 938 } 939 } 940 941 /* 942 * @implemented 943 */ 944 BOOLEAN 945 NTAPI 946 KeIsExecutingDpc(VOID) 947 { 948 /* Return if the Dpc Routine is active */ 949 return KeGetCurrentPrcb()->DpcRoutineActive; 950 } 951 952 /* 953 * @implemented 954 */ 955 VOID 956 NTAPI 957 KeSetImportanceDpc (IN PKDPC Dpc, 958 IN KDPC_IMPORTANCE Importance) 959 { 960 /* Set the DPC Importance */ 961 ASSERT_DPC(Dpc); 962 Dpc->Importance = Importance; 963 } 964 965 /* 966 * @implemented 967 */ 968 VOID 969 NTAPI 970 KeSetTargetProcessorDpc(IN PKDPC Dpc, 971 IN CCHAR Number) 972 { 973 /* Set a target CPU */ 974 ASSERT_DPC(Dpc); 975 Dpc->Number = Number + MAXIMUM_PROCESSORS; 976 } 977 978 /* 979 * @implemented 980 */ 981 VOID 982 NTAPI 983 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine, 984 IN PVOID Context) 985 { 986 ULONG Barrier = KeNumberProcessors; 987 KIRQL OldIrql; 988 DEFERRED_REVERSE_BARRIER ReverseBarrier; 989 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL); 990 991 // 992 // The barrier is the number of processors, each processor will decrement it 993 // by one, so when all processors have run the DPC, the barrier reaches zero 994 // 995 ReverseBarrier.Barrier = Barrier; 996 ReverseBarrier.TotalProcessors = Barrier; 997 998 // 999 // But we don't need the barrier on UP, since we can simply call the routine 1000 // directly while at DISPATCH_LEVEL and not worry about anything else 1001 // 1002 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 1003 Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier); 1004 KeLowerIrql(OldIrql); 1005 } 1006 1007 /* 1008 * @implemented 1009 */ 1010 VOID 1011 NTAPI 1012 KeSignalCallDpcDone(IN PVOID SystemArgument1) 1013 { 1014 // 1015 // Decrement the barrier, which is actually the processor count 1016 // 1017 InterlockedDecrement((PLONG)SystemArgument1); 1018 } 1019 1020 /* 1021 * @implemented 1022 */ 1023 BOOLEAN 1024 NTAPI 1025 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2) 1026 { 1027 // 1028 // There is nothing to do on UP systems -- the processor calling this wins 1029 // 1030 UNREFERENCED_PARAMETER(SystemArgument2); 1031 return TRUE; 1032 } 1033 1034 /* EOF */ 1035