svn commit: r274076 - head/usr.sbin/bhyve

Tycho Nightingale tychon at FreeBSD.org
Tue Nov 4 01:06:34 UTC 2014


Author: tychon
Date: Tue Nov  4 01:06:33 2014
New Revision: 274076
URL: https://svnweb.freebsd.org/changeset/base/274076

Log:
  Improve the ability to cancel an in-flight request by using an
  interrupt, via SIGCONT, to force the read or write system call to
  return prematurely.
  
  Reviewed by:	grehan

Modified:
  head/usr.sbin/bhyve/block_if.c

Modified: head/usr.sbin/bhyve/block_if.c
==============================================================================
--- head/usr.sbin/bhyve/block_if.c	Tue Nov  4 00:56:25 2014	(r274075)
+++ head/usr.sbin/bhyve/block_if.c	Tue Nov  4 01:06:33 2014	(r274076)
@@ -43,9 +43,13 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <pthread.h>
 #include <pthread_np.h>
+#include <signal.h>
 #include <unistd.h>
 
+#include <machine/atomic.h>
+
 #include "bhyverun.h"
+#include "mevent.h"
 #include "block_if.h"
 
 #define BLOCKIF_SIG	0xb109b109
@@ -60,7 +64,9 @@ enum blockop {
 
 enum blockstat {
 	BST_FREE,
-	BST_INUSE
+	BST_PEND,
+	BST_BUSY,
+	BST_DONE
 };
 
 struct blockif_elem {
@@ -68,6 +74,7 @@ struct blockif_elem {
 	struct blockif_req  *be_req;
 	enum blockop	     be_op;
 	enum blockstat	     be_status;
+	pthread_t            be_tid;
 };
 
 struct blockif_ctxt {
@@ -81,13 +88,25 @@ struct blockif_ctxt {
         pthread_cond_t		bc_cond;
 	int			bc_closing;
 
-	/* Request elements and free/inuse queues */
+	/* Request elements and free/pending/busy queues */
 	TAILQ_HEAD(, blockif_elem) bc_freeq;       
-	TAILQ_HEAD(, blockif_elem) bc_inuseq;       
+	TAILQ_HEAD(, blockif_elem) bc_pendq;
+	TAILQ_HEAD(, blockif_elem) bc_busyq;
 	u_int			bc_req_count;
 	struct blockif_elem	bc_reqs[BLOCKIF_MAXREQ];
 };
 
+static pthread_once_t blockif_once = PTHREAD_ONCE_INIT;
+
+struct blockif_sig_elem {
+	pthread_mutex_t			bse_mtx;
+	pthread_cond_t			bse_cond;
+	int				bse_pending;
+	struct blockif_sig_elem		*bse_next;
+};
+
+static struct blockif_sig_elem *blockif_bse_head;
+
 static int
 blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
 		enum blockop op)
@@ -101,10 +120,10 @@ blockif_enqueue(struct blockif_ctxt *bc,
 	assert(be->be_status == BST_FREE);
 
 	TAILQ_REMOVE(&bc->bc_freeq, be, be_link);
-	be->be_status = BST_INUSE;
+	be->be_status = BST_PEND;
 	be->be_req = breq;
 	be->be_op = op;
-	TAILQ_INSERT_TAIL(&bc->bc_inuseq, be, be_link);
+	TAILQ_INSERT_TAIL(&bc->bc_pendq, be, be_link);
 
 	bc->bc_req_count++;
 
@@ -112,26 +131,38 @@ blockif_enqueue(struct blockif_ctxt *bc,
 }
 
 static int
-blockif_dequeue(struct blockif_ctxt *bc, struct blockif_elem *el)
+blockif_dequeue(struct blockif_ctxt *bc, struct blockif_elem **bep)
 {
 	struct blockif_elem *be;
 
 	if (bc->bc_req_count == 0)
 		return (ENOENT);
 
-	be = TAILQ_FIRST(&bc->bc_inuseq);
+	be = TAILQ_FIRST(&bc->bc_pendq);
 	assert(be != NULL);
-	assert(be->be_status == BST_INUSE);
-	*el = *be;
+	assert(be->be_status == BST_PEND);
+	TAILQ_REMOVE(&bc->bc_pendq, be, be_link);
+	be->be_status = BST_BUSY;
+	be->be_tid = bc->bc_btid;
+	TAILQ_INSERT_TAIL(&bc->bc_busyq, be, be_link);
+
+	*bep = be;
 
-	TAILQ_REMOVE(&bc->bc_inuseq, be, be_link);
+	return (0);
+}
+
+static void
+blockif_complete(struct blockif_ctxt *bc, struct blockif_elem *be)
+{
+	assert(be->be_status == BST_DONE);
+
+	TAILQ_REMOVE(&bc->bc_busyq, be, be_link);
+	be->be_tid = 0;
 	be->be_status = BST_FREE;
 	be->be_req = NULL;
 	TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
-	
-	bc->bc_req_count--;
 
-	return (0);
+	bc->bc_req_count--;
 }
 
 static void
@@ -163,6 +194,8 @@ blockif_proc(struct blockif_ctxt *bc, st
 		break;
 	}
 
+	be->be_status = BST_DONE;
+
 	(*br->br_callback)(br, err);
 }
 
@@ -170,16 +203,17 @@ static void *
 blockif_thr(void *arg)
 {
 	struct blockif_ctxt *bc;
-	struct blockif_elem req;
+	struct blockif_elem *be;
 
 	bc = arg;
 
 	for (;;) {
 		pthread_mutex_lock(&bc->bc_mtx);
-		while (!blockif_dequeue(bc, &req)) {
+		while (!blockif_dequeue(bc, &be)) {
 			pthread_mutex_unlock(&bc->bc_mtx);
-			blockif_proc(bc, &req);
+			blockif_proc(bc, be);
 			pthread_mutex_lock(&bc->bc_mtx);
+			blockif_complete(bc, be);
 		}
 		pthread_cond_wait(&bc->bc_cond, &bc->bc_mtx);
 		pthread_mutex_unlock(&bc->bc_mtx);
@@ -195,6 +229,38 @@ blockif_thr(void *arg)
 	return (NULL);
 }
 
+static void
+blockif_sigcont_handler(int signal, enum ev_type type, void *arg)
+{
+	struct blockif_sig_elem *bse;
+
+	for (;;) {
+		/*
+		 * Process the entire list even if not intended for
+		 * this thread.
+		 */
+		do {
+			bse = blockif_bse_head;
+			if (bse == NULL)
+				return;
+		} while (!atomic_cmpset_ptr((uintptr_t *)&blockif_bse_head,
+					    (uintptr_t)bse,
+					    (uintptr_t)bse->bse_next));
+
+		pthread_mutex_lock(&bse->bse_mtx);
+		bse->bse_pending = 0;
+		pthread_cond_signal(&bse->bse_cond);
+		pthread_mutex_unlock(&bse->bse_mtx);
+	}
+}
+
+static void
+blockif_init(void)
+{
+	mevent_add(SIGCONT, EVF_SIGNAL, blockif_sigcont_handler, NULL);
+	(void) signal(SIGCONT, SIG_IGN);
+}
+
 struct blockif_ctxt *
 blockif_open(const char *optstr, const char *ident)
 {
@@ -206,6 +272,8 @@ blockif_open(const char *optstr, const c
 	int extra, fd, i, sectsz;
 	int nocache, sync, ro;
 
+	pthread_once(&blockif_once, blockif_init);
+
 	nocache = 0;
 	sync = 0;
 	ro = 0;
@@ -280,7 +348,8 @@ blockif_open(const char *optstr, const c
 	pthread_mutex_init(&bc->bc_mtx, NULL);
 	pthread_cond_init(&bc->bc_cond, NULL);
 	TAILQ_INIT(&bc->bc_freeq);
-	TAILQ_INIT(&bc->bc_inuseq);
+	TAILQ_INIT(&bc->bc_pendq);
+	TAILQ_INIT(&bc->bc_busyq);
 	bc->bc_req_count = 0;
 	for (i = 0; i < BLOCKIF_MAXREQ; i++) {
 		bc->bc_reqs[i].be_status = BST_FREE;
@@ -357,23 +426,76 @@ blockif_cancel(struct blockif_ctxt *bc, 
 	assert(bc->bc_magic == BLOCKIF_SIG);
 
 	pthread_mutex_lock(&bc->bc_mtx);
-	TAILQ_FOREACH(be, &bc->bc_inuseq, be_link) {
+	/*
+	 * Check pending requests.
+	 */
+	TAILQ_FOREACH(be, &bc->bc_pendq, be_link) {
+		if (be->be_req == breq)
+			break;
+	}
+	if (be != NULL) {
+		/*
+		 * Found it.
+		 */
+		TAILQ_REMOVE(&bc->bc_pendq, be, be_link);
+		be->be_status = BST_FREE;
+		be->be_req = NULL;
+		TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
+		bc->bc_req_count--;
+		pthread_mutex_unlock(&bc->bc_mtx);
+
+		return (0);
+	}
+
+	/*
+	 * Check in-flight requests.
+	 */
+	TAILQ_FOREACH(be, &bc->bc_busyq, be_link) {
 		if (be->be_req == breq)
 			break;
 	}
 	if (be == NULL) {
+		/*
+		 * Didn't find it.
+		 */
 		pthread_mutex_unlock(&bc->bc_mtx);
 		return (EINVAL);
 	}
 
-	TAILQ_REMOVE(&bc->bc_inuseq, be, be_link);
-	be->be_status = BST_FREE;
-	be->be_req = NULL;
-	TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
-	bc->bc_req_count--;
+	/*
+	 * Interrupt the processing thread to force it return
+	 * prematurely via it's normal callback path.
+	 */
+	while (be->be_status == BST_BUSY) {
+		struct blockif_sig_elem bse, *old_head;
+
+		pthread_mutex_init(&bse.bse_mtx, NULL);
+		pthread_cond_init(&bse.bse_cond, NULL);
+
+		bse.bse_pending = 1;
+
+		do {
+			old_head = blockif_bse_head;
+			bse.bse_next = old_head;
+		} while (!atomic_cmpset_ptr((uintptr_t *)&blockif_bse_head,
+					    (uintptr_t)old_head,
+					    (uintptr_t)&bse));
+
+		pthread_kill(be->be_tid, SIGCONT);
+
+		pthread_mutex_lock(&bse.bse_mtx);
+		while (bse.bse_pending)
+			pthread_cond_wait(&bse.bse_cond, &bse.bse_mtx);
+		pthread_mutex_unlock(&bse.bse_mtx);
+	}
+
 	pthread_mutex_unlock(&bc->bc_mtx);
 
-	return (0);
+	/*
+	 * The processing thread has been interrupted.  Since it's not
+	 * clear if the callback has been invoked yet, return EBUSY.
+	 */
+	return (EBUSY);
 }
 
 int


More information about the svn-src-all mailing list