From 97c227885e0ad87bbb56846140c7eb20395134f0 Mon Sep 17 00:00:00 2001
From: Jiajun Chen <chenjiajun8@huawei.com>
Date: Thu, 17 Jun 2021 16:17:11 +0800
Subject: [PATCH] cpuidle: fix container_of err in cpuidle_device and
 cpuidle_driver

euleros inclusion
category: bugfix
bugzilla: 34, https://gitee.com/openeuler/kernel/issues/I3VXYP
CVE: NA

--------------------------------------------------------------

Since we use struct wrapper to fix kabi broken in cpuidle_device
and cpuidle_driver, but it only cares about haltpoll device and driver,
which will cause memory error in case of that we use other cpuidle driver.

Fixes: 5c14d4a79275 ("cpuidle: fix kabi broken in cpuidle_device and cpuidle_driver")
Signed-off-by: Jiajun Chen <chenjiajun8@huawei.com>
Reviewed-by: Xiangyou Xie <xiexiangyou@huawei.com>
Signed-off-by: Cheng Jian <cj.chengjian@huawei.com>
---
 drivers/cpuidle/cpuidle-haltpoll.c |  4 +++-
 drivers/cpuidle/cpuidle.c          | 22 ++++++++++++-------
 drivers/cpuidle/driver.c           | 35 +++++++++++++++++-------------
 drivers/cpuidle/poll_state.c       |  5 ++++-
 drivers/cpuidle/sysfs.c            |  6 -----
 include/linux/cpuidle.h            |  8 +++++++
 6 files changed, 49 insertions(+), 31 deletions(-)

diff --git a/drivers/cpuidle/cpuidle-haltpoll.c b/drivers/cpuidle/cpuidle-haltpoll.c
index fc351f093ce1..ae4f06f05079 100644
--- a/drivers/cpuidle/cpuidle-haltpoll.c
+++ b/drivers/cpuidle/cpuidle-haltpoll.c
@@ -117,7 +117,9 @@ static int __init haltpoll_init(void)
 		return -ENODEV;
 
 	ret = cpuidle_register_driver(drv);
-	if (ret < 0)
+	if (ret == 0)
+		haltpoll_switch_governor(drv);
+	else if (ret < 0)
 		return ret;
 
 	haltpoll_cpuidle_dev_wrap = alloc_percpu(struct cpuidle_device_wrapper);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index b7a7125f1cde..42fc94773753 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -330,15 +330,9 @@ void cpuidle_reflect(struct cpuidle_device *dev, int index)
 u64 cpuidle_poll_time(struct cpuidle_driver *drv,
 		      struct cpuidle_device *dev)
 {
-	struct cpuidle_device_wrapper *devw =
-		container_of(dev, struct cpuidle_device_wrapper, dev);
+	u64 limit_ns = TICK_NSEC;
 	int i;
-	u64 limit_ns;
 
-	if (devw->poll_limit_ns)
-		return devw->poll_limit_ns;
-
-	limit_ns = TICK_NSEC;
 	for (i = 1; i < drv->state_count; i++) {
 		if (drv->states[i].disabled || dev->states_usage[i].disable)
 			continue;
@@ -348,7 +342,19 @@ u64 cpuidle_poll_time(struct cpuidle_driver *drv,
 		break;
 	}
 
-	devw->poll_limit_ns = limit_ns;
+	return limit_ns;
+}
+
+u64 cpuidle_haltpoll_time(struct cpuidle_driver *drv,
+			struct cpuidle_device *dev)
+{
+	struct cpuidle_device_wrapper *devw =
+		container_of(dev, struct cpuidle_device_wrapper, dev);
+
+	if (devw->poll_limit_ns)
+		return devw->poll_limit_ns;
+
+	devw->poll_limit_ns = cpuidle_poll_time(drv, dev);
 
 	return devw->poll_limit_ns;
 }
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 47930a0ecb0f..484d0e9655fc 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -241,6 +241,25 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
 	__cpuidle_unset_driver(drv);
 }
 
+void haltpoll_switch_governor(struct cpuidle_driver *drv)
+{
+	struct cpuidle_governor *gov;
+	struct cpuidle_driver_wrapper *drvw;
+
+	drvw = container_of(drv, struct cpuidle_driver_wrapper, drv);
+	if (!strlen(param_governor) && drvw->governor &&
+	    (cpuidle_get_driver() == drv)) {
+		mutex_lock(&cpuidle_lock);
+		gov = cpuidle_find_governor(drvw->governor);
+		if (gov) {
+			cpuidle_prev_governor = cpuidle_curr_governor;
+			if (cpuidle_switch_governor(gov) < 0)
+				cpuidle_prev_governor = NULL;
+		}
+		mutex_unlock(&cpuidle_lock);
+	}
+}
+
 /**
  * cpuidle_register_driver - registers a driver
  * @drv: a pointer to a valid struct cpuidle_driver
@@ -253,29 +272,15 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
  */
 int cpuidle_register_driver(struct cpuidle_driver *drv)
 {
-	struct cpuidle_governor *gov;
-	struct cpuidle_driver_wrapper *drvw;
 	int ret;
 
 	spin_lock(&cpuidle_driver_lock);
 	ret = __cpuidle_register_driver(drv);
 	spin_unlock(&cpuidle_driver_lock);
-	drvw = container_of(drv, struct cpuidle_driver_wrapper, drv);
-
-	if (!ret && !strlen(param_governor) && drvw->governor &&
-	    (cpuidle_get_driver() == drv)) {
-		mutex_lock(&cpuidle_lock);
-		gov = cpuidle_find_governor(drvw->governor);
-		if (gov) {
-			cpuidle_prev_governor = cpuidle_curr_governor;
-			if (cpuidle_switch_governor(gov) < 0)
-				cpuidle_prev_governor = NULL;
-		}
-		mutex_unlock(&cpuidle_lock);
-	}
 
 	return ret;
 }
+
 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
 
 /**
diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c
index aa9842b1f1cc..a73c09464429 100644
--- a/drivers/cpuidle/poll_state.c
+++ b/drivers/cpuidle/poll_state.c
@@ -26,7 +26,10 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev,
 		unsigned int loop_count = 0;
 		u64 limit;
 
-		limit = cpuidle_poll_time(drv, dev);
+		if (drv->name && !strcmp(drv->name, "haltpoll"))
+			limit = cpuidle_haltpoll_time(drv, dev);
+		else
+			limit = cpuidle_poll_time(drv, dev);
 
 		while (!need_resched()) {
 			cpu_relax();
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index f966a343daa7..4c8042f19a96 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -410,16 +410,10 @@ static ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr,
 	struct cpuidle_state *state = kobj_to_state(kobj);
 	struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
 	struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
-	struct cpuidle_device *dev = kobj_to_device(kobj);
-	struct cpuidle_device_wrapper *devw =
-		container_of(dev, struct cpuidle_device_wrapper, dev);
 
 	if (cattr->store)
 		ret = cattr->store(state, state_usage, buf, size);
 
-	/* reset poll time cache */
-	devw->poll_limit_ns = 0;
-
 	return ret;
 }
 
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 364e28be3155..e365f5dfe6e7 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -155,7 +155,10 @@ extern int cpuidle_enter(struct cpuidle_driver *drv,
 extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
 extern u64 cpuidle_poll_time(struct cpuidle_driver *drv,
 			     struct cpuidle_device *dev);
+extern u64 cpuidle_haltpoll_time(struct cpuidle_driver *drv,
+				  struct cpuidle_device *dev);
 
+extern void haltpoll_switch_governor(struct cpuidle_driver *drv);
 extern int cpuidle_register_driver(struct cpuidle_driver *drv);
 extern struct cpuidle_driver *cpuidle_get_driver(void);
 extern struct cpuidle_driver *cpuidle_driver_ref(void);
@@ -192,8 +195,13 @@ static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
 static inline u64 cpuidle_poll_time(struct cpuidle_driver *drv,
 			     struct cpuidle_device *dev)
 {return 0; }
+static inline u64 cpuidle_haltpoll_time(struct cpuidle_driver *drv,
+					struct cpuidle_device *dev)
+{return 0; }
 static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
 {return -ENODEV; }
+static inline void haltpoll_switch_governor(struct cpuidle_driver *drv)
+{return -ENODEV; }
 static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
 static inline struct cpuidle_driver *cpuidle_driver_ref(void) {return NULL; }
 static inline void cpuidle_driver_unref(void) {}
-- 
GitLab