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)
committerRuchi Kandoi <kandoiruchi@google.com>
Wed, 29 Oct 2014 17:36:27 +0000 (10:36 -0700)
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/irq/pm.c
kernel/power/process.c
kernel/power/suspend.c
kernel/power/wakeup_reason.c

index 6a33dd85c044f250c012b57d9afec915f12fe647..5131ad8ca17f7550d53666bb7d3b86ffd2a063db 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/suspend.h>
 #include <linux/cpuidle.h>
 #include <linux/timer.h>
+#include <linux/wakeup_reason.h>
 
 #include "../base.h"
 #include "power.h"
@@ -938,6 +939,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
 static int dpm_suspend_noirq(pm_message_t state)
 {
        ktime_t starttime = ktime_get();
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        int error = 0;
 
        cpuidle_pause();
@@ -965,6 +967,9 @@ static int dpm_suspend_noirq(pm_message_t state)
                put_device(dev);
 
                if (pm_wakeup_pending()) {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                        error = -EBUSY;
                        break;
                }
@@ -1023,6 +1028,7 @@ static int device_suspend_late(struct device *dev, pm_message_t state)
 static int dpm_suspend_late(pm_message_t state)
 {
        ktime_t starttime = ktime_get();
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        int error = 0;
 
        mutex_lock(&dpm_list_mtx);
@@ -1048,6 +1054,9 @@ static int dpm_suspend_late(pm_message_t state)
                put_device(dev);
 
                if (pm_wakeup_pending()) {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                        error = -EBUSY;
                        break;
                }
@@ -1115,6 +1124,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        char *info = NULL;
        int error = 0;
        struct dpm_watchdog wd;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
 
        dpm_wait_for_children(dev, async);
 
@@ -1131,6 +1141,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 79715e7fa43e34c9cbf9a4521a657d7bb952f9c6..bea700736f2476f8de18f943ec1090b82d11bd37 100644 (file)
@@ -659,6 +659,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);
+
 static void print_active_wakeup_sources(void)
 {
        struct wakeup_source *ws;
index e8d11b6630eeb6ed7b815ffe2e21588965882587..0ab546558c4e2e125ba5ab3c1a294e2bd3b3e144 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/wakeup_reason.h>
 
 static LIST_HEAD(syscore_ops_list);
 static DEFINE_MUTEX(syscore_ops_lock);
@@ -73,6 +74,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 d4e3f16d5e8932c56e059d906add432cf88c8dbc..a34821358ae5055a11de88dbeff5b108fd5aaf12 100644 (file)
@@ -363,7 +363,7 @@ extern bool pm_wakeup_pending(void);
 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_get_active_wakeup_sources(char *pending_sources, size_t max);
 static inline void lock_system_sleep(void)
 {
        current->flags |= PF_FREEZER_SKIP;
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 fe4b09cf829ca2625559e972d6eb8671e3182cdd..08d0916150d596e4d587f8210434618614a3e3de 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/syscore_ops.h>
-
+#include <linux/wakeup_reason.h>
 #include "internals.h"
 
 /**
@@ -100,11 +100,16 @@ EXPORT_SYMBOL_GPL(resume_device_irqs);
 int check_wakeup_irqs(void)
 {
        struct irq_desc *desc;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        int irq;
 
        for_each_irq_desc(irq, desc) {
                if (irqd_is_wakeup_set(&desc->irq_data)) {
                        if (desc->istate & IRQS_PENDING) {
+                               log_suspend_abort_reason("Wakeup IRQ %d %s pending",
+                                       irq,
+                                       desc->action && desc->action->name ?
+                                       desc->action->name : "");
                                pr_info("Wakeup IRQ %d %s pending, suspend aborted\n",
                                        irq,
                                        desc->action && desc->action->name ?
index d26dcb5dff870b7b0cde2800e2cda992b3c3080a..86a40fa3509526b85b7433e302ab6d16e46575cc 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/kmod.h>
-
+#include <linux/wakeup_reason.h>
 /* 
  * Timeout for stopping processes
  */
@@ -34,6 +34,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);
 
@@ -63,6 +64,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 454568e6c8d280a59a9f2e3e0ed50c6af6f27f90..7c53fea31cba66727d0f99514aa9717e6af68392 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/ftrace.h>
 #include <linux/rtc.h>
 #include <trace/events/power.h>
+#include <linux/wakeup_reason.h>
 
 #include "power.h"
 
@@ -147,7 +148,7 @@ static int suspend_prepare(suspend_state_t state)
        error = suspend_freeze_processes();
        if (!error)
                return 0;
-
+       log_suspend_abort_reason("One or more tasks refusing to freeze");
        suspend_stats.failed_freeze++;
        dpm_save_failed_step(SUSPEND_FREEZE);
  Finish:
@@ -177,7 +178,8 @@ void __attribute__ ((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;
 
        if (need_suspend_ops(state) && suspend_ops->prepare) {
                error = suspend_ops->prepare();
@@ -187,7 +189,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
        error = dpm_suspend_end(PMSG_SUSPEND);
        if (error) {
+               last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+               last_dev %= REC_FAILED_NUM;
                printk(KERN_ERR "PM: Some devices failed to power down\n");
+               log_suspend_abort_reason("%s device failed to power down",
+                       suspend_stats.failed_devs[last_dev]);
                goto Platform_finish;
        }
 
@@ -212,8 +218,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());
@@ -224,6 +232,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                if (!(suspend_test(TEST_CORE) || *wakeup)) {
                        error = suspend_ops->enter(state);
                        events_check_enabled = false;
+               } else {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                }
                syscore_resume();
        }
@@ -271,6 +283,7 @@ int suspend_devices_and_enter(suspend_state_t state)
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to suspend\n");
+               log_suspend_abort_reason("Some devices failed to suspend");
                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: