PM: Add sysfs attr for rechecking dev hash from PM trace

If the device which fails to resume is part of a loadable kernel module
it won't be checked at startup against the magic number stored in the
RTC.

Add a read-only sysfs attribute /sys/power/pm_trace_dev_match which
contains a list of newline separated devices (usually just the one)
which currently match the last magic number. This allows the device
which is failing to resume to be found after the modules are loaded
again.

Signed-off-by: James Hogan <james@albanarts.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

authored by

James Hogan and committed by
Rafael J. Wysocki
d33ac60b 2ac21c6b

+87
+29
Documentation/ABI/testing/sysfs-power
··· 99 100 dmesg -s 1000000 | grep 'hash matches' 101 102 CAUTION: Using it will cause your machine's real-time (CMOS) 103 clock to be set to a random invalid time after a resume. 104 105 What: /sys/power/pm_async 106 Date: January 2009
··· 99 100 dmesg -s 1000000 | grep 'hash matches' 101 102 + If you do not get any matches (or they appear to be false 103 + positives), it is possible that the last PM event point 104 + referred to a device created by a loadable kernel module. In 105 + this case cat /sys/power/pm_trace_dev_match (see below) after 106 + your system is started up and the kernel modules are loaded. 107 + 108 CAUTION: Using it will cause your machine's real-time (CMOS) 109 clock to be set to a random invalid time after a resume. 110 + 111 + What; /sys/power/pm_trace_dev_match 112 + Date: October 2010 113 + Contact: James Hogan <james@albanarts.com> 114 + Description: 115 + The /sys/power/pm_trace_dev_match file contains the name of the 116 + device associated with the last PM event point saved in the RTC 117 + across reboots when pm_trace has been used. More precisely it 118 + contains the list of current devices (including those 119 + registered by loadable kernel modules since boot) which match 120 + the device hash in the RTC at boot, with a newline after each 121 + one. 122 + 123 + The advantage of this file over the hash matches printed to the 124 + kernel log (see /sys/power/pm_trace), is that it includes 125 + devices created after boot by loadable kernel modules. 126 + 127 + Due to the small hash size necessary to fit in the RTC, it is 128 + possible that more than one device matches the hash, in which 129 + case further investigation is required to determine which 130 + device is causing the problem. Note that genuine RTC clock 131 + values (such as when pm_trace has not been used), can still 132 + match a device and output it's name here. 133 134 What: /sys/power/pm_async 135 Date: January 2009
+7
Documentation/power/s2ram.txt
··· 49 device (lspci and /sys/devices/pci* is your friend), and see if you can 50 fix it, disable it, or trace into its resume function. 51 52 For example, the above happens to be the VGA device on my EVO, which I 53 used to run with "radeonfb" (it's an ATI Radeon mobility). It turns out 54 that "radeonfb" simply cannot resume that device - it tries to set the
··· 49 device (lspci and /sys/devices/pci* is your friend), and see if you can 50 fix it, disable it, or trace into its resume function. 51 52 + If no device matches the hash (or any matches appear to be false positives), 53 + the culprit may be a device from a loadable kernel module that is not loaded 54 + until after the hash is checked. You can check the hash against the current 55 + devices again after more modules are loaded using sysfs: 56 + 57 + cat /sys/power/pm_trace_dev_match 58 + 59 For example, the above happens to be the VGA device on my EVO, which I 60 used to run with "radeonfb" (it's an ATI Radeon mobility). It turns out 61 that "radeonfb" simply cannot resume that device - it tries to set the
+31
drivers/base/power/trace.c
··· 207 208 static unsigned int hash_value_early_read; 209 210 static int early_resume_init(void) 211 { 212 hash_value_early_read = read_magic_time();
··· 207 208 static unsigned int hash_value_early_read; 209 210 + int show_trace_dev_match(char *buf, size_t size) 211 + { 212 + unsigned int value = hash_value_early_read / (USERHASH * FILEHASH); 213 + int ret = 0; 214 + struct list_head *entry; 215 + 216 + /* 217 + * It's possible that multiple devices will match the hash and we can't 218 + * tell which is the culprit, so it's best to output them all. 219 + */ 220 + device_pm_lock(); 221 + entry = dpm_list.prev; 222 + while (size && entry != &dpm_list) { 223 + struct device *dev = to_device(entry); 224 + unsigned int hash = hash_string(DEVSEED, dev_name(dev), 225 + DEVHASH); 226 + if (hash == value) { 227 + int len = snprintf(buf, size, "%s\n", 228 + dev_driver_string(dev)); 229 + if (len > size) 230 + len = size; 231 + buf += len; 232 + ret += len; 233 + size -= len; 234 + } 235 + entry = entry->prev; 236 + } 237 + device_pm_unlock(); 238 + return ret; 239 + } 240 + 241 static int early_resume_init(void) 242 { 243 hash_value_early_read = read_magic_time();
+2
include/linux/resume-trace.h
··· 3 4 #ifdef CONFIG_PM_TRACE 5 #include <asm/resume-trace.h> 6 7 extern int pm_trace_enabled; 8 ··· 15 struct device; 16 extern void set_trace_device(struct device *); 17 extern void generate_resume_trace(const void *tracedata, unsigned int user); 18 19 #define TRACE_DEVICE(dev) do { \ 20 if (pm_trace_enabled) \
··· 3 4 #ifdef CONFIG_PM_TRACE 5 #include <asm/resume-trace.h> 6 + #include <linux/types.h> 7 8 extern int pm_trace_enabled; 9 ··· 14 struct device; 15 extern void set_trace_device(struct device *); 16 extern void generate_resume_trace(const void *tracedata, unsigned int user); 17 + extern int show_trace_dev_match(char *buf, size_t size); 18 19 #define TRACE_DEVICE(dev) do { \ 20 if (pm_trace_enabled) \
+18
kernel/power/main.c
··· 281 } 282 283 power_attr(pm_trace); 284 #endif /* CONFIG_PM_TRACE */ 285 286 static struct attribute * g[] = { 287 &state_attr.attr, 288 #ifdef CONFIG_PM_TRACE 289 &pm_trace_attr.attr, 290 #endif 291 #ifdef CONFIG_PM_SLEEP 292 &pm_async_attr.attr,
··· 281 } 282 283 power_attr(pm_trace); 284 + 285 + static ssize_t pm_trace_dev_match_show(struct kobject *kobj, 286 + struct kobj_attribute *attr, 287 + char *buf) 288 + { 289 + return show_trace_dev_match(buf, PAGE_SIZE); 290 + } 291 + 292 + static ssize_t 293 + pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr, 294 + const char *buf, size_t n) 295 + { 296 + return -EINVAL; 297 + } 298 + 299 + power_attr(pm_trace_dev_match); 300 + 301 #endif /* CONFIG_PM_TRACE */ 302 303 static struct attribute * g[] = { 304 &state_attr.attr, 305 #ifdef CONFIG_PM_TRACE 306 &pm_trace_attr.attr, 307 + &pm_trace_dev_match_attr.attr, 308 #endif 309 #ifdef CONFIG_PM_SLEEP 310 &pm_async_attr.attr,