diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-09-11 04:28:20 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-09-11 04:29:38 -0400 |
commit | be7a2ddce66991c05a1c6ad19790289591e53547 (patch) | |
tree | 89b74961a7da107e7eebe4486f48bb9c4426938e /drivers/s390/cio/device.c | |
parent | e2910bcf8ca762b306767a0894ab1987be014c9e (diff) |
[S390] cio: ensure to hold a reference for deferred deregistration
Ensure to always hold an extra device reference for scheduling a
subchannel deregistration, by moving the get_device to
ccw_device_schedule_sch_unregister. This fixes an use after free
error in ccw_device_call_sch_unregister where put_device was called
on an already freed device structure.
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.c | 14 |
1 files changed, 6 insertions, 8 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 19b4469a3ca4..a7a340b1d713 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -333,15 +333,15 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) | |||
333 | * Forced offline in disconnected state means | 333 | * Forced offline in disconnected state means |
334 | * 'throw away device'. | 334 | * 'throw away device'. |
335 | */ | 335 | */ |
336 | /* Get cdev reference for workqueue processing. */ | ||
337 | if (!get_device(&cdev->dev)) | ||
338 | return; | ||
339 | if (ccw_device_is_orphan(cdev)) { | 336 | if (ccw_device_is_orphan(cdev)) { |
340 | /* | 337 | /* |
341 | * Deregister ccw device. | 338 | * Deregister ccw device. |
342 | * Unfortunately, we cannot do this directly from the | 339 | * Unfortunately, we cannot do this directly from the |
343 | * attribute method. | 340 | * attribute method. |
344 | */ | 341 | */ |
342 | /* Get cdev reference for workqueue processing. */ | ||
343 | if (!get_device(&cdev->dev)) | ||
344 | return; | ||
345 | spin_lock_irqsave(cdev->ccwlock, flags); | 345 | spin_lock_irqsave(cdev->ccwlock, flags); |
346 | cdev->private->state = DEV_STATE_NOT_OPER; | 346 | cdev->private->state = DEV_STATE_NOT_OPER; |
347 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 347 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
@@ -1032,6 +1032,9 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) | |||
1032 | 1032 | ||
1033 | void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) | 1033 | void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) |
1034 | { | 1034 | { |
1035 | /* Get cdev reference for workqueue processing. */ | ||
1036 | if (!get_device(&cdev->dev)) | ||
1037 | return; | ||
1035 | PREPARE_WORK(&cdev->private->kick_work, | 1038 | PREPARE_WORK(&cdev->private->kick_work, |
1036 | ccw_device_call_sch_unregister); | 1039 | ccw_device_call_sch_unregister); |
1037 | queue_work(slow_path_wq, &cdev->private->kick_work); | 1040 | queue_work(slow_path_wq, &cdev->private->kick_work); |
@@ -1052,9 +1055,6 @@ io_subchannel_recog_done(struct ccw_device *cdev) | |||
1052 | /* Device did not respond in time. */ | 1055 | /* Device did not respond in time. */ |
1053 | case DEV_STATE_NOT_OPER: | 1056 | case DEV_STATE_NOT_OPER: |
1054 | cdev->private->flags.recog_done = 1; | 1057 | cdev->private->flags.recog_done = 1; |
1055 | /* Remove device found not operational. */ | ||
1056 | if (!get_device(&cdev->dev)) | ||
1057 | break; | ||
1058 | ccw_device_schedule_sch_unregister(cdev); | 1058 | ccw_device_schedule_sch_unregister(cdev); |
1059 | if (atomic_dec_and_test(&ccw_device_init_count)) | 1059 | if (atomic_dec_and_test(&ccw_device_init_count)) |
1060 | wake_up(&ccw_device_init_wq); | 1060 | wake_up(&ccw_device_init_wq); |
@@ -1565,8 +1565,6 @@ static int purge_fn(struct device *dev, void *data) | |||
1565 | spin_unlock_irq(cdev->ccwlock); | 1565 | spin_unlock_irq(cdev->ccwlock); |
1566 | if (!unreg) | 1566 | if (!unreg) |
1567 | goto out; | 1567 | goto out; |
1568 | if (!get_device(&cdev->dev)) | ||
1569 | goto out; | ||
1570 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, | 1568 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, |
1571 | priv->dev_id.devno); | 1569 | priv->dev_id.devno); |
1572 | ccw_device_schedule_sch_unregister(cdev); | 1570 | ccw_device_schedule_sch_unregister(cdev); |