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");