Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: Rewrite devfreq_update_status() to fix multiple bugs

The current devfreq_update_status() has the following bugs:
- If previous frequency doesn't have a valid level, it does an out of bounds
access into the trans_table and causes memory corruption.
- When the new frequency doesn't have a valid level, the time spent in the
new frequency is counted towards the next valid frequency switch instead of
being ignored.
- The time spent on the previous frequency is added to the new frequency's
stats instead of the previous frequency's stats.

This patch fixes all of this.

Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>

authored by

Saravana Kannan and committed by
MyungJoo Ham
e35d35a1 dcb99fd9

+20 -11
+20 -11
drivers/devfreq/devfreq.c
··· 91 91 */ 92 92 static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) 93 93 { 94 - int lev, prev_lev; 94 + int lev, prev_lev, ret = 0; 95 95 unsigned long cur_time; 96 96 97 - lev = devfreq_get_freq_level(devfreq, freq); 98 - if (lev < 0) 99 - return lev; 100 - 101 97 cur_time = jiffies; 102 - devfreq->time_in_state[lev] += 98 + 99 + prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq); 100 + if (prev_lev < 0) { 101 + ret = prev_lev; 102 + goto out; 103 + } 104 + 105 + devfreq->time_in_state[prev_lev] += 103 106 cur_time - devfreq->last_stat_updated; 104 - if (freq != devfreq->previous_freq) { 105 - prev_lev = devfreq_get_freq_level(devfreq, 106 - devfreq->previous_freq); 107 + 108 + lev = devfreq_get_freq_level(devfreq, freq); 109 + if (lev < 0) { 110 + ret = lev; 111 + goto out; 112 + } 113 + 114 + if (lev != prev_lev) { 107 115 devfreq->trans_table[(prev_lev * 108 116 devfreq->profile->max_state) + lev]++; 109 117 devfreq->total_trans++; 110 118 } 111 - devfreq->last_stat_updated = cur_time; 112 119 113 - return 0; 120 + out: 121 + devfreq->last_stat_updated = cur_time; 122 + return ret; 114 123 } 115 124 116 125 /**