#include #include #include #include #include #include #include "sysfs.h" #include "cpufreq.h" #include "debug.h" #include "cputemp2maxfreq.h" #include "failsafe.h" #include "version.h" #include "argparse.h" #include "logger.h" // Set default config struct s_config config={ "", // Name of this program, set by argparse "conservative", // Governor 70000, // Temperature "/sys/devices/virtual/thermal/thermal_zone0/temp", // Temperature input 100000, // Frequency step 2000000, // Fallback frequency 10, // Interval "stdout", // Logger name &logger_stdout, // Logger function 0, // Don't log measurement data "", // CSV logfile 0, // Don't overwrite CSV logfile NULL, // File handler for CSV file 0, // Don't use unix timestamps in log outputs }; struct s_cpudata cpudata; void signal_handler(int signum) { config.logger("Received signal %d, exiting.",signum); failsafe(0); } int main(int argc,char **argv) { long int diff; long int newfreq; argparse(argc,argv); config.logger("%s version %s, buildtime %s %s",config.name,version(),__DATE__,__TIME__); // Log configuration config.logger("Configuration:"); config.logger("Governor: %s",config.governor); config.logger("Temperature: %ld",config.max_temp); config.logger("Temp input: %s",config.temp_input); config.logger("Frequency step: %ld",config.freq_step); config.logger("Fallback frquency: %ld",config.fallback_freq); config.logger("Interval: %d",config.interval); config.logger("Logger: %s (%p)",config.logger_name,config.logger); config.logger("Log measurement data: %d",config.log_data); if (config.csvlog[0]==0) { config.logger("CSV Log file: Disabled"); } else { config.logger("CSV Log file: %s",config.csvlog); config.logger("Overwrite CSV log: %d",config.csvoverwrite); } config.logger("Use unix timestamps: %d",config.use_unixtime); if ((config.max_tempVALID_TEMP_MAX)) { config.logger("Invalid temperature, range is %d-%d",VALID_TEMP_MIN,VALID_TEMP_MAX); exit(1); } if ((config.freq_stepVALID_STEP_MAX)) { config.logger("Invalid frequency step, range is %d-%d",VALID_STEP_MIN,VALID_STEP_MAX); exit(1); } if ((config.intervalVALID_INTERVAL_MAX)) { config.logger("Invalid poll interval, range is %d-%d",VALID_INTERVAL_MIN,VALID_INTERVAL_MAX); exit(1); } // Get and validate CPU data cpudata.min_freq=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_min_freq"); cpudata.max_freq=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq"); cpudata.scale_max=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq"); cpudata.transition_latency=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_transition_latency"); config.logger("CPU data:"); config.logger("Minimum frequency: %ld",cpudata.min_freq); config.logger("Maximum frequency: %ld",cpudata.max_freq); config.logger("Scaling maximum frequency: %ld",cpudata.scale_max); config.logger("Transition Latency: %ld",cpudata.transition_latency); if ((cpudata.min_freqVALID_FREQ_MAX) || (cpudata.max_freqVALID_FREQ_MAX) || (cpudata.scale_maxVALID_FREQ_MAX) || (cpudata.transition_latencyVALID_TRANS_MAX)) { // If we have to fail now, there's not much we can do because we have no data config.logger("Invalid CPU data, exiting"); exit(1); } // Modify fallback frequency if needed if (config.fallback_freqcpudata.max_freq) { config.logger("Fallback frequency is higher than CPU's maximum frequency, setting to %ld",cpudata.max_freq); config.fallback_freq=cpudata.max_freq; } // Set the governor if (strcmp(config.governor,"keep")!=0) { if (cpufreq_set_str("scaling_governor",config.governor,0)<0) { config.logger("Failed to set governor, error %d (%s)",errno,strerror(errno)); // We failed to set the governor, call the failsafe failsafe(1); } } // Initialise CSV logging if (config.csvlog[0]!=0) csvlog_init(); // Set signal handlers signal(SIGTERM,signal_handler); signal(SIGINT,signal_handler); signal(SIGQUIT,signal_handler); while(1) { // Get new measurements cpudata.cur_freq=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq"); if ((cpudata.cur_freqVALID_FREQ_MAX)) { config.logger("Invalid current frequency reported by CPU, exiting"); failsafe(1); } cpudata.cur_temp=sysfs_read_long_int(config.temp_input); if ((cpudata.cur_tempVALID_TEMP_MAX)) { config.logger("Invalid current tempature reported by CPU, exiting"); failsafe(1); } DEBUG1_MAIN("Data: %ld %ld %ld %ld %ld\n",cpudata.cur_temp,config.max_temp,cpudata.max_freq,cpudata.scale_max,cpudata.cur_freq); if (config.log_data>0) { config.logger("CPU Temperature: %ld, CPU Frequency: %ld",cpudata.cur_temp/1000,cpudata.cur_freq); } if (config.csvfile!=NULL) csvlog_write(); // Check if we should increase if ((cpudata.cur_tempcpudata.max_freq) newfreq=cpudata.max_freq; DEBUG1_MAIN("Increase to %ld\n",newfreq); config.logger("Increase scaling_max_freq to %ld",newfreq); // Set new value and validate if (cpufreq_set_long_int("scaling_max_freq",newfreq,cpudata.transition_latency)<0) { config.logger("Failed to set scaling_max_freq, exiting"); failsafe(1); } cpudata.scale_max=sysfs_read_long_int("/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq"); if ((cpudata.scale_maxVALID_FREQ_MAX)) { config.logger("Invalid scale_max_freq reported, exiting"); failsafe(1); } } // Check if we should decrease if ((cpudata.cur_temp>config.max_temp) && (cpudata.scale_max>cpudata.min_freq)) { diff=(cpudata.cur_temp-config.max_temp)/1000; newfreq=cpudata.scale_max-(config.freq_step*diff); if (newfreqVALID_FREQ_MAX)) { config.logger("Invalid scale_max_freq reported, exiting"); failsafe(1); } } sleep(config.interval); } }