Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Oct 2016 02:42:08 +0000 (UTC)
From:      Sepherosa Ziehau <sephe@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r307249 - in stable/10/sys/dev/hyperv: include vmbus
Message-ID:  <201610140242.u9E2g8Ag054689@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sephe
Date: Fri Oct 14 02:42:08 2016
New Revision: 307249
URL: https://svnweb.freebsd.org/changeset/base/307249

Log:
  MFC 306360,306387,306389
  
  306360
      hyperv/vmbus: Add dynamic device add and remove support
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8008
  
  306387
      hyperv/vmbus: Add functions to test RX/TX bufring emptiness
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8044
  
  306389
      hyperv/vmbus: Add function to drain channel interrupt task.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8045

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

Modified: stable/10/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- stable/10/sys/dev/hyperv/include/vmbus.h	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/include/vmbus.h	Fri Oct 14 02:42:08 2016	(r307249)
@@ -134,6 +134,7 @@ 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);
+void		vmbus_chan_intr_drain(struct vmbus_channel *chan);
 
 int		vmbus_chan_gpadl_connect(struct vmbus_channel *chan,
 		    bus_addr_t paddr, int size, uint32_t *gpadl);
@@ -174,5 +175,7 @@ const struct hyperv_guid *
 		vmbus_chan_guid_inst(const struct vmbus_channel *chan);
 int		vmbus_chan_prplist_nelem(int br_size, int prpcnt_max,
 		    int dlen_max);
+bool		vmbus_chan_rx_empty(const struct vmbus_channel *chan);
+bool		vmbus_chan_tx_empty(const struct vmbus_channel *chan);
 
 #endif	/* !_VMBUS_H_ */

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus.c	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus.c	Fri Oct 14 02:42:08 2016	(r307249)
@@ -85,9 +85,7 @@ static int			vmbus_connect(struct vmbus_
 static int			vmbus_req_channels(struct vmbus_softc *sc);
 static void			vmbus_disconnect(struct vmbus_softc *);
 static int			vmbus_scan(struct vmbus_softc *);
-static void			vmbus_scan_wait(struct vmbus_softc *);
-static void			vmbus_scan_newchan(struct vmbus_softc *);
-static void			vmbus_scan_newdev(struct vmbus_softc *);
+static void			vmbus_scan_teardown(struct vmbus_softc *);
 static void			vmbus_scan_done(struct vmbus_softc *,
 				    const struct vmbus_message *);
 static void			vmbus_chanmsg_handle(struct vmbus_softc *,
@@ -395,50 +393,22 @@ vmbus_req_channels(struct vmbus_softc *s
 }
 
 static void
-vmbus_scan_newchan(struct vmbus_softc *sc)
+vmbus_scan_done_task(void *xsc, int pending __unused)
 {
-	mtx_lock(&sc->vmbus_scan_lock);
-	if ((sc->vmbus_scan_chcnt & VMBUS_SCAN_CHCNT_DONE) == 0)
-		sc->vmbus_scan_chcnt++;
-	mtx_unlock(&sc->vmbus_scan_lock);
+	struct vmbus_softc *sc = xsc;
+
+	mtx_lock(&Giant);
+	sc->vmbus_scandone = true;
+	mtx_unlock(&Giant);
+	wakeup(&sc->vmbus_scandone);
 }
 
 static void
 vmbus_scan_done(struct vmbus_softc *sc,
     const struct vmbus_message *msg __unused)
 {
-	mtx_lock(&sc->vmbus_scan_lock);
-	sc->vmbus_scan_chcnt |= VMBUS_SCAN_CHCNT_DONE;
-	mtx_unlock(&sc->vmbus_scan_lock);
-	wakeup(&sc->vmbus_scan_chcnt);
-}
-
-static void
-vmbus_scan_newdev(struct vmbus_softc *sc)
-{
-	mtx_lock(&sc->vmbus_scan_lock);
-	sc->vmbus_scan_devcnt++;
-	mtx_unlock(&sc->vmbus_scan_lock);
-	wakeup(&sc->vmbus_scan_devcnt);
-}
-
-static void
-vmbus_scan_wait(struct vmbus_softc *sc)
-{
-	uint32_t chancnt;
-
-	mtx_lock(&sc->vmbus_scan_lock);
-	while ((sc->vmbus_scan_chcnt & VMBUS_SCAN_CHCNT_DONE) == 0) {
-		mtx_sleep(&sc->vmbus_scan_chcnt, &sc->vmbus_scan_lock, 0,
-		    "waitch", 0);
-	}
-	chancnt = sc->vmbus_scan_chcnt & ~VMBUS_SCAN_CHCNT_DONE;
 
-	while (sc->vmbus_scan_devcnt != chancnt) {
-		mtx_sleep(&sc->vmbus_scan_devcnt, &sc->vmbus_scan_lock, 0,
-		    "waitdev", 0);
-	}
-	mtx_unlock(&sc->vmbus_scan_lock);
+	taskqueue_enqueue(sc->vmbus_devtq, &sc->vmbus_scandone_task);
 }
 
 static int
@@ -447,31 +417,71 @@ vmbus_scan(struct vmbus_softc *sc)
 	int error;
 
 	/*
+	 * Identify, probe and attach for non-channel devices.
+	 */
+	bus_generic_probe(sc->vmbus_dev);
+	bus_generic_attach(sc->vmbus_dev);
+
+	/*
+	 * This taskqueue serializes vmbus devices' attach and detach
+	 * for channel offer and rescind messages.
+	 */
+	sc->vmbus_devtq = taskqueue_create("vmbus dev", M_WAITOK,
+	    taskqueue_thread_enqueue, &sc->vmbus_devtq);
+	taskqueue_start_threads(&sc->vmbus_devtq, 1, PI_NET, "vmbusdev");
+	TASK_INIT(&sc->vmbus_scandone_task, 0, vmbus_scan_done_task, sc);
+
+	/*
+	 * This taskqueue handles sub-channel detach, so that vmbus
+	 * device's detach running in vmbus_devtq can drain its sub-
+	 * channels.
+	 */
+	sc->vmbus_subchtq = taskqueue_create("vmbus subch", M_WAITOK,
+	    taskqueue_thread_enqueue, &sc->vmbus_subchtq);
+	taskqueue_start_threads(&sc->vmbus_subchtq, 1, PI_NET, "vmbussch");
+
+	/*
 	 * Start vmbus scanning.
 	 */
 	error = vmbus_req_channels(sc);
 	if (error) {
 		device_printf(sc->vmbus_dev, "channel request failed: %d\n",
 		    error);
-		return error;
+		return (error);
 	}
 
 	/*
-	 * Wait for all devices are added to vmbus.
+	 * Wait for all vmbus devices from the initial channel offers to be
+	 * attached.
 	 */
-	vmbus_scan_wait(sc);
-
-	/*
-	 * Identify, probe and attach.
-	 */
-	bus_generic_probe(sc->vmbus_dev);
-	bus_generic_attach(sc->vmbus_dev);
+	GIANT_REQUIRED;
+	while (!sc->vmbus_scandone)
+		mtx_sleep(&sc->vmbus_scandone, &Giant, 0, "vmbusdev", 0);
 
 	if (bootverbose) {
 		device_printf(sc->vmbus_dev, "device scan, probe and attach "
 		    "done\n");
 	}
-	return 0;
+	return (0);
+}
+
+static void
+vmbus_scan_teardown(struct vmbus_softc *sc)
+{
+
+	GIANT_REQUIRED;
+	if (sc->vmbus_devtq != NULL) {
+		mtx_unlock(&Giant);
+		taskqueue_free(sc->vmbus_devtq);
+		mtx_lock(&Giant);
+		sc->vmbus_devtq = NULL;
+	}
+	if (sc->vmbus_subchtq != NULL) {
+		mtx_unlock(&Giant);
+		taskqueue_free(sc->vmbus_subchtq);
+		mtx_lock(&Giant);
+		sc->vmbus_subchtq = NULL;
+	}
 }
 
 static void
@@ -1000,45 +1010,35 @@ vmbus_add_child(struct vmbus_channel *ch
 {
 	struct vmbus_softc *sc = chan->ch_vmbus;
 	device_t parent = sc->vmbus_dev;
-	int error = 0;
 
-	/* New channel has been offered */
-	vmbus_scan_newchan(sc);
+	mtx_lock(&Giant);
 
 	chan->ch_dev = device_add_child(parent, NULL, -1);
 	if (chan->ch_dev == NULL) {
+		mtx_unlock(&Giant);
 		device_printf(parent, "device_add_child for chan%u failed\n",
 		    chan->ch_id);
-		error = ENXIO;
-		goto done;
+		return (ENXIO);
 	}
 	device_set_ivars(chan->ch_dev, chan);
+	device_probe_and_attach(chan->ch_dev);
 
-done:
-	/* New device has been/should be added to vmbus. */
-	vmbus_scan_newdev(sc);
-	return error;
+	mtx_unlock(&Giant);
+	return (0);
 }
 
 int
 vmbus_delete_child(struct vmbus_channel *chan)
 {
-	int error;
-
-	if (chan->ch_dev == NULL) {
-		/* Failed to add a device. */
-		return 0;
-	}
+	int error = 0;
 
-	/*
-	 * XXXKYS: Ensure that this is the opposite of
-	 * device_add_child()
-	 */
 	mtx_lock(&Giant);
-	error = device_delete_child(chan->ch_vmbus->vmbus_dev, chan->ch_dev);
+	if (chan->ch_dev != NULL) {
+		error = device_delete_child(chan->ch_vmbus->vmbus_dev,
+		    chan->ch_dev);
+	}
 	mtx_unlock(&Giant);
-
-	return error;
+	return (error);
 }
 
 static int
@@ -1110,10 +1110,11 @@ vmbus_doattach(struct vmbus_softc *sc)
 		return (0);
 	sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
 
-	mtx_init(&sc->vmbus_scan_lock, "vmbus scan", NULL, MTX_DEF);
 	sc->vmbus_gpadl = VMBUS_GPADL_START;
 	mtx_init(&sc->vmbus_prichan_lock, "vmbus prichan", NULL, MTX_DEF);
 	TAILQ_INIT(&sc->vmbus_prichans);
+	mtx_init(&sc->vmbus_chan_lock, "vmbus channel", NULL, MTX_DEF);
+	TAILQ_INIT(&sc->vmbus_chans);
 	sc->vmbus_chmap = malloc(
 	    sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX, M_DEVBUF,
 	    M_WAITOK | M_ZERO);
@@ -1177,6 +1178,7 @@ vmbus_doattach(struct vmbus_softc *sc)
 	return (ret);
 
 cleanup:
+	vmbus_scan_teardown(sc);
 	vmbus_intr_teardown(sc);
 	vmbus_dma_free(sc);
 	if (sc->vmbus_xc != NULL) {
@@ -1184,8 +1186,8 @@ cleanup:
 		sc->vmbus_xc = NULL;
 	}
 	free(sc->vmbus_chmap, M_DEVBUF);
-	mtx_destroy(&sc->vmbus_scan_lock);
 	mtx_destroy(&sc->vmbus_prichan_lock);
+	mtx_destroy(&sc->vmbus_chan_lock);
 
 	return (ret);
 }
@@ -1225,8 +1227,11 @@ vmbus_detach(device_t dev)
 {
 	struct vmbus_softc *sc = device_get_softc(dev);
 
+	bus_generic_detach(dev);
 	vmbus_chan_destroy_all(sc);
 
+	vmbus_scan_teardown(sc);
+
 	vmbus_disconnect(sc);
 
 	if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
@@ -1243,8 +1248,8 @@ vmbus_detach(device_t dev)
 	}
 
 	free(sc->vmbus_chmap, M_DEVBUF);
-	mtx_destroy(&sc->vmbus_scan_lock);
 	mtx_destroy(&sc->vmbus_prichan_lock);
+	mtx_destroy(&sc->vmbus_chan_lock);
 
 	return (0);
 }

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h	Fri Oct 14 02:42:08 2016	(r307249)
@@ -83,6 +83,20 @@ vmbus_txbr_maxpktsz(const struct vmbus_t
 	return (tbr->txbr_dsize - sizeof(uint64_t) - 1);
 }
 
+static __inline bool
+vmbus_txbr_empty(const struct vmbus_txbr *tbr)
+{
+
+	return (tbr->txbr_windex == tbr->txbr_rindex ? true : false);
+}
+
+static __inline bool
+vmbus_rxbr_empty(const struct vmbus_rxbr *rbr)
+{
+
+	return (rbr->rxbr_windex == rbr->rxbr_rindex ? true : false);
+}
+
 static __inline int
 vmbus_br_nelem(int br_size, int elem_size)
 {

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Fri Oct 14 02:42:08 2016	(r307249)
@@ -59,10 +59,30 @@ static struct vmbus_channel	*vmbus_chan_
 static void			vmbus_chan_free(struct vmbus_channel *);
 static int			vmbus_chan_add(struct vmbus_channel *);
 static void			vmbus_chan_cpu_default(struct vmbus_channel *);
+static int			vmbus_chan_release(struct vmbus_channel *);
+static void			vmbus_chan_set_chmap(struct vmbus_channel *);
+static void			vmbus_chan_clear_chmap(struct vmbus_channel *);
+
+static void			vmbus_chan_ins_prilist(struct vmbus_softc *,
+				    struct vmbus_channel *);
+static void			vmbus_chan_rem_prilist(struct vmbus_softc *,
+				    struct vmbus_channel *);
+static void			vmbus_chan_ins_list(struct vmbus_softc *,
+				    struct vmbus_channel *);
+static void			vmbus_chan_rem_list(struct vmbus_softc *,
+				    struct vmbus_channel *);
+static void			vmbus_chan_ins_sublist(struct vmbus_channel *,
+				    struct vmbus_channel *);
+static void			vmbus_chan_rem_sublist(struct vmbus_channel *,
+				    struct vmbus_channel *);
 
 static void			vmbus_chan_task(void *, int);
 static void			vmbus_chan_task_nobatch(void *, int);
-static void			vmbus_chan_detach_task(void *, int);
+static void			vmbus_chan_clrchmap_task(void *, int);
+static void			vmbus_prichan_attach_task(void *, int);
+static void			vmbus_subchan_attach_task(void *, int);
+static void			vmbus_prichan_detach_task(void *, int);
+static void			vmbus_subchan_detach_task(void *, int);
 
 static void			vmbus_chan_msgproc_choffer(struct vmbus_softc *,
 				    const struct vmbus_message *);
@@ -96,6 +116,83 @@ vmbus_chan_signal_tx(const struct vmbus_
 		hypercall_signal_event(chan->ch_monprm_dma.hv_paddr);
 }
 
+static void
+vmbus_chan_ins_prilist(struct vmbus_softc *sc, struct vmbus_channel *chan)
+{
+
+	mtx_assert(&sc->vmbus_prichan_lock, MA_OWNED);
+	if (atomic_testandset_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONPRIL_SHIFT))
+		panic("channel is already on the prilist");
+	TAILQ_INSERT_TAIL(&sc->vmbus_prichans, chan, ch_prilink);
+}
+
+static void
+vmbus_chan_rem_prilist(struct vmbus_softc *sc, struct vmbus_channel *chan)
+{
+
+	mtx_assert(&sc->vmbus_prichan_lock, MA_OWNED);
+	if (atomic_testandclear_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONPRIL_SHIFT) == 0)
+		panic("channel is not on the prilist");
+	TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
+}
+
+static void
+vmbus_chan_ins_sublist(struct vmbus_channel *prichan,
+    struct vmbus_channel *chan)
+{
+
+	mtx_assert(&prichan->ch_subchan_lock, MA_OWNED);
+
+	if (atomic_testandset_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONSUBL_SHIFT))
+		panic("channel is already on the sublist");
+	TAILQ_INSERT_TAIL(&prichan->ch_subchans, chan, ch_sublink);
+
+	/* Bump sub-channel count. */
+	prichan->ch_subchan_cnt++;
+}
+
+static void
+vmbus_chan_rem_sublist(struct vmbus_channel *prichan,
+    struct vmbus_channel *chan)
+{
+
+	mtx_assert(&prichan->ch_subchan_lock, MA_OWNED);
+
+	KASSERT(prichan->ch_subchan_cnt > 0,
+	    ("invalid subchan_cnt %d", prichan->ch_subchan_cnt));
+	prichan->ch_subchan_cnt--;
+
+	if (atomic_testandclear_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONSUBL_SHIFT) == 0)
+		panic("channel is not on the sublist");
+	TAILQ_REMOVE(&prichan->ch_subchans, chan, ch_sublink);
+}
+
+static void
+vmbus_chan_ins_list(struct vmbus_softc *sc, struct vmbus_channel *chan)
+{
+
+	mtx_assert(&sc->vmbus_chan_lock, MA_OWNED);
+	if (atomic_testandset_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONLIST_SHIFT))
+		panic("channel is already on the list");
+	TAILQ_INSERT_TAIL(&sc->vmbus_chans, chan, ch_link);
+}
+
+static void
+vmbus_chan_rem_list(struct vmbus_softc *sc, struct vmbus_channel *chan)
+{
+
+	mtx_assert(&sc->vmbus_chan_lock, MA_OWNED);
+	if (atomic_testandclear_int(&chan->ch_stflags,
+	    VMBUS_CHAN_ST_ONLIST_SHIFT) == 0)
+		panic("channel is not on the list");
+	TAILQ_REMOVE(&sc->vmbus_chans, chan, ch_link);
+}
+
 static int
 vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS)
 {
@@ -235,6 +332,7 @@ vmbus_chan_open_br(struct vmbus_channel 
 	struct vmbus_msghc *mh;
 	uint32_t status;
 	int error, txbr_size, rxbr_size;
+	task_fn_t *task_fn;
 	uint8_t *br;
 
 	if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
@@ -269,9 +367,10 @@ vmbus_chan_open_br(struct vmbus_channel 
 
 	chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid);
 	if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
-		TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan);
+		task_fn = vmbus_chan_task;
 	else
-		TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan);
+		task_fn = vmbus_chan_task_nobatch;
+	TASK_INIT(&chan->ch_task, 0, task_fn, chan);
 
 	/* TX bufring comes first */
 	vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size);
@@ -293,6 +392,12 @@ vmbus_chan_open_br(struct vmbus_channel 
 	}
 
 	/*
+	 * Install this channel, before it is opened, but after everything
+	 * else has been setup.
+	 */
+	vmbus_chan_set_chmap(chan);
+
+	/*
 	 * Open channel w/ the bufring GPADL on the target CPU.
 	 */
 	mh = vmbus_msghc_get(sc, sizeof(*req));
@@ -341,6 +446,7 @@ vmbus_chan_open_br(struct vmbus_channel 
 	error = ENXIO;
 
 failed:
+	vmbus_chan_clear_chmap(chan);
 	if (chan->ch_bufring_gpadl) {
 		vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
 		chan->ch_bufring_gpadl = 0;
@@ -517,12 +623,38 @@ vmbus_chan_gpadl_disconnect(struct vmbus
 }
 
 static void
+vmbus_chan_clrchmap_task(void *xchan, int pending __unused)
+{
+	struct vmbus_channel *chan = xchan;
+
+	critical_enter();
+	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = NULL;
+	critical_exit();
+}
+
+static void
+vmbus_chan_clear_chmap(struct vmbus_channel *chan)
+{
+	struct task chmap_task;
+
+	TASK_INIT(&chmap_task, 0, vmbus_chan_clrchmap_task, chan);
+	taskqueue_enqueue(chan->ch_tq, &chmap_task);
+	taskqueue_drain(chan->ch_tq, &chmap_task);
+}
+
+static void
+vmbus_chan_set_chmap(struct vmbus_channel *chan)
+{
+	__compiler_membar();
+	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = chan;
+}
+
+static void
 vmbus_chan_close_internal(struct vmbus_channel *chan)
 {
 	struct vmbus_softc *sc = chan->ch_vmbus;
 	struct vmbus_msghc *mh;
 	struct vmbus_chanmsg_chclose *req;
-	struct taskqueue *tq = chan->ch_tq;
 	int error;
 
 	/* TODO: stringent check */
@@ -535,12 +667,14 @@ vmbus_chan_close_internal(struct vmbus_c
 	sysctl_ctx_free(&chan->ch_sysctl_ctx);
 
 	/*
-	 * Set ch_tq to NULL to avoid more requests be scheduled.
-	 * XXX pretty broken; need rework.
+	 * NOTE:
+	 * Order is critical.  This channel _must_ be uninstalled first,
+	 * else the channel task may be enqueued by the IDT after it has
+	 * been drained.
 	 */
+	vmbus_chan_clear_chmap(chan);
+	taskqueue_drain(chan->ch_tq, &chan->ch_task);
 	chan->ch_tq = NULL;
-	taskqueue_drain(tq, &chan->ch_task);
-	chan->ch_cb = NULL;
 
 	/*
 	 * Close this channel.
@@ -622,6 +756,13 @@ vmbus_chan_close(struct vmbus_channel *c
 	vmbus_chan_close_internal(chan);
 }
 
+void
+vmbus_chan_intr_drain(struct vmbus_channel *chan)
+{
+
+	taskqueue_drain(chan->ch_tq, &chan->ch_task);
+}
+
 int
 vmbus_chan_send(struct vmbus_channel *chan, uint16_t type, uint16_t flags,
     void *data, int dlen, uint64_t xactid)
@@ -884,10 +1025,11 @@ vmbus_event_flags_proc(struct vmbus_soft
 			flags &= ~(1UL << chid_ofs);
 
 			chan = sc->vmbus_chmap[chid_base + chid_ofs];
-
-			/* if channel is closed or closing */
-			if (chan == NULL || chan->ch_tq == NULL)
+			if (__predict_false(chan == NULL)) {
+				/* Channel is closed. */
 				continue;
+			}
+			__compiler_membar();
 
 			if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
 				vmbus_rxbr_intr_mask(&chan->ch_rxbr);
@@ -968,7 +1110,6 @@ vmbus_chan_alloc(struct vmbus_softc *sc)
 	chan->ch_vmbus = sc;
 	mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
 	TAILQ_INIT(&chan->ch_subchans);
-	TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
 	vmbus_rxbr_init(&chan->ch_rxbr);
 	vmbus_txbr_init(&chan->ch_txbr);
 
@@ -978,9 +1119,14 @@ vmbus_chan_alloc(struct vmbus_softc *sc)
 static void
 vmbus_chan_free(struct vmbus_channel *chan)
 {
-	/* TODO: assert sub-channel list is empty */
-	/* TODO: asset no longer on the primary channel's sub-channel list */
-	/* TODO: asset no longer on the vmbus channel list */
+
+	KASSERT(TAILQ_EMPTY(&chan->ch_subchans) && chan->ch_subchan_cnt == 0,
+	    ("still owns sub-channels"));
+	KASSERT((chan->ch_stflags &
+	    (VMBUS_CHAN_ST_OPENED |
+	     VMBUS_CHAN_ST_ONPRIL |
+	     VMBUS_CHAN_ST_ONSUBL |
+	     VMBUS_CHAN_ST_ONLIST)) == 0, ("free busy channel"));
 	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
 	mtx_destroy(&chan->ch_subchan_lock);
 	vmbus_rxbr_deinit(&chan->ch_rxbr);
@@ -1007,7 +1153,6 @@ vmbus_chan_add(struct vmbus_channel *new
 		    newchan->ch_id);
 		return EINVAL;
 	}
-	sc->vmbus_chmap[newchan->ch_id] = newchan;
 
 	if (bootverbose) {
 		device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
@@ -1029,10 +1174,9 @@ vmbus_chan_add(struct vmbus_channel *new
 	if (VMBUS_CHAN_ISPRIMARY(newchan)) {
 		if (prichan == NULL) {
 			/* Install the new primary channel */
-			TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
-			    ch_prilink);
+			vmbus_chan_ins_prilist(sc, newchan);
 			mtx_unlock(&sc->vmbus_prichan_lock);
-			return 0;
+			goto done;
 		} else {
 			mtx_unlock(&sc->vmbus_prichan_lock);
 			device_printf(sc->vmbus_dev, "duplicated primary "
@@ -1066,16 +1210,20 @@ vmbus_chan_add(struct vmbus_channel *new
 	newchan->ch_dev = prichan->ch_dev;
 
 	mtx_lock(&prichan->ch_subchan_lock);
-	TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
+	vmbus_chan_ins_sublist(prichan, newchan);
+	mtx_unlock(&prichan->ch_subchan_lock);
 	/*
-	 * Bump up sub-channel count and notify anyone that is
-	 * interested in this sub-channel, after this sub-channel
-	 * is setup.
+	 * Notify anyone that is interested in this sub-channel,
+	 * after this sub-channel is setup.
 	 */
-	prichan->ch_subchan_cnt++;
-	mtx_unlock(&prichan->ch_subchan_lock);
 	wakeup(prichan);
-
+done:
+	/*
+	 * Hook this channel up for later rescind.
+	 */
+	mtx_lock(&sc->vmbus_chan_lock);
+	vmbus_chan_ins_list(sc, newchan);
+	mtx_unlock(&sc->vmbus_chan_lock);
 	return 0;
 }
 
@@ -1126,6 +1274,7 @@ vmbus_chan_msgproc_choffer(struct vmbus_
 {
 	const struct vmbus_chanmsg_choffer *offer;
 	struct vmbus_channel *chan;
+	task_fn_t *detach_fn, *attach_fn;
 	int error;
 
 	offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
@@ -1174,6 +1323,21 @@ vmbus_chan_msgproc_choffer(struct vmbus_
 	    &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT];
 	chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK);
 
+	/*
+	 * Setup attach and detach tasks.
+	 */
+	if (VMBUS_CHAN_ISPRIMARY(chan)) {
+		chan->ch_mgmt_tq = sc->vmbus_devtq;
+		attach_fn = vmbus_prichan_attach_task;
+		detach_fn = vmbus_prichan_detach_task;
+	} else {
+		chan->ch_mgmt_tq = sc->vmbus_subchtq;
+		attach_fn = vmbus_subchan_attach_task;
+		detach_fn = vmbus_subchan_detach_task;
+	}
+	TASK_INIT(&chan->ch_attach_task, 0, attach_fn, chan);
+	TASK_INIT(&chan->ch_detach_task, 0, detach_fn, chan);
+
 	/* Select default cpu for this channel. */
 	vmbus_chan_cpu_default(chan);
 
@@ -1184,22 +1348,9 @@ vmbus_chan_msgproc_choffer(struct vmbus_
 		vmbus_chan_free(chan);
 		return;
 	}
-
-	if (VMBUS_CHAN_ISPRIMARY(chan)) {
-		/*
-		 * Add device for this primary channel.
-		 *
-		 * NOTE:
-		 * Error is ignored here; don't have much to do if error
-		 * really happens.
-		 */
-		vmbus_add_child(chan);
-	}
+	taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_attach_task);
 }
 
-/*
- * XXX pretty broken; need rework.
- */
 static void
 vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
     const struct vmbus_message *msg)
@@ -1219,91 +1370,162 @@ vmbus_chan_msgproc_chrescind(struct vmbu
 		    note->chm_chanid);
 	}
 
-	chan = sc->vmbus_chmap[note->chm_chanid];
-	if (chan == NULL)
+	/*
+	 * Find and remove the target channel from the channel list.
+	 */
+	mtx_lock(&sc->vmbus_chan_lock);
+	TAILQ_FOREACH(chan, &sc->vmbus_chans, ch_link) {
+		if (chan->ch_id == note->chm_chanid)
+			break;
+	}
+	if (chan == NULL) {
+		mtx_unlock(&sc->vmbus_chan_lock);
+		device_printf(sc->vmbus_dev, "chan%u is not offered\n",
+		    note->chm_chanid);
 		return;
-	sc->vmbus_chmap[note->chm_chanid] = NULL;
+	}
+	vmbus_chan_rem_list(sc, chan);
+	mtx_unlock(&sc->vmbus_chan_lock);
+
+	if (VMBUS_CHAN_ISPRIMARY(chan)) {
+		/*
+		 * The target channel is a primary channel; remove the
+		 * target channel from the primary channel list now,
+		 * instead of later, so that it will not be found by
+		 * other sub-channel offers, which are processed in
+		 * this thread.
+		 */
+		mtx_lock(&sc->vmbus_prichan_lock);
+		vmbus_chan_rem_prilist(sc, chan);
+		mtx_unlock(&sc->vmbus_prichan_lock);
+	}
 
-	taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
+	/* Detach the target channel. */
+	taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task);
 }
 
-static void
-vmbus_chan_detach_task(void *xchan, int pending __unused)
+static int
+vmbus_chan_release(struct vmbus_channel *chan)
 {
-	struct vmbus_channel *chan = xchan;
+	struct vmbus_softc *sc = chan->ch_vmbus;
+	struct vmbus_chanmsg_chfree *req;
+	struct vmbus_msghc *mh;
+	int error;
 
-	if (VMBUS_CHAN_ISPRIMARY(chan)) {
-		/* Only primary channel owns the device */
-		vmbus_delete_child(chan);
-		/* NOTE: DO NOT free primary channel for now */
+	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->ch_id);
+		return (ENXIO);
+	}
+
+	req = vmbus_msghc_dataptr(mh);
+	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
+	req->chm_chanid = chan->ch_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->ch_id, error);
 	} else {
-		struct vmbus_softc *sc = chan->ch_vmbus;
-		struct vmbus_channel *pri_chan = chan->ch_prichan;
-		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",
+		if (bootverbose) {
+			device_printf(sc->vmbus_dev, "chan%u freed\n",
 			    chan->ch_id);
-			goto remove;
 		}
+	}
+	return (error);
+}
 
-		req = vmbus_msghc_dataptr(mh);
-		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
-		req->chm_chanid = chan->ch_id;
+static void
+vmbus_prichan_detach_task(void *xchan, int pending __unused)
+{
+	struct vmbus_channel *chan = xchan;
 
-		error = vmbus_msghc_exec_noresult(mh);
-		vmbus_msghc_put(sc, mh);
+	KASSERT(VMBUS_CHAN_ISPRIMARY(chan),
+	    ("chan%u is not primary channel", chan->ch_id));
 
-		if (error) {
-			device_printf(sc->vmbus_dev,
-			    "chfree(chan%u) failed: %d",
-			    chan->ch_id, error);
-			/* NOTE: Move on! */
-		} else {
-			if (bootverbose) {
-				device_printf(sc->vmbus_dev, "chan%u freed\n",
-				    chan->ch_id);
-			}
-		}
-remove:
-		mtx_lock(&pri_chan->ch_subchan_lock);
-		TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
-		KASSERT(pri_chan->ch_subchan_cnt > 0,
-		    ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
-		pri_chan->ch_subchan_cnt--;
-		mtx_unlock(&pri_chan->ch_subchan_lock);
-		wakeup(pri_chan);
+	/* Delete and detach the device associated with this channel. */
+	vmbus_delete_child(chan);
 
-		vmbus_chan_free(chan);
-	}
+	/* Release this channel (back to vmbus). */
+	vmbus_chan_release(chan);
+
+	/* Free this channel's resource. */
+	vmbus_chan_free(chan);
+}
+
+static void
+vmbus_subchan_detach_task(void *xchan, int pending __unused)
+{
+	struct vmbus_channel *chan = xchan;
+	struct vmbus_channel *pri_chan = chan->ch_prichan;
+
+	KASSERT(!VMBUS_CHAN_ISPRIMARY(chan),
+	    ("chan%u is primary channel", chan->ch_id));
+
+	/* Release this channel (back to vmbus). */
+	vmbus_chan_release(chan);
+
+	/* Unlink from its primary channel's sub-channel list. */
+	mtx_lock(&pri_chan->ch_subchan_lock);
+	vmbus_chan_rem_sublist(pri_chan, chan);
+	mtx_unlock(&pri_chan->ch_subchan_lock);
+	/* Notify anyone that is waiting for this sub-channel to vanish. */
+	wakeup(pri_chan);
+
+	/* Free this channel's resource. */
+	vmbus_chan_free(chan);
+}
+
+static void
+vmbus_prichan_attach_task(void *xchan, int pending __unused)
+{
+
+	/*
+	 * Add device for this primary channel.
+	 */
+	vmbus_add_child(xchan);
+}
+
+static void
+vmbus_subchan_attach_task(void *xchan __unused, int pending __unused)
+{
+
+	/* Nothing */
 }
 
-/*
- * Detach all devices and destroy the corresponding primary channels.
- */
 void
 vmbus_chan_destroy_all(struct vmbus_softc *sc)
 {
-	struct vmbus_channel *chan;
 
-	mtx_lock(&sc->vmbus_prichan_lock);
-	while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
-		KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
-		TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
-		mtx_unlock(&sc->vmbus_prichan_lock);
+	/*
+	 * Detach all devices and destroy the corresponding primary
+	 * channels.
+	 */
+	for (;;) {
+		struct vmbus_channel *chan;
 
-		vmbus_delete_child(chan);
-		vmbus_chan_free(chan);
+		mtx_lock(&sc->vmbus_chan_lock);
+		TAILQ_FOREACH(chan, &sc->vmbus_chans, ch_link) {
+			if (VMBUS_CHAN_ISPRIMARY(chan))
+				break;
+		}
+		if (chan == NULL) {
+			/* No more primary channels; done. */
+			mtx_unlock(&sc->vmbus_chan_lock);
+			break;
+		}
+		vmbus_chan_rem_list(sc, chan);
+		mtx_unlock(&sc->vmbus_chan_lock);
 
 		mtx_lock(&sc->vmbus_prichan_lock);
+		vmbus_chan_rem_prilist(sc, chan);
+		mtx_unlock(&sc->vmbus_prichan_lock);
+
+		taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task);
 	}
-	bzero(sc->vmbus_chmap,
-	    sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
-	mtx_unlock(&sc->vmbus_prichan_lock);
 }
 
 /*
@@ -1477,3 +1699,17 @@ vmbus_chan_prplist_nelem(int br_size, in
 
 	return (vmbus_br_nelem(br_size, elem_size));
 }
+
+bool
+vmbus_chan_tx_empty(const struct vmbus_channel *chan)
+{
+
+	return (vmbus_txbr_empty(&chan->ch_txbr));
+}
+
+bool
+vmbus_chan_rx_empty(const struct vmbus_channel *chan)
+{
+
+	return (vmbus_rxbr_empty(&chan->ch_rxbr));
+}

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h	Fri Oct 14 02:42:08 2016	(r307249)
@@ -124,8 +124,14 @@ struct vmbus_channel {
 	struct hyperv_dma		ch_bufring_dma;
 	uint32_t			ch_bufring_gpadl;
 
-	struct task			ch_detach_task;
+	struct task			ch_attach_task;	/* run in ch_mgmt_tq */
+	struct task			ch_detach_task;	/* run in ch_mgmt_tq */
+	struct taskqueue		*ch_mgmt_tq;
+
+	/* If this is a primary channel */
 	TAILQ_ENTRY(vmbus_channel)	ch_prilink;	/* primary chan link */
+
+	TAILQ_ENTRY(vmbus_channel)	ch_link;	/* channel link */
 	uint32_t			ch_subidx;	/* subchan index */
 	volatile uint32_t		ch_stflags;	/* atomic-op */
 							/* VMBUS_CHAN_ST_ */
@@ -150,7 +156,13 @@ struct vmbus_channel {
 #define VMBUS_CHAN_TXF_HASMNF		0x0001
 
 #define VMBUS_CHAN_ST_OPENED_SHIFT	0
+#define VMBUS_CHAN_ST_ONPRIL_SHIFT	1
+#define VMBUS_CHAN_ST_ONSUBL_SHIFT	2
+#define VMBUS_CHAN_ST_ONLIST_SHIFT	3
 #define VMBUS_CHAN_ST_OPENED		(1 << VMBUS_CHAN_ST_OPENED_SHIFT)
+#define VMBUS_CHAN_ST_ONPRIL		(1 << VMBUS_CHAN_ST_ONPRIL_SHIFT)
+#define VMBUS_CHAN_ST_ONSUBL		(1 << VMBUS_CHAN_ST_ONSUBL_SHIFT)
+#define VMBUS_CHAN_ST_ONLIST		(1 << VMBUS_CHAN_ST_ONLIST_SHIFT)
 
 struct vmbus_softc;
 struct vmbus_message;

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_var.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_var.h	Fri Oct 14 02:36:51 2016	(r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_var.h	Fri Oct 14 02:42:08 2016	(r307249)
@@ -107,14 +107,19 @@ struct vmbus_softc {
 	struct hyperv_dma	vmbus_mnf1_dma;
 	struct hyperv_dma	vmbus_mnf2_dma;
 
-	struct mtx		vmbus_scan_lock;
-	uint32_t		vmbus_scan_chcnt;
-#define VMBUS_SCAN_CHCNT_DONE	0x80000000
-	uint32_t		vmbus_scan_devcnt;
+	bool			vmbus_scandone;
+	struct task		vmbus_scandone_task;
+
+	struct taskqueue	*vmbus_devtq;	/* for dev attach/detach */
+	struct taskqueue	*vmbus_subchtq;	/* for sub-chan attach/detach */
 
 	/* Primary channels */
 	struct mtx		vmbus_prichan_lock;
 	TAILQ_HEAD(, vmbus_channel) vmbus_prichans;
+
+	/* Complete channel list */
+	struct mtx		vmbus_chan_lock;
+	TAILQ_HEAD(, vmbus_channel) vmbus_chans;
 };
 
 #define VMBUS_FLAG_ATTACHED	0x0001	/* vmbus was attached */



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