Module Name:    src
Committed By:   nonaka
Date:           Tue Dec 10 12:20:20 UTC 2019

Modified Files:
        src/sys/dev/hyperv: hyperv_common.c hypervvar.h if_hvn.c vmbus.c
            vmbusvar.h

Log Message:
hvn(4) can be added and deleted dynamically.


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/hyperv/hyperv_common.c
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/hyperv/hypervvar.h \
    src/sys/dev/hyperv/vmbusvar.h
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/hyperv/if_hvn.c
cvs rdiff -u -r1.7 -r1.8 src/sys/dev/hyperv/vmbus.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/hyperv/hyperv_common.c
diff -u src/sys/dev/hyperv/hyperv_common.c:1.4 src/sys/dev/hyperv/hyperv_common.c:1.5
--- src/sys/dev/hyperv/hyperv_common.c:1.4	Sat Dec  7 11:45:45 2019
+++ src/sys/dev/hyperv/hyperv_common.c	Tue Dec 10 12:20:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: hyperv_common.c,v 1.4 2019/12/07 11:45:45 nonaka Exp $	*/
+/*	$NetBSD: hyperv_common.c,v 1.5 2019/12/10 12:20:20 nonaka Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
@@ -29,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hyperv_common.c,v 1.4 2019/12/07 11:45:45 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hyperv_common.c,v 1.5 2019/12/10 12:20:20 nonaka Exp $");
 
 #include "hyperv.h"
 
@@ -111,17 +111,18 @@ hyperv_guid2str(const struct hyperv_guid
  */
 void *
 hyperv_dma_alloc(bus_dma_tag_t dmat, struct hyperv_dma *dma, bus_size_t size,
-    bus_size_t alignment, bus_size_t boundary, int nsegs)
+    bus_size_t alignment, bus_size_t boundary, int nsegs, int flags)
 {
-	const int kmemflags = cold ? KM_NOSLEEP : KM_SLEEP;
-	const int dmaflags = cold ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
+	const int waitok = (flags & HYPERV_DMA_NOSLEEP) != HYPERV_DMA_NOSLEEP;
+	const int kmemflags = waitok ? KM_SLEEP: KM_NOSLEEP;
+	const int dmaflags = waitok ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT;
 	int rseg, error;
 
 	KASSERT(dma != NULL);
 	KASSERT(dma->segs == NULL);
 	KASSERT(nsegs > 0);
 
-	dma->segs = kmem_zalloc(sizeof(*dma->segs) * nsegs, kmemflags);
+	dma->segs = kmem_intr_zalloc(sizeof(*dma->segs) * nsegs, kmemflags);
 	if (dma->segs == NULL)
 		return NULL;
 

Index: src/sys/dev/hyperv/hypervvar.h
diff -u src/sys/dev/hyperv/hypervvar.h:1.3 src/sys/dev/hyperv/hypervvar.h:1.4
--- src/sys/dev/hyperv/hypervvar.h:1.3	Sat Dec  7 11:45:45 2019
+++ src/sys/dev/hyperv/hypervvar.h	Tue Dec 10 12:20:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: hypervvar.h,v 1.3 2019/12/07 11:45:45 nonaka Exp $	*/
+/*	$NetBSD: hypervvar.h,v 1.4 2019/12/10 12:20:20 nonaka Exp $	*/
 
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
@@ -108,8 +108,10 @@ hyperv_dma_get_paddr(struct hyperv_dma *
 	return dma->map->dm_segs[0].ds_addr;
 }
 
+#define HYPERV_DMA_SLEEPOK	0
+#define HYPERV_DMA_NOSLEEP	__BIT(0)
 void *hyperv_dma_alloc(bus_dma_tag_t, struct hyperv_dma *, bus_size_t,
-    bus_size_t, bus_size_t, int);
+    bus_size_t, bus_size_t, int, int);
 void hyperv_dma_free(bus_dma_tag_t, struct hyperv_dma *);
 
 #endif	/* _KERNEL */
Index: src/sys/dev/hyperv/vmbusvar.h
diff -u src/sys/dev/hyperv/vmbusvar.h:1.3 src/sys/dev/hyperv/vmbusvar.h:1.4
--- src/sys/dev/hyperv/vmbusvar.h:1.3	Sat Dec  7 11:45:45 2019
+++ src/sys/dev/hyperv/vmbusvar.h	Tue Dec 10 12:20:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: vmbusvar.h,v 1.3 2019/12/07 11:45:45 nonaka Exp $	*/
+/*	$NetBSD: vmbusvar.h,v 1.4 2019/12/10 12:20:20 nonaka Exp $	*/
 /*	$OpenBSD: hypervvar.h,v 1.13 2017/06/23 19:05:42 mikeb Exp $	*/
 
 /*
@@ -24,6 +24,7 @@
 #include <sys/device.h>
 #include <sys/atomic.h>
 #include <sys/bus.h>
+#include <sys/condvar.h>
 #include <sys/evcnt.h>
 #include <sys/kcpuset.h>
 #include <sys/mutex.h>
@@ -43,6 +44,7 @@
 typedef void (*vmbus_channel_callback_t)(void *);
 
 struct vmbus_softc;
+struct vmbus_channel;
 
 struct vmbus_msg {
 	uint64_t			msg_flags;
@@ -57,11 +59,14 @@ struct vmbus_msg {
 __CTASSERT((offsetof(struct vmbus_msg, msg_req) % 8) == 0);
 TAILQ_HEAD(vmbus_queue, vmbus_msg);
 
-struct vmbus_offer {
-	struct vmbus_chanmsg_choffer	co_chan;
-	SIMPLEQ_ENTRY(vmbus_offer)	co_entry;
+struct vmbus_dev {
+	int					vd_type;
+#define VMBUS_DEV_TYPE_ATTACH			0
+#define VMBUS_DEV_TYPE_DETACH			1
+	struct vmbus_channel			*vd_chan;
+	SIMPLEQ_ENTRY(vmbus_dev)		vd_entry;
 };
-SIMPLEQ_HEAD(vmbus_offers, vmbus_offer);
+SIMPLEQ_HEAD(vmbus_devq, vmbus_dev);
 
 struct vmbus_ring_data {
 	struct vmbus_bufring		*rd_ring;
@@ -72,7 +77,6 @@ struct vmbus_ring_data {
 	uint32_t			rd_dsize;
 };
 
-struct vmbus_channel;
 TAILQ_HEAD(vmbus_channels, vmbus_channel);
 
 struct vmbus_channel {
@@ -111,6 +115,7 @@ struct vmbus_channel {
 	uint32_t			ch_flags;
 #define  CHF_BATCHED			__BIT(0)
 #define  CHF_MONITOR			__BIT(1)
+#define  CHF_REVOKED			__BIT(2)
 
 	uint8_t				ch_mgroup;
 	uint8_t				ch_mindex;
@@ -137,12 +142,6 @@ struct vmbus_attach_args {
 	bus_space_tag_t			aa_memt;
 };
 
-struct vmbus_dev {
-	struct vmbus_attach_args	dv_aa;
-	SLIST_ENTRY(vmbus_dev)		dv_entry;
-};
-SLIST_HEAD(vmbus_devices, vmbus_dev);
-
 struct vmbus_percpu_data {
 	void			*simp;	/* Synthetic Interrupt Message Page */
 	void			*siep;	/* Synthetic Interrupt Event Flags Page */
@@ -190,19 +189,14 @@ struct vmbus_softc {
 	struct vmbus_queue 	sc_rsps;	/* Response queue */
 	kmutex_t		sc_rsp_lock;
 
-	struct vmbus_offers	sc_offers;
-	kmutex_t		sc_offer_lock;
+	struct vmbus_devq	sc_devq;
+	kmutex_t		sc_devq_lock;
+	kcondvar_t		sc_devq_cv;
 
 	struct vmbus_channels	sc_channels;
 	kmutex_t		sc_channel_lock;
 
 	volatile uint32_t	sc_handle;
-
-	struct vmbus_devices	sc_icdevs;
-	kmutex_t		sc_icdev_lock;
-
-	struct vmbus_devices	sc_devs;
-	kmutex_t		sc_dev_lock;
 };
 
 static __inline void
@@ -268,6 +262,7 @@ int	vmbus_channel_recv(struct vmbus_chan
 	    uint64_t *, int);
 void	vmbus_channel_cpu_set(struct vmbus_channel *, int);
 void	vmbus_channel_cpu_rr(struct vmbus_channel *);
+bool	vmbus_channel_is_revoked(struct vmbus_channel *);
 
 struct vmbus_channel **
 	vmbus_subchannel_get(struct vmbus_channel *, int);

Index: src/sys/dev/hyperv/if_hvn.c
diff -u src/sys/dev/hyperv/if_hvn.c:1.12 src/sys/dev/hyperv/if_hvn.c:1.13
--- src/sys/dev/hyperv/if_hvn.c:1.12	Tue Dec 10 11:19:25 2019
+++ src/sys/dev/hyperv/if_hvn.c	Tue Dec 10 12:20:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_hvn.c,v 1.12 2019/12/10 11:19:25 nonaka Exp $	*/
+/*	$NetBSD: if_hvn.c,v 1.13 2019/12/10 12:20:20 nonaka Exp $	*/
 /*	$OpenBSD: if_hvn.c,v 1.39 2018/03/11 14:31:34 mikeb Exp $	*/
 
 /*-
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_hvn.c,v 1.12 2019/12/10 11:19:25 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_hvn.c,v 1.13 2019/12/10 12:20:20 nonaka Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -249,8 +249,6 @@ hvn_attach(device_t parent, device_t sel
 	aprint_naive("\n");
 	aprint_normal(": Hyper-V NetVSC\n");
 
-	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
-
 	if (hvn_nvs_attach(sc)) {
 		aprint_error_dev(self, "failed to init NVSP\n");
 		return;
@@ -263,9 +261,10 @@ hvn_attach(device_t parent, device_t sel
 
 	if (hvn_tx_ring_create(sc)) {
 		aprint_error_dev(self, "failed to create Tx ring\n");
-		goto fail1;
+		goto fail2;
 	}
 
+	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
 	ifp->if_softc = sc;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = hvn_ioctl;
@@ -300,14 +299,14 @@ hvn_attach(device_t parent, device_t sel
 	error = if_initialize(ifp);
 	if (error) {
 		aprint_error_dev(self, "if_initialize failed(%d)\n", error);
-		goto fail2;
+		goto fail3;
 	}
 	sc->sc_ipq = if_percpuq_create(ifp);
 	if_deferred_start_init(ifp, NULL);
 
 	if (hvn_rndis_attach(sc)) {
 		aprint_error_dev(self, "failed to init RNDIS\n");
-		goto fail1;
+		goto fail3;
 	}
 
 	aprint_normal_dev(self, "NVS %d.%d NDIS %d.%d\n",
@@ -316,13 +315,13 @@ hvn_attach(device_t parent, device_t sel
 
 	if (hvn_set_capabilities(sc)) {
 		aprint_error_dev(self, "failed to setup offloading\n");
-		goto fail2;
+		goto fail4;
 	}
 
 	if (hvn_get_lladdr(sc, enaddr)) {
 		aprint_error_dev(self,
 		    "failed to obtain an ethernet address\n");
-		goto fail2;
+		goto fail4;
 	}
 	aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(enaddr));
 
@@ -337,10 +336,11 @@ hvn_attach(device_t parent, device_t sel
 	SET(sc->sc_flags, HVN_SCF_ATTACHED);
 	return;
 
-fail2:	hvn_rndis_detach(sc);
-fail1:	hvn_rx_ring_destroy(sc);
-	hvn_tx_ring_destroy(sc);
-	hvn_nvs_detach(sc);
+fail4:	hvn_rndis_detach(sc);
+	if_percpuq_destroy(sc->sc_ipq);
+fail3:	hvn_tx_ring_destroy(sc);
+fail2:	hvn_rx_ring_destroy(sc);
+fail1:	hvn_nvs_detach(sc);
 }
 
 static int
@@ -352,7 +352,8 @@ hvn_detach(device_t self, int flags)
 	if (!ISSET(sc->sc_flags, HVN_SCF_ATTACHED))
 		return 0;
 
-	hvn_stop(ifp, 1);
+	if (ifp->if_flags & IFF_RUNNING)
+		hvn_stop(ifp, 1);
 
 	pmf_device_deregister(self);
 
@@ -693,7 +694,8 @@ hvn_rx_ring_create(struct hvn_softc *sc)
 	else
 		sc->sc_rx_size = 16 * 1024 * 1024; 	/* 16MB */
 	sc->sc_rx_ring = hyperv_dma_alloc(sc->sc_dmat, &sc->sc_rx_dma,
-	    sc->sc_rx_size, PAGE_SIZE, PAGE_SIZE, sc->sc_rx_size / PAGE_SIZE);
+	    sc->sc_rx_size, PAGE_SIZE, PAGE_SIZE, sc->sc_rx_size / PAGE_SIZE,
+	    HYPERV_DMA_SLEEPOK);
 	if (sc->sc_rx_ring == NULL) {
 		DPRINTF("%s: failed to allocate Rx ring buffer\n",
 		    device_xname(sc->sc_dev));
@@ -735,7 +737,7 @@ hvn_rx_ring_create(struct hvn_softc *sc)
 		sc->sc_rx_hndl = 0;
 	}
 	if (sc->sc_rx_ring) {
-		kmem_free(sc->sc_rx_ring, sc->sc_rx_size);
+		hyperv_dma_free(sc->sc_dmat, &sc->sc_rx_dma);
 		sc->sc_rx_ring = NULL;
 	}
 	return -1;
@@ -761,10 +763,9 @@ hvn_rx_ring_destroy(struct hvn_softc *sc
 	delay(100);
 
 	vmbus_handle_free(sc->sc_chan, sc->sc_rx_hndl);
-
 	sc->sc_rx_hndl = 0;
 
-	kmem_free(sc->sc_rx_ring, sc->sc_rx_size);
+	hyperv_dma_free(sc->sc_dmat, &sc->sc_rx_dma);
 	sc->sc_rx_ring = NULL;
 
 	return 0;
@@ -854,25 +855,25 @@ hvn_tx_ring_destroy(struct hvn_softc *sc
 		txd->txd_dmap = NULL;
 		if (txd->txd_buf == NULL)
 			continue;
-		m_free(txd->txd_buf);
+		m_freem(txd->txd_buf);
 		txd->txd_buf = NULL;
 	}
-	if (sc->sc_tx_rmap) {
+	if (sc->sc_tx_rmap != NULL) {
 		bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_rmap,
-		    0, txd->txd_dmap->dm_mapsize,
+		    0, sc->sc_tx_rmap->dm_mapsize,
 		    BUS_DMASYNC_POSTWRITE);
 		bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_rmap);
 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_rmap);
+		sc->sc_tx_rmap = NULL;
 	}
-	if (sc->sc_tx_msgs) {
+	if (sc->sc_tx_msgs != NULL) {
 		size_t msgsize = roundup(HVN_RNDIS_PKT_LEN, 128);
 
 		bus_dmamem_unmap(sc->sc_dmat, sc->sc_tx_msgs,
 		    msgsize * HVN_TX_DESC);
 		bus_dmamem_free(sc->sc_dmat, &sc->sc_tx_mseg, 1);
+		sc->sc_tx_msgs = NULL;
 	}
-	sc->sc_tx_rmap = NULL;
-	sc->sc_tx_msgs = NULL;
 }
 
 static int
@@ -1414,6 +1415,10 @@ hvn_rndis_cmd(struct hvn_softc *sc, stru
 		    "RNDIS operation %u send error %d\n", hdr->rm_type, rv);
 		return rv;
 	}
+	if (vmbus_channel_is_revoked(sc->sc_chan)) {
+		/* No response */
+		return 0;
+	}
 
 	bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE,
 	    BUS_DMASYNC_POSTWRITE);
@@ -1843,4 +1848,8 @@ hvn_rndis_detach(struct hvn_softc *sc)
 		    device_xname(sc->sc_dev), rv);
 	}
 	hvn_free_cmd(sc, rc);
+
+	mutex_destroy(&sc->sc_cntl_sqlck);
+	mutex_destroy(&sc->sc_cntl_cqlck);
+	mutex_destroy(&sc->sc_cntl_fqlck);
 }

Index: src/sys/dev/hyperv/vmbus.c
diff -u src/sys/dev/hyperv/vmbus.c:1.7 src/sys/dev/hyperv/vmbus.c:1.8
--- src/sys/dev/hyperv/vmbus.c:1.7	Sat Dec  7 11:45:45 2019
+++ src/sys/dev/hyperv/vmbus.c	Tue Dec 10 12:20:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: vmbus.c,v 1.7 2019/12/07 11:45:45 nonaka Exp $	*/
+/*	$NetBSD: vmbus.c,v 1.8 2019/12/10 12:20:20 nonaka Exp $	*/
 /*	$OpenBSD: hyperv.c,v 1.43 2017/06/27 13:56:15 mikeb Exp $	*/
 
 /*-
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.7 2019/12/07 11:45:45 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.8 2019/12/10 12:20:20 nonaka Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -46,6 +46,7 @@ __KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.
 #include <sys/cpu.h>
 #include <sys/intr.h>
 #include <sys/kmem.h>
+#include <sys/kthread.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
 #include <sys/xcall.h>
@@ -62,6 +63,7 @@ __KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.
 #define HCF_NOREPLY	0x0004
 
 static void	vmbus_attach_deferred(device_t);
+static int	vmbus_attach_print(void *, const char *);
 static int	vmbus_alloc_dma(struct vmbus_softc *);
 static void	vmbus_free_dma(struct vmbus_softc *);
 static int	vmbus_init_interrupts(struct vmbus_softc *);
@@ -92,16 +94,22 @@ static void	vmbus_channel_delivered(stru
 		    struct vmbus_chanmsg_hdr *);
 static int	vmbus_channel_scan(struct vmbus_softc *);
 static void	vmbus_channel_cpu_default(struct vmbus_channel *);
-static void	vmbus_process_offer(struct vmbus_softc *, struct vmbus_offer *);
+static void	vmbus_process_offer(struct vmbus_softc *,
+		    struct vmbus_chanmsg_choffer *);
+static void	vmbus_process_rescind(struct vmbus_softc *,
+		    struct vmbus_chanmsg_chrescind *);
 static struct vmbus_channel *
 		vmbus_channel_lookup(struct vmbus_softc *, uint32_t);
 static int	vmbus_channel_ring_create(struct vmbus_channel *, uint32_t);
 static void	vmbus_channel_ring_destroy(struct vmbus_channel *);
+static void	vmbus_channel_detach(struct vmbus_channel *);
 static void	vmbus_channel_pause(struct vmbus_channel *);
 static uint32_t	vmbus_channel_unpause(struct vmbus_channel *);
 static uint32_t	vmbus_channel_ready(struct vmbus_channel *);
-static int	vmbus_attach_icdevs(struct vmbus_softc *);
-static int	vmbus_attach_devices(struct vmbus_softc *);
+static void	vmbus_devq_enqueue(struct vmbus_softc *, int,
+		    struct vmbus_channel *);
+static void	vmbus_process_devq(void *);
+static void	vmbus_devq_thread(void *);
 
 static struct vmbus_softc *vmbus_sc;
 
@@ -290,12 +298,6 @@ vmbus_attach(struct vmbus_softc *sc)
 	if (vmbus_channel_scan(sc))
 		goto cleanup;
 
-	/* Attach heartbeat, KVP and other "internal" services */
-	vmbus_attach_icdevs(sc);
-
-	/* Attach devices with external drivers */
-	vmbus_attach_devices(sc);
-
 	config_interrupts(sc->sc_dev, vmbus_attach_deferred);
 
 	return 0;
@@ -339,18 +341,18 @@ vmbus_alloc_dma(struct vmbus_softc *sc)
 		pd = &sc->sc_percpu[cpu_index(ci)];
 
 		pd->simp = hyperv_dma_alloc(sc->sc_dmat, &pd->simp_dma,
-		    PAGE_SIZE, PAGE_SIZE, 0, 1);
+		    PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
 		if (pd->simp == NULL)
 			return ENOMEM;
 
 		pd->siep = hyperv_dma_alloc(sc->sc_dmat, &pd->siep_dma,
-		    PAGE_SIZE, PAGE_SIZE, 0, 1);
+		    PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
 		if (pd->siep == NULL)
 			return ENOMEM;
 	}
 
 	sc->sc_events = hyperv_dma_alloc(sc->sc_dmat, &sc->sc_events_dma,
-	    PAGE_SIZE, PAGE_SIZE, 0, 1);
+	    PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
 	if (sc->sc_events == NULL)
 		return ENOMEM;
 	sc->sc_wevents = (u_long *)sc->sc_events;
@@ -358,7 +360,8 @@ vmbus_alloc_dma(struct vmbus_softc *sc)
 
 	for (i = 0; i < __arraycount(sc->sc_monitor); i++) {
 		sc->sc_monitor[i] = hyperv_dma_alloc(sc->sc_dmat,
-		    &sc->sc_monitor_dma[i], PAGE_SIZE, PAGE_SIZE, 0, 1);
+		    &sc->sc_monitor_dma[i], PAGE_SIZE, PAGE_SIZE, 0, 1,
+		    HYPERV_DMA_SLEEPOK);
 		if (sc->sc_monitor[i] == NULL)
 			return ENOMEM;
 	}
@@ -851,28 +854,15 @@ vmbus_channel_response(struct vmbus_soft
 static void
 vmbus_channel_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
 {
-	struct vmbus_offer *co;
-
-	co = kmem_intr_zalloc(sizeof(*co), KM_NOSLEEP);
-	if (co == NULL) {
-		device_printf(sc->sc_dev, "couldn't allocate offer\n");
-		return;
-	}
-
-	memcpy(&co->co_chan, hdr, sizeof(co->co_chan));
 
-	mutex_enter(&sc->sc_offer_lock);
-	SIMPLEQ_INSERT_TAIL(&sc->sc_offers, co, co_entry);
-	mutex_exit(&sc->sc_offer_lock);
+	vmbus_process_offer(sc, (struct vmbus_chanmsg_choffer *)hdr);
 }
 
 static void
 vmbus_channel_rescind(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
 {
-	const struct vmbus_chanmsg_chrescind *cmd;
 
-	cmd = (const struct vmbus_chanmsg_chrescind *)hdr;
-	device_printf(sc->sc_dev, "revoking channel %u\n", cmd->chm_chanid);
+	vmbus_process_rescind(sc, (struct vmbus_chanmsg_chrescind *)hdr);
 }
 
 static void
@@ -880,7 +870,7 @@ vmbus_channel_delivered(struct vmbus_sof
 {
 
 	atomic_or_32(&sc->sc_flags, VMBUS_SCFLAG_OFFERS_DELIVERED);
-	wakeup(&sc->sc_offers);
+	wakeup(&sc->sc_devq);
 }
 
 static void
@@ -935,10 +925,20 @@ vmbus_channel_scan(struct vmbus_softc *s
 {
 	struct vmbus_chanmsg_hdr hdr;
 	struct vmbus_chanmsg_choffer rsp;
-	struct vmbus_offer *co;
 
-	SIMPLEQ_INIT(&sc->sc_offers);
-	mutex_init(&sc->sc_offer_lock, MUTEX_DEFAULT, IPL_NET);
+	TAILQ_INIT(&sc->sc_channels);
+	mutex_init(&sc->sc_channel_lock, MUTEX_DEFAULT, IPL_NET);
+
+	SIMPLEQ_INIT(&sc->sc_devq);
+	mutex_init(&sc->sc_devq_lock, MUTEX_DEFAULT, IPL_NET);
+	cv_init(&sc->sc_devq_cv, "hvdevqcv");
+
+	if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
+	    vmbus_devq_thread, sc, NULL, "hvoffer") != 0) {
+		DPRINTF("%s: failed to create offer thread\n",
+		    device_xname(sc->sc_dev));
+		return -1;
+	}
 
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.chm_type = VMBUS_CHANMSG_CHREQUEST;
@@ -950,23 +950,11 @@ vmbus_channel_scan(struct vmbus_softc *s
 	}
 
 	vmbus_wait(sc, vmbus_channel_scan_done, (struct vmbus_msg *)&hdr,
-	    &sc->sc_offers, "hvscan");
-
-	TAILQ_INIT(&sc->sc_channels);
-	mutex_init(&sc->sc_channel_lock, MUTEX_DEFAULT, IPL_NET);
+	    &sc->sc_devq, "hvscan");
 
-	mutex_enter(&sc->sc_offer_lock);
-	while (!SIMPLEQ_EMPTY(&sc->sc_offers)) {
-		co = SIMPLEQ_FIRST(&sc->sc_offers);
-		SIMPLEQ_REMOVE_HEAD(&sc->sc_offers, co_entry);
-		mutex_exit(&sc->sc_offer_lock);
-
-		vmbus_process_offer(sc, co);
-		kmem_free(co, sizeof(*co));
-
-		mutex_enter(&sc->sc_offer_lock);
-	}
-	mutex_exit(&sc->sc_offer_lock);
+	mutex_enter(&sc->sc_devq_lock);
+	vmbus_process_devq(sc);
+	mutex_exit(&sc->sc_devq_lock);
 
 	return 0;
 }
@@ -976,10 +964,10 @@ vmbus_channel_alloc(struct vmbus_softc *
 {
 	struct vmbus_channel *ch;
 
-	ch = kmem_zalloc(sizeof(*ch), cold ? KM_NOSLEEP : KM_SLEEP);
+	ch = kmem_intr_zalloc(sizeof(*ch), KM_NOSLEEP);
 
 	ch->ch_monprm = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_monprm_dma,
-	    sizeof(*ch->ch_monprm), 8, 0, 1);
+	    sizeof(*ch->ch_monprm), 8, 0, 1, HYPERV_DMA_NOSLEEP);
 	if (ch->ch_monprm == NULL) {
 		device_printf(sc->sc_dev, "monprm alloc failed\n");
 		kmem_free(ch, sizeof(*ch));
@@ -1011,7 +999,8 @@ vmbus_channel_free(struct vmbus_channel 
 	hyperv_dma_free(sc->sc_dmat, &ch->ch_monprm_dma);
 	mutex_destroy(&ch->ch_subchannel_lock);
 	/* XXX ch_evcnt */
-	softint_disestablish(ch->ch_taskq);
+	if (ch->ch_taskq != NULL)
+		softint_disestablish(ch->ch_taskq);
 	kmem_free(ch, sizeof(*ch));
 }
 
@@ -1061,8 +1050,8 @@ vmbus_channel_add(struct vmbus_channel *
 	KASSERT(!VMBUS_CHAN_ISPRIMARY(nch));
 	KASSERT(ch != NULL);
 
-	refs = atomic_add_int_nv(&nch->ch_refs, 1);
-	KASSERT(refs == 1);
+	refs = atomic_inc_uint_nv(&nch->ch_refs);
+	KASSERT(refs == 2);
 
 	nch->ch_primary_channel = ch;
 	nch->ch_dev = ch->ch_dev;
@@ -1102,7 +1091,7 @@ vmbus_channel_cpu_rr(struct vmbus_channe
 	static uint32_t vmbus_channel_nextcpu;
 	int cpu;
 
-	cpu = atomic_add_32_nv(&vmbus_channel_nextcpu, 1) % ncpu;
+	cpu = atomic_inc_32_nv(&vmbus_channel_nextcpu) % ncpu;
 	vmbus_channel_cpu_set(ch, cpu);
 }
 
@@ -1118,15 +1107,23 @@ vmbus_channel_cpu_default(struct vmbus_c
 	vmbus_channel_cpu_set(ch, 0);
 }
 
+bool
+vmbus_channel_is_revoked(struct vmbus_channel *ch)
+{
+
+	return (ch->ch_flags & CHF_REVOKED) ? true : false;
+}
+
+
 static void
-vmbus_process_offer(struct vmbus_softc *sc, struct vmbus_offer *co)
+vmbus_process_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_choffer *co)
 {
 	struct vmbus_channel *ch;
 
 	ch = vmbus_channel_alloc(sc);
 	if (ch == NULL) {
 		device_printf(sc->sc_dev, "allocate channel %u failed\n",
-		    co->co_chan.chm_chanid);
+		    co->chm_chanid);
 		return;
 	}
 
@@ -1137,48 +1134,75 @@ vmbus_process_offer(struct vmbus_softc *
 	 */
 	ch->ch_flags |= CHF_BATCHED;
 
-	hyperv_guid_sprint(&co->co_chan.chm_chtype, ch->ch_ident,
+	hyperv_guid_sprint(&co->chm_chtype, ch->ch_ident,
 	    sizeof(ch->ch_ident));
 
 	ch->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
 	if (sc->sc_proto > VMBUS_VERSION_WS2008)
-		ch->ch_monprm->mp_connid = co->co_chan.chm_connid;
+		ch->ch_monprm->mp_connid = co->chm_connid;
 
-	if (co->co_chan.chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
-		ch->ch_mgroup = co->co_chan.chm_montrig / VMBUS_MONTRIG_LEN;
-		ch->ch_mindex = co->co_chan.chm_montrig % VMBUS_MONTRIG_LEN;
+	if (co->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
+		ch->ch_mgroup = co->chm_montrig / VMBUS_MONTRIG_LEN;
+		ch->ch_mindex = co->chm_montrig % VMBUS_MONTRIG_LEN;
 		ch->ch_flags |= CHF_MONITOR;
 	}
 
-	ch->ch_id = co->co_chan.chm_chanid;
-	ch->ch_subidx = co->co_chan.chm_subidx;
+	ch->ch_id = co->chm_chanid;
+	ch->ch_subidx = co->chm_subidx;
 
-	memcpy(&ch->ch_type, &co->co_chan.chm_chtype, sizeof(ch->ch_type));
-	memcpy(&ch->ch_inst, &co->co_chan.chm_chinst, sizeof(ch->ch_inst));
-
-	if (VMBUS_CHAN_ISPRIMARY(ch)) {
-		/* set primary channel mgmt wq */
-	} else {
-		/* set sub channel mgmt wq */
-	}
+	memcpy(&ch->ch_type, &co->chm_chtype, sizeof(ch->ch_type));
+	memcpy(&ch->ch_inst, &co->chm_chinst, sizeof(ch->ch_inst));
 
 	if (vmbus_channel_add(ch) != 0) {
+		atomic_dec_uint(&ch->ch_refs);
 		vmbus_channel_free(ch);
 		return;
 	}
 
 	ch->ch_state = VMBUS_CHANSTATE_OFFERED;
 
+	vmbus_devq_enqueue(sc, VMBUS_DEV_TYPE_ATTACH, ch);
+
 #ifdef HYPERV_DEBUG
 	printf("%s: channel %u: \"%s\"", device_xname(sc->sc_dev), ch->ch_id,
 	    ch->ch_ident);
 	if (ch->ch_flags & CHF_MONITOR)
-		printf(", monitor %u\n", co->co_chan.chm_montrig);
+		printf(", monitor %u\n", co->chm_montrig);
 	else
 		printf("\n");
 #endif
 }
 
+static void
+vmbus_process_rescind(struct vmbus_softc *sc,
+    struct vmbus_chanmsg_chrescind *cr)
+{
+	struct vmbus_channel *ch;
+
+	if (cr->chm_chanid > VMBUS_CHAN_MAX) {
+		device_printf(sc->sc_dev, "invalid revoked channel%u\n",
+		    cr->chm_chanid);
+		return;
+	}
+
+	mutex_enter(&sc->sc_channel_lock);
+	ch = vmbus_channel_lookup(sc, cr->chm_chanid);
+	if (ch == NULL) {
+		mutex_exit(&sc->sc_channel_lock);
+		device_printf(sc->sc_dev, "channel%u is not offered\n",
+		    cr->chm_chanid);
+		return;
+	}
+	TAILQ_REMOVE(&sc->sc_channels, ch, ch_entry);
+	mutex_exit(&sc->sc_channel_lock);
+
+	KASSERTMSG(!(ch->ch_flags & CHF_REVOKED),
+	    "channel%u has already been revoked", ch->ch_id);
+	atomic_or_uint(&ch->ch_flags, CHF_REVOKED);
+
+	vmbus_channel_detach(ch);
+}
+
 static int
 vmbus_channel_release(struct vmbus_channel *ch)
 {
@@ -1256,8 +1280,9 @@ vmbus_channel_ring_create(struct vmbus_c
 
 	buflen = roundup(buflen, PAGE_SIZE) + sizeof(struct vmbus_bufring);
 	ch->ch_ring_size = 2 * buflen;
+	/* page aligned memory */
 	ch->ch_ring = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_ring_dma,
-	    ch->ch_ring_size, PAGE_SIZE, 0, 1);	/* page aligned memory */
+	    ch->ch_ring_size, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
 	if (ch->ch_ring == NULL) {
 		device_printf(sc->sc_dev,
 		    "failed to allocate channel ring\n");
@@ -1354,25 +1379,13 @@ vmbus_channel_detach(struct vmbus_channe
 {
 	u_int refs;
 
-	refs = atomic_add_int_nv(&ch->ch_refs, -1);
-	if (refs == 1) {
-		/* XXX on workqueue? */
-		if (VMBUS_CHAN_ISPRIMARY(ch)) {
-			vmbus_channel_release(ch);
-			vmbus_channel_free(ch);
-		} else {
-			struct vmbus_channel *prich = ch->ch_primary_channel;
-
-			vmbus_channel_release(ch);
-
-			mutex_enter(&prich->ch_subchannel_lock);
-			TAILQ_REMOVE(&prich->ch_subchannels, ch, ch_subentry);
-			prich->ch_subchannel_count--;
-			mutex_exit(&prich->ch_subchannel_lock);
-			wakeup(prich);
+	KASSERTMSG(ch->ch_refs > 0, "channel%u: invalid refcnt %d",
+	    ch->ch_id, ch->ch_refs);
 
-			vmbus_channel_free(ch);
-		}
+	refs = atomic_dec_uint_nv(&ch->ch_refs);
+	if (refs == 0) {
+		/* Detach the target channel. */
+		vmbus_devq_enqueue(ch->ch_sc, VMBUS_DEV_TYPE_DETACH, ch);
 	}
 }
 
@@ -1908,7 +1921,7 @@ vmbus_handle_alloc(struct vmbus_channel 
 		}
 	}
 
-	*handle = atomic_add_int_nv(&sc->sc_handle, 1);
+	*handle = atomic_inc_32_nv(&sc->sc_handle);
 
 	hdr->chm_hdr.chm_type = VMBUS_CHANMSG_GPADL_CONN;
 	hdr->chm_chanid = ch->ch_id;
@@ -1998,87 +2011,120 @@ vmbus_handle_free(struct vmbus_channel *
 	}
 }
 
-static int
-vmbus_attach_print(void *aux, const char *name)
+static void
+vmbus_devq_enqueue(struct vmbus_softc *sc, int type, struct vmbus_channel *ch)
 {
-	struct vmbus_attach_args *aa = aux;
+	struct vmbus_dev *vd;
 
-	if (name)
-		printf("\"%s\" at %s", aa->aa_ident, name);
+	vd = kmem_intr_zalloc(sizeof(*vd), KM_NOSLEEP);
+	if (vd == NULL) {
+		device_printf(sc->sc_dev, "failed to allocate devq\n");
+		return;
+	}
 
-	return UNCONF;
+	vd->vd_type = type;
+	vd->vd_chan = ch;
+
+	mutex_enter(&sc->sc_devq_lock);
+	SIMPLEQ_INSERT_TAIL(&sc->sc_devq, vd, vd_entry);
+	cv_broadcast(&sc->sc_devq_cv);
+	mutex_exit(&sc->sc_devq_lock);
 }
 
-static int
-vmbus_attach_icdevs(struct vmbus_softc *sc)
+static void
+vmbus_process_devq(void *arg)
 {
-	struct vmbus_dev *dv;
-	struct vmbus_channel *ch;
+	struct vmbus_softc *sc = arg;
+	struct vmbus_dev *vd;
+	struct vmbus_channel *ch, *prich;
 
-	SLIST_INIT(&sc->sc_icdevs);
-	mutex_init(&sc->sc_icdev_lock, MUTEX_DEFAULT, IPL_NET);
+	KASSERT(mutex_owned(&sc->sc_devq_lock));
 
-	TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
-		if (ch->ch_state != VMBUS_CHANSTATE_OFFERED)
-			continue;
-		if (ch->ch_flags & CHF_MONITOR)
-			continue;
+	while (!SIMPLEQ_EMPTY(&sc->sc_devq)) {
+		vd = SIMPLEQ_FIRST(&sc->sc_devq);
+		SIMPLEQ_REMOVE_HEAD(&sc->sc_devq, vd_entry);
+		mutex_exit(&sc->sc_devq_lock);
+
+		switch (vd->vd_type) {
+		case VMBUS_DEV_TYPE_ATTACH:
+			ch = vd->vd_chan;
+			if (VMBUS_CHAN_ISPRIMARY(ch)) {
+				struct vmbus_attach_args vaa;
+
+				vaa.aa_type = &ch->ch_type;
+				vaa.aa_inst = &ch->ch_inst;
+				vaa.aa_ident = ch->ch_ident;
+				vaa.aa_chan = ch;
+				vaa.aa_iot = sc->sc_iot;
+				vaa.aa_memt = sc->sc_memt;
+				ch->ch_dev = config_found_ia(sc->sc_dev,
+				    "hypervvmbus", &vaa, vmbus_attach_print);
+			}
+			break;
 
-		dv = kmem_zalloc(sizeof(*dv), cold ? KM_NOSLEEP : KM_SLEEP);
-		if (dv == NULL) {
-			device_printf(sc->sc_dev,
-			    "failed to allocate ic device object\n");
-			return ENOMEM;
+		case VMBUS_DEV_TYPE_DETACH:
+			ch = vd->vd_chan;
+			if (VMBUS_CHAN_ISPRIMARY(ch)) {
+				if (ch->ch_dev != NULL) {
+					config_detach(ch->ch_dev, DETACH_FORCE);
+					ch->ch_dev = NULL;
+				}
+				vmbus_channel_release(ch);
+				vmbus_channel_free(ch);
+				break;
+			}
+
+			vmbus_channel_release(ch);
+
+			prich = ch->ch_primary_channel;
+			mutex_enter(&prich->ch_subchannel_lock);
+			TAILQ_REMOVE(&prich->ch_subchannels, ch, ch_subentry);
+			prich->ch_subchannel_count--;
+			mutex_exit(&prich->ch_subchannel_lock);
+			wakeup(prich);
+
+			vmbus_channel_free(ch);
+			break;
+
+		default:
+			DPRINTF("%s: unknown offer type %d\n",
+			    device_xname(sc->sc_dev), vd->vd_type);
+			break;
 		}
-		dv->dv_aa.aa_type = &ch->ch_type;
-		dv->dv_aa.aa_inst = &ch->ch_inst;
-		dv->dv_aa.aa_ident = ch->ch_ident;
-		dv->dv_aa.aa_chan = ch;
-		dv->dv_aa.aa_iot = sc->sc_iot;
-		dv->dv_aa.aa_memt = sc->sc_memt;
-		mutex_enter(&sc->sc_icdev_lock);
-		SLIST_INSERT_HEAD(&sc->sc_icdevs, dv, dv_entry);
-		mutex_exit(&sc->sc_icdev_lock);
-		ch->ch_dev = config_found_ia(sc->sc_dev, "hypervvmbus",
-		    &dv->dv_aa, vmbus_attach_print);
+		kmem_free(vd, sizeof(*vd));
+
+		mutex_enter(&sc->sc_devq_lock);
 	}
-	return 0;
 }
 
-static int
-vmbus_attach_devices(struct vmbus_softc *sc)
+static void
+vmbus_devq_thread(void *arg)
 {
-	struct vmbus_dev *dv;
-	struct vmbus_channel *ch;
-
-	SLIST_INIT(&sc->sc_devs);
-	mutex_init(&sc->sc_dev_lock, MUTEX_DEFAULT, IPL_NET);
+	struct vmbus_softc *sc = arg;
 
-	TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
-		if (ch->ch_state != VMBUS_CHANSTATE_OFFERED)
-			continue;
-		if (!(ch->ch_flags & CHF_MONITOR))
+	mutex_enter(&sc->sc_devq_lock);
+	for (;;) {
+		if (SIMPLEQ_EMPTY(&sc->sc_devq)) {
+			cv_wait(&sc->sc_devq_cv, &sc->sc_devq_lock);
 			continue;
-
-		dv = kmem_zalloc(sizeof(*dv), cold ? KM_NOSLEEP : KM_SLEEP);
-		if (dv == NULL) {
-			device_printf(sc->sc_dev,
-			    "failed to allocate device object\n");
-			return ENOMEM;
 		}
-		dv->dv_aa.aa_type = &ch->ch_type;
-		dv->dv_aa.aa_inst = &ch->ch_inst;
-		dv->dv_aa.aa_ident = ch->ch_ident;
-		dv->dv_aa.aa_chan = ch;
-		dv->dv_aa.aa_iot = sc->sc_iot;
-		dv->dv_aa.aa_memt = sc->sc_memt;
-		mutex_enter(&sc->sc_dev_lock);
-		SLIST_INSERT_HEAD(&sc->sc_devs, dv, dv_entry);
-		mutex_exit(&sc->sc_dev_lock);
-		ch->ch_dev = config_found_ia(sc->sc_dev, "hypervvmbus",
-		    &dv->dv_aa, vmbus_attach_print);
+
+		vmbus_process_devq(sc);
 	}
-	return 0;
+	mutex_exit(&sc->sc_devq_lock);
+
+	kthread_exit(0);
+}
+
+static int
+vmbus_attach_print(void *aux, const char *name)
+{
+	struct vmbus_attach_args *aa = aux;
+
+	if (name)
+		printf("\"%s\" at %s", aa->aa_ident, name);
+
+	return UNCONF;
 }
 
 MODULE(MODULE_CLASS_DRIVER, vmbus, "hyperv");

Reply via email to