drbd: Fixed an issue with AHEAD -> SYNC_SOURCE transitions
authorPhilipp Reisner <philipp.reisner@linbit.com>
Wed, 22 Dec 2010 11:48:31 +0000 (12:48 +0100)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 10 Mar 2011 10:45:23 +0000 (11:45 +0100)
Create a new barrier when leaving the AHEAD mode.

  Otherwise we trigger the assertion in req_mod(, barrier_acked)
  D_ASSERT(req->rq_state & RQ_NET_SENT);

The new barrier is created by recycling the newest existing one.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_main.c

index 9bd53cf..90050ab 100644 (file)
@@ -335,6 +335,24 @@ bail:
        drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
 }
 
+
+/* In C_AHEAD mode only out_of_sync packets are sent for requests. Detach
+ * those requests from the newsest barrier when changing to an other cstate.
+ *
+ * That headless list vanishes when the last request finished its write or
+ * send out_of_sync packet.  */
+static void tl_forget(struct drbd_conf *mdev)
+{
+       struct drbd_tl_epoch *b;
+
+       if (test_bit(CREATE_BARRIER, &mdev->flags))
+               return;
+
+       b = mdev->newest_tle;
+       list_del(&b->requests);
+       _tl_add_barrier(mdev, b);
+}
+
 /**
  * _tl_restart() - Walks the transfer log, and applies an action to all requests
  * @mdev:      DRBD device.
@@ -1242,6 +1260,9 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
        if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)
                drbd_resume_al(mdev);
 
+       if (os.conn == C_AHEAD && ns.conn != C_AHEAD)
+               tl_forget(mdev);
+
        ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
        if (ascw) {
                ascw->os = os;