power: 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)
committerJohn Stultz <john.stultz@linaro.org>
Tue, 16 Feb 2016 21:53:38 +0000 (13:53 -0800)
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

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 df539322d9f5a2197a0371847f802f62af4ce3a0..a54d810f29666292845380961eed247d068d4f15 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/cpufreq.h>
 #include <linux/cpuidle.h>
 #include <linux/timer.h>
+#include <linux/wakeup_reason.h>
 
 #include "../base.h"
 #include "power.h"
@@ -1379,6 +1380,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        int error = 0;
        struct timer_list timer;
        struct dpm_drv_wd_data data;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 
        TRACE_DEVICE(dev);
@@ -1399,6 +1401,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 a1e0b9ab847a345c6a09adab3ff9f2fd9af3ad3e..2c3435d55d010032cbe800d31b4de9639b77aa5f 100644 (file)
@@ -804,6 +804,22 @@ void pm_wakeup_event(struct device *dev, unsigned int msec)
 }
 EXPORT_SYMBOL_GPL(pm_wakeup_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 8b6ec7ef0854e0f51fd94e38dbf7b831c9015f6c..c59803dc68de7e7a1d4805b83dabad5a5b00aaf8 100644 (file)
@@ -433,6 +433,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..5f095da2c977b2c1ec607f7729cdcb6cc1a2a5be 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef _LINUX_WAKEUP_REASON_H
 #define _LINUX_WAKEUP_REASON_H
 
-void log_wakeup_reason(int irq);
+#define MAX_SUSPEND_ABORT_LEN 256
 
+void log_wakeup_reason(int irq);
+void log_suspend_abort_reason(const char *fmt, ...);
 #endif /* _LINUX_WAKEUP_REASON_H */
index fa50018418dcd243093d335e90bec770e96bbc21..37eac4066e7131037be32ed7cbb26a87d2bca4c9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/workqueue.h>
 #include <linux/kmod.h>
 #include <trace/events/power.h>
+#include <linux/wakeup_reason.h>
 
 /* 
  * Timeout for stopping processes
@@ -35,6 +36,7 @@ static int try_to_freeze_tasks(bool user_only)
        unsigned int elapsed_msecs;
        bool wakeup = false;
        int sleep_usecs = USEC_PER_MSEC;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
 
        do_gettimeofday(&start);
 
@@ -64,6 +66,9 @@ static int try_to_freeze_tasks(bool user_only)
                        break;
 
                if (pm_wakeup_pending()) {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                        wakeup = true;
                        break;
                }
index a41c3fbac1e9f57abf51068df537e696cf475da9..024411816ccf2c5110247ce9be5817f1fdc9deed 100644 (file)
@@ -30,6 +30,7 @@
 #include <trace/events/power.h>
 #include <linux/compiler.h>
 #include <linux/moduleparam.h>
+#include <linux/wakeup_reason.h>
 
 #include "power.h"
 
@@ -313,7 +314,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)
@@ -321,7 +323,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;
                printk(KERN_ERR "PM: 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);
@@ -330,7 +336,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;
                printk(KERN_ERR "PM: 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);
@@ -354,8 +364,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        }
 
        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());
@@ -371,6 +383,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();
@@ -418,6 +433,7 @@ int suspend_devices_and_enter(suspend_state_t state)
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                pr_err("PM: 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..2aacc34ef17c018728631ef5aa1b82872626d175 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);
+       snprintf(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: