diff options
author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2010-07-09 23:28:10 +0200 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2010-10-14 18:31:43 +0200 |
commit | d28fd092a55b504a0d699b65802a995086d70647 (patch) | |
tree | 80071a61faa7e38cd6df6be155dd99e0f455a821 /drivers/block | |
parent | e756414f7daa93b862f1670dd0a6aaa676ea71e3 (diff) | |
download | kernel_samsung_aries-d28fd092a55b504a0d699b65802a995086d70647.zip kernel_samsung_aries-d28fd092a55b504a0d699b65802a995086d70647.tar.gz kernel_samsung_aries-d28fd092a55b504a0d699b65802a995086d70647.tar.bz2 |
drbd: fix list corruption (recent regression)
The commit 288f422ec13667de40b278535d2a5fb5c77352c4
drbd: Track all IO requests on the TL, not writes only
moved a list_add_tail(req, ) into a region where req
may have just been freed due to conflict detection.
Fix this by adding a proper cleanup section for that code path.
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/drbd/drbd_req.c | 42 |
1 files changed, 17 insertions, 25 deletions
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 4e1e10d..3b61d76 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -917,31 +917,8 @@ allocate_barrier: /* check this request on the collision detection hash tables. * if we have a conflict, just complete it here. * THINK do we want to check reads, too? (I don't think so...) */ - if (rw == WRITE && _req_conflicts(req)) { - /* this is a conflicting request. - * even though it may have been only _partially_ - * overlapping with one of the currently pending requests, - * without even submitting or sending it, we will - * pretend that it was successfully served right now. - */ - if (local) { - bio_put(req->private_bio); - req->private_bio = NULL; - drbd_al_complete_io(mdev, req->sector); - put_ldev(mdev); - local = 0; - } - if (remote) - dec_ap_pending(mdev); - _drbd_end_io_acct(mdev, req); - /* THINK: do we want to fail it (-EIO), or pretend success? */ - bio_endio(req->master_bio, 0); - req->master_bio = NULL; - dec_ap_bio(mdev); - drbd_req_free(req); - remote = 0; - } - + if (rw == WRITE && _req_conflicts(req)) + goto fail_conflicting; list_add_tail(&req->tl_requests, &mdev->newest_tle->requests); @@ -976,6 +953,21 @@ allocate_barrier: return 0; +fail_conflicting: + /* this is a conflicting request. + * even though it may have been only _partially_ + * overlapping with one of the currently pending requests, + * without even submitting or sending it, we will + * pretend that it was successfully served right now. + */ + _drbd_end_io_acct(mdev, req); + spin_unlock_irq(&mdev->req_lock); + if (remote) + dec_ap_pending(mdev); + /* THINK: do we want to fail it (-EIO), or pretend success? + * this pretends success. */ + err = 0; + fail_free_complete: if (rw == WRITE && local) drbd_al_complete_io(mdev, sector); |