diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-09-11 04:28:26 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-09-11 04:29:40 -0400 |
commit | 3b554a14f4bdf754ba9d2f64c2b6edf8dafe93b9 (patch) | |
tree | a01fc109cd60297d404e0e21f7457bd8e15f372b /drivers/s390/cio/device.c | |
parent | 6ee4fec6be06f7d138860b37cba58982cc3ccb16 (diff) |
[S390] cio: move final put_device to ccw_device_unregister
We use a test_and_clear_bit to prevent a device from being
unregistered twice. Unfortunately in this cases the "final"
put_device (from device_initialize) was issued more than once,
resulting in an use after free error. Fix this by moving this
put_device to ccw_device_unregister.
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 | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6b770f8c0a89..345a61f45a5a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -307,8 +307,11 @@ int ccw_device_is_orphan(struct ccw_device *cdev) | |||
307 | 307 | ||
308 | static void ccw_device_unregister(struct ccw_device *cdev) | 308 | static void ccw_device_unregister(struct ccw_device *cdev) |
309 | { | 309 | { |
310 | if (test_and_clear_bit(1, &cdev->private->registered)) | 310 | if (test_and_clear_bit(1, &cdev->private->registered)) { |
311 | device_del(&cdev->dev); | 311 | device_del(&cdev->dev); |
312 | /* Release reference from device_initialize(). */ | ||
313 | put_device(&cdev->dev); | ||
314 | } | ||
312 | } | 315 | } |
313 | 316 | ||
314 | static void ccw_device_remove_orphan_cb(struct work_struct *work) | 317 | static void ccw_device_remove_orphan_cb(struct work_struct *work) |
@@ -319,7 +322,6 @@ static void ccw_device_remove_orphan_cb(struct work_struct *work) | |||
319 | priv = container_of(work, struct ccw_device_private, kick_work); | 322 | priv = container_of(work, struct ccw_device_private, kick_work); |
320 | cdev = priv->cdev; | 323 | cdev = priv->cdev; |
321 | ccw_device_unregister(cdev); | 324 | ccw_device_unregister(cdev); |
322 | put_device(&cdev->dev); | ||
323 | /* Release cdev reference for workqueue processing. */ | 325 | /* Release cdev reference for workqueue processing. */ |
324 | put_device(&cdev->dev); | 326 | put_device(&cdev->dev); |
325 | } | 327 | } |
@@ -1358,7 +1360,6 @@ io_subchannel_remove (struct subchannel *sch) | |||
1358 | cdev->private->state = DEV_STATE_NOT_OPER; | 1360 | cdev->private->state = DEV_STATE_NOT_OPER; |
1359 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 1361 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
1360 | ccw_device_unregister(cdev); | 1362 | ccw_device_unregister(cdev); |
1361 | put_device(&cdev->dev); | ||
1362 | kfree(sch->private); | 1363 | kfree(sch->private); |
1363 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); | 1364 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); |
1364 | return 0; | 1365 | return 0; |