cpufreq: Disable light-weight init/teardown during suspend/resume
During suspend when nonboot CPUs are hotplugged out, CPUFreq framework
attempts to skip sysfs/kobject related operations knowing that they
will be needed during resume. This is done by marking
cpufreq_suspended to true after cpufreq_suspend() is called. Then
policies are cached instead of destroyed when CPU is hotplugged out.
Similarly, when CPUs are hotplugged back in, policies are restored
from cached location instead of being created again. Once resume
reaches cpufreq_resume(), cpufreq_suspended is marked to false to
disable light-weight init/teardown for normal operation.
This sequence assumes no external entity could interfere hotplug
during suspend/resume, which is unfortunately not true. Imagine the
following scenario, where a policy manages CPU2 and 3.
1)
Thermal driver hotplugs out CPU2 and CPU3 before suspend and policy
is freed. Suspend would thus skip these CPUs. Before cpufreq_resume()
is reached, thermal attempts to hotplug them back in. Since
cpufreq_cpu_data_fallback is never cleared, it could contain pointer
to a freed policy, causing recovery to use a freed policy.
2)
Thermal driver hotplugs out CPU2 and CPU3 before suspend and policy
is freed. After cpufreq_suspend() is called but before
disable_nonboot_cpus() is executed, thermal driver decides to plug
them back in. CPUFreq would attempt to recover from
cpufreq_cpu_data_fallback, while it could contain a stale pointer to
an already freed policy.
Both 1) and 2) can be fixed by always clearing cpufreq_cpu_data_fallback
for policy->related_cpus when policy is freed.
3)
Suspend hotplugged out CPU2 and CPU3. When enable_nonboot_cpus() are
called, thermal driver blocked it from coming up. After
cpufreq_resume(), thermal brings the cores back up. This would result
in CPUFreq framework creating another policy for the cluster while the
older one is still cached in cpufreq_cpu_data_fallback, leading to
duplicated sysfs warnings and memleak.
In short, in order for this light-weight init/teardown to really work,
a few conditions must be met:
1) No driver will do cpu_up/down() between cpufreq_suspend() and
cpufreq_resume()
2) No driver that listens to CPU hotplug notification could block
hotplug between cpufreq_suspend() and cpufreq_resume()
This is not easy to enforce and would require duplicated code
for all drivers managing hardware limits. Therefore, disable
light-weight init/teardown during suspend/resume.
Change-Id: I8a64f914685d47441ee2e02d8d145be4f9e42f8e
Signed-off-by:
Junjie Wu <junjiew@codeaurora.org>
Loading
Please register or sign in to comment