diff options
| -rw-r--r-- | Documentation/power/runtime_pm.txt | 3 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/Makefile | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/aperfmperf.c | 74 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/cpu.h | 3 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/proc.c | 6 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 31 | ||||
| -rw-r--r-- | fs/proc/cpuinfo.c | 6 | ||||
| -rw-r--r-- | include/linux/cpufreq.h | 1 |
8 files changed, 80 insertions, 46 deletions
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 57af2f7963ee..937e33c46211 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt | |||
| @@ -435,8 +435,7 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: | |||
| 435 | PM status to 'suspended' and update its parent's counter of 'active' | 435 | PM status to 'suspended' and update its parent's counter of 'active' |
| 436 | children as appropriate (it is only valid to use this function if | 436 | children as appropriate (it is only valid to use this function if |
| 437 | 'power.runtime_error' is set or 'power.disable_depth' is greater than | 437 | 'power.runtime_error' is set or 'power.disable_depth' is greater than |
| 438 | zero); it will fail and return an error code if the device has a child | 438 | zero) |
| 439 | which is active and the 'power.ignore_children' flag is unset | ||
| 440 | 439 | ||
| 441 | bool pm_runtime_active(struct device *dev); | 440 | bool pm_runtime_active(struct device *dev); |
| 442 | - return true if the device's runtime PM status is 'active' or its | 441 | - return true if the device's runtime PM status is 'active' or its |
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 90cb82dbba57..570e8bb1f386 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile | |||
| @@ -22,7 +22,7 @@ obj-y += common.o | |||
| 22 | obj-y += rdrand.o | 22 | obj-y += rdrand.o |
| 23 | obj-y += match.o | 23 | obj-y += match.o |
| 24 | obj-y += bugs.o | 24 | obj-y += bugs.o |
| 25 | obj-$(CONFIG_CPU_FREQ) += aperfmperf.o | 25 | obj-y += aperfmperf.o |
| 26 | obj-y += cpuid-deps.o | 26 | obj-y += cpuid-deps.o |
| 27 | 27 | ||
| 28 | obj-$(CONFIG_PROC_FS) += proc.o | 28 | obj-$(CONFIG_PROC_FS) += proc.o |
diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c index 957813e0180d..7eba34df54c3 100644 --- a/arch/x86/kernel/cpu/aperfmperf.c +++ b/arch/x86/kernel/cpu/aperfmperf.c | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include <linux/percpu.h> | 14 | #include <linux/percpu.h> |
| 15 | #include <linux/smp.h> | 15 | #include <linux/smp.h> |
| 16 | 16 | ||
| 17 | #include "cpu.h" | ||
| 18 | |||
| 17 | struct aperfmperf_sample { | 19 | struct aperfmperf_sample { |
| 18 | unsigned int khz; | 20 | unsigned int khz; |
| 19 | ktime_t time; | 21 | ktime_t time; |
| @@ -24,7 +26,7 @@ struct aperfmperf_sample { | |||
| 24 | static DEFINE_PER_CPU(struct aperfmperf_sample, samples); | 26 | static DEFINE_PER_CPU(struct aperfmperf_sample, samples); |
| 25 | 27 | ||
| 26 | #define APERFMPERF_CACHE_THRESHOLD_MS 10 | 28 | #define APERFMPERF_CACHE_THRESHOLD_MS 10 |
| 27 | #define APERFMPERF_REFRESH_DELAY_MS 20 | 29 | #define APERFMPERF_REFRESH_DELAY_MS 10 |
| 28 | #define APERFMPERF_STALE_THRESHOLD_MS 1000 | 30 | #define APERFMPERF_STALE_THRESHOLD_MS 1000 |
| 29 | 31 | ||
| 30 | /* | 32 | /* |
| @@ -38,8 +40,6 @@ static void aperfmperf_snapshot_khz(void *dummy) | |||
| 38 | u64 aperf, aperf_delta; | 40 | u64 aperf, aperf_delta; |
| 39 | u64 mperf, mperf_delta; | 41 | u64 mperf, mperf_delta; |
| 40 | struct aperfmperf_sample *s = this_cpu_ptr(&samples); | 42 | struct aperfmperf_sample *s = this_cpu_ptr(&samples); |
| 41 | ktime_t now = ktime_get(); | ||
| 42 | s64 time_delta = ktime_ms_delta(now, s->time); | ||
| 43 | unsigned long flags; | 43 | unsigned long flags; |
| 44 | 44 | ||
| 45 | local_irq_save(flags); | 45 | local_irq_save(flags); |
| @@ -57,38 +57,68 @@ static void aperfmperf_snapshot_khz(void *dummy) | |||
| 57 | if (mperf_delta == 0) | 57 | if (mperf_delta == 0) |
| 58 | return; | 58 | return; |
| 59 | 59 | ||
| 60 | s->time = now; | 60 | s->time = ktime_get(); |
| 61 | s->aperf = aperf; | 61 | s->aperf = aperf; |
| 62 | s->mperf = mperf; | 62 | s->mperf = mperf; |
| 63 | 63 | s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | |
| 64 | /* If the previous iteration was too long ago, discard it. */ | ||
| 65 | if (time_delta > APERFMPERF_STALE_THRESHOLD_MS) | ||
| 66 | s->khz = 0; | ||
| 67 | else | ||
| 68 | s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | ||
| 69 | } | 64 | } |
| 70 | 65 | ||
| 71 | unsigned int arch_freq_get_on_cpu(int cpu) | 66 | static bool aperfmperf_snapshot_cpu(int cpu, ktime_t now, bool wait) |
| 72 | { | 67 | { |
| 73 | s64 time_delta; | 68 | s64 time_delta = ktime_ms_delta(now, per_cpu(samples.time, cpu)); |
| 74 | unsigned int khz; | 69 | |
| 70 | /* Don't bother re-computing within the cache threshold time. */ | ||
| 71 | if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS) | ||
| 72 | return true; | ||
| 73 | |||
| 74 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, wait); | ||
| 75 | |||
| 76 | /* Return false if the previous iteration was too long ago. */ | ||
| 77 | return time_delta <= APERFMPERF_STALE_THRESHOLD_MS; | ||
| 78 | } | ||
| 75 | 79 | ||
| 80 | unsigned int aperfmperf_get_khz(int cpu) | ||
| 81 | { | ||
| 76 | if (!cpu_khz) | 82 | if (!cpu_khz) |
| 77 | return 0; | 83 | return 0; |
| 78 | 84 | ||
| 79 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | 85 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) |
| 80 | return 0; | 86 | return 0; |
| 81 | 87 | ||
| 82 | /* Don't bother re-computing within the cache threshold time. */ | 88 | aperfmperf_snapshot_cpu(cpu, ktime_get(), true); |
| 83 | time_delta = ktime_ms_delta(ktime_get(), per_cpu(samples.time, cpu)); | 89 | return per_cpu(samples.khz, cpu); |
| 84 | khz = per_cpu(samples.khz, cpu); | 90 | } |
| 85 | if (khz && time_delta < APERFMPERF_CACHE_THRESHOLD_MS) | ||
| 86 | return khz; | ||
| 87 | 91 | ||
| 88 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | 92 | void arch_freq_prepare_all(void) |
| 89 | khz = per_cpu(samples.khz, cpu); | 93 | { |
| 90 | if (khz) | 94 | ktime_t now = ktime_get(); |
| 91 | return khz; | 95 | bool wait = false; |
| 96 | int cpu; | ||
| 97 | |||
| 98 | if (!cpu_khz) | ||
| 99 | return; | ||
| 100 | |||
| 101 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | ||
| 102 | return; | ||
| 103 | |||
| 104 | for_each_online_cpu(cpu) | ||
| 105 | if (!aperfmperf_snapshot_cpu(cpu, now, false)) | ||
| 106 | wait = true; | ||
| 107 | |||
| 108 | if (wait) | ||
| 109 | msleep(APERFMPERF_REFRESH_DELAY_MS); | ||
| 110 | } | ||
| 111 | |||
| 112 | unsigned int arch_freq_get_on_cpu(int cpu) | ||
| 113 | { | ||
| 114 | if (!cpu_khz) | ||
| 115 | return 0; | ||
| 116 | |||
| 117 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | ||
| 118 | return 0; | ||
| 119 | |||
| 120 | if (aperfmperf_snapshot_cpu(cpu, ktime_get(), true)) | ||
| 121 | return per_cpu(samples.khz, cpu); | ||
| 92 | 122 | ||
| 93 | msleep(APERFMPERF_REFRESH_DELAY_MS); | 123 | msleep(APERFMPERF_REFRESH_DELAY_MS); |
| 94 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | 124 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); |
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index f52a370b6c00..e806b11a99af 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h | |||
| @@ -47,4 +47,7 @@ extern const struct cpu_dev *const __x86_cpu_dev_start[], | |||
| 47 | 47 | ||
| 48 | extern void get_cpu_cap(struct cpuinfo_x86 *c); | 48 | extern void get_cpu_cap(struct cpuinfo_x86 *c); |
| 49 | extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); | 49 | extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); |
| 50 | |||
| 51 | unsigned int aperfmperf_get_khz(int cpu); | ||
| 52 | |||
| 50 | #endif /* ARCH_X86_CPU_H */ | 53 | #endif /* ARCH_X86_CPU_H */ |
diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index 6b7e17bf0b71..e7ecedafa1c8 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <linux/seq_file.h> | 5 | #include <linux/seq_file.h> |
| 6 | #include <linux/cpufreq.h> | 6 | #include <linux/cpufreq.h> |
| 7 | 7 | ||
| 8 | #include "cpu.h" | ||
| 9 | |||
| 8 | /* | 10 | /* |
| 9 | * Get CPU information for use by the procfs. | 11 | * Get CPU information for use by the procfs. |
| 10 | */ | 12 | */ |
| @@ -78,9 +80,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
| 78 | seq_printf(m, "microcode\t: 0x%x\n", c->microcode); | 80 | seq_printf(m, "microcode\t: 0x%x\n", c->microcode); |
| 79 | 81 | ||
| 80 | if (cpu_has(c, X86_FEATURE_TSC)) { | 82 | if (cpu_has(c, X86_FEATURE_TSC)) { |
| 81 | unsigned int freq = cpufreq_quick_get(cpu); | 83 | unsigned int freq = aperfmperf_get_khz(cpu); |
| 82 | 84 | ||
| 83 | if (!freq) | 85 | if (!freq) |
| 86 | freq = cpufreq_quick_get(cpu); | ||
| 87 | if (!freq) | ||
| 84 | freq = cpu_khz; | 88 | freq = cpu_khz; |
| 85 | seq_printf(m, "cpu MHz\t\t: %u.%03u\n", | 89 | seq_printf(m, "cpu MHz\t\t: %u.%03u\n", |
| 86 | freq / 1000, (freq % 1000)); | 90 | freq / 1000, (freq % 1000)); |
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 2362b9e9701e..027d159ac381 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -1101,29 +1101,13 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) | |||
| 1101 | goto out; | 1101 | goto out; |
| 1102 | } | 1102 | } |
| 1103 | 1103 | ||
| 1104 | if (dev->power.runtime_status == status) | 1104 | if (dev->power.runtime_status == status || !parent) |
| 1105 | goto out_set; | 1105 | goto out_set; |
| 1106 | 1106 | ||
| 1107 | if (status == RPM_SUSPENDED) { | 1107 | if (status == RPM_SUSPENDED) { |
| 1108 | /* | 1108 | atomic_add_unless(&parent->power.child_count, -1, 0); |
| 1109 | * It is invalid to suspend a device with an active child, | 1109 | notify_parent = !parent->power.ignore_children; |
| 1110 | * unless it has been set to ignore its children. | 1110 | } else { |
| 1111 | */ | ||
| 1112 | if (!dev->power.ignore_children && | ||
| 1113 | atomic_read(&dev->power.child_count)) { | ||
| 1114 | dev_err(dev, "runtime PM trying to suspend device but active child\n"); | ||
| 1115 | error = -EBUSY; | ||
| 1116 | goto out; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | if (parent) { | ||
| 1120 | atomic_add_unless(&parent->power.child_count, -1, 0); | ||
| 1121 | notify_parent = !parent->power.ignore_children; | ||
| 1122 | } | ||
| 1123 | goto out_set; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | if (parent) { | ||
| 1127 | spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING); | 1111 | spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING); |
| 1128 | 1112 | ||
| 1129 | /* | 1113 | /* |
| @@ -1307,6 +1291,13 @@ void pm_runtime_enable(struct device *dev) | |||
| 1307 | else | 1291 | else |
| 1308 | dev_warn(dev, "Unbalanced %s!\n", __func__); | 1292 | dev_warn(dev, "Unbalanced %s!\n", __func__); |
| 1309 | 1293 | ||
| 1294 | WARN(!dev->power.disable_depth && | ||
| 1295 | dev->power.runtime_status == RPM_SUSPENDED && | ||
| 1296 | !dev->power.ignore_children && | ||
| 1297 | atomic_read(&dev->power.child_count) > 0, | ||
| 1298 | "Enabling runtime PM for inactive device (%s) with active children\n", | ||
| 1299 | dev_name(dev)); | ||
| 1300 | |||
| 1310 | spin_unlock_irqrestore(&dev->power.lock, flags); | 1301 | spin_unlock_irqrestore(&dev->power.lock, flags); |
| 1311 | } | 1302 | } |
| 1312 | EXPORT_SYMBOL_GPL(pm_runtime_enable); | 1303 | EXPORT_SYMBOL_GPL(pm_runtime_enable); |
diff --git a/fs/proc/cpuinfo.c b/fs/proc/cpuinfo.c index e0f867cd8553..96f1087e372c 100644 --- a/fs/proc/cpuinfo.c +++ b/fs/proc/cpuinfo.c | |||
| @@ -1,12 +1,18 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include <linux/cpufreq.h> | ||
| 2 | #include <linux/fs.h> | 3 | #include <linux/fs.h> |
| 3 | #include <linux/init.h> | 4 | #include <linux/init.h> |
| 4 | #include <linux/proc_fs.h> | 5 | #include <linux/proc_fs.h> |
| 5 | #include <linux/seq_file.h> | 6 | #include <linux/seq_file.h> |
| 6 | 7 | ||
| 8 | __weak void arch_freq_prepare_all(void) | ||
| 9 | { | ||
| 10 | } | ||
| 11 | |||
| 7 | extern const struct seq_operations cpuinfo_op; | 12 | extern const struct seq_operations cpuinfo_op; |
| 8 | static int cpuinfo_open(struct inode *inode, struct file *file) | 13 | static int cpuinfo_open(struct inode *inode, struct file *file) |
| 9 | { | 14 | { |
| 15 | arch_freq_prepare_all(); | ||
| 10 | return seq_open(file, &cpuinfo_op); | 16 | return seq_open(file, &cpuinfo_op); |
| 11 | } | 17 | } |
| 12 | 18 | ||
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 28734ee185a7..065f3a8eb486 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h | |||
| @@ -917,6 +917,7 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy) | |||
| 917 | } | 917 | } |
| 918 | #endif | 918 | #endif |
| 919 | 919 | ||
| 920 | extern void arch_freq_prepare_all(void); | ||
| 920 | extern unsigned int arch_freq_get_on_cpu(int cpu); | 921 | extern unsigned int arch_freq_get_on_cpu(int cpu); |
| 921 | 922 | ||
| 922 | extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, | 923 | extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, |
