[PATCH 2/7] devfreq: protect devfreq stats data with spinlock

From: Kamil Konieczny
Date: Wed Nov 13 2019 - 04:14:28 EST


Protect access to devfreq transitions stats with spinlock.

Signed-off-by: Kamil Konieczny <k.konieczny@xxxxxxxxxxx>
---
drivers/devfreq/devfreq.c | 18 +++++++++++++++---
include/linux/devfreq.h | 3 +++
2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 1602cca20fc4..ac04b5baef70 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -163,10 +163,16 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
cur_time = get_jiffies_64();

/* Immediately exit if previous_freq is not initialized yet. */
- if (!devfreq->previous_freq)
- goto out;
+ if (!devfreq->previous_freq) {
+ spin_lock(&devfreq->stats_lock);
+ devfreq->last_stat_updated = cur_time;
+ spin_unlock(&devfreq->stats_lock);
+ return 0;
+ }

prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
+
+ spin_lock(&devfreq->stats_lock);
if (prev_lev < 0) {
ret = prev_lev;
goto out;
@@ -174,7 +180,6 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)

devfreq->time_in_state[prev_lev] +=
cur_time - devfreq->last_stat_updated;
-
lev = devfreq_get_freq_level(devfreq, freq);
if (lev < 0) {
ret = lev;
@@ -189,6 +194,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)

out:
devfreq->last_stat_updated = cur_time;
+ spin_unlock(&devfreq->stats_lock);
return ret;
}
EXPORT_SYMBOL(devfreq_update_status);
@@ -478,7 +484,9 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
queue_delayed_work(devfreq_wq, &devfreq->work,
msecs_to_jiffies(devfreq->profile->polling_ms));

+ spin_lock(&devfreq->stats_lock);
devfreq->last_stat_updated = get_jiffies_64();
+ spin_unlock(&devfreq->stats_lock);
devfreq->stop_polling = false;

if (devfreq->profile->get_cur_freq &&
@@ -707,6 +715,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
}

devfreq->last_stat_updated = get_jiffies_64();
+ spin_lock_init(&devfreq->stats_lock);

srcu_init_notifier_head(&devfreq->transition_notifier_list);

@@ -1405,6 +1414,8 @@ static ssize_t trans_stat_show(struct device *dev,

len = sprintf(buf, " From : To\n");
len += sprintf(buf + len, " :");
+
+ spin_lock(&devfreq->stats_lock);
for (i = 0; i < max_state; i++)
len += sprintf(buf + len, "%10lu",
devfreq->profile->freq_table[i]);
@@ -1429,6 +1440,7 @@ static ssize_t trans_stat_show(struct device *dev,

len += sprintf(buf + len, "Total transition : %u\n",
devfreq->total_trans);
+ spin_unlock(&devfreq->stats_lock);
return len;
}
static DEVICE_ATTR_RO(trans_stat);
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index b81a86e47fb9..a344e0be99f3 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -135,6 +135,8 @@ struct devfreq_dev_profile {
* @trans_table: Statistics of devfreq transitions
* @time_in_state: Statistics of devfreq states
* @last_stat_updated: The last time stat updated
+ * @stats_lock: Lock protecting trans_table, time_in_state, last_time
+ * and total_trans used for statistics
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
*
* This structure stores the devfreq information for a give device.
@@ -176,6 +178,7 @@ struct devfreq {
unsigned int *trans_table;
u64 *time_in_state;
unsigned long long last_stat_updated;
+ spinlock_t stats_lock;

struct srcu_notifier_head transition_notifier_list;
};
--
2.24.0