forked from Minki/linux
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>
This commit is contained in:
parent
dbeeb816e8
commit
7c45d1913f
@ -650,13 +650,14 @@ static void sbp2_login(struct work_struct *work)
|
||||
if (sbp2_send_management_orb(lu, node_id, generation,
|
||||
SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) {
|
||||
if (lu->retries++ < 5) {
|
||||
queue_delayed_work(sbp2_wq, &lu->work,
|
||||
DIV_ROUND_UP(HZ, 5));
|
||||
if (queue_delayed_work(sbp2_wq, &lu->work,
|
||||
DIV_ROUND_UP(HZ, 5)))
|
||||
kref_get(&lu->tgt->kref);
|
||||
} else {
|
||||
fw_error("failed to login to %s LUN %04x\n",
|
||||
unit->device.bus_id, lu->lun);
|
||||
kref_put(&lu->tgt->kref, sbp2_release_target);
|
||||
}
|
||||
kref_put(&lu->tgt->kref, sbp2_release_target);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -914,7 +915,9 @@ static void sbp2_reconnect(struct work_struct *work)
|
||||
lu->retries = 0;
|
||||
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
|
||||
}
|
||||
queue_delayed_work(sbp2_wq, &lu->work, DIV_ROUND_UP(HZ, 5));
|
||||
if (queue_delayed_work(sbp2_wq, &lu->work, DIV_ROUND_UP(HZ, 5)))
|
||||
kref_get(&lu->tgt->kref);
|
||||
kref_put(&lu->tgt->kref, sbp2_release_target);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user