From: Daniel Wagner Date: Tue, 17 Feb 2015 09:37:44 +0100 Subject: [PATCH 174/351] thermal: Defer thermal wakups to threads Origin: https://git.kernel.org/cgit/linux/kernel/git/rt/linux-stable-rt.git/commit?id=1a5eb814bba630b57b3b562a0cbb1f43b755455d On RT the spin lock in pkg_temp_thermal_platfrom_thermal_notify will call schedule while we run in irq context. [] dump_stack+0x4e/0x8f [] __schedule_bug+0xa6/0xb4 [] __schedule+0x5b4/0x700 [] schedule+0x2a/0x90 [] rt_spin_lock_slowlock+0xe5/0x2d0 [] rt_spin_lock+0x25/0x30 [] pkg_temp_thermal_platform_thermal_notify+0x45/0x134 [x86_pkg_temp_thermal] [] ? therm_throt_process+0x1b/0x160 [] intel_thermal_interrupt+0x211/0x250 [] smp_thermal_interrupt+0x21/0x40 [] thermal_interrupt+0x6d/0x80 Let's defer the work to a kthread. Signed-off-by: Daniel Wagner [bigeasy: reoder init/denit position. TODO: flush swork on exit] Signed-off-by: Sebastian Andrzej Siewior --- drivers/thermal/x86_pkg_temp_thermal.c | 52 ++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 1ef937d799e4..a5991cbb408f 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -329,7 +330,7 @@ static void pkg_thermal_schedule_work(int cpu, struct delayed_work *work) schedule_delayed_work_on(cpu, work, ms); } -static int pkg_thermal_notify(u64 msr_val) +static void pkg_thermal_notify_work(struct swork_event *event) { int cpu = smp_processor_id(); struct pkg_device *pkgdev; @@ -348,9 +349,47 @@ static int pkg_thermal_notify(u64 msr_val) } spin_unlock_irqrestore(&pkg_temp_lock, flags); +} + +#ifdef CONFIG_PREEMPT_RT_FULL +static struct swork_event notify_work; + +static int pkg_thermal_notify_work_init(void) +{ + int err; + + err = swork_get(); + if (err) + return err; + + INIT_SWORK(¬ify_work, pkg_thermal_notify_work); return 0; } +static void pkg_thermal_notify_work_cleanup(void) +{ + swork_put(); +} + +static int pkg_thermal_notify(u64 msr_val) +{ + swork_queue(¬ify_work); + return 0; +} + +#else /* !CONFIG_PREEMPT_RT_FULL */ + +static int pkg_thermal_notify_work_init(void) { return 0; } + +static void pkg_thermal_notify_work_cleanup(void) { } + +static int pkg_thermal_notify(u64 msr_val) +{ + pkg_thermal_notify_work(NULL); + return 0; +} +#endif /* CONFIG_PREEMPT_RT_FULL */ + static int pkg_temp_thermal_device_add(unsigned int cpu) { int pkgid = topology_logical_package_id(cpu); @@ -515,11 +554,16 @@ static int __init pkg_temp_thermal_init(void) if (!x86_match_cpu(pkg_temp_thermal_ids)) return -ENODEV; + if (!pkg_thermal_notify_work_init()) + return -ENODEV; + max_packages = topology_max_packages(); packages = kcalloc(max_packages, sizeof(struct pkg_device *), GFP_KERNEL); - if (!packages) - return -ENOMEM; + if (!packages) { + ret = -ENOMEM; + goto err; + } ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "thermal/x86_pkg:online", pkg_thermal_cpu_online, pkg_thermal_cpu_offline); @@ -537,6 +581,7 @@ static int __init pkg_temp_thermal_init(void) return 0; err: + pkg_thermal_notify_work_cleanup(); kfree(packages); return ret; } @@ -550,6 +595,7 @@ static void __exit pkg_temp_thermal_exit(void) cpuhp_remove_state(pkg_thermal_hp_state); debugfs_remove_recursive(debugfs); kfree(packages); + pkg_thermal_notify_work_cleanup(); } module_exit(pkg_temp_thermal_exit)