Skip to content

Commit 40c3bd4

Browse files
vireshkrafaeljw
authored andcommitted
cpufreq: stats: Defer stats update to cpufreq_stats_record_transition()
In order to prepare for lock-less stats update, add support to defer any updates to it until cpufreq_stats_record_transition() is called. The stats were updated from two places earlier: - show_time_in_state(): This can be easily deferred, all we need is to calculate the delta duration again in this routine to show the current state's time-in-state. - store_reset(): This is a bit tricky as we need to clear the stats here and avoid races with simultaneous call to cpufreq_stats_record_transition(). Fix that by deferring the reset of the stats (within the code) to the next call to cpufreq_stats_record_transition(), but since we need to keep showing the right stats until that time, we capture the reset time and account for the time since last time reset was called until the time cpufreq_stats_record_transition() update the stats. User space will continue seeing the stats correctly, everything will be 0 after the stats are reset, apart from the time-in-state of the current state, until the time a frequency switch happens. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> [ rjw: Minor changelog edits ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent fccd2f0 commit 40c3bd4

1 file changed

Lines changed: 56 additions & 19 deletions

File tree

drivers/cpufreq/cpufreq_stats.c

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,22 @@ struct cpufreq_stats {
2222
spinlock_t lock;
2323
unsigned int *freq_table;
2424
unsigned int *trans_table;
25+
26+
/* Deferred reset */
27+
unsigned int reset_pending;
28+
unsigned long long reset_time;
2529
};
2630

27-
static void cpufreq_stats_update(struct cpufreq_stats *stats)
31+
static void cpufreq_stats_update(struct cpufreq_stats *stats,
32+
unsigned long long time)
2833
{
2934
unsigned long long cur_time = get_jiffies_64();
3035

31-
stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
36+
stats->time_in_state[stats->last_index] += cur_time - time;
3237
stats->last_time = cur_time;
3338
}
3439

35-
static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
40+
static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
3641
{
3742
unsigned int count = stats->max_state;
3843

@@ -41,51 +46,77 @@ static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
4146
memset(stats->trans_table, 0, count * count * sizeof(int));
4247
stats->last_time = get_jiffies_64();
4348
stats->total_trans = 0;
49+
50+
/* Adjust for the time elapsed since reset was requested */
51+
WRITE_ONCE(stats->reset_pending, 0);
52+
cpufreq_stats_update(stats, READ_ONCE(stats->reset_time));
4453
spin_unlock(&stats->lock);
4554
}
4655

4756
static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
4857
{
49-
return sprintf(buf, "%d\n", policy->stats->total_trans);
58+
struct cpufreq_stats *stats = policy->stats;
59+
60+
if (READ_ONCE(stats->reset_pending))
61+
return sprintf(buf, "%d\n", 0);
62+
else
63+
return sprintf(buf, "%d\n", stats->total_trans);
5064
}
5165
cpufreq_freq_attr_ro(total_trans);
5266

5367
static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
5468
{
5569
struct cpufreq_stats *stats = policy->stats;
70+
bool pending = READ_ONCE(stats->reset_pending);
71+
unsigned long long time;
5672
ssize_t len = 0;
5773
int i;
5874

5975
if (policy->fast_switch_enabled)
6076
return 0;
6177

62-
spin_lock(&stats->lock);
63-
cpufreq_stats_update(stats);
64-
spin_unlock(&stats->lock);
65-
6678
for (i = 0; i < stats->state_num; i++) {
79+
if (pending) {
80+
if (i == stats->last_index)
81+
time = get_jiffies_64() - READ_ONCE(stats->reset_time);
82+
else
83+
time = 0;
84+
} else {
85+
time = stats->time_in_state[i];
86+
if (i == stats->last_index)
87+
time += get_jiffies_64() - stats->last_time;
88+
}
89+
6790
len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
68-
(unsigned long long)
69-
jiffies_64_to_clock_t(stats->time_in_state[i]));
91+
jiffies_64_to_clock_t(time));
7092
}
7193
return len;
7294
}
7395
cpufreq_freq_attr_ro(time_in_state);
7496

97+
/* We don't care what is written to the attribute */
7598
static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
7699
size_t count)
77100
{
78-
/* We don't care what is written to the attribute. */
79-
cpufreq_stats_clear_table(policy->stats);
101+
struct cpufreq_stats *stats = policy->stats;
102+
103+
/*
104+
* Defer resetting of stats to cpufreq_stats_record_transition() to
105+
* avoid races.
106+
*/
107+
WRITE_ONCE(stats->reset_time, get_jiffies_64());
108+
WRITE_ONCE(stats->reset_pending, 1);
109+
80110
return count;
81111
}
82112
cpufreq_freq_attr_wo(reset);
83113

84114
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
85115
{
86116
struct cpufreq_stats *stats = policy->stats;
117+
bool pending = READ_ONCE(stats->reset_pending);
87118
ssize_t len = 0;
88-
int i, j;
119+
int i, j, count;
89120

90121
if (policy->fast_switch_enabled)
91122
return 0;
@@ -113,8 +144,13 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
113144
for (j = 0; j < stats->state_num; j++) {
114145
if (len >= PAGE_SIZE)
115146
break;
116-
len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ",
117-
stats->trans_table[i*stats->max_state+j]);
147+
148+
if (pending)
149+
count = 0;
150+
else
151+
count = stats->trans_table[i * stats->max_state + j];
152+
153+
len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", count);
118154
}
119155
if (len >= PAGE_SIZE)
120156
break;
@@ -228,10 +264,11 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
228264
struct cpufreq_stats *stats = policy->stats;
229265
int old_index, new_index;
230266

231-
if (!stats) {
232-
pr_debug("%s: No stats found\n", __func__);
267+
if (!stats)
233268
return;
234-
}
269+
270+
if (unlikely(READ_ONCE(stats->reset_pending)))
271+
cpufreq_stats_reset_table(stats);
235272

236273
old_index = stats->last_index;
237274
new_index = freq_table_get_index(stats, new_freq);
@@ -241,7 +278,7 @@ void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
241278
return;
242279

243280
spin_lock(&stats->lock);
244-
cpufreq_stats_update(stats);
281+
cpufreq_stats_update(stats, stats->last_time);
245282

246283
stats->last_index = new_index;
247284
stats->trans_table[old_index * stats->max_state + new_index]++;

0 commit comments

Comments
 (0)