hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/cpufreq/cpufreq_stats.c
....@@ -1,12 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * drivers/cpufreq/cpufreq_stats.c
34 *
45 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
56 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
6
- *
7
- * This program is free software; you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License version 2 as
9
- * published by the Free Software Foundation.
107 */
118
129 #include <linux/cpu.h>
....@@ -14,7 +11,6 @@
1411 #include <linux/module.h>
1512 #include <linux/slab.h>
1613
17
-static DEFINE_SPINLOCK(cpufreq_stats_lock);
1814
1915 struct cpufreq_stats {
2016 unsigned int total_trans;
....@@ -25,19 +21,22 @@
2521 u64 *time_in_state;
2622 unsigned int *freq_table;
2723 unsigned int *trans_table;
24
+
25
+ /* Deferred reset */
26
+ unsigned int reset_pending;
27
+ unsigned long long reset_time;
2828 };
2929
30
-static void cpufreq_stats_update(struct cpufreq_stats *stats)
30
+static void cpufreq_stats_update(struct cpufreq_stats *stats,
31
+ unsigned long long time)
3132 {
3233 unsigned long long cur_time = get_jiffies_64();
3334
34
- spin_lock(&cpufreq_stats_lock);
35
- stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
35
+ stats->time_in_state[stats->last_index] += cur_time - time;
3636 stats->last_time = cur_time;
37
- spin_unlock(&cpufreq_stats_lock);
3837 }
3938
40
-static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
39
+static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
4140 {
4241 unsigned int count = stats->max_state;
4342
....@@ -45,77 +44,124 @@
4544 memset(stats->trans_table, 0, count * count * sizeof(int));
4645 stats->last_time = get_jiffies_64();
4746 stats->total_trans = 0;
47
+
48
+ /* Adjust for the time elapsed since reset was requested */
49
+ WRITE_ONCE(stats->reset_pending, 0);
50
+ /*
51
+ * Prevent the reset_time read from being reordered before the
52
+ * reset_pending accesses in cpufreq_stats_record_transition().
53
+ */
54
+ smp_rmb();
55
+ cpufreq_stats_update(stats, READ_ONCE(stats->reset_time));
4856 }
4957
5058 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
5159 {
52
- return sprintf(buf, "%d\n", policy->stats->total_trans);
60
+ struct cpufreq_stats *stats = policy->stats;
61
+
62
+ if (READ_ONCE(stats->reset_pending))
63
+ return sprintf(buf, "%d\n", 0);
64
+ else
65
+ return sprintf(buf, "%u\n", stats->total_trans);
5366 }
67
+cpufreq_freq_attr_ro(total_trans);
5468
5569 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
5670 {
5771 struct cpufreq_stats *stats = policy->stats;
72
+ bool pending = READ_ONCE(stats->reset_pending);
73
+ unsigned long long time;
5874 ssize_t len = 0;
5975 int i;
6076
61
- if (policy->fast_switch_enabled)
62
- return 0;
63
-
64
- cpufreq_stats_update(stats);
6577 for (i = 0; i < stats->state_num; i++) {
78
+ if (pending) {
79
+ if (i == stats->last_index) {
80
+ /*
81
+ * Prevent the reset_time read from occurring
82
+ * before the reset_pending read above.
83
+ */
84
+ smp_rmb();
85
+ time = get_jiffies_64() - READ_ONCE(stats->reset_time);
86
+ } else {
87
+ time = 0;
88
+ }
89
+ } else {
90
+ time = stats->time_in_state[i];
91
+ if (i == stats->last_index)
92
+ time += get_jiffies_64() - stats->last_time;
93
+ }
94
+
6695 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
67
- (unsigned long long)
68
- jiffies_64_to_clock_t(stats->time_in_state[i]));
96
+ jiffies_64_to_clock_t(time));
6997 }
7098 return len;
7199 }
100
+cpufreq_freq_attr_ro(time_in_state);
72101
102
+/* We don't care what is written to the attribute */
73103 static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
74104 size_t count)
75105 {
76
- /* We don't care what is written to the attribute. */
77
- cpufreq_stats_clear_table(policy->stats);
106
+ struct cpufreq_stats *stats = policy->stats;
107
+
108
+ /*
109
+ * Defer resetting of stats to cpufreq_stats_record_transition() to
110
+ * avoid races.
111
+ */
112
+ WRITE_ONCE(stats->reset_time, get_jiffies_64());
113
+ /*
114
+ * The memory barrier below is to prevent the readers of reset_time from
115
+ * seeing a stale or partially updated value.
116
+ */
117
+ smp_wmb();
118
+ WRITE_ONCE(stats->reset_pending, 1);
119
+
78120 return count;
79121 }
122
+cpufreq_freq_attr_wo(reset);
80123
81124 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
82125 {
83126 struct cpufreq_stats *stats = policy->stats;
127
+ bool pending = READ_ONCE(stats->reset_pending);
84128 ssize_t len = 0;
85
- int i, j;
129
+ int i, j, count;
86130
87
- if (policy->fast_switch_enabled)
88
- return 0;
89
-
90
- len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
91
- len += snprintf(buf + len, PAGE_SIZE - len, " : ");
131
+ len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n");
132
+ len += scnprintf(buf + len, PAGE_SIZE - len, " : ");
92133 for (i = 0; i < stats->state_num; i++) {
93134 if (len >= PAGE_SIZE)
94135 break;
95
- len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
136
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ",
96137 stats->freq_table[i]);
97138 }
98139 if (len >= PAGE_SIZE)
99140 return PAGE_SIZE;
100141
101
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
142
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
102143
103144 for (i = 0; i < stats->state_num; i++) {
104145 if (len >= PAGE_SIZE)
105146 break;
106147
107
- len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
148
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%9u: ",
108149 stats->freq_table[i]);
109150
110151 for (j = 0; j < stats->state_num; j++) {
111152 if (len >= PAGE_SIZE)
112153 break;
113
- len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
114
- stats->trans_table[i*stats->max_state+j]);
154
+
155
+ if (pending)
156
+ count = 0;
157
+ else
158
+ count = stats->trans_table[i * stats->max_state + j];
159
+
160
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", count);
115161 }
116162 if (len >= PAGE_SIZE)
117163 break;
118
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
164
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
119165 }
120166
121167 if (len >= PAGE_SIZE) {
....@@ -125,10 +171,6 @@
125171 return len;
126172 }
127173 cpufreq_freq_attr_ro(trans_table);
128
-
129
-cpufreq_freq_attr_ro(total_trans);
130
-cpufreq_freq_attr_ro(time_in_state);
131
-cpufreq_freq_attr_wo(reset);
132174
133175 static struct attribute *default_attrs[] = {
134176 &total_trans.attr,
....@@ -228,19 +270,20 @@
228270 struct cpufreq_stats *stats = policy->stats;
229271 int old_index, new_index;
230272
231
- if (!stats) {
232
- pr_debug("%s: No stats found\n", __func__);
273
+ if (unlikely(!stats))
233274 return;
234
- }
275
+
276
+ if (unlikely(READ_ONCE(stats->reset_pending)))
277
+ cpufreq_stats_reset_table(stats);
235278
236279 old_index = stats->last_index;
237280 new_index = freq_table_get_index(stats, new_freq);
238281
239282 /* We can't do stats->time_in_state[-1]= .. */
240
- if (old_index == -1 || new_index == -1 || old_index == new_index)
283
+ if (unlikely(old_index == -1 || new_index == -1 || old_index == new_index))
241284 return;
242285
243
- cpufreq_stats_update(stats);
286
+ cpufreq_stats_update(stats, stats->last_time);
244287
245288 stats->last_index = new_index;
246289 stats->trans_table[old_index * stats->max_state + new_index]++;