[PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition

Vimal Kumar posted 1 patch 2 years ago
There is a newer version of this series
drivers/base/power/wakeup.c | 75 ++++++++++++++++++++++++++++++++++++-
include/linux/suspend.h     |  2 +
kernel/power/suspend.c      |  1 +
3 files changed, 77 insertions(+), 1 deletion(-)
[PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by Vimal Kumar 2 years ago
Sometimes kernel suspend transitions can be aborted unconditionally by
manipulating pm_abort_suspend value using "hard" wakeup triggers or
through "pm_system_wakeup()".

There is no way to trace the source path of module or subsystem which
aborted the suspend transitions. This change will create a list of
wakeup sources aborting suspend in progress through "hard" events as
well as subsytems aborting suspend using "pm_system_wakeup()".

Example: Existing suspend failure logs:
[  349.708359] PM: Some devices failed to suspend, or early wake event detected
[  350.327842] PM: suspend exit

Suspend failure logs with this change:
[  518.761835] PM: Some devices failed to suspend, or early wake event detected
[  519.486939] Abort: ws or subsystem uart_suspend_port aborted suspend
[  519.500594] PM: suspend exit

Here we can clearly identify the module triggerring abort suspend.

Co-developed-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
Signed-off-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
Co-developed-by: Mintu Patel <mintupatel89@gmail.com>
Signed-off-by: Mintu Patel <mintupatel89@gmail.com>
Co-developed-by: Vishal Badole <badolevishal1116@gmail.com>
Signed-off-by: Vishal Badole <badolevishal1116@gmail.com>
Signed-off-by: Vimal Kumar <vimal.kumar32@gmail.com>
---
 drivers/base/power/wakeup.c | 75 ++++++++++++++++++++++++++++++++++++-
 include/linux/suspend.h     |  2 +
 kernel/power/suspend.c      |  1 +
 3 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index a917219feea6..f640034cab6d 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -73,6 +73,13 @@ static struct wakeup_source deleted_ws = {
 
 static DEFINE_IDA(wakeup_ida);
 
+struct pm_abort_suspend_source {
+	struct list_head list;     //linux kernel list implementation
+	char *source_triggering_abort_suspend;
+};
+
+LIST_HEAD(pm_abort_suspend_list);
+
 /**
  * wakeup_source_create - Create a struct wakeup_source object.
  * @name: Name of the new wakeup source.
@@ -575,6 +582,53 @@ static void wakeup_source_activate(struct wakeup_source *ws)
 	trace_wakeup_source_activate(ws->name, cec);
 }
 
+/**
+ * clear_abort_suspend_list: To clear the list containing sources which
+ * aborted suspend transitions.
+ * Functionality: The list will be cleared every time system PM exits as we
+ * can find sources which aborted suspend in the current suspend transisions.
+ */
+void clear_abort_suspend_list(void)
+{
+	struct pm_abort_suspend_source *info, *tmp;
+
+	if (!list_empty(&pm_abort_suspend_list))
+		list_for_each_entry_safe(info, tmp, &pm_abort_suspend_list, list) {
+			list_del(&info->list);
+			kfree(info);
+		}
+}
+EXPORT_SYMBOL_GPL(clear_abort_suspend_list);
+
+/**
+ * pm_add_abort_suspend_source: add sources who aborted system suspend transitions.
+ * @func_name: Name of the WS or subsystem which needs to added in the list
+ */
+void pm_add_abort_suspend_source(const char *source_name)
+{
+	struct pm_abort_suspend_source *info = NULL;
+
+	info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);
+	if (unlikely(!info)) {
+		pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");
+		return;
+	}
+
+	/* Initialize the list within the struct if it's not already initialized */
+	if (list_empty(&info->list))
+		INIT_LIST_HEAD(&info->list);
+
+	info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
+	if (unlikely(!info->source_triggering_abort_suspend)) {
+		pr_err("Failed to get abort_suspend source_name\n");
+		kfree(info);
+		return;
+	}
+
+	list_add_tail(&info->list, &pm_abort_suspend_list);
+}
+EXPORT_SYMBOL_GPL(pm_add_abort_suspend_source);
+
 /**
  * wakeup_source_report_event - Report wakeup event using the given source.
  * @ws: Wakeup source to report the event for.
@@ -590,8 +644,11 @@ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
 	if (!ws->active)
 		wakeup_source_activate(ws);
 
-	if (hard)
+	if (hard) {
+		if (pm_suspend_target_state != PM_SUSPEND_ON)
+			pm_add_abort_suspend_source(ws->name);
 		pm_system_wakeup();
+	}
 }
 
 /**
@@ -893,12 +950,28 @@ bool pm_wakeup_pending(void)
 		pm_print_active_wakeup_sources();
 	}
 
+	if (atomic_read(&pm_abort_suspend) > 0) {
+		if (!list_empty(&pm_abort_suspend_list))
+			list_for_each_entry(info, &pm_abort_suspend_list, list) {
+				log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
+						info->source_triggering_abort_suspend);
+			}
+	}
+
 	return ret || atomic_read(&pm_abort_suspend) > 0;
 }
 EXPORT_SYMBOL_GPL(pm_wakeup_pending);
 
 void pm_system_wakeup(void)
 {
+	char buf[MAX_SUSPEND_ABORT_LEN];
+
+	if (pm_suspend_target_state != PM_SUSPEND_ON) {
+		sprintf(buf, "%ps", __builtin_return_address(0));
+		if (strcmp(buf, "pm_wakeup_ws_event"))
+			pm_add_abort_suspend_source(buf);
+	}
+
 	atomic_inc(&pm_abort_suspend);
 	s2idle_wake();
 }
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index ef503088942d..803071f664e7 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -528,6 +528,7 @@ extern void pm_print_active_wakeup_sources(void);
 
 extern unsigned int lock_system_sleep(void);
 extern void unlock_system_sleep(unsigned int);
+extern void clear_abort_suspend_list(void);
 
 #else /* !CONFIG_PM_SLEEP */
 
@@ -556,6 +557,7 @@ static inline void pm_system_irq_wakeup(unsigned int irq_number) {}
 
 static inline unsigned int lock_system_sleep(void) { return 0; }
 static inline void unlock_system_sleep(unsigned int flags) {}
+static inline void clear_abort_suspend_list(void) {}
 
 #endif /* !CONFIG_PM_SLEEP */
 
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index fa3bf161d13f..64a99f26bd51 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -623,6 +623,7 @@ int pm_suspend(suspend_state_t state)
 	} else {
 		suspend_stats.success++;
 	}
+	clear_abort_suspend_list();
 	pr_info("suspend exit\n");
 	return error;
 }
-- 
2.25.1
Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by kernel test robot 2 years ago
Hi Vimal,

kernel test robot noticed the following build errors:

[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/acpi-bus linus/master rafael-pm/devprop v6.7-rc4 next-20231208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vimal-Kumar/PM-sleep-Mechanism-to-find-source-aborting-kernel-suspend-transition/20231209-161237
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20231209081056.1497-1-vimal.kumar32%40gmail.com
patch subject: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
config: arm-lpc32xx_defconfig (https://download.01.org/0day-ci/archive/20231210/202312100547.4pJ5OE1O-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231210/202312100547.4pJ5OE1O-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312100547.4pJ5OE1O-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/base/power/wakeup.c:607:6: warning: no previous prototype for function 'pm_add_abort_suspend_source' [-Wmissing-prototypes]
     607 | void pm_add_abort_suspend_source(const char *source_name)
         |      ^
   drivers/base/power/wakeup.c:607:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     607 | void pm_add_abort_suspend_source(const char *source_name)
         | ^
         | static 
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'; did you mean 'int'?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
         |                                             int
   include/linux/list.h:780:29: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                                    ^
   include/linux/list.h:645:14: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |                     ^
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |                      ^
   include/linux/container_of.h:19:26: note: expanded from macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^
>> drivers/base/power/wakeup.c:955:4: error: reference to overloaded function could not be resolved; did you mean to call it?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:780:13: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:645:13: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
   include/linux/container_of.h:19:26: note: expanded from macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'; did you mean 'int'?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
         |                                             int
   include/linux/list.h:780:29: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                                    ^
   include/linux/list.h:645:14: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |                     ^
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |                      ^
   note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:376:63: note: expanded from macro '__same_type'
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                                                               ^
   include/linux/build_bug.h:77:50: note: expanded from macro 'static_assert'
      77 | #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
         |                                                  ^
   include/linux/build_bug.h:78:56: note: expanded from macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^
>> drivers/base/power/wakeup.c:955:4: error: reference to overloaded function could not be resolved; did you mean to call it?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:780:13: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:645:13: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
   note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:376:63: note: expanded from macro '__same_type'
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                                                               ^
   include/linux/build_bug.h:77:50: note: expanded from macro 'static_assert'
      77 | #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
         |                                  ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: expanded from macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'; did you mean 'int'?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
         |                                             int
   include/linux/list.h:780:29: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                                    ^
   include/linux/list.h:645:14: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |                     ^
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |                      ^
   note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:376:63: note: expanded from macro '__same_type'
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                                                               ^
   include/linux/build_bug.h:77:50: note: expanded from macro 'static_assert'
      77 | #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
         |                                                  ^
   include/linux/build_bug.h:78:56: note: expanded from macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^
>> drivers/base/power/wakeup.c:955:4: error: reference to overloaded function could not be resolved; did you mean to call it?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:780:13: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:645:13: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:601:15: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |         ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
   note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:376:63: note: expanded from macro '__same_type'
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                                                               ^
   include/linux/build_bug.h:77:50: note: expanded from macro 'static_assert'
      77 | #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
         |                                  ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: expanded from macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'; did you mean 'int'?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
         |                                             int
   include/linux/list.h:780:29: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                                    ^
   include/linux/list.h:645:42: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |                                                 ^
   include/linux/list.h:601:20: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |                           ^
   include/linux/container_of.h:23:4: note: expanded from macro 'container_of'
      23 |         ((type *)(__mptr - offsetof(type, member))); })
         |           ^
>> drivers/base/power/wakeup.c:955:4: error: reference to overloaded function could not be resolved; did you mean to call it?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:780:13: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:645:41: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
   include/linux/list.h:601:20: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |         ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
   include/linux/container_of.h:23:4: note: expanded from macro 'container_of'
      23 |         ((type *)(__mptr - offsetof(type, member))); })
         |           ^~~~
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'; did you mean 'int'?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
         |                                             int
   include/linux/list.h:780:29: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                                    ^
   include/linux/list.h:645:42: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |                                                 ^
   include/linux/list.h:601:20: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |                           ^
   include/linux/container_of.h:23:30: note: expanded from macro 'container_of'
      23 |         ((type *)(__mptr - offsetof(type, member))); })
         |                                     ^
   include/linux/stddef.h:16:51: note: expanded from macro 'offsetof'
      16 | #define offsetof(TYPE, MEMBER)  __builtin_offsetof(TYPE, MEMBER)
         |                                                    ^
>> drivers/base/power/wakeup.c:955:4: error: reference to overloaded function could not be resolved; did you mean to call it?
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:780:13: note: expanded from macro 'list_for_each_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/list.h:645:41: note: expanded from macro 'list_next_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
   include/linux/list.h:601:20: note: expanded from macro 'list_entry'
     601 |         container_of(ptr, type, member)
         |         ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
   include/linux/container_of.h:23:30: note: expanded from macro 'container_of'
      23 |         ((type *)(__mptr - offsetof(type, member))); })
         |                            ~~~~~~~~~^~~~~~~~~~~~~
   include/linux/stddef.h:16:51: note: expanded from macro 'offsetof'
      16 | #define offsetof(TYPE, MEMBER)  __builtin_offsetof(TYPE, MEMBER)
         |                                                    ^~~~
>> drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^
>> drivers/base/power/wakeup.c:956:5: error: call to undeclared function 'log_suspend_abort_reason'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     956 |                                 log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
         |                                 ^
   drivers/base/power/wakeup.c:957:7: error: use of undeclared identifier 'info'
     957 |                                                 info->source_triggering_abort_suspend);
         |                                                 ^
>> drivers/base/power/wakeup.c:967:11: error: use of undeclared identifier 'MAX_SUSPEND_ABORT_LEN'
     967 |         char buf[MAX_SUSPEND_ABORT_LEN];
         |                  ^
   1 warning and 18 errors generated.


vim +/info +955 drivers/base/power/wakeup.c

   924	
   925	/**
   926	 * pm_wakeup_pending - Check if power transition in progress should be aborted.
   927	 *
   928	 * Compare the current number of registered wakeup events with its preserved
   929	 * value from the past and return true if new wakeup events have been registered
   930	 * since the old value was stored.  Also return true if the current number of
   931	 * wakeup events being processed is different from zero.
   932	 */
   933	bool pm_wakeup_pending(void)
   934	{
   935		unsigned long flags;
   936		bool ret = false;
   937	
   938		raw_spin_lock_irqsave(&events_lock, flags);
   939		if (events_check_enabled) {
   940			unsigned int cnt, inpr;
   941	
   942			split_counters(&cnt, &inpr);
   943			ret = (cnt != saved_count || inpr > 0);
   944			events_check_enabled = !ret;
   945		}
   946		raw_spin_unlock_irqrestore(&events_lock, flags);
   947	
   948		if (ret) {
   949			pm_pr_dbg("Wakeup pending, aborting suspend\n");
   950			pm_print_active_wakeup_sources();
   951		}
   952	
   953		if (atomic_read(&pm_abort_suspend) > 0) {
   954			if (!list_empty(&pm_abort_suspend_list))
 > 955				list_for_each_entry(info, &pm_abort_suspend_list, list) {
 > 956					log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
   957							info->source_triggering_abort_suspend);
   958				}
   959		}
   960	
   961		return ret || atomic_read(&pm_abort_suspend) > 0;
   962	}
   963	EXPORT_SYMBOL_GPL(pm_wakeup_pending);
   964	
   965	void pm_system_wakeup(void)
   966	{
 > 967		char buf[MAX_SUSPEND_ABORT_LEN];
   968	
   969		if (pm_suspend_target_state != PM_SUSPEND_ON) {
   970			sprintf(buf, "%ps", __builtin_return_address(0));
   971			if (strcmp(buf, "pm_wakeup_ws_event"))
   972				pm_add_abort_suspend_source(buf);
   973		}
   974	
   975		atomic_inc(&pm_abort_suspend);
   976		s2idle_wake();
   977	}
   978	EXPORT_SYMBOL_GPL(pm_system_wakeup);
   979	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by kernel test robot 2 years ago
Hi Vimal,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/acpi-bus linus/master rafael-pm/devprop v6.7-rc4 next-20231208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vimal-Kumar/PM-sleep-Mechanism-to-find-source-aborting-kernel-suspend-transition/20231209-161237
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20231209081056.1497-1-vimal.kumar32%40gmail.com
patch subject: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
config: x86_64-defconfig (https://download.01.org/0day-ci/archive/20231210/202312100146.Nv4INLR5-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231210/202312100146.Nv4INLR5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312100146.Nv4INLR5-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/base/power/wakeup.c:607:6: warning: no previous prototype for 'pm_add_abort_suspend_source' [-Wmissing-prototypes]
     607 | void pm_add_abort_suspend_source(const char *source_name)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/rculist.h:10,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from include/linux/ratelimit.h:6,
                    from include/linux/dev_printk.h:16,
                    from include/linux/device.h:15,
                    from drivers/base/power/wakeup.c:9:
   drivers/base/power/wakeup.c: In function 'pm_wakeup_pending':
   drivers/base/power/wakeup.c:955:45: error: 'info' undeclared (first use in this function)
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
   include/linux/list.h:778:14: note: in definition of macro 'list_for_each_entry'
     778 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |              ^~~
   drivers/base/power/wakeup.c:955:45: note: each undeclared identifier is reported only once for each function it appears in
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^~~~
   include/linux/list.h:778:14: note: in definition of macro 'list_for_each_entry'
     778 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |              ^~~
   In file included from include/linux/bits.h:21,
                    from include/linux/ratelimit_types.h:5,
                    from include/linux/ratelimit.h:5,
                    from include/linux/dev_printk.h:16,
                    from include/linux/device.h:15,
                    from drivers/base/power/wakeup.c:9:
   include/linux/compiler_types.h:376:27: error: expression in static assertion is not an integer
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:601:9: note: in expansion of macro 'container_of'
     601 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:612:9: note: in expansion of macro 'list_entry'
     612 |         list_entry((ptr)->next, type, member)
         |         ^~~~~~~~~~
   include/linux/list.h:778:20: note: in expansion of macro 'list_first_entry'
     778 |         for (pos = list_first_entry(head, typeof(*pos), member);        \
         |                    ^~~~~~~~~~~~~~~~
   drivers/base/power/wakeup.c:955:25: note: in expansion of macro 'list_for_each_entry'
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:376:27: error: expression in static assertion is not an integer
     376 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/list.h:601:9: note: in expansion of macro 'container_of'
     601 |         container_of(ptr, type, member)
         |         ^~~~~~~~~~~~
   include/linux/list.h:645:9: note: in expansion of macro 'list_entry'
     645 |         list_entry((pos)->member.next, typeof(*(pos)), member)
         |         ^~~~~~~~~~
   include/linux/list.h:780:20: note: in expansion of macro 'list_next_entry'
     780 |              pos = list_next_entry(pos, member))
         |                    ^~~~~~~~~~~~~~~
   drivers/base/power/wakeup.c:955:25: note: in expansion of macro 'list_for_each_entry'
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                         ^~~~~~~~~~~~~~~~~~~
   drivers/base/power/wakeup.c:956:33: error: implicit declaration of function 'log_suspend_abort_reason' [-Werror=implicit-function-declaration]
     956 |                                 log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/base/power/wakeup.c: In function 'pm_system_wakeup':
   drivers/base/power/wakeup.c:967:18: error: 'MAX_SUSPEND_ABORT_LEN' undeclared (first use in this function)
     967 |         char buf[MAX_SUSPEND_ABORT_LEN];
         |                  ^~~~~~~~~~~~~~~~~~~~~
>> drivers/base/power/wakeup.c:967:14: warning: unused variable 'buf' [-Wunused-variable]
     967 |         char buf[MAX_SUSPEND_ABORT_LEN];
         |              ^~~
   cc1: some warnings being treated as errors


vim +/pm_add_abort_suspend_source +607 drivers/base/power/wakeup.c

   602	
   603	/**
   604	 * pm_add_abort_suspend_source: add sources who aborted system suspend transitions.
   605	 * @func_name: Name of the WS or subsystem which needs to added in the list
   606	 */
 > 607	void pm_add_abort_suspend_source(const char *source_name)
   608	{
   609		struct pm_abort_suspend_source *info = NULL;
   610	
   611		info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);
   612		if (unlikely(!info)) {
   613			pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");
   614			return;
   615		}
   616	
   617		/* Initialize the list within the struct if it's not already initialized */
   618		if (list_empty(&info->list))
   619			INIT_LIST_HEAD(&info->list);
   620	
   621		info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
   622		if (unlikely(!info->source_triggering_abort_suspend)) {
   623			pr_err("Failed to get abort_suspend source_name\n");
   624			kfree(info);
   625			return;
   626		}
   627	
   628		list_add_tail(&info->list, &pm_abort_suspend_list);
   629	}
   630	EXPORT_SYMBOL_GPL(pm_add_abort_suspend_source);
   631	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by kernel test robot 2 years ago
Hi Vimal,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/acpi-bus linus/master rafael-pm/devprop v6.7-rc4 next-20231208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vimal-Kumar/PM-sleep-Mechanism-to-find-source-aborting-kernel-suspend-transition/20231209-161237
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20231209081056.1497-1-vimal.kumar32%40gmail.com
patch subject: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
config: powerpc-randconfig-r071-20231209 (https://download.01.org/0day-ci/archive/20231210/202312100106.hPaGgy7C-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231210/202312100106.hPaGgy7C-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312100106.hPaGgy7C-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/base/power/wakeup.c:607:6: warning: no previous prototype for function 'pm_add_abort_suspend_source' [-Wmissing-prototypes]
     607 | void pm_add_abort_suspend_source(const char *source_name)
         |      ^
   drivers/base/power/wakeup.c:607:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     607 | void pm_add_abort_suspend_source(const char *source_name)
         | ^
         | static 
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
     955 |                         list_for_each_entry(info, &pm_abort_suspend_list, list) {
         |                                             ^
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:955:24: error: use of undeclared identifier 'info'
   drivers/base/power/wakeup.c:956:5: error: call to undeclared function 'log_suspend_abort_reason'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     956 |                                 log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
         |                                 ^
   drivers/base/power/wakeup.c:957:7: error: use of undeclared identifier 'info'
     957 |                                                 info->source_triggering_abort_suspend);
         |                                                 ^
   drivers/base/power/wakeup.c:967:11: error: use of undeclared identifier 'MAX_SUSPEND_ABORT_LEN'
     967 |         char buf[MAX_SUSPEND_ABORT_LEN];
         |                  ^
   1 warning and 12 errors generated.


vim +/pm_add_abort_suspend_source +607 drivers/base/power/wakeup.c

   602	
   603	/**
   604	 * pm_add_abort_suspend_source: add sources who aborted system suspend transitions.
   605	 * @func_name: Name of the WS or subsystem which needs to added in the list
   606	 */
 > 607	void pm_add_abort_suspend_source(const char *source_name)
   608	{
   609		struct pm_abort_suspend_source *info = NULL;
   610	
   611		info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);
   612		if (unlikely(!info)) {
   613			pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");
   614			return;
   615		}
   616	
   617		/* Initialize the list within the struct if it's not already initialized */
   618		if (list_empty(&info->list))
   619			INIT_LIST_HEAD(&info->list);
   620	
   621		info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
   622		if (unlikely(!info->source_triggering_abort_suspend)) {
   623			pr_err("Failed to get abort_suspend source_name\n");
   624			kfree(info);
   625			return;
   626		}
   627	
   628		list_add_tail(&info->list, &pm_abort_suspend_list);
   629	}
   630	EXPORT_SYMBOL_GPL(pm_add_abort_suspend_source);
   631	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by Christophe JAILLET 2 years ago
Le 09/12/2023 à 09:10, Vimal Kumar a écrit :
> Sometimes kernel suspend transitions can be aborted unconditionally by
> manipulating pm_abort_suspend value using "hard" wakeup triggers or
> through "pm_system_wakeup()".
> 
> There is no way to trace the source path of module or subsystem which
> aborted the suspend transitions. This change will create a list of
> wakeup sources aborting suspend in progress through "hard" events as
> well as subsytems aborting suspend using "pm_system_wakeup()".
> 
> Example: Existing suspend failure logs:
> [  349.708359] PM: Some devices failed to suspend, or early wake event detected
> [  350.327842] PM: suspend exit
> 
> Suspend failure logs with this change:
> [  518.761835] PM: Some devices failed to suspend, or early wake event detected
> [  519.486939] Abort: ws or subsystem uart_suspend_port aborted suspend
> [  519.500594] PM: suspend exit
> 
> Here we can clearly identify the module triggerring abort suspend.
> 
> Co-developed-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> Signed-off-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> Co-developed-by: Mintu Patel <mintupatel89@gmail.com>
> Signed-off-by: Mintu Patel <mintupatel89@gmail.com>
> Co-developed-by: Vishal Badole <badolevishal1116@gmail.com>
> Signed-off-by: Vishal Badole <badolevishal1116@gmail.com>
> Signed-off-by: Vimal Kumar <vimal.kumar32@gmail.com>
> ---

...

> +void pm_add_abort_suspend_source(const char *source_name)
> +{
> +	struct pm_abort_suspend_source *info = NULL;

No need to init.

> +
> +	info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);

Could be sizeof(*info).

> +	if (unlikely(!info)) {
> +		pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");

No need to log a message if kmalloc() fails, a trace is already logged.

> +		return;
> +	}
> +
> +	/* Initialize the list within the struct if it's not already initialized */
> +	if (list_empty(&info->list))
> +		INIT_LIST_HEAD(&info->list);
> +
> +	info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
> +	if (unlikely(!info->source_triggering_abort_suspend)) {
> +		pr_err("Failed to get abort_suspend source_name\n");

Same here.

> +		kfree(info);
> +		return;
> +	}
> +
> +	list_add_tail(&info->list, &pm_abort_suspend_list);

...

CJ

Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by Greg Kroah-Hartman 2 years ago
On Sat, Dec 09, 2023 at 01:40:54PM +0530, Vimal Kumar wrote:
> Sometimes kernel suspend transitions can be aborted unconditionally by
> manipulating pm_abort_suspend value using "hard" wakeup triggers or
> through "pm_system_wakeup()".
> 
> There is no way to trace the source path of module or subsystem which
> aborted the suspend transitions. This change will create a list of
> wakeup sources aborting suspend in progress through "hard" events as
> well as subsytems aborting suspend using "pm_system_wakeup()".
> 
> Example: Existing suspend failure logs:
> [  349.708359] PM: Some devices failed to suspend, or early wake event detected
> [  350.327842] PM: suspend exit
> 
> Suspend failure logs with this change:
> [  518.761835] PM: Some devices failed to suspend, or early wake event detected
> [  519.486939] Abort: ws or subsystem uart_suspend_port aborted suspend
> [  519.500594] PM: suspend exit
> 
> Here we can clearly identify the module triggerring abort suspend.
> 
> Co-developed-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> Signed-off-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> Co-developed-by: Mintu Patel <mintupatel89@gmail.com>
> Signed-off-by: Mintu Patel <mintupatel89@gmail.com>
> Co-developed-by: Vishal Badole <badolevishal1116@gmail.com>
> Signed-off-by: Vishal Badole <badolevishal1116@gmail.com>
> Signed-off-by: Vimal Kumar <vimal.kumar32@gmail.com>
> ---
>  drivers/base/power/wakeup.c | 75 ++++++++++++++++++++++++++++++++++++-
>  include/linux/suspend.h     |  2 +
>  kernel/power/suspend.c      |  1 +
>  3 files changed, 77 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> index a917219feea6..f640034cab6d 100644
> --- a/drivers/base/power/wakeup.c
> +++ b/drivers/base/power/wakeup.c
> @@ -73,6 +73,13 @@ static struct wakeup_source deleted_ws = {
>  
>  static DEFINE_IDA(wakeup_ida);
>  
> +struct pm_abort_suspend_source {
> +	struct list_head list;     //linux kernel list implementation

Nit, we know this is a list implementation, no need to comment that.

Also did you run checkpatch on this?  You need a ' ' after "//" to be
correct.

> +	char *source_triggering_abort_suspend;
> +};
> +
> +LIST_HEAD(pm_abort_suspend_list);

No blank line needed, and also, shouldn't this be static?

> +
>  /**
>   * wakeup_source_create - Create a struct wakeup_source object.
>   * @name: Name of the new wakeup source.
> @@ -575,6 +582,53 @@ static void wakeup_source_activate(struct wakeup_source *ws)
>  	trace_wakeup_source_activate(ws->name, cec);
>  }
>  
> +/**
> + * clear_abort_suspend_list: To clear the list containing sources which
> + * aborted suspend transitions.
> + * Functionality: The list will be cleared every time system PM exits as we
> + * can find sources which aborted suspend in the current suspend transisions.

This isn't the correct way to write kernel doc formats, please see the
documentation for how to do it.

> + */
> +void clear_abort_suspend_list(void)
> +{
> +	struct pm_abort_suspend_source *info, *tmp;
> +
> +	if (!list_empty(&pm_abort_suspend_list))

Why check this, doesn't the list loop work properly here?

> +		list_for_each_entry_safe(info, tmp, &pm_abort_suspend_list, list) {
> +			list_del(&info->list);
> +			kfree(info);
> +		}

No locking at all for this list?

> +}
> +EXPORT_SYMBOL_GPL(clear_abort_suspend_list);

Global functions should be "subsystem_action", not "action_something"
like you did here.  Otherwise this gets really messy very quickly.

> +
> +/**
> + * pm_add_abort_suspend_source: add sources who aborted system suspend transitions.
> + * @func_name: Name of the WS or subsystem which needs to added in the list
> + */
> +void pm_add_abort_suspend_source(const char *source_name)
> +{
> +	struct pm_abort_suspend_source *info = NULL;
> +
> +	info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);
> +	if (unlikely(!info)) {

Only ever use unlikely/likely if you have documented proof that the code
is faster (i.e. you can measure it.)  For normal calls like this, the
compiler and the processor knows better than you, so no need to do
anything.

> +		pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");
> +		return;
> +	}
> +
> +	/* Initialize the list within the struct if it's not already initialized */
> +	if (list_empty(&info->list))
> +		INIT_LIST_HEAD(&info->list);
> +
> +	info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
> +	if (unlikely(!info->source_triggering_abort_suspend)) {

Again, don't use likely/unlikely

> +		pr_err("Failed to get abort_suspend source_name\n");
> +		kfree(info);
> +		return;
> +	}
> +
> +	list_add_tail(&info->list, &pm_abort_suspend_list);
> +}
> +EXPORT_SYMBOL_GPL(pm_add_abort_suspend_source);
> +
>  /**
>   * wakeup_source_report_event - Report wakeup event using the given source.
>   * @ws: Wakeup source to report the event for.
> @@ -590,8 +644,11 @@ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
>  	if (!ws->active)
>  		wakeup_source_activate(ws);
>  
> -	if (hard)
> +	if (hard) {
> +		if (pm_suspend_target_state != PM_SUSPEND_ON)
> +			pm_add_abort_suspend_source(ws->name);
>  		pm_system_wakeup();
> +	}
>  }
>  
>  /**
> @@ -893,12 +950,28 @@ bool pm_wakeup_pending(void)
>  		pm_print_active_wakeup_sources();
>  	}
>  
> +	if (atomic_read(&pm_abort_suspend) > 0) {
> +		if (!list_empty(&pm_abort_suspend_list))
> +			list_for_each_entry(info, &pm_abort_suspend_list, list) {
> +				log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",

What is "ws"?

And again, no locking is just not going to work.

But step back, are you _sure_ you really need this information?  Who is
going to use it?  Where are they going to use it?  And when are they
going to use it?

thanks,

greg k-h
Re: [PATCH] PM / sleep: Mechanism to find source aborting kernel suspend transition
Posted by Vimal Kumar 2 years ago
Hi Greg k-h,

On Sat, Dec 09, 2023 at 10:21:12AM +0100, Greg Kroah-Hartman wrote:
> On Sat, Dec 09, 2023 at 01:40:54PM +0530, Vimal Kumar wrote:
> > Sometimes kernel suspend transitions can be aborted unconditionally by
> > manipulating pm_abort_suspend value using "hard" wakeup triggers or
> > through "pm_system_wakeup()".
> > 
> > There is no way to trace the source path of module or subsystem which
> > aborted the suspend transitions. This change will create a list of
> > wakeup sources aborting suspend in progress through "hard" events as
> > well as subsytems aborting suspend using "pm_system_wakeup()".
> > 
> > Example: Existing suspend failure logs:
> > [  349.708359] PM: Some devices failed to suspend, or early wake event detected
> > [  350.327842] PM: suspend exit
> > 
> > Suspend failure logs with this change:
> > [  518.761835] PM: Some devices failed to suspend, or early wake event detected
> > [  519.486939] Abort: ws or subsystem uart_suspend_port aborted suspend
> > [  519.500594] PM: suspend exit
> > 
> > Here we can clearly identify the module triggerring abort suspend.
> > 
> > Co-developed-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> > Signed-off-by: Chinmoy Ghosh <chinmoyghosh2001@gmail.com>
> > Co-developed-by: Mintu Patel <mintupatel89@gmail.com>
> > Signed-off-by: Mintu Patel <mintupatel89@gmail.com>
> > Co-developed-by: Vishal Badole <badolevishal1116@gmail.com>
> > Signed-off-by: Vishal Badole <badolevishal1116@gmail.com>
> > Signed-off-by: Vimal Kumar <vimal.kumar32@gmail.com>
> > ---
> >  drivers/base/power/wakeup.c | 75 ++++++++++++++++++++++++++++++++++++-
> >  include/linux/suspend.h     |  2 +
> >  kernel/power/suspend.c      |  1 +
> >  3 files changed, 77 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> > index a917219feea6..f640034cab6d 100644
> > --- a/drivers/base/power/wakeup.c
> > +++ b/drivers/base/power/wakeup.c
> > @@ -73,6 +73,13 @@ static struct wakeup_source deleted_ws = {
> >  
> >  static DEFINE_IDA(wakeup_ida);
> >  
> > +struct pm_abort_suspend_source {
> > +	struct list_head list;     //linux kernel list implementation
> 
> Nit, we know this is a list implementation, no need to comment that.
> 
> Also did you run checkpatch on this?  You need a ' ' after "//" to be
> correct.
> 
I do run checkpatch but it was not highlighted. I will remove the comment

> > +	char *source_triggering_abort_suspend;
> > +};
> > +
> > +LIST_HEAD(pm_abort_suspend_list);
> 
> No blank line needed, and also, shouldn't this be static?
>
I missed it, this should be static.

> > +
> >  /**
> >   * wakeup_source_create - Create a struct wakeup_source object.
> >   * @name: Name of the new wakeup source.
> > @@ -575,6 +582,53 @@ static void wakeup_source_activate(struct wakeup_source *ws)
> >  	trace_wakeup_source_activate(ws->name, cec);
> >  }
> >  
> > +/**
> > + * clear_abort_suspend_list: To clear the list containing sources which
> > + * aborted suspend transitions.
> > + * Functionality: The list will be cleared every time system PM exits as we
> > + * can find sources which aborted suspend in the current suspend transisions.
> 
> This isn't the correct way to write kernel doc formats, please see the
> documentation for how to do it.
>
I will correct it in the next version.

> > + */
> > +void clear_abort_suspend_list(void)
> > +{
> > +	struct pm_abort_suspend_source *info, *tmp;
> > +
> > +	if (!list_empty(&pm_abort_suspend_list))
> 
> Why check this, doesn't the list loop work properly here?
> 
The list worked properly, just added for extra check. It will be removed in the next version.

> > +		list_for_each_entry_safe(info, tmp, &pm_abort_suspend_list, list) {
> > +			list_del(&info->list);
> > +			kfree(info);
> > +		}
> 
> No locking at all for this list?
> 
I will gaurd it in the next version.

> > +}
> > +EXPORT_SYMBOL_GPL(clear_abort_suspend_list);
> 
> Global functions should be "subsystem_action", not "action_something"
> like you did here.  Otherwise this gets really messy very quickly.
> 
I will take care of this in future.

> > +
> > +/**
> > + * pm_add_abort_suspend_source: add sources who aborted system suspend transitions.
> > + * @func_name: Name of the WS or subsystem which needs to added in the list
> > + */
> > +void pm_add_abort_suspend_source(const char *source_name)
> > +{
> > +	struct pm_abort_suspend_source *info = NULL;
> > +
> > +	info = kmalloc(sizeof(struct pm_abort_suspend_source), GFP_KERNEL);
> > +	if (unlikely(!info)) {
> 
> Only ever use unlikely/likely if you have documented proof that the code
> is faster (i.e. you can measure it.)  For normal calls like this, the
> compiler and the processor knows better than you, so no need to do
> anything.
> 
I will remove it in the next version.

> > +		pr_err("Failed to alloc memory for pm_abort_suspend_source info\n");
> > +		return;
> > +	}
> > +
> > +	/* Initialize the list within the struct if it's not already initialized */
> > +	if (list_empty(&info->list))
> > +		INIT_LIST_HEAD(&info->list);
> > +
> > +	info->source_triggering_abort_suspend = kstrdup(source_name, GFP_KERNEL);
> > +	if (unlikely(!info->source_triggering_abort_suspend)) {
> 
> Again, don't use likely/unlikely
> 
It will be fixed in the next version.

> > +		pr_err("Failed to get abort_suspend source_name\n");
> > +		kfree(info);
> > +		return;
> > +	}
> > +
> > +	list_add_tail(&info->list, &pm_abort_suspend_list);
> > +}
> > +EXPORT_SYMBOL_GPL(pm_add_abort_suspend_source);
> > +
> >  /**
> >   * wakeup_source_report_event - Report wakeup event using the given source.
> >   * @ws: Wakeup source to report the event for.
> > @@ -590,8 +644,11 @@ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
> >  	if (!ws->active)
> >  		wakeup_source_activate(ws);
> >  
> > -	if (hard)
> > +	if (hard) {
> > +		if (pm_suspend_target_state != PM_SUSPEND_ON)
> > +			pm_add_abort_suspend_source(ws->name);
> >  		pm_system_wakeup();
> > +	}
> >  }
> >  
> >  /**
> > @@ -893,12 +950,28 @@ bool pm_wakeup_pending(void)
> >  		pm_print_active_wakeup_sources();
> >  	}
> >  
> > +	if (atomic_read(&pm_abort_suspend) > 0) {
> > +		if (!list_empty(&pm_abort_suspend_list))
> > +			list_for_each_entry(info, &pm_abort_suspend_list, list) {
> > +				log_suspend_abort_reason("ws or subsystem %s aborted suspend\n",
> 
> What is "ws"?
> 
This is for "wakeup_source", we will add it instead of "ws".

> And again, no locking is just not going to work.
> 
I will add lock and gaurd the list in the next version.

> But step back, are you _sure_ you really need this information?  Who is
> going to use it?  Where are they going to use it?  And when are they
> going to use it?
> 
> thanks,
> 
> greg k-h


Suspend-to-RAM and Suspend-to-Idle are widely used features. Sometimes, we have encountered
suspend failures, but the cause of these issues is unknown.

This occurred whenever the suspend was unconditionally aborted using the functionality below:
- Suspend can be aborted via "hard" wakeup triggers using either "pm_wakeup_ws_event", or "pm_wakeup_dev_event".
- Any module can abort the suspend directly  using "pm_system_wakeup"

This change can give developers an upper hand by tracing any such aborted suspend calls and
pointing them in the right direction to debug further.


Warm Regards,
Vimal Kumar