aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device.c
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2009-12-07 06:51:37 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2009-12-07 06:51:32 -0500
commit0c609fca243d456af014e92ad1caca045072dfe8 (patch)
tree4e1ef91c88627e2a4af29dc2ff5aba38397e2f0c /drivers/s390/cio/device.c
parentec64333c3a9bc52e34d79cff23acf4e5764b1353 (diff)
[S390] cio: handle busy subchannel in ccw_device_move_to_sch
Try to disable the old subchannel before we ask the driver core to move the attached device to a new parent. This way we can use the QUIESCE state during shutdown which prevents a possible use after free situation in some error cases. Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r--drivers/s390/cio/device.c24
1 files changed, 22 insertions, 2 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 2b50f93b7fef..af500aac24ef 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -892,12 +892,27 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
892 struct subchannel *sch) 892 struct subchannel *sch)
893{ 893{
894 struct subchannel *old_sch; 894 struct subchannel *old_sch;
895 int rc; 895 int rc, old_enabled = 0;
896 896
897 old_sch = to_subchannel(cdev->dev.parent); 897 old_sch = to_subchannel(cdev->dev.parent);
898 /* Obtain child reference for new parent. */ 898 /* Obtain child reference for new parent. */
899 if (!get_device(&sch->dev)) 899 if (!get_device(&sch->dev))
900 return -ENODEV; 900 return -ENODEV;
901
902 if (!sch_is_pseudo_sch(old_sch)) {
903 spin_lock_irq(old_sch->lock);
904 old_enabled = old_sch->schib.pmcw.ena;
905 rc = 0;
906 if (old_enabled)
907 rc = cio_disable_subchannel(old_sch);
908 spin_unlock_irq(old_sch->lock);
909 if (rc == -EBUSY) {
910 /* Release child reference for new parent. */
911 put_device(&sch->dev);
912 return rc;
913 }
914 }
915
901 mutex_lock(&sch->reg_mutex); 916 mutex_lock(&sch->reg_mutex);
902 rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); 917 rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
903 mutex_unlock(&sch->reg_mutex); 918 mutex_unlock(&sch->reg_mutex);
@@ -906,6 +921,12 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
906 cdev->private->dev_id.ssid, 921 cdev->private->dev_id.ssid,
907 cdev->private->dev_id.devno, sch->schid.ssid, 922 cdev->private->dev_id.devno, sch->schid.ssid,
908 sch->schib.pmcw.dev, rc); 923 sch->schib.pmcw.dev, rc);
924 if (old_enabled) {
925 /* Try to reenable the old subchannel. */
926 spin_lock_irq(old_sch->lock);
927 cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch);
928 spin_unlock_irq(old_sch->lock);
929 }
909 /* Release child reference for new parent. */ 930 /* Release child reference for new parent. */
910 put_device(&sch->dev); 931 put_device(&sch->dev);
911 return rc; 932 return rc;
@@ -914,7 +935,6 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev,
914 if (!sch_is_pseudo_sch(old_sch)) { 935 if (!sch_is_pseudo_sch(old_sch)) {
915 spin_lock_irq(old_sch->lock); 936 spin_lock_irq(old_sch->lock);
916 sch_set_cdev(old_sch, NULL); 937 sch_set_cdev(old_sch, NULL);
917 cio_disable_subchannel(old_sch);
918 spin_unlock_irq(old_sch->lock); 938 spin_unlock_irq(old_sch->lock);
919 css_schedule_eval(old_sch->schid); 939 css_schedule_eval(old_sch->schid);
920 } 940 }