Ffreq

From Yefu's notes
Jump to: navigation, search
/***************************************************************************
 *   Copyright (C) 2008 by Yefu Wang   *
 *   root@wangyefu.com   *
 *                                                                         *
 *   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 2 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include "yefudebug.h"

#define O_PERIOD 10000

int cpu_num;
int o_min;
int o_max;
int freq;
float o_balance=0;
int *aval_freqs;
int NumOfAval_freqs=0;

unsigned long getms()
{
  struct timeval s;
  gettimeofday(&s,NULL );
  return (s.tv_sec*1000 + s.tv_usec/1000);
}

void init_freq()
{
 cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
 char fname[1024];
 int i;
 for(i=0;i<cpu_num;i++)
 {

   bzero(fname,1024);

   sprintf(fname, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor",i);
   FILE* fd = fopen(fname,"w");
   if(fd==NULL)
   {
    perror("fopen");
    exit(1);
   }

   fprintf(fd, "userspace");
   fclose(fd);
 }
}

void parse_level(float level)
{

 int i=0;

 int minindex=NumOfAval_freqs-1;
 int maxindex=0;
 freq=(int)(level*aval_freqs[0]);

  if(level>=1)
 {
  o_min=aval_freqs[0];
  o_max=aval_freqs[0];
 }
 else if(freq<=aval_freqs[NumOfAval_freqs-1])
 {
  o_min=aval_freqs[NumOfAval_freqs-1];
  o_max=aval_freqs[NumOfAval_freqs-1];
 }
 else
 {
  while(aval_freqs[minindex-1]<freq )
  {
   minindex--;
  }
  maxindex=minindex-1;
  o_min = aval_freqs[minindex];
  o_max = aval_freqs[maxindex];
 }
 printf("min, max %d %d %d\n", freq, o_min, o_max );
}

void change_cpu_freq(int freq)
{
  int i;
   char fname[1024];
 //printf("Changing into %d\n",freq);
 for(i=0;i<cpu_num;i++)
 {
   bzero(fname,1024);
   sprintf(fname, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_setspeed",i);
   int j;
   //for(j=0;j<3;j++)
   {
   FILE* fd = fopen(fname,"w");
     if(fd==NULL)perror(fname);
   fprintf(fd, "%d\n", freq);
   fclose(fd);
   }
 }

}


int *get_aval_freqs()
{
  char buf[2048];
  memset(buf,0,2048);
  int fd = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies",O_RDONLY);
  read(fd,buf,2048);
  close(fd);
  //printf("%s",buf);
  int m[256];
  char* p = strtok(buf," ");
  m[0]=atoi(p);
  int i=1;
  while(p)
  {
    p = strtok(NULL, " ");
    if(!p)
    {
        break;
    }
   // printf("##%s\n",p);
    m[i]=atoi(p);
    NumOfAval_freqs=i;
    if(m[i]!=0)
     i++;
    else
      break;
  }
  //realloc(m, i*sizeof(int));
  //printf("before return, i is %i\n",i);
  int * mm = (int *)malloc(i*sizeof(int));
  int j;
  for(j=0;j<i;j++)
  {
    mm[j]=m[j];
  }
  //printf("before return, i is %i\n",i);

  return mm;
}

void *run_min_max(void* nothing)
{
 printf("Running: %d %d\n",o_min,o_max);
 int o_max_time = freq-o_min;
 int o_min_time = o_max-freq;


 int total = o_max_time+o_min_time;
 if( o_max_time<=0 || o_min_time<=0)
 {
   o_max_time=o_min_time=O_PERIOD/2;
 }
 else
 {
  o_max_time = (((float)o_max_time)/(float)total) * O_PERIOD;
  o_min_time = (((float)o_min_time)/(float)total) * O_PERIOD;
 }
#define MINSLEEP 50000

  if(o_max_time<MINSLEEP)
  {
   o_max_time=MINSLEEP;
  }
  if(o_min_time<MINSLEEP)
  {
   o_min_time=MINSLEEP;
  }

 int adjust=0;
 int counter=0;
 int nomax=0;
 int nomin=0;
 while(1)
 {
  if(o_min==o_max)
  {
   //printf("Change into: %d\n",o_max);
   change_cpu_freq(o_max);
   sleep(1);
   continue;
  }
  unsigned long t1 = getms();

  if(!nomax)
  {
    change_cpu_freq(o_max);
    usleep(o_max_time);
  }
  unsigned long t2 = getms();
  float max_balance = (t2-t1)/(float)(freq-o_min);
  if(nomax)
     max_balance=0;
  o_balance += max_balance;
  if(!nomin)
  {
   change_cpu_freq(o_min);
   usleep(o_min_time);
  }
  unsigned long t3 = getms();
  float min_balance = (t3-t2)/(float)(o_max-freq);
  if(nomin)
  {
    min_balance=0;
  }
  o_balance -= min_balance;

   adjust=-o_balance*640000-(max_balance-min_balance)*1000000;

   if(o_max_time+adjust>10000 && o_min_time-adjust>10000)
   {
     o_max_time += adjust;
     o_min_time -= adjust;
   }

   nomax=0;
   if(o_max_time+adjust<200)
   {
     nomax=1;
   }

    nomin=0;
    if( o_min_time-adjust<200)
    {
      nomin=1;
    }
//  counter++;
/*
  if(counter>500)
  {
  // printf("%d %d %d\n",freq,o_min,o_max);
//   printf("max_balance:%f min_banlance:%f o_balance*10000:%f adjust:%d max:%d min:%d max+min:%d\n",max_balance,min_balance,o_balance *10000 ,adjust,o_max_time,o_min_time,o_max_time+o_min_time);
   counter=0;
   //o_balance=0;
  }
*/
 }
}

void readpipe( void* nothing)
{
  char buf[1024];
 int ret=mkfifo("/tmp/ffreq",0777);
 chmod("/tmp/ffreq",S_IRUSR| S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH);

 if(!ret)perror("mkfifo");
 while(1)
 {
  FILE* ft = fopen("/tmp/ffreq", "r");
  bzero(buf,1024);
  fgets(buf,1024,ft);
  fclose(ft);
  printf("%s\n",buf);
  FILE* fd = fopen("/tmp/fq","w");
  fprintf(fd,"%s",buf);
  fclose(fd);
  float f = atof(buf);
  if(f>1)f=1;
  if(f<=1)
  {
    parse_level(f);
  }
  sleep(1);
 }

}

int main(int argc, char** argv)
{
  if(argc==1)exit(1);
  float level = atof(argv[1]);
  aval_freqs = get_aval_freqs();


  parse_level(level);

  init_freq();

  pthread_t tcb;

  if (pthread_create(&tcb, NULL, run_min_max, NULL) != 0)
  {
    perror("pthread_create");
    exit(1);
  }
  void *retval;

  pthread_t tcb_pipe;
  if (pthread_create(&tcb_pipe, NULL, readpipe, NULL) != 0)
  {
    perror("pthread_create");
    exit(1);
  }
  pthread_join(tcb, &retval);
  pthread_join(tcb_pipe, &retval);
}