diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2009-03-04 06:44:00 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-03-24 19:38:26 -0400 |
commit | ffa6a7054d172a2f57248dff2de600ca795c5656 (patch) | |
tree | f93c6234bb90295c52fcef09e367ed77ff6c3645 /drivers | |
parent | 60530afe1ee8a5532cb09d0ab5bc3f1a6495b780 (diff) |
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: Cornelia Huck <cornelia.huck@de.ibm.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/core.c | 19 | ||||
-rw-r--r-- | drivers/base/power/main.c | 44 | ||||
-rw-r--r-- | drivers/base/power/power.h | 8 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 9 |
4 files changed, 75 insertions, 5 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 95c67ffd71da..e73c92d13a23 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -1561,8 +1561,10 @@ out: | |||
1561 | * device_move - moves a device to a new parent | 1561 | * device_move - moves a device to a new parent |
1562 | * @dev: the pointer to the struct device to be moved | 1562 | * @dev: the pointer to the struct device to be moved |
1563 | * @new_parent: the new parent of the device (can by NULL) | 1563 | * @new_parent: the new parent of the device (can by NULL) |
1564 | * @dpm_order: how to reorder the dpm_list | ||
1564 | */ | 1565 | */ |
1565 | int device_move(struct device *dev, struct device *new_parent) | 1566 | int device_move(struct device *dev, struct device *new_parent, |
1567 | enum dpm_order dpm_order) | ||
1566 | { | 1568 | { |
1567 | int error; | 1569 | int error; |
1568 | struct device *old_parent; | 1570 | struct device *old_parent; |
@@ -1572,6 +1574,7 @@ int device_move(struct device *dev, struct device *new_parent) | |||
1572 | if (!dev) | 1574 | if (!dev) |
1573 | return -EINVAL; | 1575 | return -EINVAL; |
1574 | 1576 | ||
1577 | device_pm_lock(); | ||
1575 | new_parent = get_device(new_parent); | 1578 | new_parent = get_device(new_parent); |
1576 | new_parent_kobj = get_device_parent(dev, new_parent); | 1579 | new_parent_kobj = get_device_parent(dev, new_parent); |
1577 | 1580 | ||
@@ -1613,9 +1616,23 @@ int device_move(struct device *dev, struct device *new_parent) | |||
1613 | put_device(new_parent); | 1616 | put_device(new_parent); |
1614 | goto out; | 1617 | goto out; |
1615 | } | 1618 | } |
1619 | switch (dpm_order) { | ||
1620 | case DPM_ORDER_NONE: | ||
1621 | break; | ||
1622 | case DPM_ORDER_DEV_AFTER_PARENT: | ||
1623 | device_pm_move_after(dev, new_parent); | ||
1624 | break; | ||
1625 | case DPM_ORDER_PARENT_BEFORE_DEV: | ||
1626 | device_pm_move_before(new_parent, dev); | ||
1627 | break; | ||
1628 | case DPM_ORDER_DEV_LAST: | ||
1629 | device_pm_move_last(dev); | ||
1630 | break; | ||
1631 | } | ||
1616 | out_put: | 1632 | out_put: |
1617 | put_device(old_parent); | 1633 | put_device(old_parent); |
1618 | out: | 1634 | out: |
1635 | device_pm_unlock(); | ||
1619 | put_device(dev); | 1636 | put_device(dev); |
1620 | return error; | 1637 | return error; |
1621 | } | 1638 | } |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 2d14f4ae6c01..e255341682c8 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -107,6 +107,50 @@ void device_pm_remove(struct device *dev) | |||
107 | } | 107 | } |
108 | 108 | ||
109 | /** | 109 | /** |
110 | * device_pm_move_before - move device in dpm_list | ||
111 | * @deva: Device to move in dpm_list | ||
112 | * @devb: Device @deva should come before | ||
113 | */ | ||
114 | void device_pm_move_before(struct device *deva, struct device *devb) | ||
115 | { | ||
116 | pr_debug("PM: Moving %s:%s before %s:%s\n", | ||
117 | deva->bus ? deva->bus->name : "No Bus", | ||
118 | kobject_name(&deva->kobj), | ||
119 | devb->bus ? devb->bus->name : "No Bus", | ||
120 | kobject_name(&devb->kobj)); | ||
121 | /* Delete deva from dpm_list and reinsert before devb. */ | ||
122 | list_move_tail(&deva->power.entry, &devb->power.entry); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * device_pm_move_after - move device in dpm_list | ||
127 | * @deva: Device to move in dpm_list | ||
128 | * @devb: Device @deva should come after | ||
129 | */ | ||
130 | void device_pm_move_after(struct device *deva, struct device *devb) | ||
131 | { | ||
132 | pr_debug("PM: Moving %s:%s after %s:%s\n", | ||
133 | deva->bus ? deva->bus->name : "No Bus", | ||
134 | kobject_name(&deva->kobj), | ||
135 | devb->bus ? devb->bus->name : "No Bus", | ||
136 | kobject_name(&devb->kobj)); | ||
137 | /* Delete deva from dpm_list and reinsert after devb. */ | ||
138 | list_move(&deva->power.entry, &devb->power.entry); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * device_pm_move_last - move device to end of dpm_list | ||
143 | * @dev: Device to move in dpm_list | ||
144 | */ | ||
145 | void device_pm_move_last(struct device *dev) | ||
146 | { | ||
147 | pr_debug("PM: Moving %s:%s to end of list\n", | ||
148 | dev->bus ? dev->bus->name : "No Bus", | ||
149 | kobject_name(&dev->kobj)); | ||
150 | list_move_tail(&dev->power.entry, &dpm_list); | ||
151 | } | ||
152 | |||
153 | /** | ||
110 | * pm_op - execute the PM operation appropiate for given PM event | 154 | * pm_op - execute the PM operation appropiate for given PM event |
111 | * @dev: Device. | 155 | * @dev: Device. |
112 | * @ops: PM operations to choose from. | 156 | * @ops: PM operations to choose from. |
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 41f51fae042f..c7cb4fc3735c 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -18,11 +18,19 @@ static inline struct device *to_device(struct list_head *entry) | |||
18 | 18 | ||
19 | extern void device_pm_add(struct device *); | 19 | extern void device_pm_add(struct device *); |
20 | extern void device_pm_remove(struct device *); | 20 | extern void device_pm_remove(struct device *); |
21 | extern void device_pm_move_before(struct device *, struct device *); | ||
22 | extern void device_pm_move_after(struct device *, struct device *); | ||
23 | extern void device_pm_move_last(struct device *); | ||
21 | 24 | ||
22 | #else /* CONFIG_PM_SLEEP */ | 25 | #else /* CONFIG_PM_SLEEP */ |
23 | 26 | ||
24 | static inline void device_pm_add(struct device *dev) {} | 27 | static inline void device_pm_add(struct device *dev) {} |
25 | static inline void device_pm_remove(struct device *dev) {} | 28 | static inline void device_pm_remove(struct device *dev) {} |
29 | static inline void device_pm_move_before(struct device *deva, | ||
30 | struct device *devb) {} | ||
31 | static inline void device_pm_move_after(struct device *deva, | ||
32 | struct device *devb) {} | ||
33 | static inline void device_pm_move_last(struct device *dev) {} | ||
26 | 34 | ||
27 | #endif | 35 | #endif |
28 | 36 | ||
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 611d2e001dd5..e28f8ae53453 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -799,7 +799,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch, | |||
799 | return; | 799 | return; |
800 | other_sch = to_subchannel(cdev->dev.parent); | 800 | other_sch = to_subchannel(cdev->dev.parent); |
801 | /* Note: device_move() changes cdev->dev.parent */ | 801 | /* Note: device_move() changes cdev->dev.parent */ |
802 | ret = device_move(&cdev->dev, &sch->dev); | 802 | ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); |
803 | if (ret) { | 803 | if (ret) { |
804 | CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " | 804 | CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " |
805 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | 805 | "(ret=%d)!\n", cdev->private->dev_id.ssid, |
@@ -830,7 +830,7 @@ static void sch_attach_orphaned_device(struct subchannel *sch, | |||
830 | * Try to move the ccw device to its new subchannel. | 830 | * Try to move the ccw device to its new subchannel. |
831 | * Note: device_move() changes cdev->dev.parent | 831 | * Note: device_move() changes cdev->dev.parent |
832 | */ | 832 | */ |
833 | ret = device_move(&cdev->dev, &sch->dev); | 833 | ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); |
834 | if (ret) { | 834 | if (ret) { |
835 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " | 835 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " |
836 | "failed (ret=%d)!\n", | 836 | "failed (ret=%d)!\n", |
@@ -897,7 +897,8 @@ void ccw_device_move_to_orphanage(struct work_struct *work) | |||
897 | * ccw device can take its place on the subchannel. | 897 | * ccw device can take its place on the subchannel. |
898 | * Note: device_move() changes cdev->dev.parent | 898 | * Note: device_move() changes cdev->dev.parent |
899 | */ | 899 | */ |
900 | ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev); | 900 | ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev, |
901 | DPM_ORDER_NONE); | ||
901 | if (ret) { | 902 | if (ret) { |
902 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " | 903 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " |
903 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | 904 | "(ret=%d)!\n", cdev->private->dev_id.ssid, |
@@ -1129,7 +1130,7 @@ static void ccw_device_move_to_sch(struct work_struct *work) | |||
1129 | * Try to move the ccw device to its new subchannel. | 1130 | * Try to move the ccw device to its new subchannel. |
1130 | * Note: device_move() changes cdev->dev.parent | 1131 | * Note: device_move() changes cdev->dev.parent |
1131 | */ | 1132 | */ |
1132 | rc = device_move(&cdev->dev, &sch->dev); | 1133 | rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); |
1133 | mutex_unlock(&sch->reg_mutex); | 1134 | mutex_unlock(&sch->reg_mutex); |
1134 | if (rc) { | 1135 | if (rc) { |
1135 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel " | 1136 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel " |