From: Thomas Gleixner <tglx@linutronix.de>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Zhang Rui <rui.zhang@intel.com>,
Eduardo Valentin <edubezval@gmail.com>,
linux-pm@vger.kernel.org, Peter Zijlstra <peterz@infradead.org>,
x86@kernel.org, rt@linutronix.de, Borislav Petkov <bp@alien8.de>
Subject: [patch 10/12] thermal/x86_pkg_temp: Move work into package struct
Date: Fri, 18 Nov 2016 00:03:41 -0000 [thread overview]
Message-ID: <20161117234810.673181485@linutronix.de> (raw)
In-Reply-To: 20161117231435.891545908@linutronix.de
[-- Attachment #1: thermal-x86_pkg_temp--Move-work-into-package-struct.patch --]
[-- Type: text/plain, Size: 5901 bytes --]
Delayed work structs are held in a static percpu storage, which makes no
sense at all because work is strictly per package and we never schedule
more than one work per package.
Aside of that the work cancelation in the hotplug is broken when the work
is queued on the outgoing cpu and canceled. Nothing reschedules the work on
another online cpu in the package, so the interrupts stay disabled and the
work_scheduled flag stays active.
Move the delayed work struct into the package struct, which is the only
sensible place to have it.
To simplify the cancelation logic schedule the work always on the cpu which
is the target for the sysfs files. This is required so the cancelation
logic in the cpu offline path cancels only when the outgoing cpu is the
current target and reschedule the work when there is still a online
CPU in the package.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
drivers/thermal/x86_pkg_temp_thermal.c | 73 +++++++++++++++++++++++----------
1 file changed, 52 insertions(+), 21 deletions(-)
--- a/drivers/thermal/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -65,6 +65,7 @@ struct pkg_device {
u32 tj_max;
u32 msr_pkg_therm_low;
u32 msr_pkg_therm_high;
+ struct delayed_work work;
struct thermal_zone_device *tzone;
};
@@ -79,9 +80,6 @@ static DEFINE_SPINLOCK(pkg_temp_lock);
/* Protects zone operation in the work function against hotplug removal */
static DEFINE_MUTEX(thermal_zone_mutex);
-/* Interrupt to work function schedule queue */
-static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work);
-
/* Debug counters to show using debugfs */
static struct dentry *debugfs;
static unsigned int pkg_interrupt_cnt;
@@ -325,6 +323,13 @@ static void pkg_temp_thermal_threshold_w
mutex_unlock(&thermal_zone_mutex);
}
+static void pkg_thermal_schedule_work(int cpu, struct delayed_work *work)
+{
+ unsigned long ms = msecs_to_jiffies(notify_delay_ms);
+
+ schedule_delayed_work_on(cpu, work, ms);
+}
+
static int pkg_thermal_notify(u64 msr_val)
{
int cpu = smp_processor_id();
@@ -340,9 +345,7 @@ static int pkg_thermal_notify(u64 msr_va
pkgdev = pkg_temp_thermal_get_dev(cpu);
if (pkgdev && !pkgdev->work_scheduled) {
pkgdev->work_scheduled = true;
- schedule_delayed_work_on(cpu,
- &per_cpu(pkg_temp_thermal_threshold_work, cpu),
- msecs_to_jiffies(notify_delay_ms));
+ pkg_thermal_schedule_work(pkgdev->cpu, &pkgdev->work);
}
spin_unlock_irqrestore(&pkg_temp_lock, flags);
@@ -373,6 +376,7 @@ static int pkg_temp_thermal_device_add(u
if (!pkgdev)
return -ENOMEM;
+ INIT_DELAYED_WORK(&pkgdev->work, pkg_temp_thermal_threshold_work_fn);
pkgdev->phys_proc_id = topology_physical_package_id(cpu);
pkgdev->cpu = cpu;
pkgdev->tj_max = tj_max;
@@ -400,7 +404,7 @@ static void put_core_offline(unsigned in
{
int target = cpumask_any_but(topology_core_cpumask(cpu), cpu);
struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
- bool lastcpu;
+ bool lastcpu, was_target;
if (!pkgdev)
return;
@@ -426,13 +430,24 @@ static void put_core_offline(unsigned in
thermal_zone_device_unregister(tzone);
}
+ /* Protect against work and interrupts */
+ spin_lock_irq(&pkg_temp_lock);
+
/*
- * If this is the last CPU in the package, restore the interrupt
- * MSR and remove the package reference from the array.
+ * Check whether this cpu was the current target and store the new
+ * one. When we drop the lock, then the interrupt notify function
+ * will see the new target.
+ */
+ was_target = pkgdev->cpu == cpu;
+ pkgdev->cpu = target;
+
+ /*
+ * If this is the last CPU in the package remove the package
+ * reference from the list and restore the interrupt MSR. When we
+ * drop the lock neither the interrupt notify function nor the
+ * worker will see the package anymore.
*/
if (lastcpu) {
- /* Protect against work and interrupts */
- spin_lock_irq(&pkg_temp_lock);
list_del(&pkgdev->list);
/*
* After this point nothing touches the MSR anymore. We
@@ -443,17 +458,36 @@ static void put_core_offline(unsigned in
wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
pkgdev->msr_pkg_therm_low,
pkgdev->msr_pkg_therm_high);
- kfree(pkgdev);
+ spin_lock_irq(&pkg_temp_lock);
}
/*
- * Note, this is broken when work was really scheduled on the
- * outgoing cpu because this will leave the work_scheduled flag set
- * and the thermal interrupts disabled. Will be fixed in the next
- * step as there is no way to fix it in a sane way with the per cpu
- * work nonsense.
+ * Check whether there is work scheduled and whether the work is
+ * targeted at the outgoing CPU.
*/
- cancel_delayed_work_sync(&per_cpu(pkg_temp_thermal_threshold_work, cpu));
+ if (pkgdev->work_scheduled && was_target) {
+ /*
+ * To cancel the work we need to drop the lock, otherwise
+ * we might deadlock if the work needs to be flushed.
+ */
+ spin_unlock_irq(&pkg_temp_lock);
+ cancel_delayed_work_sync(&pkgdev->work);
+ spin_lock_irq(&pkg_temp_lock);
+ /*
+ * If this is not the last cpu in the package and the work
+ * did not run after we dropped the lock above, then we
+ * need to reschedule the work, otherwise the interrupt
+ * stays disabled forever.
+ */
+ if (!lastcpu && pkgdev->work_scheduled)
+ pkg_thermal_schedule_work(target, &pkgdev->work);
+ }
+
+ spin_unlock_irq(&pkg_temp_lock);
+
+ /* Final cleanup if this is the last cpu */
+ if (lastcpu)
+ kfree(pkgdev);
}
static int get_core_online(unsigned int cpu)
@@ -465,9 +499,6 @@ static int get_core_online(unsigned int
if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS))
return -ENODEV;
- INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu),
- pkg_temp_thermal_threshold_work_fn);
-
/* If the package exists, nothing to do */
if (pkgdev)
return 0;
next prev parent reply other threads:[~2016-11-18 0:03 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-11-18 0:03 [patch 00/12] thermal/x86_pkg_temp: Sanitize yet another hotplug and locking trainwreck Thomas Gleixner
2016-11-18 0:03 ` [patch 01/12] thermal/x86_pkg_temp: Cleanup thermal interrupt handling Thomas Gleixner
2016-11-18 0:03 ` [patch 02/12] thermal/x86_pkg_temp: Remove redundant package search Thomas Gleixner
2016-11-18 0:03 ` [patch 03/12] thermal/x86_pkg_temp: Replace open coded cpu search Thomas Gleixner
2016-11-18 0:03 ` [patch 04/12] thermal/x86_pkg_temp: Sanitize callback (de)initialization Thomas Gleixner
2016-11-18 0:03 ` [patch 05/12] thermal/x86_pkg_temp: Get rid of ref counting Thomas Gleixner
2016-11-18 0:03 ` [patch 06/12] thermal/x86_pkg_temp: Cleanup namespace Thomas Gleixner
2016-11-18 0:03 ` [patch 07/12] thermal/x86_pkg_temp: Cleanup code some more Thomas Gleixner
2016-11-18 0:03 ` [patch 08/12] thermal/x86_pkg_temp: Sanitize locking Thomas Gleixner
2016-11-18 0:03 ` [patch 09/12] thermal/x86_pkg_temp: Move work scheduled flag into package struct Thomas Gleixner
2016-11-18 0:03 ` Thomas Gleixner [this message]
2016-11-18 0:03 ` [patch 11/12] thermal/x86_pkg_temp: Sanitize package management Thomas Gleixner
2016-11-18 0:03 ` [patch 12/12] thermal/x86 pkg temp: Convert to hotplug state machine Thomas Gleixner
2016-11-21 20:02 ` [patch 00/12] thermal/x86_pkg_temp: Sanitize yet another hotplug and locking trainwreck Pandruvada, Srinivas
2016-11-21 21:34 ` Thomas Gleixner
2016-11-21 23:16 ` Pandruvada, Srinivas
2016-11-22 9:05 ` Thomas Gleixner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161117234810.673181485@linutronix.de \
--to=tglx@linutronix.de \
--cc=bp@alien8.de \
--cc=edubezval@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=rt@linutronix.de \
--cc=rui.zhang@intel.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).