aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2013-01-21 09:43:41 -0500
committerPhilipp Reisner <philipp.reisner@linbit.com>2013-01-21 16:58:36 -0500
commit2681f7f6ce6c7416eb619d0fb19422bcc68bd9e1 (patch)
tree31b6a1a1830c4c81d917b545e659c1f46bcc1218 /drivers/block
parentd2ec180c23a5a1bfe34d8638b0342a47c00cf70f (diff)
drbd: fix potential protocol error and resulting disconnect/reconnect
When we notice a disk failure on the receiving side, we stop sending it new incoming writes. Depending on exact timing of various events, the same transfer log epoch could end up containing both replicated (before we noticed the failure) and local-only requests (after we noticed the failure). The sanity checks in tl_release(), called when receiving a P_BARRIER_ACK, check that the ack'ed transfer log epoch matches the expected epoch, and the number of contained writes matches the number of ack'ed writes. In this case, they counted both replicated and local-only writes, but the peer only acknowledges those it has seen. We get a mismatch, resulting in a protocol error and disconnect/reconnect cycle. Messages logged are "BAD! BarrierAck #%u received with n_writes=%u, expected n_writes=%u!\n" A similar issue can also be triggered when starting a resync while having a healthy replication link, by invalidating one side, forcing a full sync, or attaching to a diskless node. Fix this by closing the current epoch if the state changes in a way that would cause the replication intent of the next write. Epochs now contain either only non-replicated, or only replicated writes. 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.c2
-rw-r--r--drivers/block/drbd/drbd_req.h1
-rw-r--r--drivers/block/drbd/drbd_state.c7
3 files changed, 9 insertions, 1 deletions
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index f58a4a4b4dfb..2b8303ad63c9 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -168,7 +168,7 @@ static void wake_all_senders(struct drbd_tconn *tconn) {
168} 168}
169 169
170/* must hold resource->req_lock */ 170/* must hold resource->req_lock */
171static void start_new_tl_epoch(struct drbd_tconn *tconn) 171void start_new_tl_epoch(struct drbd_tconn *tconn)
172{ 172{
173 /* no point closing an epoch, if it is empty, anyways. */ 173 /* no point closing an epoch, if it is empty, anyways. */
174 if (tconn->current_tle_writes == 0) 174 if (tconn->current_tle_writes == 0)
diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h
index 016de6b8bb57..c08d22964d06 100644
--- a/drivers/block/drbd/drbd_req.h
+++ b/drivers/block/drbd/drbd_req.h
@@ -267,6 +267,7 @@ struct bio_and_error {
267 int error; 267 int error;
268}; 268};
269 269
270extern void start_new_tl_epoch(struct drbd_tconn *tconn);
270extern void drbd_req_destroy(struct kref *kref); 271extern void drbd_req_destroy(struct kref *kref);
271extern void _req_may_be_done(struct drbd_request *req, 272extern void _req_may_be_done(struct drbd_request *req,
272 struct bio_and_error *m); 273 struct bio_and_error *m);
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
index 53bf6182bac4..0fe220cfb9e9 100644
--- a/drivers/block/drbd/drbd_state.c
+++ b/drivers/block/drbd/drbd_state.c
@@ -931,6 +931,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
931 enum drbd_state_rv rv = SS_SUCCESS; 931 enum drbd_state_rv rv = SS_SUCCESS;
932 enum sanitize_state_warnings ssw; 932 enum sanitize_state_warnings ssw;
933 struct after_state_chg_work *ascw; 933 struct after_state_chg_work *ascw;
934 bool did_remote, should_do_remote;
934 935
935 os = drbd_read_state(mdev); 936 os = drbd_read_state(mdev);
936 937
@@ -981,11 +982,17 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
981 (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) 982 (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))
982 atomic_inc(&mdev->local_cnt); 983 atomic_inc(&mdev->local_cnt);
983 984
985 did_remote = drbd_should_do_remote(mdev->state);
984 mdev->state.i = ns.i; 986 mdev->state.i = ns.i;
987 should_do_remote = drbd_should_do_remote(mdev->state);
985 mdev->tconn->susp = ns.susp; 988 mdev->tconn->susp = ns.susp;
986 mdev->tconn->susp_nod = ns.susp_nod; 989 mdev->tconn->susp_nod = ns.susp_nod;
987 mdev->tconn->susp_fen = ns.susp_fen; 990 mdev->tconn->susp_fen = ns.susp_fen;
988 991
992 /* put replicated vs not-replicated requests in seperate epochs */
993 if (did_remote != should_do_remote)
994 start_new_tl_epoch(mdev->tconn);
995
989 if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING) 996 if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING)
990 drbd_print_uuids(mdev, "attached to UUIDs"); 997 drbd_print_uuids(mdev, "attached to UUIDs");
991 998