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

Commit 6e0ca95a authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-sleep'

* pm-sleep:
  PM / Hibernate: Use bool for boolean fields of struct snapshot_data
  PM / Sleep: Detect device suspend/resume lockup and log event
parents 400fc452 7bc9b1cf
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
#include <linux/suspend.h>
#include <trace/events/power.h>
#include <linux/cpuidle.h>
#include <linux/timer.h>

#include "../base.h"
#include "power.h"

@@ -390,6 +392,71 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
	return error;
}

#ifdef CONFIG_DPM_WATCHDOG
struct dpm_watchdog {
	struct device		*dev;
	struct task_struct	*tsk;
	struct timer_list	timer;
};

#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
	struct dpm_watchdog wd

/**
 * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
 * @data: Watchdog object address.
 *
 * Called when a driver has timed out suspending or resuming.
 * There's not much we can do here to recover so panic() to
 * capture a crash-dump in pstore.
 */
static void dpm_watchdog_handler(unsigned long data)
{
	struct dpm_watchdog *wd = (void *)data;

	dev_emerg(wd->dev, "**** DPM device timeout ****\n");
	show_stack(wd->tsk, NULL);
	panic("%s %s: unrecoverable failure\n",
		dev_driver_string(wd->dev), dev_name(wd->dev));
}

/**
 * dpm_watchdog_set - Enable pm watchdog for given device.
 * @wd: Watchdog. Must be allocated on the stack.
 * @dev: Device to handle.
 */
static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
{
	struct timer_list *timer = &wd->timer;

	wd->dev = dev;
	wd->tsk = current;

	init_timer_on_stack(timer);
	/* use same timeout value for both suspend and resume */
	timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
	timer->function = dpm_watchdog_handler;
	timer->data = (unsigned long)wd;
	add_timer(timer);
}

/**
 * dpm_watchdog_clear - Disable suspend/resume watchdog.
 * @wd: Watchdog to disable.
 */
static void dpm_watchdog_clear(struct dpm_watchdog *wd)
{
	struct timer_list *timer = &wd->timer;

	del_timer_sync(timer);
	destroy_timer_on_stack(timer);
}
#else
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd)
#define dpm_watchdog_set(x, y)
#define dpm_watchdog_clear(x)
#endif

/*------------------------- Resume routines -------------------------*/

/**
@@ -576,6 +643,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
	pm_callback_t callback = NULL;
	char *info = NULL;
	int error = 0;
	DECLARE_DPM_WATCHDOG_ON_STACK(wd);

	TRACE_DEVICE(dev);
	TRACE_RESUME(0);
@@ -584,6 +652,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
		goto Complete;

	dpm_wait(dev->parent, async);
	dpm_watchdog_set(&wd, dev);
	device_lock(dev);

	/*
@@ -642,6 +711,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)

 Unlock:
	device_unlock(dev);
	dpm_watchdog_clear(&wd);

 Complete:
	complete_all(&dev->power.completion);
@@ -1060,6 +1130,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
	pm_callback_t callback = NULL;
	char *info = NULL;
	int error = 0;
	DECLARE_DPM_WATCHDOG_ON_STACK(wd);

	dpm_wait_for_children(dev, async);

@@ -1083,6 +1154,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
	if (dev->power.syscore)
		goto Complete;

	dpm_watchdog_set(&wd, dev);
	device_lock(dev);

	if (dev->pm_domain) {
@@ -1139,6 +1211,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
	}

	device_unlock(dev);
	dpm_watchdog_clear(&wd);

 Complete:
	complete_all(&dev->power.completion);
+16 −0
Original line number Diff line number Diff line
@@ -178,6 +178,22 @@ config PM_SLEEP_DEBUG
	def_bool y
	depends on PM_DEBUG && PM_SLEEP

config DPM_WATCHDOG
	bool "Device suspend/resume watchdog"
	depends on PM_DEBUG && PSTORE
	---help---
	  Sets up a watchdog timer to capture drivers that are
	  locked up attempting to suspend/resume a device.
	  A detected lockup causes system panic with message
	  captured in pstore device for inspection in subsequent
	  boot session.

config DPM_WATCHDOG_TIMEOUT
	int "Watchdog timeout in seconds"
	range 1 120
	default 12
	depends on DPM_WATCHDOG

config PM_TRACE
	bool
	help
+10 −10
Original line number Diff line number Diff line
@@ -36,9 +36,9 @@ static struct snapshot_data {
	struct snapshot_handle handle;
	int swap;
	int mode;
	char frozen;
	char ready;
	char platform_support;
	bool frozen;
	bool ready;
	bool platform_support;
	bool free_bitmaps;
} snapshot_state;

@@ -93,9 +93,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
	if (error)
		atomic_inc(&snapshot_device_available);

	data->frozen = 0;
	data->ready = 0;
	data->platform_support = 0;
	data->frozen = false;
	data->ready = false;
	data->platform_support = false;

 Unlock:
	unlock_system_sleep();
@@ -229,7 +229,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
		if (error)
			thaw_processes();
		else
			data->frozen = 1;
			data->frozen = true;

		break;

@@ -240,7 +240,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
		free_basic_memory_bitmaps();
		data->free_bitmaps = false;
		thaw_processes();
		data->frozen = 0;
		data->frozen = false;
		break;

	case SNAPSHOT_CREATE_IMAGE:
@@ -270,7 +270,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
	case SNAPSHOT_FREE:
		swsusp_free();
		memset(&data->handle, 0, sizeof(struct snapshot_handle));
		data->ready = 0;
		data->ready = false;
		/*
		 * It is necessary to thaw kernel threads here, because
		 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
@@ -334,7 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
		 * PM_HIBERNATION_PREPARE
		 */
		error = suspend_devices_and_enter(PM_SUSPEND_MEM);
		data->ready = 0;
		data->ready = false;
		break;

	case SNAPSHOT_PLATFORM_SUPPORT: