Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ffa6a705 authored by Cornelia Huck's avatar Cornelia Huck Committed by Greg Kroah-Hartman
Browse files

Driver core: Fix device_move() vs. dpm list ordering, v2



dpm_list currently relies on the fact that child devices will
be registered after their parents to get a correct suspend
order. Using device_move() however destroys this assumption, as
an already registered device may be moved under a newly registered
one.

This patch adds a new argument to device_move(), allowing callers
to specify how dpm_list should be adapted.

Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 60530afe
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -1561,8 +1561,10 @@ static int device_move_class_links(struct device *dev,
 * device_move - moves a device to a new parent
 * @dev: the pointer to the struct device to be moved
 * @new_parent: the new parent of the device (can by NULL)
 * @dpm_order: how to reorder the dpm_list
 */
int device_move(struct device *dev, struct device *new_parent)
int device_move(struct device *dev, struct device *new_parent,
		enum dpm_order dpm_order)
{
	int error;
	struct device *old_parent;
@@ -1572,6 +1574,7 @@ int device_move(struct device *dev, struct device *new_parent)
	if (!dev)
		return -EINVAL;

	device_pm_lock();
	new_parent = get_device(new_parent);
	new_parent_kobj = get_device_parent(dev, new_parent);

@@ -1613,9 +1616,23 @@ int device_move(struct device *dev, struct device *new_parent)
		put_device(new_parent);
		goto out;
	}
	switch (dpm_order) {
	case DPM_ORDER_NONE:
		break;
	case DPM_ORDER_DEV_AFTER_PARENT:
		device_pm_move_after(dev, new_parent);
		break;
	case DPM_ORDER_PARENT_BEFORE_DEV:
		device_pm_move_before(new_parent, dev);
		break;
	case DPM_ORDER_DEV_LAST:
		device_pm_move_last(dev);
		break;
	}
out_put:
	put_device(old_parent);
out:
	device_pm_unlock();
	put_device(dev);
	return error;
}
+44 −0
Original line number Diff line number Diff line
@@ -106,6 +106,50 @@ void device_pm_remove(struct device *dev)
	mutex_unlock(&dpm_list_mtx);
}

/**
 *	device_pm_move_before - move device in dpm_list
 *	@deva:  Device to move in dpm_list
 *	@devb:  Device @deva should come before
 */
void device_pm_move_before(struct device *deva, struct device *devb)
{
	pr_debug("PM: Moving %s:%s before %s:%s\n",
		 deva->bus ? deva->bus->name : "No Bus",
		 kobject_name(&deva->kobj),
		 devb->bus ? devb->bus->name : "No Bus",
		 kobject_name(&devb->kobj));
	/* Delete deva from dpm_list and reinsert before devb. */
	list_move_tail(&deva->power.entry, &devb->power.entry);
}

/**
 *	device_pm_move_after - move device in dpm_list
 *	@deva:  Device to move in dpm_list
 *	@devb:  Device @deva should come after
 */
void device_pm_move_after(struct device *deva, struct device *devb)
{
	pr_debug("PM: Moving %s:%s after %s:%s\n",
		 deva->bus ? deva->bus->name : "No Bus",
		 kobject_name(&deva->kobj),
		 devb->bus ? devb->bus->name : "No Bus",
		 kobject_name(&devb->kobj));
	/* Delete deva from dpm_list and reinsert after devb. */
	list_move(&deva->power.entry, &devb->power.entry);
}

/**
 * 	device_pm_move_last - move device to end of dpm_list
 * 	@dev:   Device to move in dpm_list
 */
void device_pm_move_last(struct device *dev)
{
	pr_debug("PM: Moving %s:%s to end of list\n",
		 dev->bus ? dev->bus->name : "No Bus",
		 kobject_name(&dev->kobj));
	list_move_tail(&dev->power.entry, &dpm_list);
}

/**
 *	pm_op - execute the PM operation appropiate for given PM event
 *	@dev:	Device.
+8 −0
Original line number Diff line number Diff line
@@ -18,11 +18,19 @@ static inline struct device *to_device(struct list_head *entry)

extern void device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
extern void device_pm_move_before(struct device *, struct device *);
extern void device_pm_move_after(struct device *, struct device *);
extern void device_pm_move_last(struct device *);

#else /* CONFIG_PM_SLEEP */

static inline void device_pm_add(struct device *dev) {}
static inline void device_pm_remove(struct device *dev) {}
static inline void device_pm_move_before(struct device *deva,
					 struct device *devb) {}
static inline void device_pm_move_after(struct device *deva,
					struct device *devb) {}
static inline void device_pm_move_last(struct device *dev) {}

#endif

+5 −4
Original line number Diff line number Diff line
@@ -799,7 +799,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
		return;
	other_sch = to_subchannel(cdev->dev.parent);
	/* Note: device_move() changes cdev->dev.parent */
	ret = device_move(&cdev->dev, &sch->dev);
	ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
	if (ret) {
		CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
			      "(ret=%d)!\n", cdev->private->dev_id.ssid,
@@ -830,7 +830,7 @@ static void sch_attach_orphaned_device(struct subchannel *sch,
	 * Try to move the ccw device to its new subchannel.
	 * Note: device_move() changes cdev->dev.parent
	 */
	ret = device_move(&cdev->dev, &sch->dev);
	ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
	if (ret) {
		CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
			      "failed (ret=%d)!\n",
@@ -897,7 +897,8 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
	 * ccw device can take its place on the subchannel.
	 * Note: device_move() changes cdev->dev.parent
	 */
	ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
	ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev,
		DPM_ORDER_NONE);
	if (ret) {
		CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
			      "(ret=%d)!\n", cdev->private->dev_id.ssid,
@@ -1129,7 +1130,7 @@ static void ccw_device_move_to_sch(struct work_struct *work)
	 * Try to move the ccw device to its new subchannel.
	 * Note: device_move() changes cdev->dev.parent
	 */
	rc = device_move(&cdev->dev, &sch->dev);
	rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
	mutex_unlock(&sch->reg_mutex);
	if (rc) {
		CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel "
+2 −1
Original line number Diff line number Diff line
@@ -494,7 +494,8 @@ extern int device_for_each_child(struct device *dev, void *data,
extern struct device *device_find_child(struct device *dev, void *data,
				int (*match)(struct device *dev, void *data));
extern int device_rename(struct device *dev, char *new_name);
extern int device_move(struct device *dev, struct device *new_parent);
extern int device_move(struct device *dev, struct device *new_parent,
		       enum dpm_order dpm_order);

/*
 * Root device objects for grouping under /sys/devices
Loading