Merge tag 'exynos-drm-next-for-v5.20' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / kernel / reboot.c
index a091145..3c35445 100644 (file)
@@ -315,6 +315,43 @@ static int sys_off_notify(struct notifier_block *nb,
        return handler->sys_off_cb(&data);
 }
 
+static struct sys_off_handler platform_sys_off_handler;
+
+static struct sys_off_handler *alloc_sys_off_handler(int priority)
+{
+       struct sys_off_handler *handler;
+       gfp_t flags;
+
+       /*
+        * Platforms like m68k can't allocate sys_off handler dynamically
+        * at the early boot time because memory allocator isn't available yet.
+        */
+       if (priority == SYS_OFF_PRIO_PLATFORM) {
+               handler = &platform_sys_off_handler;
+               if (handler->cb_data)
+                       return ERR_PTR(-EBUSY);
+       } else {
+               if (system_state > SYSTEM_RUNNING)
+                       flags = GFP_ATOMIC;
+               else
+                       flags = GFP_KERNEL;
+
+               handler = kzalloc(sizeof(*handler), flags);
+               if (!handler)
+                       return ERR_PTR(-ENOMEM);
+       }
+
+       return handler;
+}
+
+static void free_sys_off_handler(struct sys_off_handler *handler)
+{
+       if (handler == &platform_sys_off_handler)
+               memset(handler, 0, sizeof(*handler));
+       else
+               kfree(handler);
+}
+
 /**
  *     register_sys_off_handler - Register sys-off handler
  *     @mode: Sys-off mode
@@ -345,9 +382,9 @@ register_sys_off_handler(enum sys_off_mode mode,
        struct sys_off_handler *handler;
        int err;
 
-       handler = kzalloc(sizeof(*handler), GFP_KERNEL);
-       if (!handler)
-               return ERR_PTR(-ENOMEM);
+       handler = alloc_sys_off_handler(priority);
+       if (IS_ERR(handler))
+               return handler;
 
        switch (mode) {
        case SYS_OFF_MODE_POWER_OFF_PREPARE:
@@ -364,7 +401,7 @@ register_sys_off_handler(enum sys_off_mode mode,
                break;
 
        default:
-               kfree(handler);
+               free_sys_off_handler(handler);
                return ERR_PTR(-EINVAL);
        }
 
@@ -391,7 +428,7 @@ register_sys_off_handler(enum sys_off_mode mode,
        }
 
        if (err) {
-               kfree(handler);
+               free_sys_off_handler(handler);
                return ERR_PTR(err);
        }
 
@@ -409,7 +446,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
 {
        int err;
 
-       if (!handler)
+       if (IS_ERR_OR_NULL(handler))
                return;
 
        if (handler->blocking)
@@ -422,7 +459,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)
        /* sanity check, shall never happen */
        WARN_ON(err);
 
-       kfree(handler);
+       free_sys_off_handler(handler);
 }
 EXPORT_SYMBOL_GPL(unregister_sys_off_handler);
 
@@ -584,7 +621,23 @@ static void do_kernel_power_off_prepare(void)
  */
 void do_kernel_power_off(void)
 {
+       struct sys_off_handler *sys_off = NULL;
+
+       /*
+        * Register sys-off handlers for legacy PM callback. This allows
+        * legacy PM callbacks temporary co-exist with the new sys-off API.
+        *
+        * TODO: Remove legacy handlers once all legacy PM users will be
+        *       switched to the sys-off based APIs.
+        */
+       if (pm_power_off)
+               sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
+                                                  SYS_OFF_PRIO_DEFAULT,
+                                                  legacy_pm_power_off, NULL);
+
        atomic_notifier_call_chain(&power_off_handler_list, 0, NULL);
+
+       unregister_sys_off_handler(sys_off);
 }
 
 /**
@@ -595,7 +648,8 @@ void do_kernel_power_off(void)
  */
 bool kernel_can_power_off(void)
 {
-       return !atomic_notifier_call_chain_is_empty(&power_off_handler_list);
+       return !atomic_notifier_call_chain_is_empty(&power_off_handler_list) ||
+               pm_power_off;
 }
 EXPORT_SYMBOL_GPL(kernel_can_power_off);
 
@@ -630,7 +684,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
                void __user *, arg)
 {
        struct pid_namespace *pid_ns = task_active_pid_ns(current);
-       struct sys_off_handler *sys_off = NULL;
        char buffer[256];
        int ret = 0;
 
@@ -655,21 +708,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        if (ret)
                return ret;
 
-       /*
-        * Register sys-off handlers for legacy PM callback. This allows
-        * legacy PM callbacks temporary co-exist with the new sys-off API.
-        *
-        * TODO: Remove legacy handlers once all legacy PM users will be
-        *       switched to the sys-off based APIs.
-        */
-       if (pm_power_off) {
-               sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
-                                                  SYS_OFF_PRIO_DEFAULT,
-                                                  legacy_pm_power_off, NULL);
-               if (IS_ERR(sys_off))
-                       return PTR_ERR(sys_off);
-       }
-
        /* Instead of trying to make the power_off code look like
         * halt when pm_power_off is not set do it the easy way.
         */
@@ -727,7 +765,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
                break;
        }
        mutex_unlock(&system_transition_mutex);
-       unregister_sys_off_handler(sys_off);
        return ret;
 }
 
@@ -782,11 +819,9 @@ static int __orderly_reboot(void)
        ret = run_cmd(reboot_cmd);
 
        if (ret) {
-               printk_prefer_direct_enter();
                pr_warn("Failed to start orderly reboot: forcing the issue\n");
                emergency_sync();
                kernel_restart(NULL);
-               printk_prefer_direct_exit();
        }
 
        return ret;
@@ -799,7 +834,6 @@ static int __orderly_poweroff(bool force)
        ret = run_cmd(poweroff_cmd);
 
        if (ret && force) {
-               printk_prefer_direct_enter();
                pr_warn("Failed to start orderly shutdown: forcing the issue\n");
 
                /*
@@ -809,7 +843,6 @@ static int __orderly_poweroff(bool force)
                 */
                emergency_sync();
                kernel_power_off();
-               printk_prefer_direct_exit();
        }
 
        return ret;
@@ -867,8 +900,6 @@ EXPORT_SYMBOL_GPL(orderly_reboot);
  */
 static void hw_failure_emergency_poweroff_func(struct work_struct *work)
 {
-       printk_prefer_direct_enter();
-
        /*
         * We have reached here after the emergency shutdown waiting period has
         * expired. This means orderly_poweroff has not been able to shut off
@@ -885,8 +916,6 @@ static void hw_failure_emergency_poweroff_func(struct work_struct *work)
         */
        pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n");
        emergency_restart();
-
-       printk_prefer_direct_exit();
 }
 
 static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work,
@@ -925,13 +954,11 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced)
 {
        static atomic_t allow_proceed = ATOMIC_INIT(1);
 
-       printk_prefer_direct_enter();
-
        pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason);
 
        /* Shutdown should be initiated only once. */
        if (!atomic_dec_and_test(&allow_proceed))
-               goto out;
+               return;
 
        /*
         * Queue a backup emergency shutdown in the event of
@@ -939,8 +966,6 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced)
         */
        hw_failure_emergency_poweroff(ms_until_forced);
        orderly_poweroff(true);
-out:
-       printk_prefer_direct_exit();
 }
 EXPORT_SYMBOL_GPL(hw_protection_shutdown);