Skip to content

Commit b7898fd

Browse files
committed
cpufreq: Support for fast frequency switching
Modify the ACPI cpufreq driver to provide a method for switching CPU frequencies from interrupt context and update the cpufreq core to support that method if available. Introduce a new cpufreq driver callback, ->fast_switch, to be invoked for frequency switching from interrupt context by (future) governors supporting that feature via (new) helper function cpufreq_driver_fast_switch(). Add two new policy flags, fast_switch_possible, to be set by the cpufreq driver if fast frequency switching can be used for the given policy and fast_switch_enabled, to be set by the governor if it is going to use fast frequency switching for the given policy. Also add a helper for setting the latter. Since fast frequency switching is inherently incompatible with cpufreq transition notifiers, make it possible to set the fast_switch_enabled only if there are no transition notifiers already registered and make the registration of new transition notifiers fail if fast_switch_enabled is set for at least one policy. Implement the ->fast_switch callback in the ACPI cpufreq driver and make it set fast_switch_possible during policy initialization as appropriate. Signed-off-by: Rafael J. Wysocki <[email protected]> Acked-by: Viresh Kumar <[email protected]>
1 parent 379480d commit b7898fd

File tree

3 files changed

+183
-5
lines changed

3 files changed

+183
-5
lines changed

drivers/cpufreq/acpi-cpufreq.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,43 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
458458
return result;
459459
}
460460

461+
unsigned int acpi_cpufreq_fast_switch(struct cpufreq_policy *policy,
462+
unsigned int target_freq)
463+
{
464+
struct acpi_cpufreq_data *data = policy->driver_data;
465+
struct acpi_processor_performance *perf;
466+
struct cpufreq_frequency_table *entry;
467+
unsigned int next_perf_state, next_freq, freq;
468+
469+
/*
470+
* Find the closest frequency above target_freq.
471+
*
472+
* The table is sorted in the reverse order with respect to the
473+
* frequency and all of the entries are valid (see the initialization).
474+
*/
475+
entry = data->freq_table;
476+
do {
477+
entry++;
478+
freq = entry->frequency;
479+
} while (freq >= target_freq && freq != CPUFREQ_TABLE_END);
480+
entry--;
481+
next_freq = entry->frequency;
482+
next_perf_state = entry->driver_data;
483+
484+
perf = to_perf_data(data);
485+
if (perf->state == next_perf_state) {
486+
if (unlikely(data->resume))
487+
data->resume = 0;
488+
else
489+
return next_freq;
490+
}
491+
492+
data->cpu_freq_write(&perf->control_register,
493+
perf->states[next_perf_state].control);
494+
perf->state = next_perf_state;
495+
return next_freq;
496+
}
497+
461498
static unsigned long
462499
acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
463500
{
@@ -821,6 +858,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
821858
*/
822859
data->resume = 1;
823860

861+
policy->fast_switch_possible = !acpi_pstate_strict &&
862+
!(policy_is_shared(policy) && policy->shared_type != CPUFREQ_SHARED_TYPE_ANY);
863+
824864
return result;
825865

826866
err_freqfree:
@@ -843,6 +883,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
843883
pr_debug("acpi_cpufreq_cpu_exit\n");
844884

845885
if (data) {
886+
policy->fast_switch_possible = false;
846887
policy->driver_data = NULL;
847888
acpi_processor_unregister_performance(data->acpi_perf_cpu);
848889
free_cpumask_var(data->freqdomain_cpus);
@@ -876,6 +917,7 @@ static struct freq_attr *acpi_cpufreq_attr[] = {
876917
static struct cpufreq_driver acpi_cpufreq_driver = {
877918
.verify = cpufreq_generic_frequency_table_verify,
878919
.target_index = acpi_cpufreq_target,
920+
.fast_switch = acpi_cpufreq_fast_switch,
879921
.bios_limit = acpi_processor_get_bios_limit,
880922
.init = acpi_cpufreq_cpu_init,
881923
.exit = acpi_cpufreq_cpu_exit,

drivers/cpufreq/cpufreq.c

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ static inline bool has_target(void)
7777
static int cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
7878
static unsigned int __cpufreq_get(struct cpufreq_policy *policy);
7979
static int cpufreq_start_governor(struct cpufreq_policy *policy);
80+
static int cpufreq_exit_governor(struct cpufreq_policy *policy);
8081

8182
/**
8283
* Two notifier lists: the "policy" list is involved in the
@@ -429,6 +430,68 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy,
429430
}
430431
EXPORT_SYMBOL_GPL(cpufreq_freq_transition_end);
431432

433+
/*
434+
* Fast frequency switching status count. Positive means "enabled", negative
435+
* means "disabled" and 0 means "not decided yet".
436+
*/
437+
static int cpufreq_fast_switch_count;
438+
static DEFINE_MUTEX(cpufreq_fast_switch_lock);
439+
440+
static void cpufreq_list_transition_notifiers(void)
441+
{
442+
struct notifier_block *nb;
443+
444+
pr_info("Registered transition notifiers:\n");
445+
446+
mutex_lock(&cpufreq_transition_notifier_list.mutex);
447+
448+
for (nb = cpufreq_transition_notifier_list.head; nb; nb = nb->next)
449+
pr_info("%pF\n", nb->notifier_call);
450+
451+
mutex_unlock(&cpufreq_transition_notifier_list.mutex);
452+
}
453+
454+
/**
455+
* cpufreq_enable_fast_switch - Enable fast frequency switching for policy.
456+
* @policy: cpufreq policy to enable fast frequency switching for.
457+
*
458+
* Try to enable fast frequency switching for @policy.
459+
*
460+
* The attempt will fail if there is at least one transition notifier registered
461+
* at this point, as fast frequency switching is quite fundamentally at odds
462+
* with transition notifiers. Thus if successful, it will make registration of
463+
* transition notifiers fail going forward.
464+
*/
465+
void cpufreq_enable_fast_switch(struct cpufreq_policy *policy)
466+
{
467+
lockdep_assert_held(&policy->rwsem);
468+
469+
if (!policy->fast_switch_possible)
470+
return;
471+
472+
mutex_lock(&cpufreq_fast_switch_lock);
473+
if (cpufreq_fast_switch_count >= 0) {
474+
cpufreq_fast_switch_count++;
475+
policy->fast_switch_enabled = true;
476+
} else {
477+
pr_warn("CPU%u: Fast frequency switching not enabled\n",
478+
policy->cpu);
479+
cpufreq_list_transition_notifiers();
480+
}
481+
mutex_unlock(&cpufreq_fast_switch_lock);
482+
}
483+
EXPORT_SYMBOL_GPL(cpufreq_enable_fast_switch);
484+
485+
static void cpufreq_disable_fast_switch(struct cpufreq_policy *policy)
486+
{
487+
mutex_lock(&cpufreq_fast_switch_lock);
488+
if (policy->fast_switch_enabled) {
489+
policy->fast_switch_enabled = false;
490+
if (!WARN_ON(cpufreq_fast_switch_count <= 0))
491+
cpufreq_fast_switch_count--;
492+
}
493+
mutex_unlock(&cpufreq_fast_switch_lock);
494+
}
432495

433496
/*********************************************************************
434497
* SYSFS INTERFACE *
@@ -1319,7 +1382,7 @@ static void cpufreq_offline(unsigned int cpu)
13191382

13201383
/* If cpu is last user of policy, free policy */
13211384
if (has_target()) {
1322-
ret = cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
1385+
ret = cpufreq_exit_governor(policy);
13231386
if (ret)
13241387
pr_err("%s: Failed to exit governor\n", __func__);
13251388
}
@@ -1447,8 +1510,12 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy)
14471510

14481511
ret_freq = cpufreq_driver->get(policy->cpu);
14491512

1450-
/* Updating inactive policies is invalid, so avoid doing that. */
1451-
if (unlikely(policy_is_inactive(policy)))
1513+
/*
1514+
* Updating inactive policies is invalid, so avoid doing that. Also
1515+
* if fast frequency switching is used with the given policy, the check
1516+
* against policy->cur is pointless, so skip it in that case too.
1517+
*/
1518+
if (unlikely(policy_is_inactive(policy)) || policy->fast_switch_enabled)
14521519
return ret_freq;
14531520

14541521
if (ret_freq && policy->cur &&
@@ -1672,8 +1739,18 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
16721739

16731740
switch (list) {
16741741
case CPUFREQ_TRANSITION_NOTIFIER:
1742+
mutex_lock(&cpufreq_fast_switch_lock);
1743+
1744+
if (cpufreq_fast_switch_count > 0) {
1745+
mutex_unlock(&cpufreq_fast_switch_lock);
1746+
return -EBUSY;
1747+
}
16751748
ret = srcu_notifier_chain_register(
16761749
&cpufreq_transition_notifier_list, nb);
1750+
if (!ret)
1751+
cpufreq_fast_switch_count--;
1752+
1753+
mutex_unlock(&cpufreq_fast_switch_lock);
16771754
break;
16781755
case CPUFREQ_POLICY_NOTIFIER:
16791756
ret = blocking_notifier_chain_register(
@@ -1706,8 +1783,14 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
17061783

17071784
switch (list) {
17081785
case CPUFREQ_TRANSITION_NOTIFIER:
1786+
mutex_lock(&cpufreq_fast_switch_lock);
1787+
17091788
ret = srcu_notifier_chain_unregister(
17101789
&cpufreq_transition_notifier_list, nb);
1790+
if (!ret && !WARN_ON(cpufreq_fast_switch_count >= 0))
1791+
cpufreq_fast_switch_count++;
1792+
1793+
mutex_unlock(&cpufreq_fast_switch_lock);
17111794
break;
17121795
case CPUFREQ_POLICY_NOTIFIER:
17131796
ret = blocking_notifier_chain_unregister(
@@ -1726,6 +1809,37 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
17261809
* GOVERNORS *
17271810
*********************************************************************/
17281811

1812+
/**
1813+
* cpufreq_driver_fast_switch - Carry out a fast CPU frequency switch.
1814+
* @policy: cpufreq policy to switch the frequency for.
1815+
* @target_freq: New frequency to set (may be approximate).
1816+
*
1817+
* Carry out a fast frequency switch without sleeping.
1818+
*
1819+
* The driver's ->fast_switch() callback invoked by this function must be
1820+
* suitable for being called from within RCU-sched read-side critical sections
1821+
* and it is expected to select the minimum available frequency greater than or
1822+
* equal to @target_freq (CPUFREQ_RELATION_L).
1823+
*
1824+
* This function must not be called if policy->fast_switch_enabled is unset.
1825+
*
1826+
* Governors calling this function must guarantee that it will never be invoked
1827+
* twice in parallel for the same policy and that it will never be called in
1828+
* parallel with either ->target() or ->target_index() for the same policy.
1829+
*
1830+
* If CPUFREQ_ENTRY_INVALID is returned by the driver's ->fast_switch()
1831+
* callback to indicate an error condition, the hardware configuration must be
1832+
* preserved.
1833+
*/
1834+
unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
1835+
unsigned int target_freq)
1836+
{
1837+
clamp_val(target_freq, policy->min, policy->max);
1838+
1839+
return cpufreq_driver->fast_switch(policy, target_freq);
1840+
}
1841+
EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch);
1842+
17291843
/* Must set freqs->new to intermediate frequency */
17301844
static int __target_intermediate(struct cpufreq_policy *policy,
17311845
struct cpufreq_freqs *freqs, int index)
@@ -1946,6 +2060,12 @@ static int cpufreq_start_governor(struct cpufreq_policy *policy)
19462060
return ret ? ret : cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
19472061
}
19482062

2063+
static int cpufreq_exit_governor(struct cpufreq_policy *policy)
2064+
{
2065+
cpufreq_disable_fast_switch(policy);
2066+
return cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
2067+
}
2068+
19492069
int cpufreq_register_governor(struct cpufreq_governor *governor)
19502070
{
19512071
int err;
@@ -2101,7 +2221,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
21012221
return ret;
21022222
}
21032223

2104-
ret = cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
2224+
ret = cpufreq_exit_governor(policy);
21052225
if (ret) {
21062226
pr_err("%s: Failed to Exit Governor: %s (%d)\n",
21072227
__func__, old_gov->name, ret);
@@ -2118,7 +2238,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
21182238
pr_debug("cpufreq: governor change\n");
21192239
return 0;
21202240
}
2121-
cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
2241+
cpufreq_exit_governor(policy);
21222242
}
21232243

21242244
/* new governor failed, so re-start old one */

include/linux/cpufreq.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ struct cpufreq_policy {
102102
*/
103103
struct rw_semaphore rwsem;
104104

105+
/*
106+
* Fast switch flags:
107+
* - fast_switch_possible should be set by the driver if it can
108+
* guarantee that frequency can be changed on any CPU sharing the
109+
* policy and that the change will affect all of the policy CPUs then.
110+
* - fast_switch_enabled is to be set by governors that support fast
111+
* freqnency switching with the help of cpufreq_enable_fast_switch().
112+
*/
113+
bool fast_switch_possible;
114+
bool fast_switch_enabled;
115+
105116
/* Synchronization for frequency transitions */
106117
bool transition_ongoing; /* Tracks transition status */
107118
spinlock_t transition_lock;
@@ -156,6 +167,7 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
156167
int cpufreq_update_policy(unsigned int cpu);
157168
bool have_governor_per_policy(void);
158169
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
170+
void cpufreq_enable_fast_switch(struct cpufreq_policy *policy);
159171
#else
160172
static inline unsigned int cpufreq_get(unsigned int cpu)
161173
{
@@ -236,6 +248,8 @@ struct cpufreq_driver {
236248
unsigned int relation); /* Deprecated */
237249
int (*target_index)(struct cpufreq_policy *policy,
238250
unsigned int index);
251+
unsigned int (*fast_switch)(struct cpufreq_policy *policy,
252+
unsigned int target_freq);
239253
/*
240254
* Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
241255
* unset.
@@ -464,6 +478,8 @@ struct cpufreq_governor {
464478
};
465479

466480
/* Pass a target to the cpufreq driver */
481+
unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
482+
unsigned int target_freq);
467483
int cpufreq_driver_target(struct cpufreq_policy *policy,
468484
unsigned int target_freq,
469485
unsigned int relation);

0 commit comments

Comments
 (0)