aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device.c
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2009-09-11 04:28:20 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-09-11 04:29:38 -0400
commitbe7a2ddce66991c05a1c6ad19790289591e53547 (patch)
tree89b74961a7da107e7eebe4486f48bb9c4426938e /drivers/s390/cio/device.c
parente2910bcf8ca762b306767a0894ab1987be014c9e (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.c14
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
1033void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) 1033void 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);