svn commit: r310751 - in stable/10/sys/dev/hyperv: include vmbus

Sepherosa Ziehau sephe at FreeBSD.org
Thu Dec 29 06:59:25 UTC 2016


Author: sephe
Date: Thu Dec 29 06:59:24 2016
New Revision: 310751
URL: https://svnweb.freebsd.org/changeset/base/310751

Log:
  MFC 309128,309129,309131-309136,309138-309140,309224,309225
  
  309128
      hyperv/vmbus: Commit the GPADL id only after the connection succeeds.
  
      Minor style change.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8563
  
  309129
      hyperv/vmbus: Minor style changes.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8564
  
  309131
      hyperv/vmbus: Fix sysctl tree leakage, if channel open fails.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8565
  
  309132
      hyperv/vmbus: Don't close unopened channels.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8566
  
  309133
      hyperv/vmbus: GPADL disconnect error on a revoked channel is benign.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8567
  
  309134
      hyperv/vmbus: No stranded bufring GPADL is allowed.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8568
  
  309135
      hyperv/vmbus: Return EISCONN if the bufring GPADL can't be disconnected.
  
      So that the callers of vmbus_chan_open_br() could handle the passed in
      bufring memory properly.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8569
  
  309136
      hyperv/vmbus: Don't free the bufring if its GPADL can't be disconnected.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8570
  
  309138
      hyperv/vmbus: Always try disconnect/free bufring memory upon channel close
  
      While I'm here, minor wording and style changes.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8598
  
  309139
      hyperv/vmbus: Propagate close error.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8599
  
  309140
      hyperv/vmbus: Add a simplified version of channel close.
  
      So that the caller can know the channel close error and react accordingly.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8600
  
  309224
      hyperv/vmbus: Zero out GPADL if error happens.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8601
  
  309225
      hyperv/vmbus: Add supportive transaction wait function.
  
      This function supports channel revocation properly.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8611

Modified:
  stable/10/sys/dev/hyperv/include/vmbus.h
  stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- stable/10/sys/dev/hyperv/include/vmbus.h	Thu Dec 29 06:58:51 2016	(r310750)
+++ stable/10/sys/dev/hyperv/include/vmbus.h	Thu Dec 29 06:59:24 2016	(r310751)
@@ -117,6 +117,7 @@ struct vmbus_chan_br {
 };
 
 struct vmbus_channel;
+struct vmbus_xact;
 struct vmbus_xact_ctx;
 struct hyperv_guid;
 struct task;
@@ -130,6 +131,36 @@ vmbus_get_channel(device_t dev)
 	return device_get_ivars(dev);
 }
 
+/*
+ * vmbus_chan_open_br()
+ *
+ * Return values:
+ * 0			Succeeded.
+ * EISCONN		Failed, and the memory passed through 'br' is still
+ *			connected.  Callers must _not_ free the the memory
+ *			passed through 'br', if this error happens.
+ * other values		Failed.  The memory passed through 'br' is no longer
+ *			connected.  Callers are free to do anything with the
+ *			memory passed through 'br'.
+ *
+ *
+ *
+ * vmbus_chan_close_direct()
+ *
+ * NOTE:
+ * Callers of this function _must_ make sure to close all sub-channels before
+ * closing the primary channel.
+ *
+ * Return values:
+ * 0			Succeeded.
+ * EISCONN		Failed, and the memory associated with the bufring
+ *			is still connected.  Callers must _not_ free the the
+ *			memory associated with the bufring, if this error
+ *			happens.
+ * other values		Failed.  The memory associated with the bufring is
+ *			no longer connected.  Callers are free to do anything
+ *			with the memory associated with the bufring.
+ */
 int		vmbus_chan_open(struct vmbus_channel *chan,
 		    int txbr_size, int rxbr_size, const void *udata, int udlen,
 		    vmbus_chan_callback_t cb, void *cbarg);
@@ -137,12 +168,15 @@ int		vmbus_chan_open_br(struct vmbus_cha
 		    const struct vmbus_chan_br *cbr, const void *udata,
 		    int udlen, vmbus_chan_callback_t cb, void *cbarg);
 void		vmbus_chan_close(struct vmbus_channel *chan);
+int		vmbus_chan_close_direct(struct vmbus_channel *chan);
 void		vmbus_chan_intr_drain(struct vmbus_channel *chan);
 void		vmbus_chan_run_task(struct vmbus_channel *chan,
 		    struct task *task);
 void		vmbus_chan_set_orphan(struct vmbus_channel *chan,
 		    struct vmbus_xact_ctx *);
 void		vmbus_chan_unset_orphan(struct vmbus_channel *chan);
+const void	*vmbus_chan_xact_wait(const struct vmbus_channel *chan,
+		    struct vmbus_xact *xact, size_t *resp_len, bool can_sleep);
 
 int		vmbus_chan_gpadl_connect(struct vmbus_channel *chan,
 		    bus_addr_t paddr, int size, uint32_t *gpadl);

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Thu Dec 29 06:58:51 2016	(r310750)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Thu Dec 29 06:59:24 2016	(r310751)
@@ -53,7 +53,7 @@ __FBSDID("$FreeBSD$");
 static void			vmbus_chan_update_evtflagcnt(
 				    struct vmbus_softc *,
 				    const struct vmbus_channel *);
-static void			vmbus_chan_close_internal(
+static int			vmbus_chan_close_internal(
 				    struct vmbus_channel *);
 static int			vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS);
 static void			vmbus_chan_sysctl_create(
@@ -66,6 +66,8 @@ static int			vmbus_chan_release(struct v
 static void			vmbus_chan_set_chmap(struct vmbus_channel *);
 static void			vmbus_chan_clear_chmap(struct vmbus_channel *);
 static void			vmbus_chan_detach(struct vmbus_channel *);
+static bool			vmbus_chan_wait_revoke(
+				    const struct vmbus_channel *);
 
 static void			vmbus_chan_ins_prilist(struct vmbus_softc *,
 				    struct vmbus_channel *);
@@ -322,7 +324,21 @@ vmbus_chan_open(struct vmbus_channel *ch
 
 	error = vmbus_chan_open_br(chan, &cbr, udata, udlen, cb, cbarg);
 	if (error) {
-		hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
+		if (error == EISCONN) {
+			/*
+			 * XXX
+			 * The bufring GPADL is still connected; abandon
+			 * this bufring, instead of having mysterious
+			 * crash or trashed data later on.
+			 */
+			vmbus_chan_printf(chan, "chan%u bufring GPADL "
+			    "is still connected upon channel open error; "
+			    "leak %d bytes memory\n", chan->ch_id,
+			    txbr_size + rxbr_size);
+		} else {
+			hyperv_dmamem_free(&chan->ch_bufring_dma,
+			    chan->ch_bufring);
+		}
 		chan->ch_bufring = NULL;
 	}
 	return (error);
@@ -345,7 +361,7 @@ vmbus_chan_open_br(struct vmbus_channel 
 	if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
 		vmbus_chan_printf(chan,
 		    "invalid udata len %d for chan%u\n", udlen, chan->ch_id);
-		return EINVAL;
+		return (EINVAL);
 	}
 
 	br = cbr->cbr;
@@ -390,6 +406,8 @@ vmbus_chan_open_br(struct vmbus_channel 
 	/*
 	 * Connect the bufrings, both RX and TX, to this channel.
 	 */
+	KASSERT(chan->ch_bufring_gpadl == 0,
+	    ("bufring GPADL is still connected"));
 	error = vmbus_chan_gpadl_connect(chan, cbr->cbr_paddr,
 	    txbr_size + rxbr_size, &chan->ch_bufring_gpadl);
 	if (error) {
@@ -442,23 +460,33 @@ vmbus_chan_open_br(struct vmbus_channel 
 	vmbus_msghc_put(sc, mh);
 
 	if (status == 0) {
-		if (bootverbose) {
+		if (bootverbose)
 			vmbus_chan_printf(chan, "chan%u opened\n", chan->ch_id);
-		}
-		return 0;
+		return (0);
 	}
 
 	vmbus_chan_printf(chan, "failed to open chan%u\n", chan->ch_id);
 	error = ENXIO;
 
 failed:
+	sysctl_ctx_free(&chan->ch_sysctl_ctx);
 	vmbus_chan_clear_chmap(chan);
-	if (chan->ch_bufring_gpadl) {
-		vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
+	if (chan->ch_bufring_gpadl != 0) {
+		int error1;
+
+		error1 = vmbus_chan_gpadl_disconnect(chan,
+		    chan->ch_bufring_gpadl);
+		if (error1) {
+			/*
+			 * Give caller a hint that the bufring GPADL is still
+			 * connected.
+			 */
+			error = EISCONN;
+		}
 		chan->ch_bufring_gpadl = 0;
 	}
 	atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
-	return error;
+	return (error);
 }
 
 int
@@ -475,6 +503,12 @@ vmbus_chan_gpadl_connect(struct vmbus_ch
 	uint64_t page_id;
 
 	/*
+	 * Reset GPADL, so that the result would consistent, if error
+	 * happened later on.
+	 */
+	*gpadl0 = 0;
+
+	/*
 	 * Preliminary checks.
 	 */
 
@@ -500,7 +534,6 @@ vmbus_chan_gpadl_connect(struct vmbus_ch
 	 * Allocate GPADL id.
 	 */
 	gpadl = vmbus_gpadl_alloc(sc);
-	*gpadl0 = gpadl;
 
 	/*
 	 * Connect this GPADL to the target channel.
@@ -579,15 +612,35 @@ vmbus_chan_gpadl_connect(struct vmbus_ch
 		vmbus_chan_printf(chan, "gpadl_conn(chan%u) failed: %u\n",
 		    chan->ch_id, status);
 		return EIO;
-	} else {
-		if (bootverbose) {
-			vmbus_chan_printf(chan,
-			    "gpadl_conn(chan%u) succeeded\n", chan->ch_id);
-		}
+	}
+
+	/* Done; commit the GPADL id. */
+	*gpadl0 = gpadl;
+	if (bootverbose) {
+		vmbus_chan_printf(chan, "gpadl_conn(chan%u) succeeded\n",
+		    chan->ch_id);
 	}
 	return 0;
 }
 
+static bool
+vmbus_chan_wait_revoke(const struct vmbus_channel *chan)
+{
+#define WAIT_COUNT	200	/* 200ms */
+
+	int i;
+
+	for (i = 0; i < WAIT_COUNT; ++i) {
+		if (vmbus_chan_is_revoked(chan))
+			return (true);
+		/* Not sure about the context; use busy-wait. */
+		DELAY(1000);
+	}
+	return (false);
+
+#undef WAIT_COUNT
+}
+
 /*
  * Disconnect the GPA from the target channel
  */
@@ -604,7 +657,7 @@ vmbus_chan_gpadl_disconnect(struct vmbus
 		vmbus_chan_printf(chan,
 		    "can not get msg hypercall for gpadl_disconn(chan%u)\n",
 		    chan->ch_id);
-		return EBUSY;
+		return (EBUSY);
 	}
 
 	req = vmbus_msghc_dataptr(mh);
@@ -614,18 +667,29 @@ vmbus_chan_gpadl_disconnect(struct vmbus
 
 	error = vmbus_msghc_exec(sc, mh);
 	if (error) {
+		vmbus_msghc_put(sc, mh);
+
+		if (vmbus_chan_wait_revoke(chan)) {
+			/*
+			 * Error is benign; this channel is revoked,
+			 * so this GPADL will not be touched anymore.
+			 */
+			vmbus_chan_printf(chan,
+			    "gpadl_disconn(revoked chan%u) msg hypercall "
+			    "exec failed: %d\n", chan->ch_id, error);
+			return (0);
+		}
 		vmbus_chan_printf(chan,
 		    "gpadl_disconn(chan%u) msg hypercall exec failed: %d\n",
 		    chan->ch_id, error);
-		vmbus_msghc_put(sc, mh);
-		return error;
+		return (error);
 	}
 
 	vmbus_msghc_wait_result(sc, mh);
 	/* Discard result; no useful information */
 	vmbus_msghc_put(sc, mh);
 
-	return 0;
+	return (0);
 }
 
 static void
@@ -681,16 +745,34 @@ vmbus_chan_set_chmap(struct vmbus_channe
 	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = chan;
 }
 
-static void
+static int
 vmbus_chan_close_internal(struct vmbus_channel *chan)
 {
 	struct vmbus_softc *sc = chan->ch_vmbus;
 	struct vmbus_msghc *mh;
 	struct vmbus_chanmsg_chclose *req;
+	uint32_t old_stflags;
 	int error;
 
-	/* TODO: stringent check */
-	atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
+	/*
+	 * NOTE:
+	 * Sub-channels are closed upon their primary channel closing,
+	 * so they can be closed even before they are opened.
+	 */
+	for (;;) {
+		old_stflags = chan->ch_stflags;
+		if (atomic_cmpset_int(&chan->ch_stflags, old_stflags,
+		    old_stflags & ~VMBUS_CHAN_ST_OPENED))
+			break;
+	}
+	if ((old_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
+		/* Not opened yet; done */
+		if (bootverbose) {
+			vmbus_chan_printf(chan, "chan%u not opened\n",
+			    chan->ch_id);
+		}
+		return (0);
+	}
 
 	/*
 	 * Free this channel's sysctl tree attached to its device's
@@ -716,7 +798,8 @@ vmbus_chan_close_internal(struct vmbus_c
 		vmbus_chan_printf(chan,
 		    "can not get msg hypercall for chclose(chan%u)\n",
 		    chan->ch_id);
-		return;
+		error = ENXIO;
+		goto disconnect;
 	}
 
 	req = vmbus_msghc_dataptr(mh);
@@ -730,16 +813,37 @@ vmbus_chan_close_internal(struct vmbus_c
 		vmbus_chan_printf(chan,
 		    "chclose(chan%u) msg hypercall exec failed: %d\n",
 		    chan->ch_id, error);
-		return;
-	} else if (bootverbose) {
-		vmbus_chan_printf(chan, "close chan%u\n", chan->ch_id);
+		goto disconnect;
 	}
 
+	if (bootverbose)
+		vmbus_chan_printf(chan, "chan%u closed\n", chan->ch_id);
+
+disconnect:
 	/*
 	 * Disconnect the TX+RX bufrings from this channel.
 	 */
-	if (chan->ch_bufring_gpadl) {
-		vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
+	if (chan->ch_bufring_gpadl != 0) {
+		int error1;
+
+		error1 = vmbus_chan_gpadl_disconnect(chan,
+		    chan->ch_bufring_gpadl);
+		if (error1) {
+			/*
+			 * XXX
+			 * The bufring GPADL is still connected; abandon
+			 * this bufring, instead of having mysterious
+			 * crash or trashed data later on.
+			 */
+			vmbus_chan_printf(chan, "chan%u bufring GPADL "
+			    "is still connected after close\n", chan->ch_id);
+			chan->ch_bufring = NULL;
+			/*
+			 * Give caller a hint that the bufring GPADL is
+			 * still connected.
+			 */
+			error = EISCONN;
+		}
 		chan->ch_bufring_gpadl = 0;
 	}
 
@@ -750,6 +854,42 @@ vmbus_chan_close_internal(struct vmbus_c
 		hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
 		chan->ch_bufring = NULL;
 	}
+	return (error);
+}
+
+int
+vmbus_chan_close_direct(struct vmbus_channel *chan)
+{
+	int error;
+
+#ifdef INVARIANTS
+	if (VMBUS_CHAN_ISPRIMARY(chan)) {
+		struct vmbus_channel *subchan;
+
+		/*
+		 * All sub-channels _must_ have been closed, or are _not_
+		 * opened at all.
+		 */
+		mtx_lock(&chan->ch_subchan_lock);
+		TAILQ_FOREACH(subchan, &chan->ch_subchans, ch_sublink) {
+			KASSERT(
+			   (subchan->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0,
+			   ("chan%u: subchan%u is still opened",
+			    chan->ch_id, subchan->ch_subidx));
+		}
+		mtx_unlock(&chan->ch_subchan_lock);
+	}
+#endif
+
+	error = vmbus_chan_close_internal(chan);
+	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
+		/*
+		 * This sub-channel is referenced, when it is linked to
+		 * the primary channel; drop that reference now.
+		 */
+		vmbus_chan_detach(chan);
+	}
+	return (error);
 }
 
 /*
@@ -1787,3 +1927,37 @@ vmbus_chan_unset_orphan(struct vmbus_cha
 	chan->ch_orphan_xact = NULL;
 	sx_xunlock(&chan->ch_orphan_lock);
 }
+
+const void *
+vmbus_chan_xact_wait(const struct vmbus_channel *chan,
+    struct vmbus_xact *xact, size_t *resp_len, bool can_sleep)
+{
+	const void *ret;
+
+	if (can_sleep)
+		ret = vmbus_xact_wait(xact, resp_len);
+	else
+		ret = vmbus_xact_busywait(xact, resp_len);
+	if (vmbus_chan_is_revoked(chan)) {
+		/*
+		 * This xact probably is interrupted, and the
+		 * interruption can race the reply reception,
+		 * so we have to make sure that there are nothing
+		 * left on the RX bufring, i.e. this xact will
+		 * not be touched, once this function returns.
+		 *
+		 * Since the hypervisor will not put more data
+		 * onto the RX bufring once the channel is revoked,
+		 * the following loop will be terminated, once all
+		 * data are drained by the driver's channel
+		 * callback.
+		 */
+		while (!vmbus_chan_rx_empty(chan)) {
+			if (can_sleep)
+				pause("chxact", 1);
+			else
+				DELAY(1000);
+		}
+	}
+	return (ret);
+}


More information about the svn-src-all mailing list