PM / devfreq: Rewrite devfreq_update_status() to fix multiple bugs
authorSaravana Kannan <skannan@codeaurora.org>
Fri, 28 Feb 2014 03:38:57 +0000 (19:38 -0800)
committerChanho Park <chanho61.park@samsung.com>
Tue, 18 Nov 2014 02:47:52 +0000 (11:47 +0900)
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.

Change-Id: I9e3bb6ac6490d8f2e4bca2fd0d082f2fbd4a5b2e
Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
drivers/devfreq/devfreq.c

index 250b22d..1e4c717 100644 (file)
@@ -91,26 +91,35 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
  */
 static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 {
-       int lev, prev_lev;
+       int lev, prev_lev, ret = 0;
        unsigned long cur_time;
 
-       lev = devfreq_get_freq_level(devfreq, freq);
-       if (lev < 0)
-               return lev;
-
        cur_time = jiffies;
-       devfreq->time_in_state[lev] +=
+
+       prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
+       if (prev_lev < 0) {
+               ret = prev_lev;
+               goto out;
+       }
+
+       devfreq->time_in_state[prev_lev] +=
                         cur_time - devfreq->last_stat_updated;
-       if (freq != devfreq->previous_freq) {
-               prev_lev = devfreq_get_freq_level(devfreq,
-                                               devfreq->previous_freq);
+
+       lev = devfreq_get_freq_level(devfreq, freq);
+       if (lev < 0) {
+               ret = lev;
+               goto out;
+       }
+
+       if (lev != prev_lev) {
                devfreq->trans_table[(prev_lev *
                                devfreq->profile->max_state) + lev]++;
                devfreq->total_trans++;
        }
-       devfreq->last_stat_updated = cur_time;
 
-       return 0;
+out:
+       devfreq->last_stat_updated = cur_time;
+       return ret;
 }
 
 /**