|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
thermal.c
Abstract:
This module interfaces the policy manager a thermal zone device
Author:
Ken Reneris (kenr) 17-Jan-1997
Revision History:
--*/
#include "pop.h"
#include "stdio.h" // for sprintf
VOID PopThermalZoneCleanup ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
PUCHAR PopTemperatureString ( OUT PUCHAR TempString, IN ULONG TenthsKelvin );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PopThermalDeviceHandler)
#endif
PUCHAR PopTemperatureString ( OUT PUCHAR TempString, IN ULONG TenthsKelvin ) { #if DBG
ULONG k, c, f;
k = TenthsKelvin; if (k < 2732) {
c = 2732 - k; f = c * 9 / 5 + 320; #if 0
sprintf(TempString, "%d.%dk, -%d.%dc, -%d.%df", k / 10, k % 10, c / 10, c % 10, f / 10, f % 10 ); #else
sprintf((PCHAR) TempString, "%d.%dK", k / 10, k % 10); #endif
} else {
c = k - 2732; f = c * 9 / 5 + 320; #if 0
sprintf (TempString, "%d.%dk, %d.%dc, %d.%df", k / 10, k % 10, c / 10, c % 10, f / 10, f % 10 ); #else
sprintf((PCHAR) TempString, "%d.%dK", k / 10, k % 10); #endif
} return TempString; #else
UNREFERENCED_PARAMETER (TempString); UNREFERENCED_PARAMETER (TenthsKelvin); return (PUCHAR)""; #endif
}
PUCHAR PopTimeString( OUT PUCHAR TimeString, IN ULONGLONG CurrentTime ) { #if DBG
LARGE_INTEGER curTime; TIME_FIELDS exCurTime;
curTime.QuadPart = CurrentTime; RtlTimeToTimeFields( &curTime, &exCurTime );
sprintf( (PCHAR) TimeString, "%d:%02d:%02d.%03d", exCurTime.Hour, exCurTime.Minute, exCurTime.Second, exCurTime.Milliseconds ); return TimeString; #else
UNREFERENCED_PARAMETER (TimeString); UNREFERENCED_PARAMETER (CurrentTime); return (PUCHAR)""; #endif
}
VOID PopThermalUpdateThrottle( IN PPOP_THERMAL_ZONE ThermalZone, IN ULONGLONG CurrentTime ) /*++
Routine Description:
This routine is called to recalculate the throttle value of the thermal zone
This function is not re-entrant. Each ThermalZone can only be in this code exactly once
Arguments:
ThermalZone - The structure for which the throttle value should be recalculated CurrentTime - The time at which the kernel handler was invoked
Return Value:
None
--*/ { BOOLEAN doThrottle = FALSE; KIRQL oldIrql; LONG part1; LONG part2; LONG throttleDelta; LONG currentThrottle = 0; LONG minThrottle; LONG minThrottle2;
PKPRCB prcb; UCHAR t[40]; #if DBG
UCHAR s[40]; #endif
//
// If there are no processor throttling capablities, this function does
// nothing useful. The same applies if the thermal zone does not belong
// to a processor
//
if (!ThermalZone->Info.Processors) {
return;
}
//
// Make sure that we have the time in a format that we can print it out
// Note that by using the time that was passed in (instead of fetching
// again), we make sure that the printouts always read the same thing
//
PopTimeString(t, CurrentTime );
//
// Make sure to run on the context of the appropriate processor
//
KeSetSystemAffinityThread( ThermalZone->Info.Processors );
//
// Make sure to raise IRQL so that we can synchronize
//
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
//
// Is there any support for throttling on this processor?
//
prcb = KeGetCurrentPrcb(); if ((prcb->PowerState.Flags & PSTATE_SUPPORTS_THROTTLE) == 0) {
KeLowerIrql( oldIrql ); KeRevertToUserAffinityThread(); return;
}
//
// Do these calculations now, while its safe
//
minThrottle2 = (LONG) (PopPolicy->MinThrottle * PO_TZ_THROTTLE_SCALE); minThrottle = prcb->PowerState.ProcessorMinThrottle * PO_TZ_THROTTLE_SCALE;
//
// No longer need to lock with the processor
//
KeLowerIrql( oldIrql ); KeRevertToUserAffinityThread();
//
// If Temperature isn't above the passive trip point, stop passive cooling
//
if (ThermalZone->Info.CurrentTemperature < ThermalZone->Info.PassiveTripPoint) {
//
// If we aren't already throttling, then there isn't much to do
//
if (!(ThermalZone->Flags & PO_TZ_THROTTLING) ) {
return;
}
//
// Make sure that we wait long enough...
//
if ( (CurrentTime - ThermalZone->LastTime) < ThermalZone->SampleRate) {
return;
}
//
// We were throttling, so now we must stop
//
doThrottle = FALSE; currentThrottle = PO_TZ_NO_THROTTLE; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - ending throttle #1\n", ThermalZone, t ) );
//
// Remove Thermal Throttle Flag since we are done throttling
//
KeSetSystemAffinityThread(ThermalZone->Info.Processors); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
prcb = KeGetCurrentPrcb(); RtlInterlockedClearBits(&prcb->PowerState.Flags, PSTATE_THERMAL_THROTTLE_APPLIED);
//
// Set up timer to fire now that we have completed our thermal event.
//
PopSetTimer(&prcb->PowerState, prcb->PowerState.CurrentThrottle); KeLowerIrql(oldIrql); KeRevertToUserAffinityThread();
goto PopThermalUpdateThrottleExit;
}
//
// Are we already throttling?
//
if (!(ThermalZone->Flags & PO_TZ_THROTTLING) ) {
//
// Throttling is not enabled, but the thermal zone has exceeded
// it's passive cooling point. We need to start throttling
//
doThrottle = TRUE; currentThrottle = PO_TZ_NO_THROTTLE;
ASSERT( ThermalZone->Info.SamplingPeriod && ThermalZone->Info.SamplingPeriod < 4096 );
ThermalZone->SampleRate = 1000000 * ThermalZone->Info.SamplingPeriod; ThermalZone->LastTime = 0; ThermalZone->LastTemp = ThermalZone->Info.PassiveTripPoint;
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - starting to throttle\n", ThermalZone, t ) );
//
// Set Thermal Throttle Flag since we are now throttling
//
KeSetSystemAffinityThread(ThermalZone->Info.Processors); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
prcb = KeGetCurrentPrcb(); RtlInterlockedSetBits(&prcb->PowerState.Flags, PSTATE_THERMAL_THROTTLE_APPLIED); KeLowerIrql(oldIrql); KeRevertToUserAffinityThread();
} else if ( (CurrentTime - ThermalZone->LastTime) < ThermalZone->SampleRate) {
//
// The sample period has not yet expired, so wait until it has
//
return;
} else {
//
// We need to get the current throttle value since our calculations
// will use it
//
// It is not necessary to synchronize access to this variable since
// the flags are not also being accessed at the same time.
//
// KeAcquireSpinLock( &PopThermalLock, &oldIrql );
currentThrottle = ThermalZone->Throttle; // KeReleaseSpinLock( &PopThermalLock, oldIrql );
}
//
// Compute throttle adjustment
//
part1 = ThermalZone->Info.CurrentTemperature - ThermalZone->LastTemp; part2 = ThermalZone->Info.CurrentTemperature - ThermalZone->Info.PassiveTripPoint; throttleDelta = ThermalZone->Info.ThermalConstant1 * part1 + ThermalZone->Info.ThermalConstant2 * part2; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - LastTemp %s ThrottleDelta = %d.%d%%\n", ThermalZone, t, PopTemperatureString(s, ThermalZone->LastTemp), (throttleDelta / 10), (throttleDelta % 10) ) );
//
// Only apply the throttle adjustment if it is in the same
// direction as the tempature motion.
//
if ( (part1 ^ throttleDelta) >= 0) {
currentThrottle -= throttleDelta;
#if DBG
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Subtracting delta from throttle\n", ThermalZone, t) );
} else {
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - TempDelta (%d.%d) ^ (%d.%d) < 0)\n", ThermalZone, t, (part1 / 10), (part1 % 10), (throttleDelta / 10), (throttleDelta % 10) ) );
#endif
}
//
// If throttle is over 100% then we're done throttling
//
if (currentThrottle > PO_TZ_NO_THROTTLE) {
currentThrottle = PO_TZ_NO_THROTTLE; doThrottle = FALSE; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - ending throttle #2\n", ThermalZone, t) );
} else {
//
// Show the world what the two mininums are
//
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Min #1 %d.%d Min #2 %d.%d \n", ThermalZone, t, (minThrottle / 10), (minThrottle % 10), (minThrottle2 / 10), (minThrottle2 % 10) ) );
if (currentThrottle < minThrottle) {
currentThrottle = minThrottle;
}
//
// Remember to start throttling
//
doThrottle = TRUE;
}
PopThermalUpdateThrottleExit:
//
// Do this at the end
//
ThermalZone->LastTemp = ThermalZone->Info.CurrentTemperature; ThermalZone->LastTime = CurrentTime;
//
// At this point, we will set and remember the value that we calculated
// in the above function
//
KeAcquireSpinLock( &PopThermalLock, &oldIrql); if (doThrottle) {
ThermalZone->Flags |= PO_TZ_THROTTLING; ThermalZone->Throttle = currentThrottle;
} else {
ThermalZone->Flags &= ~PO_TZ_THROTTLING; ThermalZone->Throttle = PO_TZ_NO_THROTTLE;
}
//
// Apply thermal zone throttles to all effected processors
//
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - throttle set to %d.%d\n", ThermalZone, t, (ThermalZone->Throttle / 10), (ThermalZone->Throttle % 10) ) );
KeReleaseSpinLock( &PopThermalLock, oldIrql );
//
// Make sure to apply the new throttle
//
PopApplyThermalThrottle ();
//
// Done
//
return; }
VOID PopThermalDeviceHandler ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
N.B. PopPolicyLock must be held.
Arguments:
DeviceObject - DeviceObject of the switch device
Irp - Irp which has completed
Context - type of switch device
Return Value:
None.
--*/ { BOOLEAN sendActiveIrp = FALSE; PIO_STACK_LOCATION irpSp; PPOP_THERMAL_ZONE thermalZone; LARGE_INTEGER dueTime; ULONGLONG currentTime; ULONG activePoint; #if DBG
ULONG i; UCHAR s[40]; #endif
UCHAR t[40];
ASSERT_POLICY_LOCK_OWNED();
irpSp = IoGetCurrentIrpStackLocation(Irp); thermalZone = (PPOP_THERMAL_ZONE) Context; currentTime = KeQueryInterruptTime (); PopTimeString(t, currentTime );
//
// Irp had an error. See if the thermal zone is being removed
//
if (Irp->IoStatus.Status == STATUS_NO_SUCH_DEVICE) {
//
// Thermal zone device has disappeared, clean up
//
thermalZone->State = PO_TZ_NO_STATE; thermalZone->Flags |= PO_TZ_CLEANUP;
//
// Pass it to the DPC function to ensure the timer & dpc are idle
//
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - going away\n", thermalZone, t) ); dueTime.QuadPart = -1; KeSetTimer (&thermalZone->PassiveTimer, dueTime, &thermalZone->PassiveDpc);
//
// Do not issue next IRP
//
return ; }
//
// If irp completed with success, handle it
//
if (NT_SUCCESS(Irp->IoStatus.Status)) { switch (thermalZone->State) { case PO_TZ_READ_STATE:
//
// Read of thermal information has completed.
//
PoPrint( PO_THERM, ("Thermal - Zone %p - %s\n Current Temp: %s", thermalZone, t, PopTemperatureString(s, thermalZone->Info.CurrentTemperature) ) ); PoPrint( PO_THERM, (" Critical Trip: %s", PopTemperatureString(s, thermalZone->Info.CriticalTripPoint) ) ); PoPrint( PO_THERM, (" Passive Trip: %s\n", PopTemperatureString(s, thermalZone->Info.PassiveTripPoint) ) ); #if DBG
for ( i=0; i < thermalZone->Info.ActiveTripPointCount; i++) {
PoPrint( PO_THERM, (" Active Trip %d: %s\n", i, PopTemperatureString(s, thermalZone->Info.ActiveTripPoint[i]) ) ); } #endif
//
// Update the throttle
//
PopThermalUpdateThrottle( thermalZone, currentTime );
//
// Check for change in active cooling
//
for (activePoint = 0; activePoint < thermalZone->Info.ActiveTripPointCount; activePoint++) {
if (thermalZone->Info.CurrentTemperature >= thermalZone->Info.ActiveTripPoint[activePoint]) {
break;
}
} if (activePoint != thermalZone->ActivePoint) {
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Pending Coooling Point is %x\n", thermalZone, t, activePoint) ); thermalZone->PendingActivePoint = (UCHAR) activePoint; sendActiveIrp = TRUE;
}
//
// Check for critical trip point
//
if (thermalZone->Info.CurrentTemperature > thermalZone->Info.CriticalTripPoint) { PoPrint( PO_THERM | PO_ERROR, ("Thermal - Zone %p - %s - Above critical (%x %x)\n", thermalZone, t, thermalZone->Info.CurrentTemperature, thermalZone->Info.CriticalTripPoint ));
PopCriticalShutdown (PolicyDeviceThermalZone); }
break;
case PO_TZ_SET_MODE:
//
// Thermal zone cooling mode was successfully set
//
thermalZone->Mode = thermalZone->PendingMode; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - cooling mode set to %x\n", thermalZone, t, thermalZone->Mode) );
//
// We want to force a resend of the Active Trip Point irp since
// there is a situation where the ACPI driver decides that as a
// matter of policy, it will not actually turn on fans if the
// system is in passive cooling mode. If we go back to active
// mode, then we want to turn the fans on. The same holds true
// if the fans are running and we transition to passive mode.
//
sendActiveIrp = TRUE;
break;
case PO_TZ_SET_ACTIVE: thermalZone->ActivePoint = thermalZone->PendingActivePoint; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - active cooling point set to %x\n", thermalZone, t, thermalZone->ActivePoint) ); break;
default: PopInternalAddToDumpFile( Irp, sizeof(IRP), DeviceObject, NULL, NULL, NULL ); KeBugCheckEx( INTERNAL_POWER_ERROR, 0x500, POP_THERMAL, (ULONG_PTR)Irp, (ULONG_PTR)DeviceObject ); }
#if DBG
} else if (Irp->IoStatus.Status != STATUS_DEVICE_NOT_CONNECTED && Irp->IoStatus.Status != STATUS_CANCELLED) {
//
// Unexpected error
//
PoPrint( PO_ERROR, ("Thermal - Zone - %p - %s - unexpected error %x\n", thermalZone, t, Irp->IoStatus.Status));
#endif
}
//
// Determine type of irp to send zone
//
irpSp = IoGetNextIrpStackLocation(Irp); if (sendActiveIrp) {
//
// Thermal zone active cooling point not current
//
thermalZone->State = PO_TZ_SET_ACTIVE;
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_RUN_ACTIVE_COOLING_METHOD; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG); irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; Irp->AssociatedIrp.SystemBuffer = &thermalZone->PendingActivePoint;
PoPrint( PO_THERM, ("Thermal - Zone %p - %s Sending Run Cooling Method: %x\n", thermalZone, t, thermalZone->PendingActivePoint) );
} else if (thermalZone->Mode != PopCoolingMode) {
//
// Thermal zone cooling mode does not match system cooling mode.
//
thermalZone->State = PO_TZ_SET_MODE; thermalZone->PendingMode = (UCHAR) PopCoolingMode;
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_THERMAL_SET_COOLING_POLICY; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(thermalZone->PendingMode); irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; Irp->AssociatedIrp.SystemBuffer = &thermalZone->PendingMode;
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Sending Set Cooling Policy: %x\n", thermalZone, t, thermalZone->PendingMode) );
} else {
//
// Issue query to get tempture of thermal zone
//
thermalZone->State = PO_TZ_READ_STATE; if (thermalZone->Flags & PO_TZ_THROTTLING && thermalZone->SampleRate) {
//
// Compute time for next read
//
dueTime.QuadPart = thermalZone->LastTime + thermalZone->SampleRate; if (dueTime.QuadPart > (LONGLONG) currentTime) {
#if DBG
PoPrint( PO_THERM, ("Thermal - Zone %x - %s waituntil", thermalZone, t) ); PoPrint( PO_THERM, (" %s (%d sec)\n", PopTimeString(t, dueTime.QuadPart), ( (thermalZone->SampleRate ) / (US2TIME * US2SEC) ) ) ); PopTimeString(t, currentTime); #endif
//
// Set timer for duration of wait
//
dueTime.QuadPart = currentTime - dueTime.QuadPart; KeSetTimer (&thermalZone->PassiveTimer, dueTime, &thermalZone->PassiveDpc);
} else {
//
// Perform non-blocking IRP query information to get the Temperature now
//
thermalZone->Info.ThermalStamp = 0;
} }
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_THERMAL_QUERY_INFORMATION; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(thermalZone->Info); irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(thermalZone->Info); Irp->AssociatedIrp.SystemBuffer = &thermalZone->Info; PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Sending Query Temp - ThermalStamp = %x\n", thermalZone, t, thermalZone->Info.ThermalStamp) );
}
//
// Send irp to driver
//
IoSetCompletionRoutine (Irp, PopCompletePolicyIrp, NULL, TRUE, TRUE, TRUE); IoCallDriver (DeviceObject, Irp); }
VOID PopThermalZoneCleanup ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { KIRQL oldIrql; PPOP_THERMAL_ZONE thermalZone;
ASSERT_POLICY_LOCK_OWNED();
thermalZone = (PPOP_THERMAL_ZONE) Context;
//
// Acquire the Spinlock required to delete the thermal zone
//
KeAcquireSpinLock( &PopThermalLock, &oldIrql );
//
// Delete thermal zone from the linked list of thermal zones
//
RemoveEntryList (&thermalZone->Link);
//
// Remember what the irp associated with the thermal zone was
//
Irp = thermalZone->Irp;
//
// Make sure to cleanup the entry, so that any further reference is
// bogus
//
#if DBG
RtlZeroMemory( thermalZone, sizeof(POP_THERMAL_ZONE) ); #endif
//
// Release the spinlock that was protecting the thermal zone
//
KeReleaseSpinLock( &PopThermalLock, oldIrql );
//
// Free the Irp that we had associated with it...
//
IoFreeIrp (Irp);
//
// Free the reference we had to the device object
//
ObDereferenceObject (DeviceObject);
//
// Finally, free the memory associated with the thermal zone
//
ExFreePool (thermalZone); }
VOID PopThermalZoneDpc ( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
Timer dpc used to unblock pending read of thermal zone Temperature in order to get the Temperature now
Arguments:
DeferredConext - ThermalZone
Return Value:
None.
--*/ { PPOP_THERMAL_ZONE thermalZone; PIO_STACK_LOCATION irpSp; #if DBG
ULONGLONG currentTime; UCHAR t[40];
currentTime = KeQueryInterruptTime(); PopTimeString(t, currentTime); #endif
UNREFERENCED_PARAMETER (Dpc); UNREFERENCED_PARAMETER (SystemArgument1); UNREFERENCED_PARAMETER (SystemArgument2);
thermalZone = (PPOP_THERMAL_ZONE) DeferredContext;
//
// If cleanup is set queue the thread zone to be cleaned up
//
if (thermalZone->Flags & PO_TZ_CLEANUP) {
//
// The irp is idle, use it to queue the request to the cleanup procedure
//
irpSp = IoGetCurrentIrpStackLocation(thermalZone->Irp); irpSp += 1; // get previous location
irpSp->Parameters.Others.Argument3 = (PVOID) PopThermalZoneCleanup; PopCompletePolicyIrp (NULL, thermalZone->Irp, NULL);
}
//
// Time to read current Temperature to adjust passive cooling throttle.
// If the current state is reading, then cancel it to either to the
// Temperature now or to issue a non-blocking thermal read state
//
if (thermalZone->State == PO_TZ_READ_STATE) {
#if DBG
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - Cancel Irp %p\n", thermalZone, t, thermalZone->Irp) ); #endif
IoCancelIrp (thermalZone->Irp);
#if DBG
} else {
PoPrint( PO_THERM, ("Thermal - Zone %p - %s - In state %08lx\n", thermalZone, t, thermalZone->State ) );
#endif
} }
VOID PopApplyThermalThrottle ( VOID ) /*++
Routine Description:
Computes each processors best possible speed as dictated by thermal restrants. Will also examine the thermal settings to determine if the cooling mode should be adjusted.
Arguments:
Return Value:
None.
--*/ { KAFFINITY processors; KAFFINITY currentAffinity; KAFFINITY thermalProcessors; KIRQL oldIrql; PLIST_ENTRY link; PPOP_THERMAL_ZONE thermalZone; PPROCESSOR_POWER_STATE pState; UCHAR thermalLimit; UCHAR thermalLimitIndex; UCHAR forcedLimit; UCHAR forcedLimitIndex; UCHAR limit; UCHAR index; ULONG thermalThrottle; ULONG forcedThrottle; ULONG mode; ULONG processorNumber; #if DBG
ULONGLONG currentTime; UCHAR t[40];
currentTime = KeQueryInterruptTime(); PopTimeString(t, currentTime); #endif
ASSERT_POLICY_LOCK_OWNED();
//
// If the system doesn't have processor throttle capabilities then
// don't bother
//
if (!PopCapabilities.ProcessorThrottle) {
return ;
}
#if 0
//
// Compute overthrottled into thermal zone throttle units
//
MinThrottle = PopPolicy->MinThrottle * PO_TZ_THROTTLE_SCALE; #endif
//
// Make sure to hold the spinlock for find the LCD. We don't actually
// use the lock to walk the list, but we need it to reference the
// Throttle Value
//
KeAcquireSpinLock( &PopThermalLock, &oldIrql );
//
// Get the LCD of the thermal zones
//
thermalThrottle = PO_TZ_NO_THROTTLE; thermalProcessors = 0; for (link = PopThermal.Flink; link != &PopThermal; link = link->Flink) {
thermalZone = CONTAINING_RECORD (link, POP_THERMAL_ZONE, Link);
//
// Handle zones which are throttling
//
if (thermalZone->Flags & PO_TZ_THROTTLING) {
//
// Include processors for this zone
//
thermalProcessors |= thermalZone->Info.Processors;
//
// If zone is less then current thermal throttle, lower it
//
if ((ULONG) thermalZone->Throttle < thermalThrottle) { thermalThrottle = thermalZone->Throttle; }
//
// Until I can get the user guys to add a thermal tab such that
// the OverThrottle policy becomes configurable by the user,
// always putting the system to sleep on an overthrottle is a bad
// idea. Note that there is some code in PopThermalDeviceHandler
// that will have to be changed when the following is uncommented
//
#if 0
//
// Check if zone has overthrottled the system
//
if ((ULONG) thermalZone->Throttle < MinThrottle) { #if DBG
PoPrint( PO_THERM | PO_ERROR, ("Thermal - Zone %p - %s - overthrottled (%x %x)\n", thermalZone, t, thermalZone->Throttle, MinThrottle) ); #endif
//
// If we are going to do an S1-Critical standby, then we
// will return immediately and not try to throttle the
// CPU
//
PopSetPowerAction ( &thermalZone->OverThrottled, 0, &PopPolicy->OverThrottled, PowerSystemSleeping1, SubstituteLightestOverallDownwardBounded );
return;
} else {
//
// Zone is not overthrottled, make sure trigger is clear
//
thermalZone->OverThrottled.Flags &= ~(PO_TRG_USER | PO_TRG_SYSTEM);
} #endif
} }
//
// Done with the lock
//
KeReleaseSpinLock( &PopThermalLock, oldIrql );
#if DBG
PoPrint( PO_THERM, ("PopApplyThermalThrottle - %s - Thermal throttle = %d.%d\n", t, (thermalThrottle / 10), (thermalThrottle % 10) ) ); #endif
//
// Use Min of thermal throttle and forced system throttle
//
forcedThrottle = PopGetThrottle() * PO_TZ_THROTTLE_SCALE; if (thermalThrottle > forcedThrottle) {
thermalThrottle = forcedThrottle; #if DBG
PoPrint( PO_THERM, ("PopApplyThermalThrottle - %s - Set to Forced throttle = %d.%d\n", t, (thermalThrottle / 10), (thermalThrottle % 10) ) ); #endif
}
//
// Check active vs. passive cooling
//
if (thermalThrottle <= (ULONG) PopPolicy->FanThrottleTolerance * PO_TZ_THROTTLE_SCALE) {
//
// Throttle is below tolerance, we should be in active cooling
//
mode = PO_TZ_ACTIVE;
} else {
//
// Throttle is above tolerance. If optimize for power is set then
// use passive cooling else use active cooling
//
mode = PopPolicy->OptimizeForPower ? PO_TZ_PASSIVE : PO_TZ_ACTIVE;
}
//
// If current cooling mode is not correct, update it
//
if (mode != PopCoolingMode) {
#if DBG
ULONG fanTolerance = (ULONG) PopPolicy->FanThrottleTolerance * PO_TZ_THROTTLE_SCALE;
PoPrint( PO_THERM, ("PopApplyThermalThrottle - %s - Throttle (%d.%d) %s FanTolerance (%d.%d)\n", t, (thermalThrottle / 10), (thermalThrottle % 10), (thermalThrottle <= fanTolerance ? "<=" : ">"), (fanTolerance / 10), (fanTolerance % 10) ) ); PoPrint( PO_THERM, ("PopApplyThermalThrottle - %s - OptimizeForPower is %s\n", t, (PopPolicy->OptimizeForPower ? "True" : "False") ) ); PoPrint( PO_THERM, ("PopApplyThermalThrottle - %s - Changing cooling mode to %s\n", t, (mode == PO_TZ_ACTIVE ? "Active" : "Passive") ) ); #endif
PopCoolingMode = mode;
//
// We are going to touch the Thermal list --- make sure that we hold
// the correct lock
//
KeAcquireSpinLock(&PopThermalLock, &oldIrql );
//
// Cancel any blocked thermal reads in order to send set mode irps
//
for (link = PopThermal.Flink; link != &PopThermal; link = link->Flink) {
thermalZone = CONTAINING_RECORD (link, POP_THERMAL_ZONE, Link); if (thermalZone->State == PO_TZ_READ_STATE) {
IoCancelIrp (thermalZone->Irp);
}
}
//
// Done with the thermal lock
//
KeReleaseSpinLock(& PopThermalLock, oldIrql );
}
//
// Set limit on effected processors
//
processorNumber = 0; currentAffinity = 1; processors = KeActiveProcessors;
do {
if (!(processors & currentAffinity)) {
currentAffinity <<= 1; continue;
} processors &= ~currentAffinity;
//
// We must run on the target processor
//
KeSetSystemAffinityThread(currentAffinity);
//
// We need to be running at DISPATCH_LEVEL to access the
// structures referenced within the pState...
//
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql ); pState = &(KeGetCurrentPrcb()->PowerState);
//
// Does this processor support throttling?
//
if ((pState->Flags & PSTATE_SUPPORTS_THROTTLE) == 0) {
//
// No, then we don't care about it...
//
currentAffinity <<= 1; KeLowerIrql( oldIrql ); continue;
}
//
// Convert throttles to processor buck size. We need to
// do this in the context of the target processor to make
// sure that we get the correct set of perf levels
//
PopRoundThrottle( (UCHAR)(thermalThrottle/PO_TZ_THROTTLE_SCALE), &thermalLimit, NULL, &thermalLimitIndex, NULL ); PopRoundThrottle( (UCHAR)(forcedThrottle/PO_TZ_THROTTLE_SCALE), &forcedLimit, NULL, &forcedLimitIndex, NULL );
#if DBG
PoPrint( PO_THROTTLE, ("PopApplyThermalThrottle - %s - Thermal throttle = %d.%d -> Limit = %d\n", t, (thermalThrottle / 10), (thermalThrottle % 10), thermalLimit ) ); PoPrint( PO_THROTTLE, ("PopApplyThermalThrottle - %s - Forced throttle = %d.%d -> Limit = %d\n", t, (forcedThrottle / 10), (forcedThrottle % 10), forcedLimit ) ); #endif
//
// Figure out which one we are going to use...
//
limit = (thermalProcessors & currentAffinity) ? thermalLimit : forcedLimit; index = (thermalProcessors & currentAffinity) ? thermalLimitIndex : forcedLimitIndex;
//
// Done with current affinity mask
//
currentAffinity <<= 1;
//
// Check processor limits for to see if value is okay
//
if (limit > pState->ProcessorMaxThrottle) {
#if DBG
PoPrint( PO_THROTTLE, ("PopApplyThermalThrottle - %s - Limit (%d) > MaxThrottle (%d)\n", t, limit, pState->ProcessorMaxThrottle) ); #endif
limit = pState->ProcessorMaxThrottle;
} else if (limit < pState->ProcessorMinThrottle) {
#if DBG
PoPrint( PO_THROTTLE, ("PopApplyThermalThrottle - %s - Limit (%d) < MinThrottle (%d)\n", t, limit, pState->ProcessorMinThrottle) ); #endif
limit = pState->ProcessorMinThrottle;
}
//
// Update the limit (if required...)
//
if (pState->ThermalThrottleLimit != limit) {
pState->ThermalThrottleLimit = limit; pState->ThermalThrottleIndex = index; #if DBG
PoPrint( PO_THROTTLE, ("PopApplyThermalThrottle - %s - New Limit (%d) Index (%d)\n", t, limit, index) ); #endif
}
//
// Rever back to our previous IRQL
//
KeLowerIrql( oldIrql );
} while (processors);
//
// We should revert back to the proper affinity
//
KeRevertToUserAffinityThread();
//
// Apply thermal throttles if necessary. Note we always do this
// whether or not the limits were changed. This routine also gets
// called whenever the system transitions from AC to DC, and that
// may also require a throttle update due to dynamic throttling.
//
PopUpdateAllThrottles(); }
|