ANDROID: power: wakeup_reason: Adds functionality to log the last suspend abort reason.
authorRuchi Kandoi <kandoiruchi@google.com>
Wed, 29 Oct 2014 17:36:27 +0000 (10:36 -0700)
committerAmit Pundir <amit.pundir@linaro.org>
Mon, 18 Dec 2017 15:41:22 +0000 (21:11 +0530)
Extends the last_resume_reason to log suspend abort reason. The abort
reasons will have "Abort:" appended at the start to distinguish itself
from the resume reason.

Signed-off-by: Ruchi Kandoi <kandoiruchi@google.com>
Change-Id: I3207f1844e3d87c706dfc298fb10e1c648814c5f

[AmitP: Folded following android-4.9 changes into this patch
        00a83e61b4fc ("ANDROID: Make suspend abort reason logging depend on CONFIG_PM_SLEEP")
        9d17e24b036e ("ANDROID: wakeup_reason: use vsnprintf instead of snsprintf for vargs.")
        7961972600ba ("ANDROID: power: Provide dummy log_suspend_abort_reason() if SUSPEND is disabled")]
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
drivers/base/power/main.c
drivers/base/power/wakeup.c
drivers/base/syscore.c
include/linux/suspend.h
include/linux/wakeup_reason.h
kernel/power/process.c
kernel/power/suspend.c
kernel/power/wakeup_reason.c

index 770b1539a083d111ba1a65b569d3a75eaa81dd38..e24f6500786e32f0c2a3d585c88975db2cdd85bf 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/cpufreq.h>
 #include <linux/cpuidle.h>
 #include <linux/timer.h>
+#include <linux/wakeup_reason.h>
 
 #include "../base.h"
 #include "power.h"
@@ -1455,6 +1456,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        pm_callback_t callback = NULL;
        const char *info = NULL;
        int error = 0;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 
        TRACE_DEVICE(dev);
@@ -1475,6 +1477,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
                pm_wakeup_event(dev, 0);
 
        if (pm_wakeup_pending()) {
+               pm_get_active_wakeup_sources(suspend_abort,
+                       MAX_SUSPEND_ABORT_LEN);
+               log_suspend_abort_reason(suspend_abort);
                async_error = -EBUSY;
                goto Complete;
        }
index cdd6f256da597cb2abad069538d09723b2250d87..85eece1baf0afd8a6143e4d43ac7562784485d31 100644 (file)
@@ -805,6 +805,22 @@ void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard)
 }
 EXPORT_SYMBOL_GPL(pm_wakeup_dev_event);
 
+void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max)
+{
+       struct wakeup_source *ws;
+       int len = 0;
+       rcu_read_lock();
+       len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: ");
+       list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+               if (ws->active) {
+                       len += snprintf(pending_wakeup_source + len, max,
+                               "%s ", ws->name);
+               }
+       }
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources);
+
 void pm_print_active_wakeup_sources(void)
 {
        struct wakeup_source *ws;
index 8d98a329f6ea63a2daf179bb3f15e5307c6a0d13..96c34a95cc625929a08ca0e76c13b9b830a4907c 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/suspend.h>
 #include <trace/events/power.h>
+#include <linux/wakeup_reason.h>
 
 static LIST_HEAD(syscore_ops_list);
 static DEFINE_MUTEX(syscore_ops_lock);
@@ -75,6 +76,8 @@ int syscore_suspend(void)
        return 0;
 
  err_out:
+       log_suspend_abort_reason("System core suspend callback %pF failed",
+               ops->suspend);
        pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend);
 
        list_for_each_entry_continue(ops, &syscore_ops_list, node)
index d60b0f5c38d504e52a2c1c2726476def808595c7..31b0b27fb375c5ff233177b52cede85067270de8 100644 (file)
@@ -442,6 +442,7 @@ extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 extern bool pm_save_wakeup_count(unsigned int count);
 extern void pm_wakep_autosleep_enabled(bool set);
 extern void pm_print_active_wakeup_sources(void);
+extern void pm_get_active_wakeup_sources(char *pending_sources, size_t max);
 
 static inline void lock_system_sleep(void)
 {
index 7ce50f0debc47eebfd241cc5cbd2ac4c770a106d..9fbe209c71779a8e9f34cf98a41df5a3487ed92c 100644 (file)
 #ifndef _LINUX_WAKEUP_REASON_H
 #define _LINUX_WAKEUP_REASON_H
 
+#define MAX_SUSPEND_ABORT_LEN 256
+
 void log_wakeup_reason(int irq);
+#ifdef CONFIG_SUSPEND
+void log_suspend_abort_reason(const char *fmt, ...);
+#else
+static inline void log_suspend_abort_reason(const char *fmt, ...) { }
+#endif
 
 #endif /* _LINUX_WAKEUP_REASON_H */
index d76e61606f51381da828e4f15f3738c41079f72f..c366e3d34a0721708b027e8c3a88d9bd6bb57184 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/kmod.h>
 #include <trace/events/power.h>
 #include <linux/cpuset.h>
+#include <linux/wakeup_reason.h>
 
 /*
  * Timeout for stopping processes
@@ -38,6 +39,9 @@ static int try_to_freeze_tasks(bool user_only)
        unsigned int elapsed_msecs;
        bool wakeup = false;
        int sleep_usecs = USEC_PER_MSEC;
+#ifdef CONFIG_PM_SLEEP
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
+#endif
 
        start = ktime_get_boottime();
 
@@ -67,6 +71,11 @@ static int try_to_freeze_tasks(bool user_only)
                        break;
 
                if (pm_wakeup_pending()) {
+#ifdef CONFIG_PM_SLEEP
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
+#endif
                        wakeup = true;
                        break;
                }
index ccd2d20e6b067f6a7c3bb83a5848e9c0b333fade..d4f7c0b90b8c3b9b8dfb816dc6de00cfebb140f9 100644 (file)
@@ -31,6 +31,7 @@
 #include <trace/events/power.h>
 #include <linux/compiler.h>
 #include <linux/moduleparam.h>
+#include <linux/wakeup_reason.h>
 
 #include "power.h"
 
@@ -389,7 +390,8 @@ void __weak arch_suspend_enable_irqs(void)
  */
 static int suspend_enter(suspend_state_t state, bool *wakeup)
 {
-       int error;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
+       int error, last_dev;
 
        error = platform_suspend_prepare(state);
        if (error)
@@ -397,7 +399,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
        error = dpm_suspend_late(PMSG_SUSPEND);
        if (error) {
+               last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+               last_dev %= REC_FAILED_NUM;
                pr_err("late suspend of devices failed\n");
+               log_suspend_abort_reason("%s device failed to power down",
+                       suspend_stats.failed_devs[last_dev]);
                goto Platform_finish;
        }
        error = platform_suspend_prepare_late(state);
@@ -411,7 +417,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
        error = dpm_suspend_noirq(PMSG_SUSPEND);
        if (error) {
+               last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+               last_dev %= REC_FAILED_NUM;
                pr_err("noirq suspend of devices failed\n");
+               log_suspend_abort_reason("noirq suspend of %s device failed",
+                       suspend_stats.failed_devs[last_dev]);
                goto Platform_early_resume;
        }
        error = platform_suspend_prepare_noirq(state);
@@ -422,8 +432,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                goto Platform_wake;
 
        error = disable_nonboot_cpus();
-       if (error || suspend_test(TEST_CPUS))
+       if (error || suspend_test(TEST_CPUS)) {
+               log_suspend_abort_reason("Disabling non-boot cpus failed");
                goto Enable_cpus;
+       }
 
        arch_suspend_disable_irqs();
        BUG_ON(!irqs_disabled());
@@ -439,6 +451,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                                state, false);
                        events_check_enabled = false;
                } else if (*wakeup) {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                        error = -EBUSY;
                }
                syscore_resume();
@@ -488,6 +503,7 @@ int suspend_devices_and_enter(suspend_state_t state)
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                pr_err("Some devices failed to suspend, or early wake event detected\n");
+               log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
                goto Recover_platform;
        }
        suspend_test_finish("suspend devices");
index 187e4e9105fbb06d95b52446a6a7946a96de4da0..5944dfe78191ec64f04787333e07d185baa84393 100644 (file)
@@ -31,6 +31,8 @@
 #define MAX_WAKEUP_REASON_IRQS 32
 static int irq_list[MAX_WAKEUP_REASON_IRQS];
 static int irqcount;
+static bool suspend_abort;
+static char abort_reason[MAX_SUSPEND_ABORT_LEN];
 static struct kobject *wakeup_reason;
 static spinlock_t resume_reason_lock;
 
@@ -40,14 +42,18 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu
        int irq_no, buf_offset = 0;
        struct irq_desc *desc;
        spin_lock(&resume_reason_lock);
-       for (irq_no = 0; irq_no < irqcount; irq_no++) {
-               desc = irq_to_desc(irq_list[irq_no]);
-               if (desc && desc->action && desc->action->name)
-                       buf_offset += sprintf(buf + buf_offset, "%d %s\n",
-                                       irq_list[irq_no], desc->action->name);
-               else
-                       buf_offset += sprintf(buf + buf_offset, "%d\n",
-                                       irq_list[irq_no]);
+       if (suspend_abort) {
+               buf_offset = sprintf(buf, "Abort: %s", abort_reason);
+       } else {
+               for (irq_no = 0; irq_no < irqcount; irq_no++) {
+                       desc = irq_to_desc(irq_list[irq_no]);
+                       if (desc && desc->action && desc->action->name)
+                               buf_offset += sprintf(buf + buf_offset, "%d %s\n",
+                                               irq_list[irq_no], desc->action->name);
+                       else
+                               buf_offset += sprintf(buf + buf_offset, "%d\n",
+                                               irq_list[irq_no]);
+               }
        }
        spin_unlock(&resume_reason_lock);
        return buf_offset;
@@ -89,6 +95,25 @@ void log_wakeup_reason(int irq)
        spin_unlock(&resume_reason_lock);
 }
 
+void log_suspend_abort_reason(const char *fmt, ...)
+{
+       va_list args;
+
+       spin_lock(&resume_reason_lock);
+
+       //Suspend abort reason has already been logged.
+       if (suspend_abort) {
+               spin_unlock(&resume_reason_lock);
+               return;
+       }
+
+       suspend_abort = true;
+       va_start(args, fmt);
+       vsnprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args);
+       va_end(args);
+       spin_unlock(&resume_reason_lock);
+}
+
 /* Detects a suspend and clears all the previous wake up reasons*/
 static int wakeup_reason_pm_event(struct notifier_block *notifier,
                unsigned long pm_event, void *unused)
@@ -97,6 +122,7 @@ static int wakeup_reason_pm_event(struct notifier_block *notifier,
        case PM_SUSPEND_PREPARE:
                spin_lock(&resume_reason_lock);
                irqcount = 0;
+               suspend_abort = false;
                spin_unlock(&resume_reason_lock);
                break;
        default: