diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-12-07 06:51:37 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:32 -0500 |
commit | 0c609fca243d456af014e92ad1caca045072dfe8 (patch) | |
tree | 4e1ef91c88627e2a4af29dc2ff5aba38397e2f0c /drivers/s390/cio | |
parent | ec64333c3a9bc52e34d79cff23acf4e5764b1353 (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')
-rw-r--r-- | drivers/s390/cio/device.c | 24 |
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 | } |