drbd: Delay/reject other state changes while establishing a connection
authorPhilipp Reisner <philipp.reisner@linbit.com>
Mon, 26 Mar 2012 14:47:11 +0000 (16:47 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Wed, 9 May 2012 13:16:55 +0000 (15:16 +0200)
Changes to the role and disk state should be delayed or rejected
while we establish a connection.

This is necessary, since the peer will base its resync decision
on the UUIDs and the state we sent in the drbd_connect() function.

The most prominent example for this race is becoming primary after
sending state and UUIDs and before the state changes to C_WF_CONNECTION.

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

index 746f7933bf8608627dff94721c68391c77d12342..f215ad430bb81716f807ca43bb852ff53dc73b53 100644 (file)
@@ -850,6 +850,7 @@ enum {
        NEW_CUR_UUID,           /* Create new current UUID when thawing IO */
        AL_SUSPENDED,           /* Activity logging is currently suspended. */
        AHEAD_TO_SYNC_SOURCE,   /* Ahead -> SyncSource queued */
+       STATE_SENT,             /* Do not change state/UUIDs while this is set */
 };
 
 struct drbd_bitmap; /* opaque for drbd_conf */
index 519f286a429936930a1a28f30fefe6e70dd3eb92..deccff3af774e17529642336139e00a88f38d78c 100644 (file)
@@ -841,6 +841,13 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
        if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS)
                rv = SS_IN_TRANSIENT_STATE;
 
+       /* While establishing a connection only allow cstate to change.
+          Delay/refuse role changes, detach attach etc... */
+       if (test_bit(STATE_SENT, &mdev->flags) &&
+           !(os.conn == C_WF_REPORT_PARAMS ||
+             (ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION)))
+               rv = SS_IN_TRANSIENT_STATE;
+
        if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
                rv = SS_NEED_CONNECTION;
 
@@ -1668,6 +1675,12 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        if (os.disk < D_UP_TO_DATE && os.conn >= C_SYNC_SOURCE && ns.conn == C_CONNECTED)
                drbd_send_state(mdev, ns);
 
+       /* Wake up role changes, that were delayed because of connection establishing */
+       if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS) {
+               clear_bit(STATE_SENT, &mdev->flags);
+               wake_up(&mdev->state_wait);
+       }
+
        /* This triggers bitmap writeout of potentially still unwritten pages
         * if the resync finished cleanly, or aborted because of peer disk
         * failure, or because of connection loss.
index 1bbbad302ae7e8df715e061b958c36918b640c9b..308beb8c0c1e4130dff271259f9b036031be341b 100644 (file)
@@ -289,7 +289,7 @@ static int _try_outdate_peer_async(void *data)
        */
        spin_lock_irq(&mdev->req_lock);
        ns = mdev->state;
-       if (ns.conn < C_WF_REPORT_PARAMS) {
+       if (ns.conn < C_WF_REPORT_PARAMS && !test_bit(STATE_SENT, &mdev->flags)) {
                ns.pdsk = nps;
                _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
        }
index d601501c336a3bb8696ff41da1306abe209ed579..9db93ff11c0284ad88304ef37af2315c3ed55051 100644 (file)
@@ -751,6 +751,7 @@ static int drbd_connect(struct drbd_conf *mdev)
 {
        struct socket *s, *sock, *msock;
        int try, h, ok;
+       enum drbd_state_rv rv;
 
        D_ASSERT(!mdev->data.socket);
 
@@ -897,6 +898,7 @@ retry:
 
        if (drbd_send_protocol(mdev) == -1)
                return -1;
+       set_bit(STATE_SENT, &mdev->flags);
        drbd_send_sync_param(mdev, &mdev->sync_conf);
        drbd_send_sizes(mdev, 0, 0);
        drbd_send_uuids(mdev);
@@ -904,7 +906,13 @@ retry:
        clear_bit(USE_DEGR_WFC_T, &mdev->flags);
        clear_bit(RESIZE_PENDING, &mdev->flags);
 
-       if (drbd_request_state(mdev, NS(conn, C_WF_REPORT_PARAMS)) < SS_SUCCESS)
+       spin_lock_irq(&mdev->req_lock);
+       rv = _drbd_set_state(_NS(mdev, conn, C_WF_REPORT_PARAMS), CS_VERBOSE, NULL);
+       if (mdev->state.conn != C_WF_REPORT_PARAMS)
+               clear_bit(STATE_SENT, &mdev->flags);
+       spin_unlock_irq(&mdev->req_lock);
+
+       if (rv < SS_SUCCESS)
                return 0;
 
        drbd_thread_start(&mdev->asender);