diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 6adb7988e0eb..19c50f3ede5c 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -590,6 +590,64 @@ This governor exposes the following tunables: It effectively causes the frequency to go down ``sampling_down_factor`` times slower than it ramps up. +``interactive`` +---------------- + +The CPUfreq governor `interactive` is designed for latency-sensitive, +interactive workloads. This governor sets the CPU speed depending on +usage, similar to `ondemand` and `conservative` governors, but with a +different set of configurable behaviors. + +The tunable values for this governor are: + +``above_hispeed_delay`` + When speed is at or above hispeed_freq, wait for + this long before raising speed in response to continued high load. + The format is a single delay value, optionally followed by pairs of + CPU speeds and the delay to use at or above those speeds. Colons can + be used between the speeds and associated delays for readability. For + example: + + 80000 1300000:200000 1500000:40000 + + uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay + 200000 uS is used until speed 1.5 GHz, at which speed (and above) + delay 40000 uS is used. If speeds are specified these must appear in + ascending order. Default is 20000 uS. + +``boost`` + If non-zero, immediately boost speed of all CPUs to at least + hispeed_freq until zero is written to this attribute. If zero, allow + CPU speeds to drop below hispeed_freq according to load as usual. + Default is zero. + +``boostpulse`` + On each write, immediately boost speed of all CPUs to + hispeed_freq for at least the period of time specified by + boostpulse_duration, after which speeds are allowed to drop below + hispeed_freq according to load as usual. Its a write-only file. + +``boostpulse_duration`` + Length of time to hold CPU speed at hispeed_freq + on a write to boostpulse, before allowing speed to drop according to + load as usual. Default is 80000 uS. + +``go_hispeed_load`` + The CPU load at which to ramp to hispeed_freq. + Default is 99%. + +``hispeed_freq`` + An intermediate "high speed" at which to initially ramp + when CPU load hits the value specified in go_hispeed_load. If load + stays high for the amount of time specified in above_hispeed_delay, + then speed may be bumped higher. Default is the maximum speed allowed + by the policy at governor initialization time. + +``io_is_busy`` + If set, the governor accounts IO time as CPU busy time. + +``min_sample_time`` + The minimum amount of time to spend at the current Frequency Boost Support ======================= diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index cf79fe19c094..434e3d0371ea 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -112,6 +112,17 @@ config CPU_FREQ_DEFAULT_GOV_SCHEDUTIL have a look at the help section of that governor. The fallback governor will be 'performance'. +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + depends on NO_GKI + select CPU_FREQ_GOV_INTERACTIVE + select CPU_FREQ_GOV_PERFORMANCE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -210,6 +221,27 @@ config CPU_FREQ_GOV_SCHEDUTIL If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + depends on NO_GKI + depends on CPU_FREQ + select CPU_FREQ_GOV_ATTR_SET + select IRQ_WORK + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + comment "CPU frequency scaling drivers" config CPUFREQ_DT diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d7c9cbc9eded..2685a2b7fe81 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7bbcdf6f64fd..a486993cda40 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -126,7 +126,6 @@ struct interactive_cpu { struct interactive_policy *ipolicy; struct irq_work irq_work; - struct irq_work boost_irq_work; u64 last_sample_time; unsigned long next_sample_jiffies; bool work_in_progress; @@ -149,9 +148,6 @@ struct interactive_cpu { u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */ int cpu; - unsigned int task_boost_freq; - unsigned long task_boost_util; - u64 task_boos_endtime; }; static DEFINE_PER_CPU(struct interactive_cpu, interactive_cpu); @@ -427,9 +423,6 @@ static void eval_target_freq(struct interactive_cpu *icpu) new_freq < tunables->touchboost_freq) { new_freq = tunables->touchboost_freq; } - if ((now < icpu->task_boos_endtime) && (new_freq < icpu->task_boost_freq)) { - new_freq = icpu->task_boost_freq; - } #endif if (policy->cur >= tunables->hispeed_freq && new_freq > policy->cur && @@ -607,15 +600,20 @@ again: struct interactive_cpu *icpu = &per_cpu(interactive_cpu, cpu); struct cpufreq_policy *policy; - if (unlikely(!down_read_trylock(&icpu->enable_sem))) + policy = cpufreq_cpu_get(cpu); + if (!policy) continue; - if (likely(icpu->ipolicy)) { - policy = icpu->ipolicy->policy; - cpufreq_interactive_adjust_cpu(cpu, policy); + down_write(&policy->rwsem); + + if (likely(down_read_trylock(&icpu->enable_sem))) { + if (likely(icpu->ipolicy)) + cpufreq_interactive_adjust_cpu(cpu, policy); + up_read(&icpu->enable_sem); } - up_read(&icpu->enable_sem); + up_write(&policy->rwsem); + cpufreq_cpu_put(policy); } goto again; @@ -671,26 +669,32 @@ static int cpufreq_interactive_notifier(struct notifier_block *nb, unsigned long val, void *data) { struct cpufreq_freqs *freq = data; - struct interactive_cpu *icpu = &per_cpu(interactive_cpu, freq->cpu); + struct cpufreq_policy *policy = freq->policy; + struct interactive_cpu *icpu; unsigned long flags; + int cpu; if (val != CPUFREQ_POSTCHANGE) return 0; - if (!down_read_trylock(&icpu->enable_sem)) - return 0; + for_each_cpu(cpu, policy->cpus) { + icpu = &per_cpu(interactive_cpu, cpu); + + if (!down_read_trylock(&icpu->enable_sem)) + continue; + + if (!icpu->ipolicy) { + up_read(&icpu->enable_sem); + continue; + } + + spin_lock_irqsave(&icpu->load_lock, flags); + update_load(icpu, cpu); + spin_unlock_irqrestore(&icpu->load_lock, flags); - if (!icpu->ipolicy) { up_read(&icpu->enable_sem); - return 0; } - spin_lock_irqsave(&icpu->load_lock, flags); - update_load(icpu, freq->cpu); - spin_unlock_irqrestore(&icpu->load_lock, flags); - - up_read(&icpu->enable_sem); - return 0; } @@ -1132,15 +1136,12 @@ static inline void gov_clear_update_util(struct cpufreq_policy *policy) for_each_cpu(i, policy->cpus) cpufreq_remove_update_util_hook(i); - synchronize_sched(); + synchronize_rcu(); } static void icpu_cancel_work(struct interactive_cpu *icpu) { irq_work_sync(&icpu->irq_work); -#ifdef CONFIG_ARCH_ROCKCHIP - irq_work_sync(&icpu->boost_irq_work); -#endif icpu->work_in_progress = false; del_timer_sync(&icpu->slack_timer); } @@ -1365,83 +1366,6 @@ static void rockchip_cpufreq_policy_init(struct interactive_policy *ipolicy) tunables->attr_set = attr_set; } } - -static unsigned int get_freq_for_util(struct cpufreq_policy *policy, unsigned long util) -{ - struct cpufreq_frequency_table *pos; - unsigned long max_cap, cur_cap; - unsigned int freq = 0; - - max_cap = arch_scale_cpu_capacity(NULL, policy->cpu); - cpufreq_for_each_valid_entry(pos, policy->freq_table) { - freq = pos->frequency; - - cur_cap = max_cap * freq / policy->max; - if (cur_cap > util) - break; - } - - return freq; -} - -static void task_boost_irq_work(struct irq_work *irq_work) -{ - struct interactive_cpu *pcpu; - struct interactive_policy *ipolicy; - unsigned long flags[2]; - u64 now, prev_boos_endtime; - unsigned int boost_freq; - - pcpu = container_of(irq_work, struct interactive_cpu, boost_irq_work); - if (!down_read_trylock(&pcpu->enable_sem)) - return; - - ipolicy = pcpu->ipolicy; - if (!ipolicy) - goto out; - - if (ipolicy->policy->cur == ipolicy->policy->max) - goto out; - - now = ktime_to_us(ktime_get()); - prev_boos_endtime = pcpu->task_boos_endtime;; - pcpu->task_boos_endtime = now + ipolicy->tunables->sampling_rate; - boost_freq = get_freq_for_util(ipolicy->policy, pcpu->task_boost_util); - if ((now < prev_boos_endtime) && (boost_freq <= pcpu->task_boost_freq)) - goto out; - pcpu->task_boost_freq = boost_freq; - - spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); - spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]); - if (pcpu->target_freq < pcpu->task_boost_freq) { - pcpu->target_freq = pcpu->task_boost_freq; - cpumask_set_cpu(pcpu->cpu, &speedchange_cpumask); - wake_up_process(speedchange_task); - } - spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]); - spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]); - -out: - up_read(&pcpu->enable_sem); -} - -extern unsigned long capacity_curr_of(int cpu); - -void cpufreq_task_boost(int cpu, unsigned long util) -{ - struct interactive_cpu *pcpu = &per_cpu(interactive_cpu, cpu); - unsigned long cap, min_util; - - if (!speedchange_task) - return; - - min_util = util + (util >> 2); - cap = capacity_curr_of(cpu); - if (min_util > cap) { - pcpu->task_boost_util = min_util; - irq_work_queue(&pcpu->boost_irq_work); - } -} #endif int cpufreq_interactive_init(struct cpufreq_policy *policy) @@ -1670,9 +1594,6 @@ static int __init cpufreq_interactive_gov_init(void) icpu = &per_cpu(interactive_cpu, cpu); init_irq_work(&icpu->irq_work, irq_work); -#ifdef CONFIG_ARCH_ROCKCHIP - init_irq_work(&icpu->boost_irq_work, task_boost_irq_work); -#endif spin_lock_init(&icpu->load_lock); spin_lock_init(&icpu->target_freq_lock); init_rwsem(&icpu->enable_sem);