diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2008-12-25 07:39:06 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:39:07 -0500 |
commit | 9cd67421977a701272820987ff9e6f197b1b97b7 (patch) | |
tree | 3d29b739749d6aeb0d3f427b23678d71d080c307 /drivers/s390/cio | |
parent | 97166f52fc84c0bc49c7dbba2a26720110acb458 (diff) |
[S390] cio: Fix reference counting for online/offline.
The current code attempts to get an extra reference count
for online devices by doing a get_device() in ccw_device_online()
and a put_device() in ccw_device_done(). However, this
- incorrectly obtains an extra reference for disconnected
devices becoming available again (since they are already
online)
- needs special checks for css_init_done in order to handle
the console device
- is not obvious and
- may incorretly drop a reference count in ccw_device_done() if
that function is called after path verification for a device
that just became not operational.
So let's just get the reference in ccw_device_set_online() and
drop it in ccw_device_set_offline(). (Unfortunately, we still
need the special case in io_subchannel_probe().)
Signed-off-by: Cornelia Huck <cornelia.huck@de.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 | 29 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 5 |
2 files changed, 22 insertions, 12 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 647cbaca298e..039ef03cf217 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -376,19 +376,23 @@ int ccw_device_set_offline(struct ccw_device *cdev) | |||
376 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | 376 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
377 | } | 377 | } |
378 | spin_unlock_irq(cdev->ccwlock); | 378 | spin_unlock_irq(cdev->ccwlock); |
379 | /* Give up reference from ccw_device_set_online(). */ | ||
380 | put_device(&cdev->dev); | ||
379 | return ret; | 381 | return ret; |
380 | } | 382 | } |
381 | spin_unlock_irq(cdev->ccwlock); | 383 | spin_unlock_irq(cdev->ccwlock); |
382 | if (ret == 0) | 384 | if (ret == 0) { |
383 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); | 385 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); |
384 | else { | 386 | /* Give up reference from ccw_device_set_online(). */ |
387 | put_device(&cdev->dev); | ||
388 | } else { | ||
385 | CIO_MSG_EVENT(0, "ccw_device_offline returned %d, " | 389 | CIO_MSG_EVENT(0, "ccw_device_offline returned %d, " |
386 | "device 0.%x.%04x\n", | 390 | "device 0.%x.%04x\n", |
387 | ret, cdev->private->dev_id.ssid, | 391 | ret, cdev->private->dev_id.ssid, |
388 | cdev->private->dev_id.devno); | 392 | cdev->private->dev_id.devno); |
389 | cdev->online = 1; | 393 | cdev->online = 1; |
390 | } | 394 | } |
391 | return ret; | 395 | return ret; |
392 | } | 396 | } |
393 | 397 | ||
394 | /** | 398 | /** |
@@ -411,6 +415,9 @@ int ccw_device_set_online(struct ccw_device *cdev) | |||
411 | return -ENODEV; | 415 | return -ENODEV; |
412 | if (cdev->online || !cdev->drv) | 416 | if (cdev->online || !cdev->drv) |
413 | return -EINVAL; | 417 | return -EINVAL; |
418 | /* Hold on to an extra reference while device is online. */ | ||
419 | if (!get_device(&cdev->dev)) | ||
420 | return -ENODEV; | ||
414 | 421 | ||
415 | spin_lock_irq(cdev->ccwlock); | 422 | spin_lock_irq(cdev->ccwlock); |
416 | ret = ccw_device_online(cdev); | 423 | ret = ccw_device_online(cdev); |
@@ -422,10 +429,15 @@ int ccw_device_set_online(struct ccw_device *cdev) | |||
422 | "device 0.%x.%04x\n", | 429 | "device 0.%x.%04x\n", |
423 | ret, cdev->private->dev_id.ssid, | 430 | ret, cdev->private->dev_id.ssid, |
424 | cdev->private->dev_id.devno); | 431 | cdev->private->dev_id.devno); |
432 | /* Give up online reference since onlining failed. */ | ||
433 | put_device(&cdev->dev); | ||
425 | return ret; | 434 | return ret; |
426 | } | 435 | } |
427 | if (cdev->private->state != DEV_STATE_ONLINE) | 436 | if (cdev->private->state != DEV_STATE_ONLINE) { |
437 | /* Give up online reference since onlining failed. */ | ||
438 | put_device(&cdev->dev); | ||
428 | return -ENODEV; | 439 | return -ENODEV; |
440 | } | ||
429 | if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) { | 441 | if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) { |
430 | cdev->online = 1; | 442 | cdev->online = 1; |
431 | return 0; | 443 | return 0; |
@@ -440,6 +452,8 @@ int ccw_device_set_online(struct ccw_device *cdev) | |||
440 | "device 0.%x.%04x\n", | 452 | "device 0.%x.%04x\n", |
441 | ret, cdev->private->dev_id.ssid, | 453 | ret, cdev->private->dev_id.ssid, |
442 | cdev->private->dev_id.devno); | 454 | cdev->private->dev_id.devno); |
455 | /* Give up online reference since onlining failed. */ | ||
456 | put_device(&cdev->dev); | ||
443 | return (ret == 0) ? -ENODEV : ret; | 457 | return (ret == 0) ? -ENODEV : ret; |
444 | } | 458 | } |
445 | 459 | ||
@@ -1168,9 +1182,8 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1168 | ccw_device_register(cdev); | 1182 | ccw_device_register(cdev); |
1169 | /* | 1183 | /* |
1170 | * Check if the device is already online. If it is | 1184 | * Check if the device is already online. If it is |
1171 | * the reference count needs to be corrected | 1185 | * the reference count needs to be corrected since we |
1172 | * (see ccw_device_online and css_init_done for the | 1186 | * didn't obtain a reference in ccw_device_set_online. |
1173 | * ugly details). | ||
1174 | */ | 1187 | */ |
1175 | if (cdev->private->state != DEV_STATE_NOT_OPER && | 1188 | if (cdev->private->state != DEV_STATE_NOT_OPER && |
1176 | cdev->private->state != DEV_STATE_OFFLINE && | 1189 | cdev->private->state != DEV_STATE_OFFLINE && |
@@ -1806,6 +1819,8 @@ ccw_device_remove (struct device *dev) | |||
1806 | "device 0.%x.%04x\n", | 1819 | "device 0.%x.%04x\n", |
1807 | ret, cdev->private->dev_id.ssid, | 1820 | ret, cdev->private->dev_id.ssid, |
1808 | cdev->private->dev_id.devno); | 1821 | cdev->private->dev_id.devno); |
1822 | /* Give up reference obtained in ccw_device_set_online(). */ | ||
1823 | put_device(&cdev->dev); | ||
1809 | } | 1824 | } |
1810 | ccw_device_set_timeout(cdev, 0); | 1825 | ccw_device_set_timeout(cdev, 0); |
1811 | cdev->drv = NULL; | 1826 | cdev->drv = NULL; |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 10bc03940fb3..01330cf5ae5e 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -399,9 +399,6 @@ ccw_device_done(struct ccw_device *cdev, int state) | |||
399 | ccw_device_oper_notify(cdev); | 399 | ccw_device_oper_notify(cdev); |
400 | } | 400 | } |
401 | wake_up(&cdev->private->wait_q); | 401 | wake_up(&cdev->private->wait_q); |
402 | |||
403 | if (css_init_done && state != DEV_STATE_ONLINE) | ||
404 | put_device (&cdev->dev); | ||
405 | } | 402 | } |
406 | 403 | ||
407 | static int cmp_pgid(struct pgid *p1, struct pgid *p2) | 404 | static int cmp_pgid(struct pgid *p1, struct pgid *p2) |
@@ -611,8 +608,6 @@ ccw_device_online(struct ccw_device *cdev) | |||
611 | (cdev->private->state != DEV_STATE_BOXED)) | 608 | (cdev->private->state != DEV_STATE_BOXED)) |
612 | return -EINVAL; | 609 | return -EINVAL; |
613 | sch = to_subchannel(cdev->dev.parent); | 610 | sch = to_subchannel(cdev->dev.parent); |
614 | if (css_init_done && !get_device(&cdev->dev)) | ||
615 | return -ENODEV; | ||
616 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); | 611 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); |
617 | if (ret != 0) { | 612 | if (ret != 0) { |
618 | /* Couldn't enable the subchannel for i/o. Sick device. */ | 613 | /* Couldn't enable the subchannel for i/o. Sick device. */ |