[PATCH] PM: sleep: Fix race condition in suspend statistics updates

tuhaowen posted 1 patch 3 weeks, 1 day ago
kernel/power/main.c | 9 +++++++++
1 file changed, 9 insertions(+)
[PATCH] PM: sleep: Fix race condition in suspend statistics updates
Posted by tuhaowen 3 weeks, 1 day ago
The suspend_stats structure tracks suspend/resume failure information
and is protected by suspend_stats_lock. However, while
dpm_save_failed_dev() correctly uses this lock, dpm_save_failed_step()
and dpm_save_errno() modify the same structure without any locking
protection.

This can cause races between writers (suspend/resume code updating
stats) and readers (userspace reading from /sys/power/suspend_stats/
or debugfs), leading to:

- Lost updates to counters (success, fail, step_failures)
- Corrupted circular buffer indices (last_failed_step,
  last_failed_errno)
- Inconsistent data when reading statistics from sysfs/debugfs

Fix this by adding mutex_lock/unlock protection to both
dpm_save_failed_step() and dpm_save_errno(). These functions are
always called in process context, so mutex can be used safely.

Signed-off-by: tuhaowen <tuhaowen@uniontech.com>
---
 kernel/power/main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/kernel/power/main.c b/kernel/power/main.c
index bb7dd73e18fc..d8b053d85dc0 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -503,16 +503,23 @@ void dpm_save_failed_dev(const char *name)
 
 void dpm_save_failed_step(enum suspend_stat_step step)
 {
+	mutex_lock(&suspend_stats_lock);
+
 	suspend_stats.step_failures[step-1]++;
 	suspend_stats.failed_steps[suspend_stats.last_failed_step] = step;
 	suspend_stats.last_failed_step++;
 	suspend_stats.last_failed_step %= REC_FAILED_NUM;
+
+	mutex_unlock(&suspend_stats_lock);
 }
 
 void dpm_save_errno(int err)
 {
+	mutex_lock(&suspend_stats_lock);
+
 	if (!err) {
 		suspend_stats.success++;
+		mutex_unlock(&suspend_stats_lock);
 		return;
 	}
 
@@ -521,6 +528,8 @@ void dpm_save_errno(int err)
 	suspend_stats.errno[suspend_stats.last_failed_errno] = err;
 	suspend_stats.last_failed_errno++;
 	suspend_stats.last_failed_errno %= REC_FAILED_NUM;
+
+	mutex_unlock(&suspend_stats_lock);
 }
 
 void pm_report_hw_sleep_time(u64 t)
-- 
2.20.1