#!/usr/bin/perl
# =======================================================================================
#
#      Filename:  likwid-setFrequencies
#
#      Description:  Application allowing to change core frequencies
#
#      Version:   <VERSION>
#      Released:  <DATE>
#
#      Author:  Jan Treibig (jt), jan.treibig@gmail.com
#      Project:  likwid
#
#      Copyright (C) 2014 Jan Treibig
#
#      This program is free software: you can redistribute it and/or modify it under
#      the terms of the GNU General Public License as published by the Free Software
#      Foundation, either version 3 of the License, or (at your option) any later
#      version.
#
#      This program is distributed in the hope that it will be useful, but WITHOUT ANY
#      WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
#      PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
#      You should have received a copy of the GNU General Public License along with
#      this program.  If not, see <http://www.gnu.org/licenses/>.
#
# =======================================================================================

use Getopt::Std;

my $LIKWIDPIN  = '<PREFIX>/bin/likwid-pin';
my $SYSPATH = '/sys/devices/system/cpu';
my $SYSCMD = '<PREFIX>/sbin/likwid-setFreq';
my $domain = 'N';
my $governor = 'ondemand';
my @processors;
my %frequencies;
my $freq_string;
use vars qw/ %opt /;

sub init
{
    my $opt_string = 'g:c:f:lph';
    getopts( $opt_string, \%opt ) or usage();
    usage() if $opt{h};
    if (scalar(keys %opt) == 0)
    {
    	usage();
    }
}

sub usage
{
    print STDERR << "EOF";

This script allows to switch governors and set fixed
frequencies on Linux system.

usage: $0 [-hlp] [-g governor] [-c domain] [-f frequency]
-h          : this (help) message
-p          : print current frequencies
-l          : list available frequencies
-c domain   : likwid thread domain which to apply settings
              (set to N if omitted)
-g governor : set governor (ondemand, performance, turbo)
              (set to ondemand if omitted)
-f frequency: set fixed frequency, implicitly sets userspace
              governor

example: $0 -c S0 -f 2.7 (set all CPUs on socket 0 to 2.7 GHz)
EOF
    exit;
}

sub extractAvailableFrequencies
{
    my @tmp_keys;
    open FILE, "<$SYSPATH/cpu0/cpufreq/scaling_available_frequencies";
    my $tmp = <FILE>;
    my @list = split(/ /,$tmp);
    close FILE;
    $frequencies{'turbo'} = $list[0];

    foreach my $item ( @list ) {
        if( not $item =~ /\n/ ) {
            my $key = $item/1000000.0;
            push @tmp_keys, $key;
            $frequencies{$key} = $item;
        }
    }

    $freq_string = join(' ', sort @tmp_keys);
}

sub extractProcessorList
{
    my $output = `$LIKWIDPIN -p`;
    my $found = 0;

    foreach my $line (split("\n",$output)) {
        if ($line =~ /Tag ([NSCM][0-9]*): ([0-9 ]+)/) {
            if ($domain eq $1) {
                $found = 1;
                @processors =  split(/ /,$2);
                last;
            }
        }
    }

    if ( not $found ) {
		print "Domain $domain not available!\n";
        exit;
    }
}


init();

if (! -s $SYSCMD) {
    die "ERROR Binary $SYSCMD not existing!\n\n";
}

if ( defined $opt{c}) {
    $domain = $opt{c};
}

extractProcessorList();
extractAvailableFrequencies();

if ($opt{f}) {
    $freq = $opt{f};

	if (not exists($frequencies{$freq})) {
		print "Frequency $freq not available!\nPlease select one of $freq_string\n";
		exit;
	}

	foreach my $processID (@processors) {
#		print "$SYSCMD $processID $frequencies{$freq}\n";
        system("$SYSCMD $processID $frequencies{$freq}");
	}
}

if ($opt{p}) {
	foreach my $processID (@processors) {
		open FILE,"<$SYSPATH/cpu".$processID."/cpufreq/scaling_governor";
		my $gov = <FILE>;
		chomp $gov;
		close FILE;
		open FILE,"<$SYSPATH/cpu".$processID."/cpufreq/scaling_cur_freq";
		my $freq = <FILE>;
		chomp $freq;
		close FILE;
		print "CPU $processID: governor $gov frequency $freq\n"
	}
	exit;
}

if ($opt{l}) {
    print "Available frequencies: $freq_string\n";
    exit;
}

if ($opt{g} eq 'turbo') {
    foreach my $processID (@processors) {
#        print "$SYSCMD $processID $frequencies{turbo}\n";
        system("$SYSCMD $processID $frequencies{turbo}");
    }
    exit;
}

if ($opt{g}) {
    $governor = $opt{g};
    if (($governor ne "ondemand") and ($governor ne "performance")) {
        print "Governor $governor not valid\n";
    } else {
        print "Set governor in domain $domain to $governor \n";
        foreach my $processID (@processors) {
            system("$SYSCMD $processID 0 $governor");
        }
    }
}

# vim: foldmethod=marker foldmarker=#<#,#>#
