Patch[2/2] Adding Interrupt Handling Support to Coretemp

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Fenghua,

This patch, the second one in the series adds the interrupt
Handling support for the thermal thresholds. When an interrupt
occurs, this is notified to the user space(so that the user space
can take some action) by a netlink event.

This patch is generated against the stable Linux-2.6 tree.

This patch depends on the "Adding notification to thermal
Framework" patch, which can be downloaded from here:
https://patchwork.kernel.org/patch/282042/

Kindly review and merge.
------------------------------------------------------------------
From: Durgadoss R <durgadoss.r@xxxxxxxxx>

Date: Tue, 7 Dec 2010 04:10:41 +0530
Subject: [PATCH 2/2] Adding_Threshold_Interrupt_Handling_to_Coretemp

This patch adds the interrupt handling for the core/package
thermal thresholds in coretemp. The interrupts are routed to coretemp
via therm_throt.c. Whenever an interrupt occurs, a notification is sent
in the form of a netlink event to the user space.

Signed-off-by: Durgadoss R <durgadoss.r@xxxxxxxxx>

---
 arch/x86/include/asm/mce.h               |    3 +
 arch/x86/kernel/cpu/mcheck/therm_throt.c |   71 +++++++++++++++++++++++++++++
 drivers/hwmon/coretemp.c                 |   72 +++++++++++++++++++++++++++++-
 3 files changed, 145 insertions(+), 1 deletions(-)

diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index c62c13c..bbb17b9 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -223,6 +223,9 @@ void intel_init_thermal(struct cpuinfo_x86 *c);
 
 void mce_log_therm_throt_event(__u64 status);
 
+/* Interrupt Handler for core/package thermal thresholds */
+extern int (*platform_thermal_notify)(__u64 msr_val, int type);
+
 #ifdef CONFIG_X86_THERMAL_VECTOR
 extern void mcheck_intel_therm_init(void);
 #else
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index 4b68326..f4afd79 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -34,6 +34,11 @@
 /* How long to wait between reporting thermal events */
 #define CHECK_INTERVAL		(300 * HZ)
 
+/* Similar to CHECK_INTERVAL. But interval is reduced because
+ * these lower and upper threhsolds will be crossed frequently.
+ * Hence the reporting should be quick enough to handle the event */
+#define THRES_INTERVAL		(25 * HZ)
+
 #define THERMAL_THROTTLING_EVENT	0
 #define POWER_LIMIT_EVENT		1
 
@@ -53,8 +58,15 @@ struct thermal_state {
 	struct _thermal_state core_power_limit;
 	struct _thermal_state package_throttle;
 	struct _thermal_state package_power_limit;
+	struct _thermal_state core_thresh0;
+	struct _thermal_state core_thresh1;
+	struct _thermal_state package_thresh0;
+	struct _thermal_state package_thresh1;
 };
 
+/* Callback to handle core/package threshold interrupts */
+int (*platform_thermal_notify)(__u64 msr_val, int type);
+
 static DEFINE_PER_CPU(struct thermal_state, thermal_state);
 
 static atomic_t therm_throt_en	= ATOMIC_INIT(0);
@@ -200,6 +212,38 @@ static int therm_throt_process(bool new_event, int event, int level)
 	return 0;
 }
 
+static int thresh_event_valid(int event)
+{
+	struct _thermal_state *state = NULL;
+
+	unsigned int this_cpu = smp_processor_id();
+	struct thermal_state *pstate = &per_cpu(thermal_state, this_cpu);
+	u64 now = get_jiffies_64();
+
+	switch (event) {
+	case 0:
+		state = &pstate->core_thresh0;
+		break;
+	case 1:
+		state = &pstate->core_thresh1;
+		break;
+	case 2:
+		state = &pstate->package_thresh0;
+		break;
+	case 3:
+		state = &pstate->package_thresh1;
+		break;
+	default:
+		WARN_ON(1);
+	}
+
+	if (time_before64(now, state->next_check))
+		return 0;
+
+	state->next_check = now + THRES_INTERVAL;
+	return 1;
+}
+
 #ifdef CONFIG_SYSFS
 /* Add/Remove thermal_throttle interface for CPU device: */
 static __cpuinit int thermal_throttle_add_dev(struct sys_device *sys_dev,
@@ -313,6 +357,28 @@ device_initcall(thermal_throttle_init_device);
 #define PACKAGE_THROTTLED	((__u64)2 << 62)
 #define PACKAGE_POWER_LIMIT	((__u64)3 << 62)
 
+static void notify_thresholds(__u64 msr_val, int type)
+{
+	/* type 0 means core threshold
+	 * type 1 means package threshold
+	 */
+	int low_event = type * 2;
+	int high_event = (type * 2) + 1;
+
+	/* check whether the interrupt handler is defined;
+	 * otherwise simply return
+	 */
+	if (!platform_thermal_notify)
+		return;
+
+	/* lower threshold reached */
+	if ((msr_val & THERM_LOG_THRESHOLD0) &&	thresh_event_valid(low_event))
+		platform_thermal_notify(msr_val, type);
+	/* higher threshold reached */
+	if ((msr_val & THERM_LOG_THRESHOLD1) && thresh_event_valid(high_event))
+		platform_thermal_notify(msr_val, type);
+}
+
 /* Thermal transition interrupt handler */
 static void intel_thermal_interrupt(void)
 {
@@ -321,6 +387,10 @@ static void intel_thermal_interrupt(void)
 
 	rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
 
+	/* Check for violation of core thermal thresholds
+	 * If so, send notification */
+	notify_thresholds(msr_val, 0);
+
 	if (therm_throt_process(msr_val & THERM_STATUS_PROCHOT,
 				THERMAL_THROTTLING_EVENT,
 				CORE_LEVEL) != 0)
@@ -334,6 +404,7 @@ static void intel_thermal_interrupt(void)
 
 	if (cpu_has(c, X86_FEATURE_PTS)) {
 		rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
+		notify_thresholds(msr_val, 1);
 		if (therm_throt_process(msr_val & PACKAGE_THERM_STATUS_PROCHOT,
 					THERMAL_THROTTLING_EVENT,
 					PACKAGE_LEVEL) != 0)
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 7aea83e..6d8b21f 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -36,9 +36,17 @@
 #include <asm/msr.h>
 #include <asm/processor.h>
 #include <asm/smp.h>
+#include <asm/mce.h>
+#include <linux/thermal.h>
 
 #define DRVNAME	"coretemp"
 
+/* An identification number to the DTS sensor.
+ * This will help the user space to figure out which
+ * sensor caused the event
+ */
+#define DTS_ID	0
+
 typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL,
 		SHOW_NAME } SHOW;
 /* C indicates core thermal thresholds
@@ -75,6 +83,52 @@ static int set_core_threshold(struct coretemp_data *data, int val,
 						enum thresholds thresh);
 static int set_pkg_threshold(struct coretemp_data *data, int val,
 						enum thresholds thresh);
+
+/* Interrupt Handlers for core/package thresholds */
+struct work_struct *t0_netlink_handlr;
+struct work_struct *t1_netlink_handlr;
+
+/* Send netlink event for DTS sensor reaching threshold0 */
+static void gen_netlink_t0(struct work_struct *work)
+{
+	generate_netlink_event(DTS_ID, THERMAL_AUX0);
+}
+
+/* Send netlink event for DTS sensor reaching threshold1 */
+static void gen_netlink_t1(struct work_struct *work)
+{
+	generate_netlink_event(DTS_ID, THERMAL_AUX1);
+}
+
+/* Platform thermal Interrupt Handler.
+ * As of now, we handle both core/pkg threshold interrupts in the same
+ * way. If the user space is good enough to distinguish between these
+ * two and handle the events in a finer way, then we can improvise.
+ */
+static int coretemp_interrupt(__u64 msr_val, int type)
+{
+	u64 status_reg = (type == 0) ? MSR_IA32_THERM_STATUS :
+					MSR_IA32_PACKAGE_THERM_STATUS;
+
+	if (msr_val & THERM_LOG_THRESHOLD0) {
+		if (!(msr_val & THERM_STATUS_THRESHOLD0))
+			schedule_work(t0_netlink_handlr);
+
+		/* Reset the Threshold0 interrupt */
+		wrmsrl(status_reg, msr_val & ~THERM_LOG_THRESHOLD0);
+	}
+
+	if (msr_val & THERM_LOG_THRESHOLD1) {
+		if (msr_val & THERM_STATUS_THRESHOLD1)
+			schedule_work(t1_netlink_handlr);
+
+		/* Reset the Threshold1 interrupt */
+		wrmsrl(status_reg, msr_val & ~THERM_LOG_THRESHOLD1);
+	}
+
+	return 0;
+}
+
 /*
  * Sysfs stuff
  */
@@ -415,7 +469,7 @@ static int set_pkg_threshold(struct coretemp_data *data, int temp,
 	mutex_lock(&data->update_lock);
 
 	diff = (data->tjmax - temp)/1000;
-
+
 	/* Mask the thermal vector in the lapic */
 	l = apic_read(APIC_LVTTHMR);
 	apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED);
@@ -469,6 +523,8 @@ exit:
 	/* Unmask the thermal vector */
 	l = apic_read(APIC_LVTTHMR);
 	apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+	/* Enable the Interrupt Handling Support */
+	platform_thermal_notify = coretemp_interrupt;
 	return 0;
 }
 
@@ -786,6 +842,20 @@ static int __init coretemp_init(void)
 #endif
 
 	register_hotcpu_notifier(&coretemp_cpu_notifier);
+
+	/* Initialize the Interrupt Handlers */
+	t0_netlink_handlr = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
+	if (!t0_netlink_handlr)
+		return -ENOMEM;
+
+	t1_netlink_handlr = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
+	if (!t1_netlink_handlr) {
+		kfree(t0_netlink_handlr);
+		return -ENOMEM;
+	}
+	INIT_WORK(t0_netlink_handlr, (void *)gen_netlink_t0);
+	INIT_WORK(t1_netlink_handlr, (void *)gen_netlink_t1);
+
 	return 0;
 
 #ifndef CONFIG_HOTPLUG_CPU
-- 
1.6.5.2

Attachment: 0002-Adding_Threshold_Interrupt_Handling_to_Coretemp.patch
Description: 0002-Adding_Threshold_Interrupt_Handling_to_Coretemp.patch

_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux