--- drivers/base/power/main.c | 67 +++++++++++++++++++++++++++++++++++++++++++++- kernel/power/main.c | 30 +++++++++++++++++++- kernel/power/power.h | 3 ++ 3 files changed, 97 insertions(+), 3 deletions(-) Index: linux-2.6.24-rc6/drivers/base/power/main.c =================================================================== --- linux-2.6.24-rc6.orig/drivers/base/power/main.c +++ linux-2.6.24-rc6/drivers/base/power/main.c @@ -37,6 +37,50 @@ static DEFINE_MUTEX(dpm_list_mtx); int (*platform_enable_wakeup)(struct device *dev, int is_on); +#ifdef CONFIG_PM_VERBOSE +unsigned int dpm_active_cnt; +unsigned int dpm_fail; + +static unsigned int dpm_off_cnt; + +static void debug_inc_active_count(void) +{ + dpm_active_cnt++; +} + +static void debug_dec_active_count(void) +{ + dpm_active_cnt--; +} + +static void debug_inc_inactive_count(void) +{ + dpm_off_cnt++; +} + +static void debug_dec_inactive_count(void) +{ + dpm_off_cnt--; +} + +static int debug_fake_suspend_failure(void) +{ + return dpm_active_cnt <= dpm_fail; +} + +static void debug_print_counts(void) +{ + printk(KERN_DEBUG "PM: Active devices: %d\n", dpm_active_cnt); + printk(KERN_DEBUG "PM: Inactive devices: %d\n", dpm_off_cnt); +} +#else /* !CONFIG_PM_VERBOSE */ +static inline void debug_inc_active_count(void) {} +static inline void debug_dec_active_count(void) {} +static inline void debug_inc_inactive_count(void) {} +static inline void debug_dec_inactive_count(void) {} +static inline int debug_fake_suspend_failure(void) { return false; } +static inline void debug_print_counts(void) {} +#endif /* !CONFIG_PM_VERBOSE */ void device_pm_add(struct device *dev) { @@ -45,6 +89,7 @@ void device_pm_add(struct device *dev) kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); + debug_inc_active_count(); mutex_unlock(&dpm_list_mtx); } @@ -56,6 +101,7 @@ void device_pm_remove(struct device *dev mutex_lock(&dpm_list_mtx); dpm_sysfs_remove(dev); list_del_init(&dev->power.entry); + debug_dec_active_count(); mutex_unlock(&dpm_list_mtx); } @@ -121,18 +167,22 @@ static int resume_device_early(struct de static void dpm_resume(void) { mutex_lock(&dpm_list_mtx); + debug_print_counts(); while(!list_empty(&dpm_off)) { struct list_head * entry = dpm_off.next; struct device * dev = to_device(entry); get_device(dev); list_move_tail(entry, &dpm_active); + debug_dec_inactive_count(); + debug_inc_active_count(); mutex_unlock(&dpm_list_mtx); resume_device(dev); mutex_lock(&dpm_list_mtx); put_device(dev); } + debug_print_counts(); mutex_unlock(&dpm_list_mtx); } @@ -173,6 +223,7 @@ static void dpm_power_up(void) struct device * dev = to_device(entry); list_move_tail(entry, &dpm_off); + debug_inc_inactive_count(); resume_device_early(dev); } } @@ -241,7 +292,15 @@ static int suspend_device(struct device { int error = 0; + if (debug_fake_suspend_failure()) + return -ECANCELED; + down(&dev->sem); + + pr_debug("PM: Suspending device %s:%s\n", + dev->bus ? dev->bus->name : "No Bus", + kobject_name(&dev->kobj)); + if (dev->power.power_state.event) { dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); @@ -312,6 +371,7 @@ int device_suspend(pm_message_t state) might_sleep(); mutex_lock(&dpm_mtx); mutex_lock(&dpm_list_mtx); + debug_print_counts(); while (!list_empty(&dpm_active) && error == 0) { struct list_head * entry = dpm_active.prev; struct device * dev = to_device(entry); @@ -326,8 +386,11 @@ int device_suspend(pm_message_t state) /* Check if the device got removed */ if (!list_empty(&dev->power.entry)) { /* Move it to the dpm_off list */ - if (!error) + if (!error) { list_move(&dev->power.entry, &dpm_off); + debug_dec_active_count(); + debug_inc_inactive_count(); + } } if (error) printk(KERN_ERR "Could not suspend device %s: " @@ -336,6 +399,7 @@ int device_suspend(pm_message_t state) error == -EAGAIN ? " (please convert to suspend_late)" : ""); put_device(dev); } + debug_print_counts(); mutex_unlock(&dpm_list_mtx); if (error) dpm_resume(); @@ -368,6 +432,7 @@ int device_power_down(pm_message_t state if (error) goto Error; list_move(&dev->power.entry, &dpm_off_irq); + debug_dec_inactive_count(); } error = sysdev_suspend(state); Index: linux-2.6.24-rc6/kernel/power/main.c =================================================================== --- linux-2.6.24-rc6.orig/kernel/power/main.c +++ linux-2.6.24-rc6/kernel/power/main.c @@ -126,6 +126,27 @@ power_attr(pm_test); static inline int suspend_test(int level) { return 0; } #endif /* !CONFIG_PM_DEBUG */ +#ifdef CONFIG_PM_VERBOSE +static ssize_t pm_drivers_show(struct kset *kset, char *buf) +{ + return sprintf(buf, "%u %u\n", dpm_fail, dpm_active_cnt); +} + +static ssize_t pm_drivers_store(struct kset *kset, const char *buf, size_t n) +{ + unsigned int nr; + + if (sscanf(buf, "%u", &nr) == 1 && nr <= dpm_active_cnt) { + dpm_fail = nr; + return n; + } + + return -EINVAL; +} + +power_attr(pm_drivers); +#endif /* CONFIG_PM_VERBOSE */ + #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_SUSPEND @@ -490,9 +511,14 @@ static struct attribute * g[] = { #ifdef CONFIG_PM_TRACE &pm_trace_attr.attr, #endif -#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) +#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, -#endif +#endif /* CONFIG_PM_DEBUG */ +#ifdef CONFIG_PM_VERBOSE + &pm_drivers_attr.attr, +#endif /* CONFIG_PM_VERBOSE */ +#endif /* CONFIG_PM_SLEEP */ NULL, }; Index: linux-2.6.24-rc6/kernel/power/power.h =================================================================== --- linux-2.6.24-rc6.orig/kernel/power/power.h +++ linux-2.6.24-rc6/kernel/power/power.h @@ -207,6 +207,9 @@ enum { extern int pm_test_level; +extern unsigned int dpm_active_cnt; +extern unsigned int dpm_fail; + #ifdef CONFIG_SUSPEND_FREEZER static inline int suspend_freeze_processes(void) {