From: Rafael J. Wysocki <rjw@xxxxxxx> It sometimes is necessary to destroy a device object during a suspend or hibernation, but the PM core is supposed to control all device objects in that cases. For this reason, it is necessary to introduce a mechanism allowing one to ask the PM core to remove a device object corresponding to a suspended device on one's behalf. Define function destroy_suspended_device() that will schedule the removal of a device object corresponding to a suspended device by the PM core during the subsequent resume. Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx> --- drivers/base/core.c | 44 +++++++++++++++++++++++++++++++++++++++----- drivers/base/power/main.c | 36 +++++++++++++++++++++++++++--------- drivers/base/power/power.h | 1 + include/linux/device.h | 8 ++++++++ 4 files changed, 75 insertions(+), 14 deletions(-) Index: linux-2.6/drivers/base/core.c =================================================================== --- linux-2.6.orig/drivers/base/core.c +++ linux-2.6/drivers/base/core.c @@ -1156,14 +1156,11 @@ error: EXPORT_SYMBOL_GPL(device_create); /** - * device_destroy - removes a device that was created with device_create() + * find_device - finds a device that was created with device_create() * @class: pointer to the struct class that this device was registered with * @devt: the dev_t of the device that was previously registered - * - * This call unregisters and cleans up a device that was created with a - * call to device_create(). */ -void device_destroy(struct class *class, dev_t devt) +static struct device *find_device(struct class *class, dev_t devt) { struct device *dev = NULL; struct device *dev_tmp; @@ -1176,12 +1173,49 @@ void device_destroy(struct class *class, } } up(&class->sem); + return dev; +} +/** + * device_destroy - removes a device that was created with device_create() + * @class: pointer to the struct class that this device was registered with + * @devt: the dev_t of the device that was previously registered + * + * This call unregisters and cleans up a device that was created with a + * call to device_create(). + */ +void device_destroy(struct class *class, dev_t devt) +{ + struct device *dev; + + dev = find_device(class, devt); if (dev) device_unregister(dev); } EXPORT_SYMBOL_GPL(device_destroy); +#ifdef CONFIG_PM_SLEEP +/** + * destroy_suspended_device - schedules the removal of a suspended device + * @class: pointer to the struct class that this device was registered with + * @devt: the dev_t of the device that was previously registered + * + * This call notifies the PM core of the necessity to unregister a suspended + * device created with a call to device_create() (devices cannot be + * unregistered directly while suspended, since the PM core controls them in + * that cases). + */ +void destroy_suspended_device(struct class *class, dev_t devt) +{ + struct device *dev; + + dev = find_device(class, devt); + if (dev) + device_pm_schedule_removal(dev); +} +EXPORT_SYMBOL_GPL(destroy_suspended_device); +#endif /* CONFIG_PM_SLEEP */ + /** * device_rename - renames a device * @dev: the pointer to the struct device to be renamed Index: linux-2.6/include/linux/device.h =================================================================== --- linux-2.6.orig/include/linux/device.h +++ linux-2.6/include/linux/device.h @@ -521,6 +521,14 @@ extern struct device *device_create(stru dev_t devt, const char *fmt, ...) __attribute__((format(printf,4,5))); extern void device_destroy(struct class *cls, dev_t devt); +#ifdef CONFIG_PM_SLEEP +extern void destroy_suspended_device(struct class *cls, dev_t devt); +#else /* !CONFIG_PM_SLEEP */ +static inline void destroy_suspended_device(struct class *cls, dev_t devt) +{ + device_destroy(cls, devt); +} +#endif /* !CONFIG_PM_SLEEP */ /* * Platform "fixup" functions - allow the platform to have their say Index: linux-2.6/drivers/base/power/power.h =================================================================== --- linux-2.6.orig/drivers/base/power/power.h +++ linux-2.6/drivers/base/power/power.h @@ -20,6 +20,7 @@ static inline struct device *to_device(s extern void device_pm_add(struct device *); extern void device_pm_remove(struct device *); +extern void device_pm_schedule_removal(struct device *); #else /* CONFIG_PM_SLEEP */ Index: linux-2.6/drivers/base/power/main.c =================================================================== --- linux-2.6.orig/drivers/base/power/main.c +++ linux-2.6/drivers/base/power/main.c @@ -31,6 +31,7 @@ LIST_HEAD(dpm_active); static LIST_HEAD(dpm_off); static LIST_HEAD(dpm_off_irq); +static LIST_HEAD(dpm_destroy); static DEFINE_MUTEX(dpm_mtx); static DEFINE_MUTEX(dpm_list_mtx); @@ -59,6 +60,17 @@ void device_pm_remove(struct device *dev mutex_unlock(&dpm_list_mtx); } +void device_pm_schedule_removal(struct device *dev) +{ + pr_debug("PM: Removing info for %s:%s\n", + dev->bus ? dev->bus->name : "No Bus", + kobject_name(&dev->kobj)); + mutex_lock(&dpm_list_mtx); + dpm_sysfs_remove(dev); + list_move_tail(&dev->power.entry, &dpm_destroy); + mutex_unlock(&dpm_list_mtx); +} + /*------------------------- Resume routines -------------------------*/ @@ -113,11 +125,14 @@ static int resume_device_early(struct de return error; } -/* - * Resume the devices that have either not gone through - * the late suspend, or that did go through it but also - * went through the early resume +/** + * dpm_resume - Restore state of each device in system. + * + * Walk the dpm_off list, remove each entry, resume the device, + * then add it to the dpm_active list. Unregister devices scheduled for + * removal. */ + static void dpm_resume(void) { mutex_lock(&dpm_list_mtx); @@ -134,14 +149,17 @@ static void dpm_resume(void) put_device(dev); } mutex_unlock(&dpm_list_mtx); -} + /* Unregister devices scheduled for removal */ + while (!list_empty(&dpm_destroy)) { + struct list_head *entry = dpm_destroy.next; + struct device *dev = to_device(entry); + device_unregister(dev); + } +} /** - * device_resume - Restore state of each device in system. - * - * Walk the dpm_off list, remove each entry, resume the device, - * then add it to the dpm_active list. + * device_resume - Invoke dpm_resume() under dpm_mtx. */ void device_resume(void) - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html