#!/usr/bin/perl

use strict;

# Ideen
# Wenn der Load 1. dauerhaft abnimmt und 2. unter eine Grenze sinkt, wird die CPU gethrottelt.
# Wenn der Load 1. dauerhaft zunimmt und 2. über eine Grenze steigt, wird die CPU beschleunigt.

print "CPU throttling adjuster\n";

my $throttleFile = '/proc/acpi/processor/CPU0/throttling';
my $maxThrottle = &getMaxState;

if ($maxThrottle <= 0)
{
	print "Throttling not supported on this CPU.\n";
	exit;
}

my $upperLimit = 0.25;
my $maxLimit = 0.85;
my $limitSteps = 0.1;
my $lowerLimit = 0.05;
my $slowdownWait = 10;
my $speedupWait = 3;
my $checkInterval = 2;

print "Supported throttling states: T0 - T$maxThrottle\n";

my $waited = 0;
while(1)
{
	my $load = &getLoad;
	my $curThrottle = &getThrottling;
	print "$load $curThrottle\n";
	
	if ($curThrottle > 0 && $load > $upperLimit && $waited >= $speedupWait)
	{
		# Need more power!
		my $decrValue = ($load - $upperLimit) / $limitSteps;
		
		if ($load > $maxLimit)
		{
			&setThrottling(0);
		}
		else
		{
			&setThrottling($curThrottle - $decrValue);
		}
		$waited = 0;
	}
	elsif ($curThrottle < $maxThrottle && $load < $lowerLimit && $waited >= $slowdownWait)
	{
		# Don't need that much power
		&setThrottling($curThrottle + 1);
		$waited = 0;
	}
	
	$waited++;
	sleep($checkInterval);
}

sub getLoad
{
	#my @ret = ();
	my $uptime = `cat /proc/loadavg`;
	my @l = split (/\s+/, $uptime);
	return $l[0];
}

sub getThrottling
{
	open(IN, "<$throttleFile") or return $maxThrottle + 1;
	while(<IN>)
	{
		/active state:\s+T(\d+)/sgi;
		if (length($1) > 0)
		{
			close(IN);
			return int($1);
		}
	}
	close(IN);
	return $maxThrottle + 2;
}

sub setThrottling
{
	my $t = int(shift);
	$t = 0 if ($t < 0);
	$t = $maxThrottle if ($t > $maxThrottle);
	
	print "Throttling to $t\n";
	`echo -n $t > $throttleFile`;
}

sub getMaxState
{
	open(IN, "<$throttleFile") or return 0;
	while(<IN>)
	{
		/state count:\s+(\d+)/sgi;
		if (length($1) > 0)
		{
			close(IN);
			return int($1) - 1;
		}
	}
	close(IN);
	return 0;
}




