drbd: fix list corruption (recent regression)
authorLars Ellenberg <lars.ellenberg@linbit.com>
Fri, 9 Jul 2010 21:28:10 +0000 (23:28 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 14 Oct 2010 16:31:43 +0000 (18:31 +0200)
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>
drivers/block/drbd/drbd_req.c

index 4e1e10d67c4b5288a0dccf8745627becf1dc4b65..3b61d767d9c43ed7eae36b2a03fb091e54c29a2d 100644 (file)
@@ -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);