cpufreq: Create DVFS stress test function.

Change-Id: I98897314ff0df9f4fcf22c9d45b80a43268c8bba
Reviewed-on: http://mcrd1-5.corpnet.asus/code-review/master/67932
Reviewed-by: Singhome Lee <singhome_lee@asus.com>
Tested-by: Singhome Lee <singhome_lee@asus.com>
This commit is contained in:
singhome_lee 2013-01-29 20:29:12 +08:00 committed by Iliyan Malchev
parent 3a6924ba6e
commit ca2d4e94af
3 changed files with 174 additions and 2 deletions

View file

@ -604,6 +604,31 @@ static ssize_t show_scaling_setspeed(struct cpufreq_policy *policy, char *buf)
return policy->governor->show_setspeed(policy, buf);
}
static ssize_t store_dvfs_test(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
unsigned int enable= 0;
unsigned int ret;
if (!policy->governor || !policy->governor->start_dvfs_test)
return -EINVAL;
ret = sscanf(buf, "%u", &enable);
if (ret != 1)
return -EINVAL;
policy->governor->start_dvfs_test(policy, enable);
return count;
}
static ssize_t show_dvfs_test(struct cpufreq_policy *policy, char *buf)
{
if (!policy->governor || !policy->governor->show_dvfs_test)
return sprintf(buf, "<unsupported>\n");
return policy->governor->show_dvfs_test(policy, buf);
}
/**
* show_scaling_driver - show the current cpufreq HW/BIOS limitation
*/
@ -634,6 +659,7 @@ cpufreq_freq_attr_rw(scaling_min_freq);
cpufreq_freq_attr_rw(scaling_max_freq);
cpufreq_freq_attr_rw(scaling_governor);
cpufreq_freq_attr_rw(scaling_setspeed);
cpufreq_freq_attr_rw(dvfs_test);
static struct attribute *default_attrs[] = {
&cpuinfo_min_freq.attr,
@ -648,6 +674,7 @@ static struct attribute *default_attrs[] = {
&scaling_driver.attr,
&scaling_available_governors.attr,
&scaling_setspeed.attr,
&dvfs_test.attr,
NULL
};

View file

@ -23,6 +23,8 @@
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
/**
* A few values needed by the userspace governor
@ -33,7 +35,7 @@ static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */
static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
userspace */
static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
static DEFINE_PER_CPU(struct cpufreq_policy *, temp_policy);
static DEFINE_MUTEX(userspace_mutex);
static int cpus_using_userspace_governor;
@ -59,8 +61,139 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
static struct notifier_block userspace_cpufreq_notifier_block = {
.notifier_call = userspace_cpufreq_notifier
};
//=========dvfs stress test ===================
extern struct cpufreq_frequency_table * cpufreq_frequency_get_table(unsigned int cpu);
struct cpufreq_frequency_table *cpu_freq_table=NULL;
struct workqueue_struct *dvfs_test_work_queue=NULL;
struct delayed_work stress_test;
bool stress_test_enable=false;
unsigned int max_cpu_freq_index=CPUFREQ_TABLE_END;
int freq_index=0;
static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq);
static bool cpufreq_set_userspace_governor(void);
void dvfs_stress_test(struct work_struct *work)
{
int cpu=0;
static int start_testing=0;
if(!start_testing){
cpufreq_set_userspace_governor();
start_testing=1;
}
cpu=0;
if(stress_test_enable)
mutex_lock(&userspace_mutex);
for_each_online_cpu(cpu){
if(per_cpu(temp_policy,cpu) && per_cpu(cpu_is_managed, cpu)){
cpufreq_set(per_cpu(temp_policy,cpu), cpu_freq_table[freq_index].frequency);
}
}
if(stress_test_enable)
mutex_unlock(&userspace_mutex);
freq_index--;
if(freq_index<0)
freq_index=max_cpu_freq_index;
if(stress_test_enable)
queue_delayed_work(dvfs_test_work_queue, &stress_test,1*HZ);
return ;
}
static bool cpufreq_set_userspace_governor(void)
{
static char *cpufreq_sysfs_governor_path="/sys/devices/system/cpu/cpu%i/cpufreq/scaling_governor";
static char *cpufreq_sysfs_online_path="/sys/devices/system/cpu/cpu%i/online";
struct file *scaling_gov = NULL;
struct file *cpux_online= NULL;
static char *governor = "userspace";
static char cpu_on[1] = {'1'};
mm_segment_t old_fs;
char buf[128];
int i;
bool err=0;
loff_t offset = 0;
struct cpufreq_policy *policy =NULL;
printk("cpufreq_set_userspace_governor+\n");
/* change to KERNEL_DS address limit */
old_fs = get_fs();
set_fs(KERNEL_DS);
for(i = 1; i <= 3; i++) {
sprintf(buf, cpufreq_sysfs_online_path, i);
cpux_online = filp_open(buf, O_RDWR, 0);
if (IS_ERR_OR_NULL(cpux_online)){
printk("cpufreq_set_userspace_governor: open %s fail\n", buf);
err = true;
goto error;
}
cpux_online->f_op->write(cpux_online, cpu_on, strlen(cpu_on), &offset);
memset(buf,0,sizeof(buf));
sprintf(buf, cpufreq_sysfs_governor_path, i);
scaling_gov = filp_open(buf, O_RDWR, 0);
if (IS_ERR_OR_NULL(scaling_gov)) {
printk("cpufreq_set_userspace_governor: open %s fail\n", buf);
err = true;
goto error;
}
policy =cpufreq_cpu_get(i);
if(policy) {
if(!strncmp(policy->governor->name, governor, 9)) {
printk("cpufreq_set_userspace_governor cpu%u is userspace\n", i);
if (policy)
cpufreq_cpu_put(policy);
continue;
}
cpufreq_cpu_put(policy);
}
if (scaling_gov->f_op != NULL && scaling_gov->f_op->write != NULL) {
scaling_gov->f_op->write(scaling_gov,
governor,
strlen(governor),
&offset);
} else
pr_err("f_op might be null\n");
filp_close(scaling_gov, NULL);
filp_close(cpux_online, NULL);
}
printk("cpufreq_set_userspace_governor-\n");
error:
set_fs(old_fs);
return err;
}
static int start_test(struct cpufreq_policy *policy, unsigned int enable)
{
int i=0;
if(policy->cpu==0 && enable ){
cpu_freq_table=cpufreq_frequency_get_table(0);
for (i = 0; (cpu_freq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (cpu_freq_table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
printk("start_test cpu_freq_table[%u].frequency=%u\n",i,cpu_freq_table[i].frequency);
}
freq_index=max_cpu_freq_index=i-1;
stress_test_enable=true;
queue_delayed_work(dvfs_test_work_queue, &stress_test,1*HZ);
}else
stress_test_enable=false;
return true;
}
static ssize_t show_test(struct cpufreq_policy *policy, char *buf)
{
return sprintf(buf, "%u\n", stress_test_enable);
}
//=========dvfs stress test ===================
/**
* cpufreq_set - set the CPU frequency
* @policy: pointer to policy struct where freq is being set
@ -72,8 +205,9 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
{
int ret = -EINVAL;
pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
printk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
if(!stress_test_enable)
mutex_lock(&userspace_mutex);
if (!per_cpu(cpu_is_managed, policy->cpu))
goto err;
@ -98,6 +232,7 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
err:
if(!stress_test_enable)
mutex_unlock(&userspace_mutex);
return ret;
}
@ -118,6 +253,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
case CPUFREQ_GOV_START:
if (!cpu_online(cpu))
return -EINVAL;
per_cpu(temp_policy, cpu) =policy;
BUG_ON(!policy->cur);
mutex_lock(&userspace_mutex);
@ -155,6 +291,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
per_cpu(cpu_min_freq, cpu) = 0;
per_cpu(cpu_max_freq, cpu) = 0;
per_cpu(cpu_set_freq, cpu) = 0;
per_cpu(temp_policy, cpu) =NULL;
pr_debug("managing cpu %u stopped\n", cpu);
mutex_unlock(&userspace_mutex);
break;
@ -194,11 +331,15 @@ struct cpufreq_governor cpufreq_gov_userspace = {
.governor = cpufreq_governor_userspace,
.store_setspeed = cpufreq_set,
.show_setspeed = show_speed,
.start_dvfs_test = start_test,
.show_dvfs_test = show_test,
.owner = THIS_MODULE,
};
static int __init cpufreq_gov_userspace_init(void)
{
INIT_DELAYED_WORK(&stress_test, dvfs_stress_test) ;
dvfs_test_work_queue = create_singlethread_workqueue("dvfs_test_workqueue");
return cpufreq_register_governor(&cpufreq_gov_userspace);
}

View file

@ -181,6 +181,10 @@ struct cpufreq_governor {
char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy,
unsigned int freq);
ssize_t (*show_dvfs_test) (struct cpufreq_policy *policy,
char *buf);
int (*start_dvfs_test) (struct cpufreq_policy *policy,
unsigned int enable);
unsigned int max_transition_latency; /* HW must be able to switch to
next freq faster than this value in nano secs or we
will fallback to performance governor */