diff --git a/MAINTAINERS b/MAINTAINERS
index 867157311dc8..ecd988b4a838 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7788,6 +7788,8 @@ F:	include/linux/freezer.h
 F:	include/linux/pm.h
 F:	include/linux/suspend.h
 F:	kernel/power/
+F:	kernel/power/suspend_watchdog.c
+F:	kernel/power/suspend_watchdog.h
 
 HID CORE LAYER
 M:	Jiri Kosina <jikos@kernel.org>
@@ -16630,6 +16632,8 @@ F:	include/linux/freezer.h
 F:	include/linux/pm.h
 F:	include/linux/suspend.h
 F:	kernel/power/
+F:	kernel/power/suspend_watchdog.c
+F:	kernel/power/suspend_watchdog.h
 
 SVGA HANDLING
 M:	Martin Mares <mj@ucw.cz>
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 205a06752ca9..b08f91e31a70 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -496,69 +496,6 @@ 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.
- * @t: The timer that PM watchdog depends on.
- *
- * 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(struct timer_list *t)
-{
-	struct dpm_watchdog *wd = from_timer(wd, t, timer);
-
-	dev_emerg(wd->dev, "**** DPM device timeout ****\n");
-	show_stack(wd->tsk, NULL, KERN_EMERG);
-	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;
-
-	timer_setup_on_stack(timer, dpm_watchdog_handler, 0);
-	/* use same timeout value for both suspend and resume */
-	timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
-	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 -------------------------*/
 
 /**
@@ -899,7 +836,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
 	pm_callback_t callback = NULL;
 	const char *info = NULL;
 	int error = 0;
-	DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
@@ -916,7 +852,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
 	if (!dpm_wait_for_superior(dev, async))
 		goto Complete;
 
-	dpm_watchdog_set(&wd, dev);
 	device_lock(dev);
 
 	/*
@@ -969,7 +904,6 @@ 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);
@@ -1593,7 +1527,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;
-	DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 
 	TRACE_DEVICE(dev);
 	TRACE_SUSPEND(0);
@@ -1647,7 +1580,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 	dev->power.may_skip_resume = true;
 	dev->power.must_resume = false;
 
-	dpm_watchdog_set(&wd, dev);
 	device_lock(dev);
 
 	if (dev->pm_domain) {
@@ -1699,7 +1631,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 	}
 
 	device_unlock(dev);
-	dpm_watchdog_clear(&wd);
 
  Complete:
 	if (error)
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index a7320f07689d..c1154e53d834 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -207,21 +207,20 @@ 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 && EXPERT
+config PM_SUSPEND_WATCHDOG
+	bool "Watchdog for linux kernel suspend/resume process"
+	depends on PM_SLEEP
 	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 120
-	depends on DPM_WATCHDOG
+	This option will enable suspend watchdog to prevent device stuck
+	during suspend/resume process. Suspend watchdog timeout handler
+	will dump disk sleep task at first round timeout and trigger
+	kernel panic at second round timeout. The timer for each round
+	is defined in CONFIG_PM_SUSPEND_WATCHDOG_TIMEOUT.
+
+config PM_SUSPEND_WATCHDOG_TIMEOUT
+	int "Suspend watchdog timeout in seconds"
+	range 30 120
+	depends on PM_SUSPEND_WATCHDOG
 
 config PM_TRACE
 	bool
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 5899260a8bef..f711e391004b 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_PM)		+= main.o
 obj-$(CONFIG_VT_CONSOLE_SLEEP)	+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
+obj-$(CONFIG_PM_SUSPEND_WATCHDOG)	+= suspend_watchdog.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o
 obj-$(CONFIG_HIBERNATION_SNAPSHOT_DEV) += user.o
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 8b1bb5ee7e5d..a0e3dfb39f6e 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -32,6 +32,7 @@
 #include <linux/moduleparam.h>
 
 #include "power.h"
+#include "suspend_watchdog.h"
 
 const char * const pm_labels[] = {
 	[PM_SUSPEND_TO_IDLE] = "freeze",
@@ -61,6 +62,8 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head);
 enum s2idle_states __read_mostly s2idle_state;
 static DEFINE_RAW_SPINLOCK(s2idle_lock);
 
+static DECLARE_SUSPEND_WATCHDOG(suspend_wd);
+
 /**
  * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend.
  *
@@ -89,6 +92,8 @@ static void s2idle_enter(void)
 {
 	trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, true);
 
+	stop_suspend_watchdog(&suspend_wd);
+
 	raw_spin_lock_irq(&s2idle_lock);
 	if (pm_wakeup_pending())
 		goto out;
@@ -114,6 +119,8 @@ static void s2idle_enter(void)
 	s2idle_state = S2IDLE_STATE_NONE;
 	raw_spin_unlock_irq(&s2idle_lock);
 
+	start_suspend_watchdog(&suspend_wd);
+
 	trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, false);
 }
 
@@ -179,6 +186,8 @@ void __init pm_states_init(void)
 	 * initialize mem_sleep_states[] accordingly here.
 	 */
 	mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE];
+
+	init_suspend_watchdog(&suspend_wd, suspend_timeout_handler);
 }
 
 static int __init mem_sleep_default_setup(char *str)
@@ -426,6 +435,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 	if (error || suspend_test(TEST_CPUS))
 		goto Enable_cpus;
 
+	stop_suspend_watchdog(&suspend_wd);
+
 	arch_suspend_disable_irqs();
 	BUG_ON(!irqs_disabled());
 
@@ -451,6 +462,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 	arch_suspend_enable_irqs();
 	BUG_ON(irqs_disabled());
 
+	start_suspend_watchdog(&suspend_wd);
+
  Enable_cpus:
 	suspend_enable_secondary_cpus();
 
@@ -610,6 +623,9 @@ int pm_suspend(suspend_state_t state)
 		return -EINVAL;
 
 	pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
+
+	start_suspend_watchdog(&suspend_wd);
+
 	error = enter_state(state);
 	if (error) {
 		suspend_stats.fail++;
@@ -617,6 +633,9 @@ int pm_suspend(suspend_state_t state)
 	} else {
 		suspend_stats.success++;
 	}
+
+	stop_suspend_watchdog(&suspend_wd);
+
 	pr_info("suspend exit\n");
 	return error;
 }
diff --git a/kernel/power/suspend_watchdog.c b/kernel/power/suspend_watchdog.c
new file mode 100644
index 000000000000..e6828f8dfb54
--- /dev/null
+++ b/kernel/power/suspend_watchdog.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * kernel/power/suspend_watchdog.c - Suspend watchdog functionality.
+ *
+ * Copyright (c) 2020 Joseph Jang
+ */
+
+#define pr_fmt(fmt) "SWD: " fmt
+
+#include <linux/mm.h>
+#include <linux/sched/debug.h>
+#include <linux/console.h>
+
+#include "suspend_watchdog.h"
+
+#ifdef CONFIG_PM_SUSPEND_WATCHDOG
+
+/**
+ * init_suspend_watchdog - Initialize suspend watchdog timer.
+ * @suspend_wd: Suspend watchdog to initialize.
+ * @func: Suspend watchdog timeout handler.
+ */
+inline void init_suspend_watchdog(struct suspend_watchdog *suspend_wd, void *func)
+{
+	struct timer_list *timer = &suspend_wd->timer;
+
+	timer_setup(timer, func, 0);
+}
+
+/**
+ * start_suspend_watchdog - Enable suspend watchdog to monitor suspend thread.
+ * @suspend_wd: Suspend watchdog to enable.
+ */
+inline void start_suspend_watchdog(struct suspend_watchdog *suspend_wd)
+{
+	struct timer_list *timer = &suspend_wd->timer;
+
+	suspend_wd->tsk = current;
+
+	/* use same timeout value for both suspend and resume */
+	timer->expires = jiffies + HZ * CONFIG_PM_SUSPEND_WATCHDOG_TIMEOUT;
+	add_timer(timer);
+}
+
+/**
+ * stop_suspend_watchdog - Disable suspend watchdog.
+ * @suspend_wd: Suspend watchdog to disable.
+ */
+inline void stop_suspend_watchdog(struct suspend_watchdog *suspend_wd)
+{
+	struct timer_list *timer = &suspend_wd->timer;
+
+	del_timer_sync(timer);
+}
+
+/**
+ * suspend_timeout_handler - suspend watchdog timeout handler.
+ * @t: The timer list that suspend watchdog timer depends on.
+ *
+ * Called when suspend thread has timeout suspending or resuming.
+ * Dump all uninterruptible tasks' call stack and call panic() to
+ * reboot system in second round timeout.
+ */
+inline void suspend_timeout_handler(struct timer_list *t)
+{
+	struct suspend_watchdog *suspend_wd = from_timer(suspend_wd, t, timer);
+	static int timeout_count;
+
+	pr_info("Suspend watchdog timeout (timer is %d seconds)\n",
+		(CONFIG_PM_SUSPEND_WATCHDOG_TIMEOUT));
+	show_stack(suspend_wd->tsk, NULL, KERN_EMERG);
+	show_state_filter(TASK_UNINTERRUPTIBLE);
+
+	if (timeout_count < 1) {
+		timeout_count++;
+		start_suspend_watchdog(suspend_wd);
+		return;
+	}
+
+	resume_console();
+
+	panic("Sleep timeout and panic\n");
+}
+#endif
diff --git a/kernel/power/suspend_watchdog.h b/kernel/power/suspend_watchdog.h
new file mode 100644
index 000000000000..18037445a77b
--- /dev/null
+++ b/kernel/power/suspend_watchdog.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SUSPEND_WATCHDOG_H
+#define _LINUX_SUSPEND_WATCHDOG_H
+
+#include <linux/sched/debug.h>
+
+struct suspend_watchdog {
+	struct task_struct *tsk;
+	struct timer_list timer;
+};
+
+#define DECLARE_SUSPEND_WATCHDOG(suspend_wd) \
+	struct suspend_watchdog suspend_wd
+
+#ifdef CONFIG_PM_SUSPEND_WATCHDOG
+inline void init_suspend_watchdog(struct suspend_watchdog *suspend_wd,
+					 void *func);
+
+inline void start_suspend_watchdog(struct suspend_watchdog *suspend_wd);
+
+inline void stop_suspend_watchdog(struct suspend_watchdog *suspend_wd);
+
+inline void suspend_timeout_handler(struct timer_list *t);
+#else
+static inline void init_suspend_watchdog(struct suspend_watchdog *suspend_wd,
+					 void *func)
+{
+}
+static inline void start_suspend_watchdog(struct suspend_watchdog *suspend_wd)
+{
+}
+static inline void stop_suspend_watchdog(struct suspend_watchdog *suspend_wd)
+{
+}
+static inline void suspend_timeout_handler(struct timer_list *t)
+{
+}
+#endif
+
+#endif /* _LINUX_SUSPEND_WATCHDOG_H */
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 9b75f6bfc333..168cb7b3148b 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2276,7 +2276,7 @@ void suspend_console(void)
 
 void resume_console(void)
 {
-	if (!console_suspend_enabled)
+	if ((!console_suspend_enabled) || (!console_suspended))
 		return;
 	down_console_sem();
 	console_suspended = 0;
