Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[sfrench/cifs-2.6.git] / kernel / reboot.c
index 31bf2611ee12f6af8763059b5cfa9762bad152d8..6bcc5d6a6572b7decf4b731d9f130374555bf7d5 100644 (file)
@@ -7,6 +7,7 @@
 
 #define pr_fmt(fmt)    "reboot: " fmt
 
+#include <linux/atomic.h>
 #include <linux/ctype.h>
 #include <linux/export.h>
 #include <linux/kexec.h>
@@ -359,7 +360,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        case LINUX_REBOOT_CMD_HALT:
                kernel_halt();
                do_exit(0);
-               panic("cannot halt");
 
        case LINUX_REBOOT_CMD_POWER_OFF:
                kernel_power_off();
@@ -519,6 +519,84 @@ void orderly_reboot(void)
 }
 EXPORT_SYMBOL_GPL(orderly_reboot);
 
+/**
+ * hw_failure_emergency_poweroff_func - emergency poweroff work after a known delay
+ * @work: work_struct associated with the emergency poweroff function
+ *
+ * This function is called in very critical situations to force
+ * a kernel poweroff after a configurable timeout value.
+ */
+static void hw_failure_emergency_poweroff_func(struct work_struct *work)
+{
+       /*
+        * We have reached here after the emergency shutdown waiting period has
+        * expired. This means orderly_poweroff has not been able to shut off
+        * the system for some reason.
+        *
+        * Try to shut down the system immediately using kernel_power_off
+        * if populated
+        */
+       pr_emerg("Hardware protection timed-out. Trying forced poweroff\n");
+       kernel_power_off();
+
+       /*
+        * Worst of the worst case trigger emergency restart
+        */
+       pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n");
+       emergency_restart();
+}
+
+static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work,
+                           hw_failure_emergency_poweroff_func);
+
+/**
+ * hw_failure_emergency_poweroff - Trigger an emergency system poweroff
+ *
+ * This may be called from any critical situation to trigger a system shutdown
+ * after a given period of time. If time is negative this is not scheduled.
+ */
+static void hw_failure_emergency_poweroff(int poweroff_delay_ms)
+{
+       if (poweroff_delay_ms <= 0)
+               return;
+       schedule_delayed_work(&hw_failure_emergency_poweroff_work,
+                             msecs_to_jiffies(poweroff_delay_ms));
+}
+
+/**
+ * hw_protection_shutdown - Trigger an emergency system poweroff
+ *
+ * @reason:            Reason of emergency shutdown to be printed.
+ * @ms_until_forced:   Time to wait for orderly shutdown before tiggering a
+ *                     forced shudown. Negative value disables the forced
+ *                     shutdown.
+ *
+ * Initiate an emergency system shutdown in order to protect hardware from
+ * further damage. Usage examples include a thermal protection or a voltage or
+ * current regulator failures.
+ * NOTE: The request is ignored if protection shutdown is already pending even
+ * if the previous request has given a large timeout for forced shutdown.
+ * Can be called from any context.
+ */
+void hw_protection_shutdown(const char *reason, int ms_until_forced)
+{
+       static atomic_t allow_proceed = ATOMIC_INIT(1);
+
+       pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason);
+
+       /* Shutdown should be initiated only once. */
+       if (!atomic_dec_and_test(&allow_proceed))
+               return;
+
+       /*
+        * Queue a backup emergency shutdown in the event of
+        * orderly_poweroff failure
+        */
+       hw_failure_emergency_poweroff(ms_until_forced);
+       orderly_poweroff(true);
+}
+EXPORT_SYMBOL_GPL(hw_protection_shutdown);
+
 static int __init reboot_setup(char *str)
 {
        for (;;) {