[ltp] [PATCH][2.6.18] ibm-acpi: fan control watchdog

Henrique de Moraes Holschuh linux-thinkpad@linux-thinkpad.org
Tue, 10 Oct 2006 21:22:58 -0300


--f61P+fpdnY2FZS1u
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

This patch applies on top of the previous patch.

It implements a watchdog that makes it safer to use usermode fan control
scripts, as requested in ThinkWiki.

Please comment.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

--f61P+fpdnY2FZS1u
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ibm-acpi_fan_watchdog.patch"

ibm-acpi: implement fan watchdog command

This patch implements a fan control safety watchdog.  When the watchdog timer
expires, the equivalent action of a "fan enable" command is executed.  The
watchdog timer is reset at every reception of a fan control command that could
change the state of the fan itself.

This command is meant to be used by userspace fan control daemons, to make sure
the fan is never left set to an unsafe level because of userspace problems.

Users of the X31/X40/X41 "speed" command are on their own, the current
implementation of "speed" is just too incomplete to be used safely, anyway.
Better to never use it, and just use the "level" command instead.

The watchdog is programmed using echo "watchdog <number>" > fan, where number
is the number of seconds to wait before doing an "enable", and zero disables
the watchdog.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>

---
 Documentation/ibm-acpi.txt |   12 +++++++
 drivers/acpi/ibm_acpi.c    |   73 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 82 insertions(+), 3 deletions(-)

Index: 2.6.18/drivers/acpi/ibm_acpi.c
===================================================================
--- 2.6.18.orig/drivers/acpi/ibm_acpi.c
+++ 2.6.18/drivers/acpi/ibm_acpi.c
@@ -80,6 +80,8 @@
 #include <linux/proc_fs.h>
 #include <asm/uaccess.h>
 #include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
 
 #include <acpi/acpi_drivers.h>
 #include <acpi/acnamesp.h>
@@ -324,7 +326,8 @@ enum fan_control_access_mode {
 enum fan_control_commands {
 	IBMACPI_FAN_CMD_SPEED 	= 0x0001,	/* speed command */
 	IBMACPI_FAN_CMD_LEVEL 	= 0x0002,	/* level command  */
-	IBMACPI_FAN_CMD_ENABLE	= 0x0004,	/* enable/disable cmd */
+	IBMACPI_FAN_CMD_ENABLE	= 0x0004,	/* enable/disable cmd,
+						 * and also watchdog cmd */
 };
 
 enum {					/* Fan control constants */
@@ -1728,12 +1731,17 @@ static enum fan_control_commands fan_con
 static int fan_control_status_known;
 static u8 fan_control_initial_status;
 
+static void fan_watchdog_fire( void *ignored );
+static int fan_watchdog_maxinterval;
+static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL);
+
 static int fan_init(void)
 {
 	fan_status_access_mode = IBMACPI_FAN_NONE;
 	fan_control_access_mode = IBMACPI_FAN_WR_NONE;
 	fan_control_commands = 0;
 	fan_control_status_known = 1;
+	fan_watchdog_maxinterval = 0;
 
 	if (gfan_handle) {
 		/* 570, 600e/x, 770e, 770x */
@@ -1785,6 +1793,23 @@ static int fan_init(void)
 	return 0;
 }
 
+static void fan_watchdog_reset( void )
+{
+	static int fan_watchdog_active = 0;
+
+	if (fan_watchdog_active)
+		cancel_delayed_work(&fan_watchdog_task);
+
+	if (fan_watchdog_maxinterval > 0) {
+		fan_watchdog_active = 1;
+		if (!schedule_delayed_work(&fan_watchdog_task,
+				msecs_to_jiffies(fan_watchdog_maxinterval * 1000)))
+			printk(IBM_ERR "failed to schedule the fan watchdog, "
+				"watchdog will not trigger\n");
+	} else
+		fan_watchdog_active = 0;
+}
+
 static int fan_read(char *p)
 {
 	int len = 0;
@@ -1868,7 +1893,9 @@ static int fan_read(char *p)
 	}
 
 	if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
-		len += sprintf(p + len, "commands:\tenable, disable\n");
+		len += sprintf(p + len, "commands:\tenable, disable\n"
+				"commands:\twatchdog <timeout> (<timeout> is 0 (off), "
+				"1-120 (seconds))\n");
 
 	if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
 		len += sprintf(p + len, "commands:\tspeed <speed>"
@@ -1991,6 +2018,9 @@ static int fan_write_cmd_level(const cha
 
 	*rc = fan_write_level(level);
 
+	if (! *rc)
+		fan_watchdog_reset();
+
 	return 1;
 }
 
@@ -2011,6 +2041,9 @@ static int fan_write_cmd_disable(const c
 
 	*rc = fan_write_disable();
 
+	if (! *rc)
+		fan_watchdog_reset();
+
 	return 1;
 }
 
@@ -2026,6 +2059,26 @@ static int fan_write_cmd_speed(const cha
 
 	*rc = fan_write_speed(speed);
 
+	if (! *rc)
+		fan_watchdog_reset();
+
+	return 1;
+}
+
+static int fan_write_cmd_watchdog(const char *cmd, int *rc)
+{
+	int interval;
+
+	if (sscanf(cmd, "watchdog %d", &interval) != 1)
+		return 0;
+
+	if (interval < 0 || interval > 120)
+		*rc = -EINVAL;
+	else {
+		fan_watchdog_maxinterval = interval;
+		fan_watchdog_reset();
+	}
+
 	return 1;
 }
 
@@ -2040,7 +2093,8 @@ static int fan_write(char *buf)
 		    	continue;
 		else if ((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
 				(fan_write_cmd_enable(cmd, &rc) ||
-				fan_write_cmd_disable(cmd, &rc)))
+				fan_write_cmd_disable(cmd, &rc) ||
+				fan_write_cmd_watchdog(cmd, &rc)))
 		    	continue;
 		else if ((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
 				fan_write_cmd_speed(cmd, &rc))
@@ -2052,6 +2106,16 @@ static int fan_write(char *buf)
 	return rc;
 }
 
+static void fan_watchdog_fire( void *ignored )
+{
+	printk(IBM_NOTICE "fan watchdog: enabling fan\n");
+	if (fan_write_enable()) {
+		printk(IBM_ERR "fan watchdog: error while enabling fan\n");
+		/* reschedule for later */
+		fan_watchdog_reset();
+	}
+}
+
 static struct ibm_struct ibms[] = {
 	{
 	 .name = "driver",
@@ -2343,6 +2407,9 @@ static int __init ibm_init(struct ibm_st
 
 static void ibm_exit(struct ibm_struct *ibm)
 {
+	cancel_delayed_work(&fan_watchdog_task);
+	flush_scheduled_work();
+
 	if (ibm->notify_installed)
 		acpi_remove_notify_handler(*ibm->handle, ibm->type,
 					   dispatch_notify);
Index: 2.6.18/Documentation/ibm-acpi.txt
===================================================================
--- 2.6.18.orig/Documentation/ibm-acpi.txt
+++ 2.6.18/Documentation/ibm-acpi.txt
@@ -677,6 +677,18 @@ ibm-acpi is started (either by the kerne
 a module).  They will remain in "unknown" state until ibm-acpi can be
 certain that the fan status was updated by a fan control operation.
 
+The ibm-acpi kernel driver can be programmed to revert the fan level
+to a safe setting if userspace does not issue any fan control commands
+(enable, disable, level) for a configurable ammount of time.  To do
+this, use the "watchdog" command.
+
+	echo 'watchdog <interval>' > /proc/acpi/ibm/fan
+
+Interval is the ammount of time in seconds to wait for a fan command
+before reseting the fan level to a safe one.  If set to zero, the
+watchdog is disabled (default).  When the watchdog timer runs out,
+it does the exact equivalent of the "enable" fan command.
+
 
 Multiple Commands, Module Parameters
 ------------------------------------

--f61P+fpdnY2FZS1u--