1291 lines
33 KiB
C
1291 lines
33 KiB
C
/*++
|
||
|
||
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(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(TempString, "%d.%dK", k / 10, k % 10);
|
||
#endif
|
||
|
||
}
|
||
return TempString;
|
||
#else
|
||
return "";
|
||
#endif
|
||
}
|
||
|
||
PUCHAR
|
||
PopTimeString(
|
||
OUT PUCHAR TimeString,
|
||
IN ULONGLONG CurrentTime
|
||
)
|
||
{
|
||
#if DBG
|
||
LARGE_INTEGER curTime;
|
||
TIME_FIELDS exCurTime;
|
||
|
||
curTime.QuadPart = CurrentTime;
|
||
RtlTimeToTimeFields( &curTime, &exCurTime );
|
||
|
||
sprintf(
|
||
TimeString,
|
||
"%d:%02d:%02d.%03d",
|
||
exCurTime.Hour,
|
||
exCurTime.Minute,
|
||
exCurTime.Second,
|
||
exCurTime.Milliseconds
|
||
);
|
||
return TimeString;
|
||
#else
|
||
return "";
|
||
#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 s[40];
|
||
UCHAR t[40];
|
||
|
||
//
|
||
// 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
|
||
) );
|
||
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
|
||
) );
|
||
|
||
} 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;
|
||
ULONG i;
|
||
UCHAR s[40];
|
||
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
|
||
|
||
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;
|
||
ULONG i;
|
||
#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();
|
||
}
|