Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7b509cf authored by Kelly Rossmoyer's avatar Kelly Rossmoyer Committed by Alistair Delva
Browse files

ANDROID: power: wakeup_reason: wake reason enhancements



These changes build upon the existing Android kernel wakeup reason code
to:
* improve the positioning of suspend abort logging calls in suspend flow
* add logging of abnormal wakeup reasons like unexpected HW IRQs and
  IRQs configured as both wake-enabled and no-suspend
* add support for capturing deferred-processing threaded nested IRQs as
  wakeup reasons rather than their synchronously-processed parents

Bug: 150970830
Bug: 140217217

Signed-off-by: default avatarKelly Rossmoyer <krossmo@google.com>
Change-Id: I903b811a0fe11a605a25815c3a341668a23de700
parent dba8725f
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -1363,6 +1363,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
	error = dpm_run_callback(callback, dev, state, info);
	if (error) {
		async_error = error;
		log_suspend_abort_reason("Callback failed on %s in %pS returned %d",
					 dev_name(dev), callback, error);
		goto Complete;
	}

@@ -1577,6 +1579,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
	error = dpm_run_callback(callback, dev, state, info);
	if (error) {
		async_error = error;
		log_suspend_abort_reason("Callback failed on %s in %pS returned %d",
					 dev_name(dev), callback, error);
		goto Complete;
	}
	dpm_propagate_wakeup_to_parent(dev);
@@ -1746,7 +1750,6 @@ 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);
@@ -1770,9 +1773,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)

	if (pm_wakeup_pending()) {
		dev->power.direct_complete = false;
		pm_get_active_wakeup_sources(suspend_abort,
			MAX_SUSPEND_ABORT_LEN);
		log_suspend_abort_reason(suspend_abort);
		async_error = -EBUSY;
		goto Complete;
	}
@@ -1847,6 +1847,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)

		dpm_propagate_wakeup_to_parent(dev);
		dpm_clear_superiors_direct_complete(dev);
	} else {
		log_suspend_abort_reason("Callback failed on %s in %pS returned %d",
					 dev_name(dev), callback, error);
	}

	device_unlock(dev);
@@ -2061,6 +2064,8 @@ int dpm_prepare(pm_message_t state)
			printk(KERN_INFO "PM: Device %s not prepared "
				"for power transition: code %d\n",
				dev_name(dev), error);
			log_suspend_abort_reason("Device %s not prepared for power transition: code %d",
						 dev_name(dev), error);
			dpm_save_failed_dev(dev_name(dev));
			put_device(dev);
			break;
+20 −3
Original line number Diff line number Diff line
@@ -15,7 +15,9 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/pm_wakeirq.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/wakeup_reason.h>
#include <trace/events/power.h>

#include "power.h"
@@ -883,6 +885,7 @@ bool pm_wakeup_pending(void)
{
	unsigned long flags;
	bool ret = false;
	char suspend_abort[MAX_SUSPEND_ABORT_LEN];

	raw_spin_lock_irqsave(&events_lock, flags);
	if (events_check_enabled) {
@@ -895,8 +898,10 @@ bool pm_wakeup_pending(void)
	raw_spin_unlock_irqrestore(&events_lock, flags);

	if (ret) {
		pr_debug("PM: Wakeup pending, aborting suspend\n");
		pm_print_active_wakeup_sources();
		pm_get_active_wakeup_sources(suspend_abort,
					     MAX_SUSPEND_ABORT_LEN);
		log_suspend_abort_reason(suspend_abort);
		pr_info("PM: %s\n", suspend_abort);
	}

	return ret || atomic_read(&pm_abort_suspend) > 0;
@@ -924,6 +929,18 @@ void pm_wakeup_clear(bool reset)
void pm_system_irq_wakeup(unsigned int irq_number)
{
	if (pm_wakeup_irq == 0) {
		struct irq_desc *desc;
		const char *name = "null";

		desc = irq_to_desc(irq_number);
		if (desc == NULL)
			name = "stray irq";
		else if (desc->action && desc->action->name)
			name = desc->action->name;

		log_irq_wakeup_reason(irq_number);
		pr_warn("%s: %d triggered %s\n", __func__, irq_number, name);

		pm_wakeup_irq = irq_number;
		pm_system_wakeup();
	}
+4 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <linux/of_irq.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/wakeup_reason.h>


#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-common.h>
@@ -362,6 +364,8 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
			err = handle_domain_irq(gic_data.domain, irqnr, regs);
			if (err) {
				WARN_ONCE(true, "Unexpected interrupt received!\n");
				log_abnormal_wakeup_reason(
						"unexpected HW IRQ %u", irqnr);
				if (static_branch_likely(&supports_deactivate_key)) {
					if (irqnr < 8192)
						gic_write_dir(irqnr);
+8 −1
Original line number Diff line number Diff line
@@ -20,11 +20,18 @@

#define MAX_SUSPEND_ABORT_LEN 256

void log_wakeup_reason(int irq);
#ifdef CONFIG_SUSPEND
void log_irq_wakeup_reason(int irq);
void log_threaded_irq_wakeup_reason(int irq, int parent_irq);
void log_suspend_abort_reason(const char *fmt, ...);
void log_abnormal_wakeup_reason(const char *fmt, ...);
void clear_wakeup_reasons(void);
#else
static inline void log_irq_wakeup_reason(int irq) { }
static inline void log_threaded_irq_wakeup_reason(int irq, int parent_irq) { }
static inline void log_suspend_abort_reason(const char *fmt, ...) { }
static inline void log_abnormal_wakeup_reason(const char *fmt, ...) { }
static inline void clear_wakeup_reasons(void) { }
#endif

#endif /* _LINUX_WAKEUP_REASON_H */
+16 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/irqdomain.h>
#include <linux/wakeup_reason.h>

#include <trace/events/irq.h>

@@ -507,8 +508,22 @@ static bool irq_may_run(struct irq_desc *desc)
	 * If the interrupt is not in progress and is not an armed
	 * wakeup interrupt, proceed.
	 */
	if (!irqd_has_set(&desc->irq_data, mask))
	if (!irqd_has_set(&desc->irq_data, mask)) {
#ifdef CONFIG_PM_SLEEP
		if (unlikely(desc->no_suspend_depth &&
			     irqd_is_wakeup_set(&desc->irq_data))) {
			unsigned int irq = irq_desc_get_irq(desc);
			const char *name = "(unnamed)";

			if (desc->action && desc->action->name)
				name = desc->action->name;

			log_abnormal_wakeup_reason("misconfigured IRQ %u %s",
						   irq, name);
		}
#endif
		return true;
	}

	/*
	 * If the interrupt is an armed wakeup source, mark it pending
Loading