From 17444a20e3948507cd1bd60dab21884742538eee Mon Sep 17 00:00:00 2001 From: Stephen Dickey Date: Fri, 12 Feb 2021 10:01:33 -0800 Subject: [PATCH] sched/pause: handle cpu online processing for pause With refcounting, the walt wrapper for pause/resume can keep track of whether a CPU should be paused or not, where this information used to be distributed to the clients using the api. Simplify the design, such that a CPU is automatically paused as it is coming online, affecting the active/inactive state of the CPU, but not adjusting the ref-counts. Going forward, client software using walt_pause/walt_resume will not be required to keep track of the online state of a cpu. Change-Id: I83e3cc8743942c3a482eb588546b6ea9bb7bf2e3 Signed-off-by: Stephen Dickey --- drivers/thermal/qcom/thermal_pause.c | 55 +++++---------------- kernel/sched/walt/walt.c | 1 + kernel/sched/walt/walt.h | 1 + kernel/sched/walt/walt_pause.c | 71 +++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 43 deletions(-) diff --git a/drivers/thermal/qcom/thermal_pause.c b/drivers/thermal/qcom/thermal_pause.c index b39817d4e500..41d511d82d59 100644 --- a/drivers/thermal/qcom/thermal_pause.c +++ b/drivers/thermal/qcom/thermal_pause.c @@ -35,9 +35,12 @@ struct thermal_pause_cdev { static DEFINE_MUTEX(cpus_pause_lock); static LIST_HEAD(thermal_pause_cdev_list); + +/* track cpus that thermal intends to pause, regardless of + * whether thermal was requesting an already paused cpu + */ static struct cpumask cpus_paused_by_thermal; static struct cpumask cpus_in_max_cooling_level; -static struct cpumask cpus_pending_online; static enum cpuhp_state cpu_hp_online; static BLOCKING_NOTIFIER_HEAD(multi_max_cooling_level_notifer); @@ -62,17 +65,11 @@ static int thermal_pause_hp_online(unsigned int online_cpu) struct thermal_pause_cdev *thermal_pause_cdev; int ret = 0; - pr_debug("online entry CPU:%d. mask:%*pbl pend:%*pbl\n", online_cpu, - cpumask_pr_args(&cpus_paused_by_thermal), - cpumask_pr_args(&cpus_pending_online)); + pr_debug("online entry CPU:%d. mask:%*pbl\n", online_cpu, + cpumask_pr_args(&cpus_paused_by_thermal)); mutex_lock(&cpus_pause_lock); - if (cpumask_test_cpu(online_cpu, &cpus_paused_by_thermal)) { - ret = NOTIFY_BAD; - cpumask_set_cpu(online_cpu, &cpus_pending_online); - } - list_for_each_entry(thermal_pause_cdev, &thermal_pause_cdev_list, node) { if (cpumask_test_cpu(online_cpu, &thermal_pause_cdev->cpu_mask) && !thermal_pause_cdev->cdev) @@ -81,9 +78,8 @@ static int thermal_pause_hp_online(unsigned int online_cpu) } mutex_unlock(&cpus_pause_lock); - pr_debug("online exit CPU:%d. mask:%*pbl pend:%*pbl\n", online_cpu, - cpumask_pr_args(&cpus_paused_by_thermal), - cpumask_pr_args(&cpus_pending_online)); + pr_debug("online exit CPU:%d. mask:%*pbl\n", online_cpu, + cpumask_pr_args(&cpus_paused_by_thermal)); return ret; } @@ -150,7 +146,7 @@ static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_c int cpu = 0; int ret = -ENODEV; struct thermal_pause_cdev *cdev; - cpumask_t cpus_offlined, new_cpu_pause, cpus_to_unpause; + cpumask_t new_cpu_pause, cpus_to_unpause; cpumask_clear(&new_cpu_pause); list_for_each_entry(cdev, &thermal_pause_cdev_list, node) { @@ -162,14 +158,10 @@ static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_c cpumask_andnot(&cpus_to_unpause, &cpus_paused_by_thermal, &new_cpu_pause); - cpumask_and(&cpus_offlined, &cpus_pending_online, - &cpus_to_unpause); - - pr_debug("Old req:%*pbl New req:%*pbl Unpause:%*pbl Online:%*pbl\n", + pr_debug("Old req:%*pbl New req:%*pbl Unpause:%*pbl\n", cpumask_pr_args(&cpus_paused_by_thermal), cpumask_pr_args(&new_cpu_pause), - cpumask_pr_args(&cpus_to_unpause), - cpumask_pr_args(&cpus_offlined)); + cpumask_pr_args(&cpus_to_unpause)); cpumask_copy(&cpus_in_max_cooling_level, &new_cpu_pause); cpumask_copy(&cpus_paused_by_thermal, &new_cpu_pause); @@ -183,15 +175,6 @@ static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_c mutex_lock(&cpus_pause_lock); } - for_each_cpu(cpu, &cpus_offlined) { - cpumask_clear_cpu(cpu, &cpus_pending_online); - mutex_unlock(&cpus_pause_lock); - ret = add_cpu(cpu); - if (ret) - pr_err("Error Adding CPU:%d. err:%d\n", cpu, ret); - mutex_lock(&cpus_pause_lock); - } - for_each_cpu(cpu, &cpus_to_unpause) blocking_notifier_call_chain( &multi_max_cooling_level_notifer, @@ -326,7 +309,6 @@ static int thermal_pause_probe(struct platform_device *pdev) INIT_LIST_HEAD(&thermal_pause_cdev_list); cpumask_clear(&cpus_in_max_cooling_level); cpumask_clear(&cpus_paused_by_thermal); - cpumask_clear(&cpus_pending_online); for_each_available_child_of_node(np, subsys_np) { @@ -382,7 +364,7 @@ static int thermal_pause_probe(struct platform_device *pdev) static int thermal_pause_remove(struct platform_device *pdev) { struct thermal_pause_cdev *thermal_pause_cdev = NULL, *next = NULL; - int ret = 0, cpu = 0; + int ret = 0; if (cpu_hp_online) { cpuhp_remove_state_nocalls(cpu_hp_online); @@ -398,26 +380,15 @@ static int thermal_pause_remove(struct platform_device *pdev) list_del(&thermal_pause_cdev->node); } - cpumask_andnot(&cpus_paused_by_thermal, &cpus_paused_by_thermal, - &cpus_pending_online); - if (!cpumask_empty(&cpus_paused_by_thermal)) { mutex_unlock(&cpus_pause_lock); ret = walt_resume_cpus(&cpus_paused_by_thermal); - if (ret) + if (ret < 0) pr_err("Error resuming CPU:%*pbl. err:%d\n", cpumask_pr_args(&cpus_paused_by_thermal), ret); mutex_lock(&cpus_pause_lock); } - for_each_cpu(cpu, &cpus_pending_online) { - mutex_unlock(&cpus_pause_lock); - ret = add_cpu(cpu); - if (ret) - pr_err("Error Adding CPU:%d. err:%d\n", cpu, ret); - mutex_lock(&cpus_pause_lock); - } - mutex_unlock(&cpus_pause_lock); return 0; diff --git a/kernel/sched/walt/walt.c b/kernel/sched/walt/walt.c index c7c4ff7c2234..2f81584124cd 100644 --- a/kernel/sched/walt/walt.c +++ b/kernel/sched/walt/walt.c @@ -4056,6 +4056,7 @@ static void walt_init(void) walt_lb_init(); walt_rt_init(); walt_cfs_init(); + walt_pause_init(); stop_machine(walt_init_stop_handler, NULL, NULL); diff --git a/kernel/sched/walt/walt.h b/kernel/sched/walt/walt.h index 1c76ba19e24b..26036f96bb26 100644 --- a/kernel/sched/walt/walt.h +++ b/kernel/sched/walt/walt.h @@ -732,6 +732,7 @@ extern void sched_update_hyst_times(void); extern enum sched_boost_policy sched_boost_policy(void); extern void walt_rt_init(void); extern void walt_cfs_init(void); +extern void walt_pause_init(void); extern void walt_fixup_init(void); extern int walt_find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sync, int sibling_count_hint); diff --git a/kernel/sched/walt/walt_pause.c b/kernel/sched/walt/walt_pause.c index 7535d27561e3..6f28cc7ff53b 100644 --- a/kernel/sched/walt/walt_pause.c +++ b/kernel/sched/walt/walt_pause.c @@ -60,8 +60,12 @@ int walt_pause_cpus(struct cpumask *cpus) cpumask_copy(&saved_cpus, cpus); - /* prior to operation, track cpus requested to be paused */ + /* add ref counts for all cpus in mask */ inc_ref_counts(cpus); + + /* only actually pause online CPUs */ + cpumask_and(cpus, cpus, cpu_online_mask); + ret = pause_cpus(cpus); if (ret < 0) { dec_test_ref_counts(&saved_cpus); @@ -81,7 +85,12 @@ int walt_resume_cpus(struct cpumask *cpus) cpumask_copy(&saved_cpus, cpus); + /* remove ref counts for all cpus in mask */ dec_test_ref_counts(cpus); + + /* only actually resume online CPUs */ + cpumask_and(cpus, cpus, cpu_online_mask); + ret = resume_cpus(cpus); if (ret < 0) { inc_ref_counts(&saved_cpus); @@ -93,4 +102,64 @@ int walt_resume_cpus(struct cpumask *cpus) } EXPORT_SYMBOL(walt_resume_cpus); +struct work_struct walt_pause_online_work; + +/* workfn to perform re-pause operation by detecting + * ref-counts and attempting to restore the state. + * It must not adjust the ref-counts for each cpu, or + * the actual paused state will no longer reflect client's + * expectations. + */ +static void walt_pause_online_workfn(struct work_struct *work) +{ + struct pause_cpu_state *pause_cpu_state; + cpumask_t re_pause_cpus; + int cpu, ret = 0; + + cpumask_clear(&re_pause_cpus); + + /* search and test all online cpus */ + spin_lock(&pause_lock); + for_each_online_cpu(cpu) { + pause_cpu_state = per_cpu_ptr(&pause_state, cpu); + if (pause_cpu_state->ref_count) + cpumask_set_cpu(cpu, &re_pause_cpus); + } + spin_unlock(&pause_lock); + + if (cpumask_empty(&re_pause_cpus)) + return; + + /* will wait for existing hp operations to complete */ + ret = pause_cpus(&re_pause_cpus); + if (ret < 0) { + pr_err("pause_cpus during online failure ret=%d cpus=%*pb1\n", ret, + cpumask_pr_args(&re_pause_cpus)); + } +} + +/* do not perform online work in hotplug context */ +static int walt_pause_hp_online(unsigned int online_cpu) +{ + struct pause_cpu_state *pause_cpu_state; + + pause_cpu_state = per_cpu_ptr(&pause_state, online_cpu); + if (pause_cpu_state->ref_count) + schedule_work(&walt_pause_online_work); + return 0; +} + +void walt_pause_init(void) +{ + int ret; + + INIT_WORK(&walt_pause_online_work, walt_pause_online_workfn); + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "walt-pause/online", + walt_pause_hp_online, NULL); + + if (ret < 0) + pr_err("failure to register cpuhp online state ret=%d\n", ret); +} + #endif /* CONFIG_HOTPLUG_CPU */