Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 12 Jul 2016 08:38:03 +0000 (UTC)
From:      Sepherosa Ziehau <sephe@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r302634 - in head/sys/dev/hyperv: include netvsc vmbus
Message-ID:  <201607120838.u6C8c3iV029550@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sephe
Date: Tue Jul 12 08:38:03 2016
New Revision: 302634
URL: https://svnweb.freebsd.org/changeset/base/302634

Log:
  hyperv/vmbus: Fix sub-channel re-open support.
  
  For multi-channel devices, once the primary channel is closed,
  a set of 'rescind' messages for sub-channels will be delivered
  by Hypervisor.  Sub-channel MUST be freed according to these
  'rescind' messages; directly re-openning sub-channels in the
  same fashion as the primary channel's re-opening does NOT work
  at all.
  
  After the primary channel is re-opened, requested # of sub-
  channels will be delivered though 'channel offer' messages, and
  this set of newly offered channels can be opened along side with
  the primary channel.
  
  This unbreaks the MTU setting for hn(4), which requires re-
  openning all existsing channels upon MTU change.
  
  MFC after:	1 week
  Sponsored by:	Microsoft OSTC
  Differential Revision:	https://reviews.freebsd.org/D6978

Modified:
  head/sys/dev/hyperv/include/hyperv.h
  head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
  head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
  head/sys/dev/hyperv/vmbus/vmbus_reg.h

Modified: head/sys/dev/hyperv/include/hyperv.h
==============================================================================
--- head/sys/dev/hyperv/include/hyperv.h	Tue Jul 12 08:28:51 2016	(r302633)
+++ head/sys/dev/hyperv/include/hyperv.h	Tue Jul 12 08:38:03 2016	(r302634)
@@ -717,6 +717,7 @@ void		vmbus_channel_cpu_rr(struct hv_vmb
 struct hv_vmbus_channel **
 		vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt);
 void		vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt);
+void		vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan);
 
 /**
  * @brief Get physical address from virtual

Modified: head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c	Tue Jul 12 08:28:51 2016	(r302633)
+++ head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c	Tue Jul 12 08:38:03 2016	(r302634)
@@ -345,6 +345,7 @@ static void hn_destroy_rx_data(struct hn
 static void hn_set_tx_chimney_size(struct hn_softc *, int);
 static void hn_channel_attach(struct hn_softc *, struct hv_vmbus_channel *);
 static void hn_subchan_attach(struct hn_softc *, struct hv_vmbus_channel *);
+static void hn_subchan_setup(struct hn_softc *);
 
 static int hn_transmit(struct ifnet *, struct mbuf *);
 static void hn_xmit_qflush(struct ifnet *);
@@ -575,25 +576,8 @@ netvsc_attach(device_t dev)
 	device_printf(dev, "%d TX ring, %d RX ring\n",
 	    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
 
-	if (sc->net_dev->num_channel > 1) {
-		struct hv_vmbus_channel **subchan;
-		int subchan_cnt = sc->net_dev->num_channel - 1;
-		int i;
-
-		/* Wait for sub-channels setup to complete. */
-		subchan = vmbus_get_subchan(pri_chan, subchan_cnt);
-
-		/* Attach the sub-channels. */
-		for (i = 0; i < subchan_cnt; ++i) {
-			/* NOTE: Calling order is critical. */
-			hn_subchan_attach(sc, subchan[i]);
-			hv_nv_subchan_attach(subchan[i]);
-		}
-
-		/* Release the sub-channels */
-		vmbus_rel_subchan(subchan, subchan_cnt);
-		device_printf(dev, "%d sub-channels setup done\n", subchan_cnt);
-	}
+	if (sc->net_dev->num_channel > 1)
+		hn_subchan_setup(sc);
 
 #if __FreeBSD_version >= 1100099
 	if (sc->hn_rx_ring_inuse > 1) {
@@ -1620,6 +1604,10 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 			NV_UNLOCK(sc);
 			break;
 		}
+
+		/* Wait for subchannels to be destroyed */
+		vmbus_drain_subchan(hn_dev->channel);
+
 		error = hv_rf_on_device_add(hn_dev, &device_info,
 		    sc->hn_rx_ring_inuse);
 		if (error) {
@@ -1628,6 +1616,26 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 			NV_UNLOCK(sc);
 			break;
 		}
+		KASSERT(sc->hn_rx_ring_cnt == sc->net_dev->num_channel,
+		    ("RX ring count %d and channel count %u mismatch",
+		     sc->hn_rx_ring_cnt, sc->net_dev->num_channel));
+		if (sc->net_dev->num_channel > 1) {
+			int r;
+
+			/*
+			 * Skip the rings on primary channel; they are
+			 * handled by the hv_rf_on_device_add() above.
+			 */
+			for (r = 1; r < sc->hn_rx_ring_cnt; ++r) {
+				sc->hn_rx_ring[r].hn_rx_flags &=
+				    ~HN_RX_FLAG_ATTACHED;
+			}
+			for (r = 1; r < sc->hn_tx_ring_cnt; ++r) {
+				sc->hn_tx_ring[r].hn_tx_flags &=
+				    ~HN_TX_FLAG_ATTACHED;
+			}
+			hn_subchan_setup(sc);
+		}
 
 		sc->hn_tx_chimney_max = sc->net_dev->send_section_size;
 		if (sc->hn_tx_ring[0].hn_tx_chimney_size >
@@ -2980,6 +2988,29 @@ hn_subchan_attach(struct hn_softc *sc, s
 }
 
 static void
+hn_subchan_setup(struct hn_softc *sc)
+{
+	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
+	struct hv_vmbus_channel **subchan;
+	int subchan_cnt = sc->net_dev->num_channel - 1;
+	int i;
+
+	/* Wait for sub-channels setup to complete. */
+	subchan = vmbus_get_subchan(device_ctx->channel, subchan_cnt);
+
+	/* Attach the sub-channels. */
+	for (i = 0; i < subchan_cnt; ++i) {
+		/* NOTE: Calling order is critical. */
+		hn_subchan_attach(sc, subchan[i]);
+		hv_nv_subchan_attach(subchan[i]);
+	}
+
+	/* Release the sub-channels */
+	vmbus_rel_subchan(subchan, subchan_cnt);
+	if_printf(sc->hn_ifp, "%d sub-channels setup done\n", subchan_cnt);
+}
+
+static void
 hn_tx_taskq_create(void *arg __unused)
 {
 	if (!hn_share_tx_taskq)

Modified: head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c	Tue Jul 12 08:28:51 2016	(r302633)
+++ head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c	Tue Jul 12 08:38:03 2016	(r302634)
@@ -367,6 +367,54 @@ vmbus_chan_detach_task(void *xchan, int 
 	if (HV_VMBUS_CHAN_ISPRIMARY(chan)) {
 		/* Only primary channel owns the hv_device */
 		hv_vmbus_child_device_unregister(chan->device);
+		/* NOTE: DO NOT free primary channel for now */
+	} else {
+		struct vmbus_softc *sc = chan->vmbus_sc;
+		struct hv_vmbus_channel *pri_chan = chan->primary_channel;
+		struct vmbus_chanmsg_chfree *req;
+		struct vmbus_msghc *mh;
+		int error;
+
+		mh = vmbus_msghc_get(sc, sizeof(*req));
+		if (mh == NULL) {
+			device_printf(sc->vmbus_dev,
+			    "can not get msg hypercall for chfree(chan%u)\n",
+			    chan->offer_msg.child_rel_id);
+			goto remove;
+		}
+
+		req = vmbus_msghc_dataptr(mh);
+		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
+		req->chm_chanid = chan->offer_msg.child_rel_id;
+
+		error = vmbus_msghc_exec_noresult(mh);
+		vmbus_msghc_put(sc, mh);
+
+		if (error) {
+			device_printf(sc->vmbus_dev,
+			    "chfree(chan%u) failed: %d",
+			    chan->offer_msg.child_rel_id, error);
+			/* NOTE: Move on! */
+		} else {
+			if (bootverbose) {
+				device_printf(sc->vmbus_dev, "chan%u freed\n",
+				    chan->offer_msg.child_rel_id);
+			}
+		}
+remove:
+		mtx_lock(&sc->vmbus_chlist_lock);
+		TAILQ_REMOVE(&sc->vmbus_chlist, chan, ch_link);
+		mtx_unlock(&sc->vmbus_chlist_lock);
+
+		mtx_lock(&pri_chan->sc_lock);
+		TAILQ_REMOVE(&pri_chan->sc_list_anchor, chan, sc_list_entry);
+		KASSERT(pri_chan->subchan_cnt > 0,
+		    ("invalid subchan_cnt %d", pri_chan->subchan_cnt));
+		pri_chan->subchan_cnt--;
+		mtx_unlock(&pri_chan->sc_lock);
+		wakeup(pri_chan);
+
+		hv_vmbus_free_vmbus_channel(chan);
 	}
 }
 
@@ -504,6 +552,15 @@ vmbus_rel_subchan(struct hv_vmbus_channe
 }
 
 void
+vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
+{
+	mtx_lock(&pri_chan->sc_lock);
+	while (pri_chan->subchan_cnt > 0)
+		mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "dsubch", 0);
+	mtx_unlock(&pri_chan->sc_lock);
+}
+
+void
 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
 {
 	vmbus_chanmsg_proc_t msg_proc;

Modified: head/sys/dev/hyperv/vmbus/vmbus_reg.h
==============================================================================
--- head/sys/dev/hyperv/vmbus/vmbus_reg.h	Tue Jul 12 08:28:51 2016	(r302633)
+++ head/sys/dev/hyperv/vmbus/vmbus_reg.h	Tue Jul 12 08:38:03 2016	(r302634)
@@ -102,6 +102,7 @@ struct vmbus_gpa_range {
 #define VMBUS_CHANMSG_TYPE_GPADL_CONNRESP	10	/* RESP */
 #define VMBUS_CHANMSG_TYPE_GPADL_DISCONN	11	/* REQ */
 #define VMBUS_CHANMSG_TYPE_GPADL_DISCONNRESP	12	/* RESP */
+#define VMBUS_CHANMSG_TYPE_CHFREE		13	/* REQ */
 #define VMBUS_CHANMSG_TYPE_CONNECT		14	/* REQ */
 #define VMBUS_CHANMSG_TYPE_CONNECT_RESP		15	/* RESP */
 #define VMBUS_CHANMSG_TYPE_DISCONNECT		16	/* REQ */
@@ -206,4 +207,10 @@ struct vmbus_chanmsg_gpadl_disconn {
 	uint32_t	chm_gpadl;
 } __packed;
 
+/* VMBUS_CHANMSG_TYPE_CHFREE */
+struct vmbus_chanmsg_chfree {
+	struct vmbus_chanmsg_hdr chm_hdr;
+	uint32_t	chm_chanid;
+} __packed;
+
 #endif	/* !_VMBUS_REG_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607120838.u6C8c3iV029550>