[sheepdog] [PATCH v4 2/2] cluster driver: handle pending block/notify event during reconnect

Kai Zhang kyle at zelin.io
Tue Jul 9 12:08:12 CEST 2013


Current implementation of reconnection doesn't handle pending block/notify
event.

It is easy to handle notify event by calling sd_notify_handler() manually
to finish it.

However, it is a little bit complex for block event.
This is because a block event need 4 steps.
1. in queue_cluster_request(), send block event by sys->cdrv->block(), and
  add to pending_block_list.
2. in sd_block_handler(), queue the event to work queue of 'block' thread.
3. in cluster_op_done(), send unblock event by sys->cdrv->unblock().
4. in sd_notify_handler(), remove it from pending_block_list.

And step 1 and 3 contains broadcast operations.
So we have to know which step has been done for a pending block event.

If step 1 has been done, we can re-queue it simply. (Any block event which sent
by this node have been removed due to the leave event)
If step 2 has been done, the event is handling by another thread. We have to mark
it as 'drop' so that it will be dropped when cluster_op_done() is called later.
if step 3 has been done, we should call sd_notify_handler() manually to finish
it.

Signed-off-by: Kai Zhang <kyle at zelin.io>
---
 sheep/group.c      |   80 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 sheep/sheep_priv.h |    8 ++++++
 2 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/sheep/group.c b/sheep/group.c
index 546e4ae..370c625 100644
--- a/sheep/group.c
+++ b/sheep/group.c
@@ -234,6 +234,9 @@ static void cluster_op_done(struct work *work)
 	size_t size;
 	int ret;
 
+	if (req->status == REQUEST_DROPPED)
+		goto drop;
+
 	sd_dprintf("%s (%p)", op_name(req->op), req);
 
 	msg = prepare_cluster_msg(req, &size);
@@ -251,6 +254,13 @@ static void cluster_op_done(struct work *work)
 	}
 
 	free(msg);
+	req->status = REQUEST_DONE;
+	return;
+drop:
+	list_del(&req->pending_list);
+	req->rp.result = SD_RES_CLUSTER_ERROR;
+	put_request(req);
+	cluster_op_running = false;
 }
 
 /*
@@ -280,6 +290,7 @@ bool sd_block_handler(const struct sd_node *sender)
 	req->work.done = cluster_op_done;
 
 	queue_work(sys->block_wqueue, &req->work);
+	req->status = REQUEST_QUEUED;
 	return true;
 }
 
@@ -323,7 +334,7 @@ void queue_cluster_request(struct request *req)
 
 		free(msg);
 	}
-
+	req->status = REQUEST_INIT;
 	return;
 error:
 	req->rp.result = ret;
@@ -921,6 +932,71 @@ static int send_join_request(struct sd_node *ent)
 	return ret;
 }
 
+static void requeue_cluster_request(void)
+{
+	struct request *req, *p;
+	struct vdi_op_message *msg;
+	size_t size;
+
+	list_for_each_entry_safe(req, p, main_thread_get(pending_notify_list),
+				 pending_list) {
+		/*
+		 * ->notify() was called and succeeded but after that
+		 * this node session-timeouted and sd_notify_handler
+		 * wasn't called from notify event handler in cluster
+		 * driver. We manually call sd_notify_handler to finish
+		 * the request.
+		 */
+		sd_dprintf("finish pending notify request, op: %s",
+			   op_name(req->op));
+		msg = prepare_cluster_msg(req, &size);
+		sd_notify_handler(&sys->this_node, msg, size);
+		free(msg);
+	}
+
+	list_for_each_entry_safe(req, p, main_thread_get(pending_block_list),
+				 pending_list) {
+		switch (req->status) {
+		case REQUEST_INIT:
+			/* this request has never been executed, re-queue it */
+			sd_dprintf("requeue a block request, op: %s",
+				   op_name(req->op));
+			list_del(&req->pending_list);
+			queue_cluster_request(req);
+			break;
+		case REQUEST_QUEUED:
+			/*
+			 * This request is being handled by the 'block' thread
+			 * and ->unblock() isn't called yet. We can't call
+			 * ->unblock thereafter because other sheep has
+			 * unblocked themselves due to cluster driver session
+			 * timeout. Mark it as dropped to stop cluster_op_done()
+			 * from calling ->unblock.
+			 */
+			sd_dprintf("drop pending block request, op: %s",
+				   op_name(req->op));
+			req->status = REQUEST_DROPPED;
+			break;
+		case REQUEST_DONE:
+			/*
+			 * ->unblock() was called and succeeded but after that
+			 * this node session-timeouted and sd_notify_handler
+			 * wasn't called from unblock event handler in cluster
+			 * driver. We manually call sd_notify_handler to finish
+			 * the request.
+			 */
+			sd_dprintf("finish pending block request, op: %s",
+				   op_name(req->op));
+			msg = prepare_cluster_msg(req, &size);
+			sd_notify_handler(&sys->this_node, msg, size);
+			free(msg);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 int sd_reconnect_handler(void)
 {
 	sys->status = SD_STATUS_WAIT_FOR_JOIN;
@@ -929,7 +1005,7 @@ int sd_reconnect_handler(void)
 		return -1;
 	if (send_join_request(&sys->this_node) != 0)
 		return -1;
-
+	requeue_cluster_request();
 	return 0;
 }
 
diff --git a/sheep/sheep_priv.h b/sheep/sheep_priv.h
index 382d246..1f002bf 100644
--- a/sheep/sheep_priv.h
+++ b/sheep/sheep_priv.h
@@ -55,6 +55,13 @@ struct client_info {
 	int refcnt;
 };
 
+enum REQUST_STATUS {
+	REQUEST_INIT,
+	REQUEST_QUEUED,
+	REQUEST_DONE,
+	REQUEST_DROPPED
+};
+
 struct request {
 	struct sd_req rq;
 	struct sd_rsp rp;
@@ -77,6 +84,7 @@ struct request {
 	struct vnode_info *vinfo;
 
 	struct work work;
+	enum REQUST_STATUS status;
 };
 
 struct system_info {
-- 
1.7.9.5




More information about the sheepdog mailing list