From: Jiajun Chen chenjiajun8@huawei.com
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) {}