From patchwork Thu May 16 09:28:54 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 16993 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-wi0-f198.google.com (mail-wi0-f198.google.com [209.85.212.198]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id DC6A42391B for ; Thu, 16 May 2013 09:33:43 +0000 (UTC) Received: by mail-wi0-f198.google.com with SMTP id hr14sf2489914wib.5 for ; Thu, 16 May 2013 02:33:04 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:mime-version:x-beenthere:x-received:received-spf :x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:x-received:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:in-reply-to:references:x-gm-message-state :x-original-sender:x-original-authentication-results:precedence :mailing-list:list-id:x-google-group-id:list-post:list-help :list-archive:list-unsubscribe; bh=1dZec4K3IWgtdkG+FBv11pFd9AyODm+YBKxtliNWjXA=; b=mT2sNMyhqWGCcLxH/tF25KFfW5EbHgz8a84O4dtbrSsuF1ymQFSvGJsnywXxvr1RLg x8ZLfOaJQ73LW+9njZ3LFsU8vnwNyETw74TZPfCr5/Cjes/FP4gTV2ROZKwa7L+N7VJE o2ttXW9mp4UcfNJKcoIxEisqPxtOe6YlPhibMiR3sW+e9jtiotJWDJs/YK2ysbEY2OVK m+OtbPZp+yzQW6IP4UW4HeMeD9YvpyvfRB7/KfD1jwDDve7U+4r8nwLyUddT9ip57Y6Q ezsuGa3eYh5Zn5oqOkAU9Lnt/3O10Ofmo9gvGPTsgr0ZRk6k2aNoFcTfiAwB/oeCWzBL 0G4Q== X-Received: by 10.180.38.73 with SMTP id e9mr3891502wik.2.1368696784252; Thu, 16 May 2013 02:33:04 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.181.13.7 with SMTP id eu7ls1750789wid.50.canary; Thu, 16 May 2013 02:33:04 -0700 (PDT) X-Received: by 10.180.149.200 with SMTP id uc8mr22824454wib.3.1368696784161; Thu, 16 May 2013 02:33:04 -0700 (PDT) Received: from mail-ve0-x231.google.com (mail-ve0-x231.google.com [2607:f8b0:400c:c01::231]) by mx.google.com with ESMTPS id j6si1118362wix.42.2013.05.16.02.33.03 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 16 May 2013 02:33:04 -0700 (PDT) Received-SPF: neutral (google.com: 2607:f8b0:400c:c01::231 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=2607:f8b0:400c:c01::231; Received: by mail-ve0-f177.google.com with SMTP id ox1so2835578veb.36 for ; Thu, 16 May 2013 02:33:02 -0700 (PDT) X-Received: by 10.52.69.200 with SMTP id g8mr5861535vdu.4.1368696782738; Thu, 16 May 2013 02:33:02 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.126.138 with SMTP id c10csp56092vcs; Thu, 16 May 2013 02:33:02 -0700 (PDT) X-Received: by 10.68.245.200 with SMTP id xq8mr967667pbc.32.1368696781608; Thu, 16 May 2013 02:33:01 -0700 (PDT) Received: from mail-pd0-f181.google.com (mail-pd0-f181.google.com [209.85.192.181]) by mx.google.com with ESMTPS id wu3si4147663pbc.10.2013.05.16.02.33.01 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 16 May 2013 02:33:01 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.192.181 is neither permitted nor denied by best guess record for domain of viresh.kumar@linaro.org) client-ip=209.85.192.181; Received: by mail-pd0-f181.google.com with SMTP id p11so2173742pdj.12 for ; Thu, 16 May 2013 02:33:01 -0700 (PDT) X-Received: by 10.68.218.34 with SMTP id pd2mr42122228pbc.204.1368696781204; Thu, 16 May 2013 02:33:01 -0700 (PDT) Received: from localhost ([106.197.193.133]) by mx.google.com with ESMTPSA id ih1sm6104607pbb.44.2013.05.16.02.32.44 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Thu, 16 May 2013 02:33:00 -0700 (PDT) From: Viresh Kumar To: toddpoynor@google.com Cc: linaro-kernel@lists.linaro.org, patches@linaro.org, robin.randhawa@arm.com, Steve.Bannister@arm.com, Liviu.Dudau@arm.com, charles.garcia-tobin@arm.com, arvind.chauhan@arm.com, android-kernel@googlegroups.com, viresh.kumar@linaro.org Subject: [PATCH 16/16] cpufreq: Interactive: Implement per policy instances of governor Date: Thu, 16 May 2013 14:58:54 +0530 Message-Id: X-Mailer: git-send-email 1.7.12.rc2.18.g61b472e In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQn03eGgBmDDc/IqYmlqo/DDX286f7lrva6wVAm5dmUdpZUc5ceXmnSXg9UKlksiwUvxIUkZ X-Original-Sender: viresh.kumar@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 2607:f8b0:400c:c01::231 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , If we have a multi-package system, where we have multiple instances of struct policy (per package), currently we can't have multiple instances of same governor. i.e. We can't have multiple instances of Interactive governor for multiple packages. This is a bottleneck for multicluster system, where we want different packages to use Interactive governor, but with different tunables. This patch uses the infrastructure provided by earlier patches pushed in Mainline in v3.10-rc1/rc2 and implements per policy instances of Interactive governor. Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq_interactive.c | 615 ++++++++++++++++++++-------------- 1 file changed, 371 insertions(+), 244 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8821873..05733ea 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,12 +30,11 @@ #include #include #include +#include "cpufreq_governor.h" #define CREATE_TRACE_POINTS #include -static int active_count; - struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; struct timer_list cpu_slack_timer; @@ -62,110 +61,131 @@ static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; static struct mutex gov_lock; -/* Hi speed to bump to from lo speed when load burst (default max) */ -static unsigned int hispeed_freq; - -/* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 99 -static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; - -/* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; -static spinlock_t target_loads_lock; -static unsigned int *target_loads = default_target_loads; -static int ntarget_loads = ARRAY_SIZE(default_target_loads); -/* - * The minimum amount of time to spend at a frequency before we can ramp down. - */ -#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) -static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME; - -/* - * The sample rate of the timer used to increase frequency - */ #define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) -static unsigned long timer_rate = DEFAULT_TIMER_RATE; - -/* - * Wait this long before raising speed above hispeed, by default a single - * timer interval. - */ #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned int default_above_hispeed_delay[] = { DEFAULT_ABOVE_HISPEED_DELAY }; -static spinlock_t above_hispeed_delay_lock; -static unsigned int *above_hispeed_delay = default_above_hispeed_delay; -static int nabove_hispeed_delay = ARRAY_SIZE(default_above_hispeed_delay); -/* Non-zero means indefinite speed boost active */ -static int boost_val; -/* Duration of a boot pulse in usecs */ -static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; -/* End time of boost pulse in ktime converted to usecs */ -static u64 boostpulse_endtime; +struct cpufreq_interactive_tunables { + int usage_count; -/* - * Max additional time to wait in idle, beyond timer_rate, at speeds above - * minimum before wakeup to reduce speed, or -1 if unnecessary. - */ + /* Hi speed to bump to from lo speed when load burst (default max) */ + unsigned int hispeed_freq; + + /* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 99 + unsigned long go_hispeed_load; + + /* Target load. Lower values result in higher CPU speeds. */ + spinlock_t target_loads_lock; + unsigned int *target_loads; + int ntarget_loads; + + /* + * The minimum amount of time to spend at a frequency before we can ramp + * down. + */ +#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) + unsigned long min_sample_time; + + /* + * The sample rate of the timer used to increase frequency + */ + unsigned long timer_rate; + + /* + * Wait this long before raising speed above hispeed, by default a + * single timer interval. + */ + spinlock_t above_hispeed_delay_lock; + unsigned int *above_hispeed_delay; + int nabove_hispeed_delay; + + /* Non-zero means indefinite speed boost active */ + int boost_val; + /* Duration of a boot pulse in usecs */ + int boostpulse_duration_val; + /* End time of boost pulse in ktime converted to usecs */ + u64 boostpulse_endtime; + + /* + * Max additional time to wait in idle, beyond timer_rate, at speeds + * above minimum before wakeup to reduce speed, or -1 if unnecessary. + */ #define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) -static int timer_slack_val = DEFAULT_TIMER_SLACK; + int timer_slack_val; + + bool io_is_busy; +}; -static bool io_is_busy; +/* For cases where we have single governor instance for system */ +struct cpufreq_interactive_tunables *common_tunables; + +static struct attribute_group *get_sysfs_attr(void); static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { - unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; + unsigned long expires = jiffies + + usecs_to_jiffies(tunables->timer_rate); unsigned long flags; mod_timer_pinned(&pcpu->cpu_timer, expires); - if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { - expires += usecs_to_jiffies(timer_slack_val); + if (tunables->timer_slack_val >= 0 && + pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(tunables->timer_slack_val); mod_timer_pinned(&pcpu->cpu_slack_timer, expires); } spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time(smp_processor_id(), - &pcpu->time_in_idle_timestamp, io_is_busy); + &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; spin_unlock_irqrestore(&pcpu->load_lock, flags); } -static unsigned int freq_to_above_hispeed_delay(unsigned int freq) +static unsigned int freq_to_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, + unsigned int freq) { int i; unsigned int ret; unsigned long flags; - spin_lock_irqsave(&above_hispeed_delay_lock, flags); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); - for (i = 0; i < nabove_hispeed_delay - 1 && - freq >= above_hispeed_delay[i+1]; i += 2) + for (i = 0; i < tunables->nabove_hispeed_delay - 1 && + freq >= tunables->above_hispeed_delay[i+1]; i += 2) ; - ret = above_hispeed_delay[i]; - spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + ret = tunables->above_hispeed_delay[i]; + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return ret; } -static unsigned int freq_to_targetload(unsigned int freq) +static unsigned int freq_to_targetload(struct cpufreq_interactive_tunables + *tunables, unsigned int freq) { int i; unsigned int ret; unsigned long flags; - spin_lock_irqsave(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); - for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + for (i = 0; i < tunables->ntarget_loads - 1 && + freq >= tunables->target_loads[i+1]; i += 2) ; - ret = target_loads[i]; - spin_unlock_irqrestore(&target_loads_lock, flags); + ret = tunables->target_loads[i]; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } @@ -174,9 +194,8 @@ static unsigned int freq_to_targetload(unsigned int freq) * choose_freq() will find the minimum frequency that does not exceed its * target load given the current load. */ - -static unsigned int choose_freq( - struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq) +static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, + unsigned int loadadjfreq) { unsigned int freq = pcpu->policy->cur; unsigned int prevfreq, freqmin, freqmax; @@ -188,7 +207,7 @@ static unsigned int choose_freq( do { prevfreq = freq; - tl = freq_to_targetload(freq); + tl = freq_to_targetload(pcpu->policy->governor_data, freq); /* * Find the lowest frequency where the computed load is less @@ -260,13 +279,15 @@ static unsigned int choose_freq( static u64 update_load(int cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; u64 now; u64 now_idle; unsigned int delta_idle; unsigned int delta_time; u64 active_time; - now_idle = get_cpu_idle_time(cpu, &now, io_is_busy); + now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); active_time = delta_time - delta_idle; @@ -285,6 +306,8 @@ static void cpufreq_interactive_timer(unsigned long data) int cpu_load; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; unsigned int new_freq; unsigned int loadadjfreq; unsigned int index; @@ -308,25 +331,25 @@ static void cpufreq_interactive_timer(unsigned long data) do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; cpu_load = loadadjfreq / pcpu->target_freq; - boosted = boost_val || now < boostpulse_endtime; + boosted = tunables->boost_val || now < tunables->boostpulse_endtime; - if (cpu_load >= go_hispeed_load || boosted) { - if (pcpu->target_freq < hispeed_freq) { - new_freq = hispeed_freq; + if (cpu_load >= tunables->go_hispeed_load || boosted) { + if (pcpu->target_freq < tunables->hispeed_freq) { + new_freq = tunables->hispeed_freq; } else { new_freq = choose_freq(pcpu, loadadjfreq); - if (new_freq < hispeed_freq) - new_freq = hispeed_freq; + if (new_freq < tunables->hispeed_freq) + new_freq = tunables->hispeed_freq; } } else { new_freq = choose_freq(pcpu, loadadjfreq); } - if (pcpu->target_freq >= hispeed_freq && + if (pcpu->target_freq >= tunables->hispeed_freq && new_freq > pcpu->target_freq && now - pcpu->hispeed_validate_time < - freq_to_above_hispeed_delay(pcpu->target_freq)) { + freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -350,7 +373,8 @@ static void cpufreq_interactive_timer(unsigned long data) * floor frequency for the minimum sample time since last validated. */ if (new_freq < pcpu->floor_freq) { - if (now - pcpu->floor_validate_time < min_sample_time) { + if (now - pcpu->floor_validate_time < + tunables->min_sample_time) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -366,7 +390,7 @@ static void cpufreq_interactive_timer(unsigned long data) * (or the indefinite boost is turned off). */ - if (!boosted || new_freq > hispeed_freq) { + if (!boosted || new_freq > tunables->hispeed_freq) { pcpu->floor_freq = new_freq; pcpu->floor_validate_time = now; } @@ -527,14 +551,16 @@ static void cpufreq_interactive_boost(void) int anyboost = 0; unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_interactive_tunables *tunables; spin_lock_irqsave(&speedchange_cpumask_lock, flags); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); + tunables = pcpu->policy->governor_data; - if (pcpu->target_freq < hispeed_freq) { - pcpu->target_freq = hispeed_freq; + if (pcpu->target_freq < tunables->hispeed_freq) { + pcpu->target_freq = tunables->hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); pcpu->hispeed_validate_time = ktime_to_us(ktime_get()); @@ -546,7 +572,7 @@ static void cpufreq_interactive_boost(void) * validated. */ - pcpu->floor_freq = hispeed_freq; + pcpu->floor_freq = tunables->hispeed_freq; pcpu->floor_validate_time = ktime_to_us(ktime_get()); } @@ -635,27 +661,26 @@ err: return ERR_PTR(err); } -static ssize_t show_target_loads( - struct kobject *kobj, struct attribute *attr, char *buf) +static ssize_t show_target_loads(struct cpufreq_interactive_tunables *tunables, + char *buf) { int i; ssize_t ret = 0; unsigned long flags; - spin_lock_irqsave(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); - for (i = 0; i < ntarget_loads; i++) - ret += sprintf(buf + ret, "%u%s", target_loads[i], + for (i = 0; i < tunables->ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], i & 0x1 ? ":" : " "); ret += sprintf(buf + ret, "\n"); - spin_unlock_irqrestore(&target_loads_lock, flags); + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } -static ssize_t store_target_loads( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_target_loads(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ntokens; unsigned int *new_target_loads = NULL; @@ -665,40 +690,36 @@ static ssize_t store_target_loads( if (IS_ERR(new_target_loads)) return PTR_RET(new_target_loads); - spin_lock_irqsave(&target_loads_lock, flags); - if (target_loads != default_target_loads) - kfree(target_loads); - target_loads = new_target_loads; - ntarget_loads = ntokens; - spin_unlock_irqrestore(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); + if (tunables->target_loads != default_target_loads) + kfree(tunables->target_loads); + tunables->target_loads = new_target_loads; + tunables->ntarget_loads = ntokens; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return count; } -static struct global_attr target_loads_attr = - __ATTR(target_loads, S_IRUGO | S_IWUSR, - show_target_loads, store_target_loads); - -static ssize_t show_above_hispeed_delay( - struct kobject *kobj, struct attribute *attr, char *buf) +static ssize_t show_above_hispeed_delay(struct cpufreq_interactive_tunables + *tunables, char *buf) { int i; ssize_t ret = 0; unsigned long flags; - spin_lock_irqsave(&above_hispeed_delay_lock, flags); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); - for (i = 0; i < nabove_hispeed_delay; i++) - ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], + for (i = 0; i < tunables->nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", + tunables->above_hispeed_delay[i], i & 0x1 ? ":" : " "); ret += sprintf(buf + ret, "\n"); - spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return ret; } -static ssize_t store_above_hispeed_delay( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_above_hispeed_delay(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ntokens; unsigned int *new_above_hispeed_delay = NULL; @@ -708,29 +729,24 @@ static ssize_t store_above_hispeed_delay( if (IS_ERR(new_above_hispeed_delay)) return PTR_RET(new_above_hispeed_delay); - spin_lock_irqsave(&above_hispeed_delay_lock, flags); - if (above_hispeed_delay != default_above_hispeed_delay) - kfree(above_hispeed_delay); - above_hispeed_delay = new_above_hispeed_delay; - nabove_hispeed_delay = ntokens; - spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); + if (tunables->above_hispeed_delay != default_above_hispeed_delay) + kfree(tunables->above_hispeed_delay); + tunables->above_hispeed_delay = new_above_hispeed_delay; + tunables->nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return count; } -static struct global_attr above_hispeed_delay_attr = - __ATTR(above_hispeed_delay, S_IRUGO | S_IWUSR, - show_above_hispeed_delay, store_above_hispeed_delay); - -static ssize_t show_hispeed_freq(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%u\n", hispeed_freq); + return sprintf(buf, "%u\n", tunables->hispeed_freq); } -static ssize_t store_hispeed_freq(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; long unsigned int val; @@ -738,22 +754,18 @@ static ssize_t store_hispeed_freq(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - hispeed_freq = val; + tunables->hispeed_freq = val; return count; } -static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, - show_hispeed_freq, store_hispeed_freq); - - -static ssize_t show_go_hispeed_load(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%lu\n", go_hispeed_load); + return sprintf(buf, "%lu\n", tunables->go_hispeed_load); } -static ssize_t store_go_hispeed_load(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -761,21 +773,18 @@ static ssize_t store_go_hispeed_load(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - go_hispeed_load = val; + tunables->go_hispeed_load = val; return count; } -static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, - show_go_hispeed_load, store_go_hispeed_load); - -static ssize_t show_min_sample_time(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%lu\n", min_sample_time); + return sprintf(buf, "%lu\n", tunables->min_sample_time); } -static ssize_t store_min_sample_time(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -783,21 +792,18 @@ static ssize_t store_min_sample_time(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - min_sample_time = val; + tunables->min_sample_time = val; return count; } -static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, - show_min_sample_time, store_min_sample_time); - -static ssize_t show_timer_rate(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%lu\n", timer_rate); + return sprintf(buf, "%lu\n", tunables->timer_rate); } -static ssize_t store_timer_rate(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -805,22 +811,18 @@ static ssize_t store_timer_rate(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - timer_rate = val; + tunables->timer_rate = val; return count; } -static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, - show_timer_rate, store_timer_rate); - -static ssize_t show_timer_slack( - struct kobject *kobj, struct attribute *attr, char *buf) +static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%d\n", timer_slack_val); + return sprintf(buf, "%d\n", tunables->timer_slack_val); } -static ssize_t store_timer_slack( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -829,19 +831,17 @@ static ssize_t store_timer_slack( if (ret < 0) return ret; - timer_slack_val = val; + tunables->timer_slack_val = val; return count; } -define_one_global_rw(timer_slack); - -static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, +static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables, char *buf) { - return sprintf(buf, "%d\n", boost_val); + return sprintf(buf, "%d\n", tunables->boost_val); } -static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, +static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, const char *buf, size_t count) { int ret; @@ -851,9 +851,9 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, if (ret < 0) return ret; - boost_val = val; + tunables->boost_val = val; - if (boost_val) { + if (tunables->boost_val) { trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); } else { @@ -863,9 +863,7 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, return count; } -define_one_global_rw(boost); - -static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, +static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, const char *buf, size_t count) { int ret; @@ -875,24 +873,21 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, if (ret < 0) return ret; - boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val; + tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + + tunables->boostpulse_duration_val; trace_cpufreq_interactive_boost("pulse"); cpufreq_interactive_boost(); return count; } -static struct global_attr boostpulse = - __ATTR(boostpulse, 0200, NULL, store_boostpulse); - -static ssize_t show_boostpulse_duration( - struct kobject *kobj, struct attribute *attr, char *buf) +static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%d\n", boostpulse_duration_val); + return sprintf(buf, "%d\n", tunables->boostpulse_duration_val); } -static ssize_t store_boostpulse_duration( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -901,20 +896,18 @@ static ssize_t store_boostpulse_duration( if (ret < 0) return ret; - boostpulse_duration_val = val; + tunables->boostpulse_duration_val = val; return count; } -define_one_global_rw(boostpulse_duration); - -static ssize_t show_io_is_busy(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%u\n", io_is_busy); + return sprintf(buf, "%u\n", tunables->io_is_busy); } -static ssize_t store_io_is_busy(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -922,33 +915,125 @@ static ssize_t store_io_is_busy(struct kobject *kobj, ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; - io_is_busy = val; + tunables->io_is_busy = val; return count; } -static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644, - show_io_is_busy, store_io_is_busy); - -static struct attribute *interactive_attributes[] = { - &target_loads_attr.attr, - &above_hispeed_delay_attr.attr, - &hispeed_freq_attr.attr, - &go_hispeed_load_attr.attr, - &min_sample_time_attr.attr, - &timer_rate_attr.attr, - &timer_slack.attr, - &boost.attr, - &boostpulse.attr, - &boostpulse_duration.attr, - &io_is_busy_attr.attr, +/* + * Create show/store routines + * - sys: One governor instance for complete SYSTEM + * - pol: One governor instance per struct cpufreq_policy + */ +#define show_gov_pol_sys(file_name) \ +static ssize_t show_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return show_##file_name(common_tunables, buf); \ +} \ + \ +static ssize_t show_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, char *buf) \ +{ \ + return show_##file_name(policy->governor_data, buf); \ +} + +#define store_gov_pol_sys(file_name) \ +static ssize_t store_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return store_##file_name(common_tunables, buf, count); \ +} \ + \ +static ssize_t store_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, const char *buf, size_t count) \ +{ \ + return store_##file_name(policy->governor_data, buf, count); \ +} + +#define show_store_gov_pol_sys(file_name) \ +show_gov_pol_sys(file_name); \ +store_gov_pol_sys(file_name) + +show_store_gov_pol_sys(target_loads); +show_store_gov_pol_sys(above_hispeed_delay); +show_store_gov_pol_sys(hispeed_freq); +show_store_gov_pol_sys(go_hispeed_load); +show_store_gov_pol_sys(min_sample_time); +show_store_gov_pol_sys(timer_rate); +show_store_gov_pol_sys(timer_slack); +show_store_gov_pol_sys(boost); +store_gov_pol_sys(boostpulse); +show_store_gov_pol_sys(boostpulse_duration); +show_store_gov_pol_sys(io_is_busy); + +gov_sys_pol_attr_rw(target_loads); +gov_sys_pol_attr_rw(above_hispeed_delay); +gov_sys_pol_attr_rw(hispeed_freq); +gov_sys_pol_attr_rw(go_hispeed_load); +gov_sys_pol_attr_rw(min_sample_time); +gov_sys_pol_attr_rw(timer_rate); +gov_sys_pol_attr_rw(timer_slack); +gov_sys_pol_attr_rw(boost); +gov_sys_pol_attr_rw(boostpulse_duration); +gov_sys_pol_attr_rw(io_is_busy); + +static struct global_attr boostpulse_gov_sys = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys); + +static struct freq_attr boostpulse_gov_pol = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol); + +/* One Governor instance for entire system */ +static struct attribute *interactive_attributes_gov_sys[] = { + &target_loads_gov_sys.attr, + &above_hispeed_delay_gov_sys.attr, + &hispeed_freq_gov_sys.attr, + &go_hispeed_load_gov_sys.attr, + &min_sample_time_gov_sys.attr, + &timer_rate_gov_sys.attr, + &timer_slack_gov_sys.attr, + &boost_gov_sys.attr, + &boostpulse_gov_sys.attr, + &boostpulse_duration_gov_sys.attr, + &io_is_busy_gov_sys.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group_gov_sys = { + .attrs = interactive_attributes_gov_sys, + .name = "interactive", +}; + +/* Per policy governor instance */ +static struct attribute *interactive_attributes_gov_pol[] = { + &target_loads_gov_pol.attr, + &above_hispeed_delay_gov_pol.attr, + &hispeed_freq_gov_pol.attr, + &go_hispeed_load_gov_pol.attr, + &min_sample_time_gov_pol.attr, + &timer_rate_gov_pol.attr, + &timer_slack_gov_pol.attr, + &boost_gov_pol.attr, + &boostpulse_gov_pol.attr, + &boostpulse_duration_gov_pol.attr, + &io_is_busy_gov_pol.attr, NULL, }; -static struct attribute_group interactive_attr_group = { - .attrs = interactive_attributes, +static struct attribute_group interactive_attr_group_gov_pol = { + .attrs = interactive_attributes_gov_pol, .name = "interactive", }; +static struct attribute_group *get_sysfs_attr(void) +{ + if (have_governor_per_policy()) + return &interactive_attr_group_gov_pol; + else + return &interactive_attr_group_gov_sys; +} + static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, unsigned long val, void *data) @@ -976,15 +1061,88 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int j; struct cpufreq_interactive_cpuinfo *pcpu; struct cpufreq_frequency_table *freq_table; + struct cpufreq_interactive_tunables *tunables; + + if (have_governor_per_policy()) + tunables = policy->governor_data; + else + tunables = common_tunables; + + WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT)); switch (event) { + case CPUFREQ_GOV_POLICY_INIT: + if (have_governor_per_policy()) { + WARN_ON(tunables); + } else if (tunables) { + tunables->usage_count++; + policy->governor_data = tunables; + return 0; + } + + tunables = kzalloc(sizeof(*tunables), GFP_KERNEL); + if (!tunables) { + pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); + return -ENOMEM; + } + + rc = sysfs_create_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + if (rc) { + kfree(tunables); + return rc; + } + + tunables->usage_count = 1; + tunables->above_hispeed_delay = default_above_hispeed_delay; + tunables->nabove_hispeed_delay = + ARRAY_SIZE(default_above_hispeed_delay); + tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + tunables->target_loads = default_target_loads; + tunables->ntarget_loads = ARRAY_SIZE(default_target_loads); + tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_rate = DEFAULT_TIMER_RATE; + tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_slack_val = DEFAULT_TIMER_SLACK; + + spin_lock_init(&tunables->target_loads_lock); + spin_lock_init(&tunables->above_hispeed_delay_lock); + + if (!policy->governor->initialized) { + idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + } + + policy->governor_data = tunables; + if (!have_governor_per_policy()) + common_tunables = tunables; + + break; + + case CPUFREQ_GOV_POLICY_EXIT: + if (!--tunables->usage_count) { + if (policy->governor->initialized == 1) { + cpufreq_unregister_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + idle_notifier_unregister(&cpufreq_interactive_idle_nb); + } + + sysfs_remove_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + kfree(tunables); + common_tunables = NULL; + } + + policy->governor_data = NULL; + break; + case CPUFREQ_GOV_START: mutex_lock(&gov_lock); - freq_table = - cpufreq_frequency_get_table(policy->cpu); - if (!hispeed_freq) - hispeed_freq = policy->max; + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (!tunables->hispeed_freq) + tunables->hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { unsigned long expires; @@ -999,11 +1157,12 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->hispeed_validate_time = pcpu->floor_validate_time; down_write(&pcpu->enable_sem); - expires = jiffies + usecs_to_jiffies(timer_rate); + expires = jiffies + + usecs_to_jiffies(tunables->timer_rate); pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, j); - if (timer_slack_val >= 0) { - expires += usecs_to_jiffies(timer_slack_val); + if (tunables->timer_slack_val >= 0) { + expires += usecs_to_jiffies(tunables->timer_slack_val); pcpu->cpu_slack_timer.expires = expires; add_timer_on(&pcpu->cpu_slack_timer, j); } @@ -1011,25 +1170,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, up_write(&pcpu->enable_sem); } - /* - * Do not register the idle hook and create sysfs - * entries if we have already done so. - */ - if (++active_count > 1) { - mutex_unlock(&gov_lock); - return 0; - } - - rc = sysfs_create_group(cpufreq_global_kobject, - &interactive_attr_group); - if (rc) { - mutex_unlock(&gov_lock); - return rc; - } - - idle_notifier_register(&cpufreq_interactive_idle_nb); - cpufreq_register_notifier( - &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); mutex_unlock(&gov_lock); break; @@ -1044,18 +1184,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, up_write(&pcpu->enable_sem); } - if (--active_count > 0) { - mutex_unlock(&gov_lock); - return 0; - } - - cpufreq_unregister_notifier( - &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); - idle_notifier_unregister(&cpufreq_interactive_idle_nb); - sysfs_remove_group(cpufreq_global_kobject, - &interactive_attr_group); mutex_unlock(&gov_lock); - break; case CPUFREQ_GOV_LIMITS: @@ -1102,9 +1231,7 @@ static int __init cpufreq_interactive_init(void) init_rwsem(&pcpu->enable_sem); } - spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); - spin_lock_init(&above_hispeed_delay_lock); mutex_init(&gov_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL,