diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-11-06 19:11:56 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-11-06 19:59:28 -0500 |
commit | 7c45d1913f0a1d597eb4bc3b2c962bc2967da9ea (patch) | |
tree | 49a6bd70e0d221ba6456d6a3c2e403e7d5f5bfaf /drivers/firewire | |
parent | dbeeb816e805091e7cfc03baf36dc40b4adb2bbd (diff) |
firewire: fw-sbp2: fix refcounting
Since patch "fw-sbp2: use an own workqueue (fix system responsiveness)"
increased parallelism between fw-sbp2 and fw-core, it was possible that
fw-sbp2 didn't release the SCSI device when the FireWire device was
disconnected.
This happened if sbp2_update() ran during sbp2_login(), because a bus
reset occurred during sbp2_login(). The sbp2_login() work would [try
to] reschedule itself because it failed due to the bus reset, and it
would _not_ drop its reference on the target. However, sbp2_update()
would schedule sbp2_login() too before sbp2_login() rescheduled itself
and hence sbp2_update() would take an additional reference. And then
we would have one reference too many.
The fix is to _always_ drop the reference when leaving the sbp2_login()
work. If the sbp2_login() work reschedules itself, it takes a
reference, but only if it wasn't already rescheduled by sbp2_update().
Ditto in the sbp2_reconnect() work.
The resulting code is actually simpler than before: We _always_ take
a reference when successfully scheduling work. And we _always_ drop
a reference when leaving a workqueue job. No exceptions.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 5596df65c8ed..624ff3e082f6 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
@@ -650,13 +650,14 @@ static void sbp2_login(struct work_struct *work) | |||
650 | if (sbp2_send_management_orb(lu, node_id, generation, | 650 | if (sbp2_send_management_orb(lu, node_id, generation, |
651 | SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { | 651 | SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { |
652 | if (lu->retries++ < 5) { | 652 | if (lu->retries++ < 5) { |
653 | queue_delayed_work(sbp2_wq, &lu->work, | 653 | if (queue_delayed_work(sbp2_wq, &lu->work, |
654 | DIV_ROUND_UP(HZ, 5)); | 654 | DIV_ROUND_UP(HZ, 5))) |
655 | kref_get(&lu->tgt->kref); | ||
655 | } else { | 656 | } else { |
656 | fw_error("failed to login to %s LUN %04x\n", | 657 | fw_error("failed to login to %s LUN %04x\n", |
657 | unit->device.bus_id, lu->lun); | 658 | unit->device.bus_id, lu->lun); |
658 | kref_put(&lu->tgt->kref, sbp2_release_target); | ||
659 | } | 659 | } |
660 | kref_put(&lu->tgt->kref, sbp2_release_target); | ||
660 | return; | 661 | return; |
661 | } | 662 | } |
662 | 663 | ||
@@ -914,7 +915,9 @@ static void sbp2_reconnect(struct work_struct *work) | |||
914 | lu->retries = 0; | 915 | lu->retries = 0; |
915 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); | 916 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); |
916 | } | 917 | } |
917 | queue_delayed_work(sbp2_wq, &lu->work, DIV_ROUND_UP(HZ, 5)); | 918 | if (queue_delayed_work(sbp2_wq, &lu->work, DIV_ROUND_UP(HZ, 5))) |
919 | kref_get(&lu->tgt->kref); | ||
920 | kref_put(&lu->tgt->kref, sbp2_release_target); | ||
918 | return; | 921 | return; |
919 | } | 922 | } |
920 | 923 | ||