You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1333 lines
36 KiB
1333 lines
36 KiB
/*++
|
|
|
|
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();
|
|
}
|