xref: /netbsd-src/sys/dev/hyperv/vmbus.c (revision fdd3eadbf8cee05009241809f2af18177a0997dc)
1*fdd3eadbSnonaka /*	$NetBSD: vmbus.c,v 1.18 2022/05/20 13:55:17 nonaka Exp $	*/
250517e57Snonaka /*	$OpenBSD: hyperv.c,v 1.43 2017/06/27 13:56:15 mikeb Exp $	*/
350517e57Snonaka 
450517e57Snonaka /*-
550517e57Snonaka  * Copyright (c) 2009-2012 Microsoft Corp.
650517e57Snonaka  * Copyright (c) 2012 NetApp Inc.
750517e57Snonaka  * Copyright (c) 2012 Citrix Inc.
850517e57Snonaka  * Copyright (c) 2016 Mike Belopuhov <mike@esdenera.com>
950517e57Snonaka  * All rights reserved.
1050517e57Snonaka  *
1150517e57Snonaka  * Redistribution and use in source and binary forms, with or without
1250517e57Snonaka  * modification, are permitted provided that the following conditions
1350517e57Snonaka  * are met:
1450517e57Snonaka  * 1. Redistributions of source code must retain the above copyright
1550517e57Snonaka  *    notice unmodified, this list of conditions, and the following
1650517e57Snonaka  *    disclaimer.
1750517e57Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
1850517e57Snonaka  *    notice, this list of conditions and the following disclaimer in the
1950517e57Snonaka  *    documentation and/or other materials provided with the distribution.
2050517e57Snonaka  *
2150517e57Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2250517e57Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2350517e57Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2450517e57Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2550517e57Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2650517e57Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2750517e57Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2850517e57Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2950517e57Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3050517e57Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3150517e57Snonaka  */
3250517e57Snonaka 
3350517e57Snonaka /*
3450517e57Snonaka  * The OpenBSD port was done under funding by Esdenera Networks GmbH.
3550517e57Snonaka  */
3650517e57Snonaka 
3750517e57Snonaka #include <sys/cdefs.h>
38*fdd3eadbSnonaka __KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.18 2022/05/20 13:55:17 nonaka Exp $");
3950517e57Snonaka 
4050517e57Snonaka #include <sys/param.h>
4150517e57Snonaka #include <sys/systm.h>
4250517e57Snonaka #include <sys/device.h>
4350517e57Snonaka #include <sys/atomic.h>
4450517e57Snonaka #include <sys/bitops.h>
4550517e57Snonaka #include <sys/bus.h>
4650517e57Snonaka #include <sys/cpu.h>
4750517e57Snonaka #include <sys/intr.h>
4850517e57Snonaka #include <sys/kmem.h>
49c9d5b091Snonaka #include <sys/kthread.h>
5050517e57Snonaka #include <sys/module.h>
5150517e57Snonaka #include <sys/mutex.h>
5250517e57Snonaka #include <sys/xcall.h>
5350517e57Snonaka 
5450517e57Snonaka #include <uvm/uvm_extern.h>
5550517e57Snonaka 
5650517e57Snonaka #include <dev/hyperv/vmbusvar.h>
5750517e57Snonaka 
5850517e57Snonaka #define VMBUS_GPADL_START		0xffff /* 0x10000 effectively */
5950517e57Snonaka 
6050517e57Snonaka /* Command submission flags */
6150517e57Snonaka #define HCF_SLEEPOK	0x0000
6250517e57Snonaka #define HCF_NOSLEEP	0x0002	/* M_NOWAIT */
6350517e57Snonaka #define HCF_NOREPLY	0x0004
6450517e57Snonaka 
6550517e57Snonaka static void	vmbus_attach_deferred(device_t);
66c9d5b091Snonaka static int	vmbus_attach_print(void *, const char *);
6750517e57Snonaka static int	vmbus_alloc_dma(struct vmbus_softc *);
6850517e57Snonaka static void	vmbus_free_dma(struct vmbus_softc *);
6950517e57Snonaka static int	vmbus_init_interrupts(struct vmbus_softc *);
7050517e57Snonaka static void	vmbus_deinit_interrupts(struct vmbus_softc *);
7149769dfeSyamaguchi static void	vmbus_init_interrupts_pcpu(void *, void *);
7249769dfeSyamaguchi static void	vmbus_deinit_interrupts_pcpu(void *, void *);
7350517e57Snonaka 
7450517e57Snonaka static int	vmbus_connect(struct vmbus_softc *);
7550517e57Snonaka static int	vmbus_cmd(struct vmbus_softc *, void *, size_t, void *, size_t,
7650517e57Snonaka 		    int);
7750517e57Snonaka static int	vmbus_start(struct vmbus_softc *, struct vmbus_msg *, paddr_t);
7850517e57Snonaka static int	vmbus_reply(struct vmbus_softc *, struct vmbus_msg *);
7950517e57Snonaka static uint16_t vmbus_intr_signal(struct vmbus_softc *, paddr_t);
8050517e57Snonaka static void	vmbus_event_proc(void *, struct cpu_info *);
8150517e57Snonaka static void	vmbus_event_proc_compat(void *, struct cpu_info *);
8250517e57Snonaka static void	vmbus_message_proc(void *, struct cpu_info *);
8350517e57Snonaka static void	vmbus_message_softintr(void *);
8450517e57Snonaka static void	vmbus_channel_response(struct vmbus_softc *,
8550517e57Snonaka 		    struct vmbus_chanmsg_hdr *);
8650517e57Snonaka static void	vmbus_channel_offer(struct vmbus_softc *,
8750517e57Snonaka 		    struct vmbus_chanmsg_hdr *);
8850517e57Snonaka static void	vmbus_channel_rescind(struct vmbus_softc *,
8950517e57Snonaka 		    struct vmbus_chanmsg_hdr *);
9050517e57Snonaka static void	vmbus_channel_delivered(struct vmbus_softc *,
9150517e57Snonaka 		    struct vmbus_chanmsg_hdr *);
9250517e57Snonaka static int	vmbus_channel_scan(struct vmbus_softc *);
9350517e57Snonaka static void	vmbus_channel_cpu_default(struct vmbus_channel *);
94c9d5b091Snonaka static void	vmbus_process_offer(struct vmbus_softc *,
95c9d5b091Snonaka 		    struct vmbus_chanmsg_choffer *);
96c9d5b091Snonaka static void	vmbus_process_rescind(struct vmbus_softc *,
97c9d5b091Snonaka 		    struct vmbus_chanmsg_chrescind *);
9850517e57Snonaka static struct vmbus_channel *
9950517e57Snonaka 		vmbus_channel_lookup(struct vmbus_softc *, uint32_t);
10050517e57Snonaka static int	vmbus_channel_ring_create(struct vmbus_channel *, uint32_t);
10150517e57Snonaka static void	vmbus_channel_ring_destroy(struct vmbus_channel *);
102c9d5b091Snonaka static void	vmbus_channel_detach(struct vmbus_channel *);
103b74d80dfSnonaka static void	vmbus_chevq_enqueue(struct vmbus_softc *, int, void *);
104b74d80dfSnonaka static void	vmbus_process_chevq(void *);
105b74d80dfSnonaka static void	vmbus_chevq_thread(void *);
106c9d5b091Snonaka static void	vmbus_devq_enqueue(struct vmbus_softc *, int,
107c9d5b091Snonaka 		    struct vmbus_channel *);
108c9d5b091Snonaka static void	vmbus_process_devq(void *);
109c9d5b091Snonaka static void	vmbus_devq_thread(void *);
110b74d80dfSnonaka static void	vmbus_subchannel_devq_thread(void *);
11150517e57Snonaka 
11250517e57Snonaka static struct vmbus_softc *vmbus_sc;
11350517e57Snonaka 
11450517e57Snonaka static const struct {
11550517e57Snonaka 	int	hmd_response;
11650517e57Snonaka 	int	hmd_request;
11750517e57Snonaka 	void	(*hmd_handler)(struct vmbus_softc *,
11850517e57Snonaka 		    struct vmbus_chanmsg_hdr *);
11950517e57Snonaka } vmbus_msg_dispatch[] = {
12050517e57Snonaka 	{ 0,					0, NULL },
12150517e57Snonaka 	{ VMBUS_CHANMSG_CHOFFER,		0, vmbus_channel_offer },
12250517e57Snonaka 	{ VMBUS_CHANMSG_CHRESCIND,		0, vmbus_channel_rescind },
12350517e57Snonaka 	{ VMBUS_CHANMSG_CHREQUEST,		VMBUS_CHANMSG_CHOFFER, NULL },
12450517e57Snonaka 	{ VMBUS_CHANMSG_CHOFFER_DONE,		0, vmbus_channel_delivered },
12550517e57Snonaka 	{ VMBUS_CHANMSG_CHOPEN,			0, NULL },
12650517e57Snonaka 	{ VMBUS_CHANMSG_CHOPEN_RESP,		VMBUS_CHANMSG_CHOPEN,
12750517e57Snonaka 	  vmbus_channel_response },
12850517e57Snonaka 	{ VMBUS_CHANMSG_CHCLOSE,		0, NULL },
12950517e57Snonaka 	{ VMBUS_CHANMSG_GPADL_CONN,		0, NULL },
13050517e57Snonaka 	{ VMBUS_CHANMSG_GPADL_SUBCONN,		0, NULL },
13150517e57Snonaka 	{ VMBUS_CHANMSG_GPADL_CONNRESP,		VMBUS_CHANMSG_GPADL_CONN,
13250517e57Snonaka 	  vmbus_channel_response },
13350517e57Snonaka 	{ VMBUS_CHANMSG_GPADL_DISCONN,		0, NULL },
13450517e57Snonaka 	{ VMBUS_CHANMSG_GPADL_DISCONNRESP,	VMBUS_CHANMSG_GPADL_DISCONN,
13550517e57Snonaka 	  vmbus_channel_response },
13650517e57Snonaka 	{ VMBUS_CHANMSG_CHFREE,			0, NULL },
13750517e57Snonaka 	{ VMBUS_CHANMSG_CONNECT,		0, NULL },
13850517e57Snonaka 	{ VMBUS_CHANMSG_CONNECT_RESP,		VMBUS_CHANMSG_CONNECT,
13950517e57Snonaka 	  vmbus_channel_response },
14050517e57Snonaka 	{ VMBUS_CHANMSG_DISCONNECT,		0, NULL },
14150517e57Snonaka };
14250517e57Snonaka 
14350517e57Snonaka const struct hyperv_guid hyperv_guid_network = {
14450517e57Snonaka 	{ 0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
14550517e57Snonaka 	  0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
14650517e57Snonaka };
14750517e57Snonaka 
14850517e57Snonaka const struct hyperv_guid hyperv_guid_ide = {
14950517e57Snonaka 	{ 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
15050517e57Snonaka 	  0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 }
15150517e57Snonaka };
15250517e57Snonaka 
15350517e57Snonaka const struct hyperv_guid hyperv_guid_scsi = {
15450517e57Snonaka 	{ 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
15550517e57Snonaka 	  0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f }
15650517e57Snonaka };
15750517e57Snonaka 
15850517e57Snonaka const struct hyperv_guid hyperv_guid_shutdown = {
15950517e57Snonaka 	{ 0x31, 0x60, 0x0b, 0x0e, 0x13, 0x52, 0x34, 0x49,
16050517e57Snonaka 	  0x81, 0x8b, 0x38, 0xd9, 0x0c, 0xed, 0x39, 0xdb }
16150517e57Snonaka };
16250517e57Snonaka 
16350517e57Snonaka const struct hyperv_guid hyperv_guid_timesync = {
16450517e57Snonaka 	{ 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
16550517e57Snonaka 	  0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf }
16650517e57Snonaka };
16750517e57Snonaka 
16850517e57Snonaka const struct hyperv_guid hyperv_guid_heartbeat = {
16950517e57Snonaka 	{ 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
17050517e57Snonaka 	  0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d }
17150517e57Snonaka };
17250517e57Snonaka 
17350517e57Snonaka const struct hyperv_guid hyperv_guid_kvp = {
17450517e57Snonaka 	{ 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
17550517e57Snonaka 	  0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x03, 0xe6 }
17650517e57Snonaka };
17750517e57Snonaka 
17850517e57Snonaka const struct hyperv_guid hyperv_guid_vss = {
17950517e57Snonaka 	{ 0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42,
18050517e57Snonaka 	  0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4, 0x40 }
18150517e57Snonaka };
18250517e57Snonaka 
18350517e57Snonaka const struct hyperv_guid hyperv_guid_dynmem = {
18450517e57Snonaka 	{ 0xdc, 0x74, 0x50, 0x52, 0x85, 0x89, 0xe2, 0x46,
18550517e57Snonaka 	  0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02 }
18650517e57Snonaka };
18750517e57Snonaka 
18850517e57Snonaka const struct hyperv_guid hyperv_guid_mouse = {
18950517e57Snonaka 	{ 0x9e, 0xb6, 0xa8, 0xcf, 0x4a, 0x5b, 0xc0, 0x4c,
19050517e57Snonaka 	  0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a }
19150517e57Snonaka };
19250517e57Snonaka 
19350517e57Snonaka const struct hyperv_guid hyperv_guid_kbd = {
19450517e57Snonaka 	{ 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48,
19550517e57Snonaka 	  0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 }
19650517e57Snonaka };
19750517e57Snonaka 
19850517e57Snonaka const struct hyperv_guid hyperv_guid_video = {
19950517e57Snonaka 	{ 0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a,
20050517e57Snonaka 	  0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 }
20150517e57Snonaka };
20250517e57Snonaka 
20350517e57Snonaka const struct hyperv_guid hyperv_guid_fc = {
20450517e57Snonaka 	{ 0x4a, 0xcc, 0x9b, 0x2f, 0x69, 0x00, 0xf3, 0x4a,
20550517e57Snonaka 	  0xb7, 0x6b, 0x6f, 0xd0, 0xbe, 0x52, 0x8c, 0xda }
20650517e57Snonaka };
20750517e57Snonaka 
20850517e57Snonaka const struct hyperv_guid hyperv_guid_fcopy = {
20950517e57Snonaka 	{ 0xe3, 0x4b, 0xd1, 0x34, 0xe4, 0xde, 0xc8, 0x41,
21050517e57Snonaka 	  0x9a, 0xe7, 0x6b, 0x17, 0x49, 0x77, 0xc1, 0x92 }
21150517e57Snonaka };
21250517e57Snonaka 
21350517e57Snonaka const struct hyperv_guid hyperv_guid_pcie = {
21450517e57Snonaka 	{ 0x1d, 0xf6, 0xc4, 0x44, 0x44, 0x44, 0x00, 0x44,
21550517e57Snonaka 	  0x9d, 0x52, 0x80, 0x2e, 0x27, 0xed, 0xe1, 0x9f }
21650517e57Snonaka };
21750517e57Snonaka 
21850517e57Snonaka const struct hyperv_guid hyperv_guid_netdir = {
21950517e57Snonaka 	{ 0x3d, 0xaf, 0x2e, 0x8c, 0xa7, 0x32, 0x09, 0x4b,
22050517e57Snonaka 	  0xab, 0x99, 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01 }
22150517e57Snonaka };
22250517e57Snonaka 
22350517e57Snonaka const struct hyperv_guid hyperv_guid_rdesktop = {
22450517e57Snonaka 	{ 0xf4, 0xac, 0x6a, 0x27, 0x15, 0xac, 0x6c, 0x42,
22550517e57Snonaka 	  0x98, 0xdd, 0x75, 0x21, 0xad, 0x3f, 0x01, 0xfe }
22650517e57Snonaka };
22750517e57Snonaka 
22850517e57Snonaka /* Automatic Virtual Machine Activation (AVMA) Services */
22950517e57Snonaka const struct hyperv_guid hyperv_guid_avma1 = {
23050517e57Snonaka 	{ 0x55, 0xb2, 0x87, 0x44, 0x8c, 0xb8, 0x3f, 0x40,
23150517e57Snonaka 	  0xbb, 0x51, 0xd1, 0xf6, 0x9c, 0xf1, 0x7f, 0x87 }
23250517e57Snonaka };
23350517e57Snonaka 
23450517e57Snonaka const struct hyperv_guid hyperv_guid_avma2 = {
23550517e57Snonaka 	{ 0xf4, 0xba, 0x75, 0x33, 0x15, 0x9e, 0x30, 0x4b,
23650517e57Snonaka 	  0xb7, 0x65, 0x67, 0xac, 0xb1, 0x0d, 0x60, 0x7b }
23750517e57Snonaka };
23850517e57Snonaka 
23950517e57Snonaka const struct hyperv_guid hyperv_guid_avma3 = {
24050517e57Snonaka 	{ 0xa0, 0x1f, 0x22, 0x99, 0xad, 0x24, 0xe2, 0x11,
24150517e57Snonaka 	  0xbe, 0x98, 0x00, 0x1a, 0xa0, 0x1b, 0xbf, 0x6e }
24250517e57Snonaka };
24350517e57Snonaka 
24450517e57Snonaka const struct hyperv_guid hyperv_guid_avma4 = {
24550517e57Snonaka 	{ 0x16, 0x57, 0xe6, 0xf8, 0xb3, 0x3c, 0x06, 0x4a,
24650517e57Snonaka 	  0x9a, 0x60, 0x18, 0x89, 0xc5, 0xcc, 0xca, 0xb5 }
24750517e57Snonaka };
24850517e57Snonaka 
24950517e57Snonaka int
vmbus_match(device_t parent,cfdata_t cf,void * aux)25050517e57Snonaka vmbus_match(device_t parent, cfdata_t cf, void *aux)
25150517e57Snonaka {
25250517e57Snonaka 
25350517e57Snonaka 	if (cf->cf_unit != 0 ||
25450517e57Snonaka 	    !hyperv_hypercall_enabled() ||
25550517e57Snonaka 	    !hyperv_synic_supported())
25650517e57Snonaka 		return 0;
25750517e57Snonaka 
25850517e57Snonaka 	return 1;
25950517e57Snonaka }
26050517e57Snonaka 
26150517e57Snonaka int
vmbus_attach(struct vmbus_softc * sc)26250517e57Snonaka vmbus_attach(struct vmbus_softc *sc)
26350517e57Snonaka {
26450517e57Snonaka 
26550517e57Snonaka 	aprint_naive("\n");
26650517e57Snonaka 	aprint_normal(": Hyper-V VMBus\n");
26750517e57Snonaka 
26850517e57Snonaka 	vmbus_sc = sc;
26950517e57Snonaka 
27050517e57Snonaka 	sc->sc_msgpool = pool_cache_init(sizeof(struct vmbus_msg), 8, 0, 0,
27150517e57Snonaka 	    "hvmsg", NULL, IPL_NET, NULL, NULL, NULL);
27250517e57Snonaka 	hyperv_set_message_proc(vmbus_message_proc, sc);
27350517e57Snonaka 
274*fdd3eadbSnonaka 	sc->sc_chanmap = kmem_zalloc(sizeof(struct vmbus_channel *) *
275*fdd3eadbSnonaka 	    VMBUS_CHAN_MAX, KM_SLEEP);
276*fdd3eadbSnonaka 
27750517e57Snonaka 	if (vmbus_alloc_dma(sc))
27850517e57Snonaka 		goto cleanup;
27950517e57Snonaka 
28050517e57Snonaka 	if (vmbus_init_interrupts(sc))
28150517e57Snonaka 		goto cleanup;
28250517e57Snonaka 
28350517e57Snonaka 	if (vmbus_connect(sc))
28450517e57Snonaka 		goto cleanup;
28550517e57Snonaka 
28650517e57Snonaka 	aprint_normal_dev(sc->sc_dev, "protocol %d.%d\n",
28750517e57Snonaka 	    VMBUS_VERSION_MAJOR(sc->sc_proto),
28850517e57Snonaka 	    VMBUS_VERSION_MINOR(sc->sc_proto));
28950517e57Snonaka 
29050517e57Snonaka 	if (sc->sc_proto == VMBUS_VERSION_WS2008 ||
29150517e57Snonaka 	    sc->sc_proto == VMBUS_VERSION_WIN7) {
29250517e57Snonaka 		hyperv_set_event_proc(vmbus_event_proc_compat, sc);
29350517e57Snonaka 		sc->sc_channel_max = VMBUS_CHAN_MAX_COMPAT;
29450517e57Snonaka 	} else {
29550517e57Snonaka 		hyperv_set_event_proc(vmbus_event_proc, sc);
29650517e57Snonaka 		sc->sc_channel_max = VMBUS_CHAN_MAX;
29750517e57Snonaka 	}
29850517e57Snonaka 
29950517e57Snonaka 	if (vmbus_channel_scan(sc))
30050517e57Snonaka 		goto cleanup;
30150517e57Snonaka 
30250517e57Snonaka 	config_interrupts(sc->sc_dev, vmbus_attach_deferred);
30350517e57Snonaka 
30450517e57Snonaka 	return 0;
30550517e57Snonaka 
30650517e57Snonaka cleanup:
30750517e57Snonaka 	vmbus_deinit_interrupts(sc);
30850517e57Snonaka 	vmbus_free_dma(sc);
309*fdd3eadbSnonaka 	kmem_free(__UNVOLATILE(sc->sc_chanmap),
310*fdd3eadbSnonaka 	    sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
31150517e57Snonaka 	return -1;
31250517e57Snonaka }
31350517e57Snonaka 
31450517e57Snonaka static void
vmbus_attach_deferred(device_t self)31550517e57Snonaka vmbus_attach_deferred(device_t self)
31650517e57Snonaka {
31750517e57Snonaka 	struct vmbus_softc *sc = device_private(self);
31849769dfeSyamaguchi 	uint64_t xc;
31950517e57Snonaka 
32049769dfeSyamaguchi 	xc = xc_broadcast(0, vmbus_init_interrupts_pcpu,
32149769dfeSyamaguchi 	    sc, NULL);
32249769dfeSyamaguchi 	xc_wait(xc);
32350517e57Snonaka }
32450517e57Snonaka 
32550517e57Snonaka int
vmbus_detach(struct vmbus_softc * sc,int flags)32650517e57Snonaka vmbus_detach(struct vmbus_softc *sc, int flags)
32750517e57Snonaka {
32850517e57Snonaka 
32950517e57Snonaka 	vmbus_deinit_interrupts(sc);
33050517e57Snonaka 	vmbus_free_dma(sc);
331*fdd3eadbSnonaka 	kmem_free(__UNVOLATILE(sc->sc_chanmap),
332*fdd3eadbSnonaka 	    sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
33350517e57Snonaka 
33450517e57Snonaka 	return 0;
33550517e57Snonaka }
33650517e57Snonaka 
33750517e57Snonaka static int
vmbus_alloc_dma(struct vmbus_softc * sc)33850517e57Snonaka vmbus_alloc_dma(struct vmbus_softc *sc)
33950517e57Snonaka {
34050517e57Snonaka 	CPU_INFO_ITERATOR cii;
34150517e57Snonaka 	struct cpu_info *ci;
34250517e57Snonaka 	struct vmbus_percpu_data *pd;
34350517e57Snonaka 	int i;
34450517e57Snonaka 
34550517e57Snonaka 	/*
34650517e57Snonaka 	 * Per-CPU messages and event flags.
34750517e57Snonaka 	 */
34850517e57Snonaka 	for (CPU_INFO_FOREACH(cii, ci)) {
34950517e57Snonaka 		pd = &sc->sc_percpu[cpu_index(ci)];
35050517e57Snonaka 
35150517e57Snonaka 		pd->simp = hyperv_dma_alloc(sc->sc_dmat, &pd->simp_dma,
352*fdd3eadbSnonaka 		    PAGE_SIZE, PAGE_SIZE, 0, 1);
35350517e57Snonaka 		if (pd->simp == NULL)
35450517e57Snonaka 			return ENOMEM;
35550517e57Snonaka 
35650517e57Snonaka 		pd->siep = hyperv_dma_alloc(sc->sc_dmat, &pd->siep_dma,
357*fdd3eadbSnonaka 		    PAGE_SIZE, PAGE_SIZE, 0, 1);
35850517e57Snonaka 		if (pd->siep == NULL)
35950517e57Snonaka 			return ENOMEM;
36050517e57Snonaka 	}
36150517e57Snonaka 
36250517e57Snonaka 	sc->sc_events = hyperv_dma_alloc(sc->sc_dmat, &sc->sc_events_dma,
363*fdd3eadbSnonaka 	    PAGE_SIZE, PAGE_SIZE, 0, 1);
36450517e57Snonaka 	if (sc->sc_events == NULL)
36550517e57Snonaka 		return ENOMEM;
36650517e57Snonaka 	sc->sc_wevents = (u_long *)sc->sc_events;
36750517e57Snonaka 	sc->sc_revents = (u_long *)((uint8_t *)sc->sc_events + (PAGE_SIZE / 2));
36850517e57Snonaka 
36950517e57Snonaka 	for (i = 0; i < __arraycount(sc->sc_monitor); i++) {
37050517e57Snonaka 		sc->sc_monitor[i] = hyperv_dma_alloc(sc->sc_dmat,
371*fdd3eadbSnonaka 		    &sc->sc_monitor_dma[i], PAGE_SIZE, PAGE_SIZE, 0, 1);
37250517e57Snonaka 		if (sc->sc_monitor[i] == NULL)
37350517e57Snonaka 			return ENOMEM;
37450517e57Snonaka 	}
37550517e57Snonaka 
37650517e57Snonaka 	return 0;
37750517e57Snonaka }
37850517e57Snonaka 
37950517e57Snonaka static void
vmbus_free_dma(struct vmbus_softc * sc)38050517e57Snonaka vmbus_free_dma(struct vmbus_softc *sc)
38150517e57Snonaka {
38250517e57Snonaka 	CPU_INFO_ITERATOR cii;
38350517e57Snonaka 	struct cpu_info *ci;
38450517e57Snonaka 	int i;
38550517e57Snonaka 
38650517e57Snonaka 	if (sc->sc_events != NULL) {
38750517e57Snonaka 		sc->sc_events = sc->sc_wevents = sc->sc_revents = NULL;
38850517e57Snonaka 		hyperv_dma_free(sc->sc_dmat, &sc->sc_events_dma);
38950517e57Snonaka 	}
39050517e57Snonaka 
39150517e57Snonaka 	for (i = 0; i < __arraycount(sc->sc_monitor); i++) {
39250517e57Snonaka 		sc->sc_monitor[i] = NULL;
39350517e57Snonaka 		hyperv_dma_free(sc->sc_dmat, &sc->sc_monitor_dma[i]);
39450517e57Snonaka 	}
39550517e57Snonaka 
39650517e57Snonaka 	for (CPU_INFO_FOREACH(cii, ci)) {
39750517e57Snonaka 		struct vmbus_percpu_data *pd = &sc->sc_percpu[cpu_index(ci)];
39850517e57Snonaka 
39950517e57Snonaka 		if (pd->simp != NULL) {
40050517e57Snonaka 			pd->simp = NULL;
40150517e57Snonaka 			hyperv_dma_free(sc->sc_dmat, &pd->simp_dma);
40250517e57Snonaka 		}
40350517e57Snonaka 		if (pd->siep != NULL) {
40450517e57Snonaka 			pd->siep = NULL;
40550517e57Snonaka 			hyperv_dma_free(sc->sc_dmat, &pd->siep_dma);
40650517e57Snonaka 		}
40750517e57Snonaka 	}
40850517e57Snonaka }
40950517e57Snonaka 
41050517e57Snonaka static int
vmbus_init_interrupts(struct vmbus_softc * sc)41150517e57Snonaka vmbus_init_interrupts(struct vmbus_softc *sc)
41250517e57Snonaka {
41349769dfeSyamaguchi 	uint64_t xc;
41450517e57Snonaka 
41550517e57Snonaka 	TAILQ_INIT(&sc->sc_reqs);
41650517e57Snonaka 	mutex_init(&sc->sc_req_lock, MUTEX_DEFAULT, IPL_NET);
41750517e57Snonaka 
41850517e57Snonaka 	TAILQ_INIT(&sc->sc_rsps);
41950517e57Snonaka 	mutex_init(&sc->sc_rsp_lock, MUTEX_DEFAULT, IPL_NET);
42050517e57Snonaka 
42150517e57Snonaka 	sc->sc_proto = VMBUS_VERSION_WS2008;
42250517e57Snonaka 
42350517e57Snonaka 	/* XXX event_tq */
42450517e57Snonaka 
42550517e57Snonaka 	sc->sc_msg_sih = softint_establish(SOFTINT_NET | SOFTINT_MPSAFE,
42650517e57Snonaka 	    vmbus_message_softintr, sc);
42750517e57Snonaka 	if (sc->sc_msg_sih == NULL)
42850517e57Snonaka 		return -1;
42950517e57Snonaka 
43050517e57Snonaka 	kcpuset_create(&sc->sc_intr_cpuset, true);
43150517e57Snonaka 	if (cold) {
43250517e57Snonaka 		/* Initialize other CPUs later. */
43349769dfeSyamaguchi 		vmbus_init_interrupts_pcpu(sc, NULL);
43449769dfeSyamaguchi 	} else {
43549769dfeSyamaguchi 		xc = xc_broadcast(0, vmbus_init_interrupts_pcpu,
43649769dfeSyamaguchi 		    sc, NULL);
43749769dfeSyamaguchi 		xc_wait(xc);
43849769dfeSyamaguchi 	}
43950517e57Snonaka 	atomic_or_32(&sc->sc_flags, VMBUS_SCFLAG_SYNIC);
44050517e57Snonaka 
44150517e57Snonaka 	return 0;
44250517e57Snonaka }
44350517e57Snonaka 
44450517e57Snonaka static void
vmbus_deinit_interrupts(struct vmbus_softc * sc)44550517e57Snonaka vmbus_deinit_interrupts(struct vmbus_softc *sc)
44650517e57Snonaka {
44749769dfeSyamaguchi 	uint64_t xc;
44850517e57Snonaka 
44949769dfeSyamaguchi 	if (cold) {
45049769dfeSyamaguchi 		vmbus_deinit_interrupts_pcpu(sc, NULL);
45149769dfeSyamaguchi 	} else {
45249769dfeSyamaguchi 		xc = xc_broadcast(0, vmbus_deinit_interrupts_pcpu,
45349769dfeSyamaguchi 		    sc, NULL);
45449769dfeSyamaguchi 		xc_wait(xc);
45550517e57Snonaka 	}
45649769dfeSyamaguchi 	atomic_and_32(&sc->sc_flags, (uint32_t)~VMBUS_SCFLAG_SYNIC);
45750517e57Snonaka 
45850517e57Snonaka 	/* XXX event_tq */
45950517e57Snonaka 
46050517e57Snonaka 	if (sc->sc_msg_sih != NULL) {
46150517e57Snonaka 		softint_disestablish(sc->sc_msg_sih);
46250517e57Snonaka 		sc->sc_msg_sih = NULL;
46350517e57Snonaka 	}
46450517e57Snonaka }
46550517e57Snonaka 
46650517e57Snonaka static void
vmbus_init_interrupts_pcpu(void * arg1,void * arg2 __unused)46749769dfeSyamaguchi vmbus_init_interrupts_pcpu(void *arg1, void *arg2 __unused)
46850517e57Snonaka {
46950517e57Snonaka 	struct vmbus_softc *sc = arg1;
47050517e57Snonaka 	cpuid_t cpu;
47150517e57Snonaka 	int s;
47250517e57Snonaka 
47350517e57Snonaka 	s = splhigh();
47450517e57Snonaka 
47550517e57Snonaka 	cpu = cpu_index(curcpu());
47650517e57Snonaka 	if (!kcpuset_isset(sc->sc_intr_cpuset, cpu)) {
47750517e57Snonaka 		kcpuset_atomic_set(sc->sc_intr_cpuset, cpu);
47849769dfeSyamaguchi 		vmbus_init_interrupts_md(sc, cpu);
47950517e57Snonaka 		vmbus_init_synic_md(sc, cpu);
48050517e57Snonaka 	}
48150517e57Snonaka 
48250517e57Snonaka 	splx(s);
48350517e57Snonaka }
48450517e57Snonaka 
48550517e57Snonaka static void
vmbus_deinit_interrupts_pcpu(void * arg1,void * arg2 __unused)48649769dfeSyamaguchi vmbus_deinit_interrupts_pcpu(void *arg1, void *arg2 __unused)
48750517e57Snonaka {
48850517e57Snonaka 	struct vmbus_softc *sc = arg1;
48950517e57Snonaka 	cpuid_t cpu;
49050517e57Snonaka 	int s;
49150517e57Snonaka 
49250517e57Snonaka 	s = splhigh();
49350517e57Snonaka 
49450517e57Snonaka 	cpu = cpu_index(curcpu());
49550517e57Snonaka 	if (kcpuset_isset(sc->sc_intr_cpuset, cpu)) {
49649769dfeSyamaguchi 		if (ISSET(sc->sc_flags, VMBUS_SCFLAG_SYNIC))
49750517e57Snonaka 			vmbus_deinit_synic_md(sc, cpu);
49849769dfeSyamaguchi 		vmbus_deinit_interrupts_md(sc, cpu);
49950517e57Snonaka 		kcpuset_atomic_clear(sc->sc_intr_cpuset, cpu);
50050517e57Snonaka 	}
50150517e57Snonaka 
50250517e57Snonaka 	splx(s);
50350517e57Snonaka }
50450517e57Snonaka 
50550517e57Snonaka static int
vmbus_connect(struct vmbus_softc * sc)50650517e57Snonaka vmbus_connect(struct vmbus_softc *sc)
50750517e57Snonaka {
50850517e57Snonaka 	static const uint32_t versions[] = {
50950517e57Snonaka 		VMBUS_VERSION_WIN8_1,
51050517e57Snonaka 		VMBUS_VERSION_WIN8,
51150517e57Snonaka 		VMBUS_VERSION_WIN7,
51250517e57Snonaka 		VMBUS_VERSION_WS2008
51350517e57Snonaka 	};
51450517e57Snonaka 	struct vmbus_chanmsg_connect cmd;
51550517e57Snonaka 	struct vmbus_chanmsg_connect_resp rsp;
51650517e57Snonaka 	int i, rv;
51750517e57Snonaka 
51850517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
51950517e57Snonaka 	cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CONNECT;
52050517e57Snonaka 	cmd.chm_evtflags = hyperv_dma_get_paddr(&sc->sc_events_dma);
52150517e57Snonaka 	cmd.chm_mnf1 = hyperv_dma_get_paddr(&sc->sc_monitor_dma[0]);
52250517e57Snonaka 	cmd.chm_mnf2 = hyperv_dma_get_paddr(&sc->sc_monitor_dma[1]);
52350517e57Snonaka 
52450517e57Snonaka 	memset(&rsp, 0, sizeof(rsp));
52550517e57Snonaka 
52650517e57Snonaka 	for (i = 0; i < __arraycount(versions); i++) {
52750517e57Snonaka 		cmd.chm_ver = versions[i];
52850517e57Snonaka 		rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp),
529*fdd3eadbSnonaka 		    HCF_NOSLEEP);
53050517e57Snonaka 		if (rv) {
53150517e57Snonaka 			DPRINTF("%s: CONNECT failed\n",
53250517e57Snonaka 			    device_xname(sc->sc_dev));
53350517e57Snonaka 			return rv;
53450517e57Snonaka 		}
53550517e57Snonaka 		if (rsp.chm_done) {
53650517e57Snonaka 			atomic_or_32(&sc->sc_flags, VMBUS_SCFLAG_CONNECTED);
53750517e57Snonaka 			sc->sc_proto = versions[i];
53850517e57Snonaka 			sc->sc_handle = VMBUS_GPADL_START;
53950517e57Snonaka 			break;
54050517e57Snonaka 		}
54150517e57Snonaka 	}
54250517e57Snonaka 	if (i == __arraycount(versions)) {
54350517e57Snonaka 		device_printf(sc->sc_dev,
54450517e57Snonaka 		    "failed to negotiate protocol version\n");
54550517e57Snonaka 		return ENXIO;
54650517e57Snonaka 	}
54750517e57Snonaka 
54850517e57Snonaka 	return 0;
54950517e57Snonaka }
55050517e57Snonaka 
55150517e57Snonaka static int
vmbus_cmd(struct vmbus_softc * sc,void * cmd,size_t cmdlen,void * rsp,size_t rsplen,int flags)55250517e57Snonaka vmbus_cmd(struct vmbus_softc *sc, void *cmd, size_t cmdlen, void *rsp,
55350517e57Snonaka     size_t rsplen, int flags)
55450517e57Snonaka {
55550517e57Snonaka 	struct vmbus_msg *msg;
55650517e57Snonaka 	paddr_t pa;
55750517e57Snonaka 	int rv;
55850517e57Snonaka 
55950517e57Snonaka 	if (cmdlen > VMBUS_MSG_DSIZE_MAX) {
56050517e57Snonaka 		device_printf(sc->sc_dev, "payload too large (%zu)\n",
56150517e57Snonaka 		    cmdlen);
56250517e57Snonaka 		return EMSGSIZE;
56350517e57Snonaka 	}
56450517e57Snonaka 
565*fdd3eadbSnonaka 	msg = pool_cache_get_paddr(sc->sc_msgpool, PR_WAITOK, &pa);
56650517e57Snonaka 	if (msg == NULL) {
56750517e57Snonaka 		device_printf(sc->sc_dev, "couldn't get msgpool\n");
56850517e57Snonaka 		return ENOMEM;
56950517e57Snonaka 	}
57050517e57Snonaka 	memset(msg, 0, sizeof(*msg));
57150517e57Snonaka 	msg->msg_req.hc_dsize = cmdlen;
57250517e57Snonaka 	memcpy(msg->msg_req.hc_data, cmd, cmdlen);
57350517e57Snonaka 
57450517e57Snonaka 	if (!(flags & HCF_NOREPLY)) {
57550517e57Snonaka 		msg->msg_rsp = rsp;
57650517e57Snonaka 		msg->msg_rsplen = rsplen;
57750517e57Snonaka 	} else
57850517e57Snonaka 		msg->msg_flags |= MSGF_NOQUEUE;
57950517e57Snonaka 
58050517e57Snonaka 	if (flags & HCF_NOSLEEP)
58150517e57Snonaka 		msg->msg_flags |= MSGF_NOSLEEP;
58250517e57Snonaka 
58350517e57Snonaka 	rv = vmbus_start(sc, msg, pa);
58450517e57Snonaka 	if (rv == 0)
58550517e57Snonaka 		rv = vmbus_reply(sc, msg);
58650517e57Snonaka 	pool_cache_put_paddr(sc->sc_msgpool, msg, pa);
58750517e57Snonaka 	return rv;
58850517e57Snonaka }
58950517e57Snonaka 
59050517e57Snonaka static int
vmbus_start(struct vmbus_softc * sc,struct vmbus_msg * msg,paddr_t msg_pa)59150517e57Snonaka vmbus_start(struct vmbus_softc *sc, struct vmbus_msg *msg, paddr_t msg_pa)
59250517e57Snonaka {
59350517e57Snonaka 	const char *wchan = "hvstart";
59450517e57Snonaka 	uint16_t status;
595*fdd3eadbSnonaka 	int wait_ms = 1;	/* milliseconds */
59650517e57Snonaka 	int i, s;
59750517e57Snonaka 
59850517e57Snonaka 	msg->msg_req.hc_connid = VMBUS_CONNID_MESSAGE;
59950517e57Snonaka 	msg->msg_req.hc_msgtype = 1;
60050517e57Snonaka 
60150517e57Snonaka 	if (!(msg->msg_flags & MSGF_NOQUEUE)) {
60250517e57Snonaka 		mutex_enter(&sc->sc_req_lock);
60350517e57Snonaka 		TAILQ_INSERT_TAIL(&sc->sc_reqs, msg, msg_entry);
60450517e57Snonaka 		mutex_exit(&sc->sc_req_lock);
60550517e57Snonaka 	}
60650517e57Snonaka 
607*fdd3eadbSnonaka 	/*
608*fdd3eadbSnonaka 	 * In order to cope with transient failures, e.g. insufficient
609*fdd3eadbSnonaka 	 * resources on host side, we retry the post message Hypercall
610*fdd3eadbSnonaka 	 * several times.  20 retries seem sufficient.
611*fdd3eadbSnonaka 	 */
612*fdd3eadbSnonaka #define HC_RETRY_MAX	20
613*fdd3eadbSnonaka #define HC_WAIT_MAX	(2 * 1000)	/* 2s */
614*fdd3eadbSnonaka 
615*fdd3eadbSnonaka 	for (i = 0; i < HC_RETRY_MAX; i++) {
61650517e57Snonaka 		status = hyperv_hypercall_post_message(
61750517e57Snonaka 		    msg_pa + offsetof(struct vmbus_msg, msg_req));
61850517e57Snonaka 		if (status == HYPERCALL_STATUS_SUCCESS)
619*fdd3eadbSnonaka 			return 0;
62050517e57Snonaka 
62150517e57Snonaka 		if (msg->msg_flags & MSGF_NOSLEEP) {
622*fdd3eadbSnonaka 			DELAY(wait_ms * 1000);
62350517e57Snonaka 			s = splnet();
62450517e57Snonaka 			hyperv_intr();
62550517e57Snonaka 			splx(s);
62650517e57Snonaka 		} else
627*fdd3eadbSnonaka 			tsleep(wchan, PRIBIO, wchan, uimax(1, mstohz(wait_ms)));
628*fdd3eadbSnonaka 
629*fdd3eadbSnonaka 		if (wait_ms < HC_WAIT_MAX)
630*fdd3eadbSnonaka 			wait_ms *= 2;
63150517e57Snonaka 	}
632*fdd3eadbSnonaka 
633*fdd3eadbSnonaka #undef HC_RETRY_MAX
634*fdd3eadbSnonaka #undef HC_WAIT_MAX
635*fdd3eadbSnonaka 
63650517e57Snonaka 	device_printf(sc->sc_dev,
63750517e57Snonaka 	    "posting vmbus message failed with %d\n", status);
638*fdd3eadbSnonaka 
63950517e57Snonaka 	if (!(msg->msg_flags & MSGF_NOQUEUE)) {
64050517e57Snonaka 		mutex_enter(&sc->sc_req_lock);
64150517e57Snonaka 		TAILQ_REMOVE(&sc->sc_reqs, msg, msg_entry);
64250517e57Snonaka 		mutex_exit(&sc->sc_req_lock);
64350517e57Snonaka 	}
64450517e57Snonaka 
645*fdd3eadbSnonaka 	return EIO;
64650517e57Snonaka }
64750517e57Snonaka 
64850517e57Snonaka static int
vmbus_reply_done(struct vmbus_softc * sc,struct vmbus_msg * msg)64950517e57Snonaka vmbus_reply_done(struct vmbus_softc *sc, struct vmbus_msg *msg)
65050517e57Snonaka {
65150517e57Snonaka 	struct vmbus_msg *m;
65250517e57Snonaka 
65350517e57Snonaka 	mutex_enter(&sc->sc_rsp_lock);
65450517e57Snonaka 	TAILQ_FOREACH(m, &sc->sc_rsps, msg_entry) {
65550517e57Snonaka 		if (m == msg) {
65650517e57Snonaka 			mutex_exit(&sc->sc_rsp_lock);
65750517e57Snonaka 			return 1;
65850517e57Snonaka 		}
65950517e57Snonaka 	}
66050517e57Snonaka 	mutex_exit(&sc->sc_rsp_lock);
66150517e57Snonaka 	return 0;
66250517e57Snonaka }
66350517e57Snonaka 
66450517e57Snonaka static int
vmbus_reply(struct vmbus_softc * sc,struct vmbus_msg * msg)66550517e57Snonaka vmbus_reply(struct vmbus_softc *sc, struct vmbus_msg *msg)
66650517e57Snonaka {
667fd5152a9Snonaka 	int s;
66850517e57Snonaka 
66950517e57Snonaka 	if (msg->msg_flags & MSGF_NOQUEUE)
67050517e57Snonaka 		return 0;
67150517e57Snonaka 
672fd5152a9Snonaka 	while (!vmbus_reply_done(sc, msg)) {
67350517e57Snonaka 		if (msg->msg_flags & MSGF_NOSLEEP) {
67450517e57Snonaka 			delay(1000);
67550517e57Snonaka 			s = splnet();
67650517e57Snonaka 			hyperv_intr();
67750517e57Snonaka 			splx(s);
67850517e57Snonaka 		} else
679*fdd3eadbSnonaka 			tsleep(msg, PRIBIO, "hvreply", uimax(1, mstohz(1)));
68050517e57Snonaka 	}
681fd5152a9Snonaka 
682fd5152a9Snonaka 	mutex_enter(&sc->sc_rsp_lock);
683fd5152a9Snonaka 	TAILQ_REMOVE(&sc->sc_rsps, msg, msg_entry);
684fd5152a9Snonaka 	mutex_exit(&sc->sc_rsp_lock);
685fd5152a9Snonaka 
686fd5152a9Snonaka 	return 0;
68750517e57Snonaka }
68850517e57Snonaka 
68950517e57Snonaka static uint16_t
vmbus_intr_signal(struct vmbus_softc * sc,paddr_t con_pa)69050517e57Snonaka vmbus_intr_signal(struct vmbus_softc *sc, paddr_t con_pa)
69150517e57Snonaka {
69250517e57Snonaka 	uint64_t status;
69350517e57Snonaka 
69450517e57Snonaka 	status = hyperv_hypercall_signal_event(con_pa);
69550517e57Snonaka 	return (uint16_t)status;
69650517e57Snonaka }
69750517e57Snonaka 
69850517e57Snonaka #if LONG_BIT == 64
69950517e57Snonaka #define ffsl(v)	ffs64(v)
70050517e57Snonaka #elif LONG_BIT == 32
70150517e57Snonaka #define ffsl(v)	ffs32(v)
70250517e57Snonaka #else
70350517e57Snonaka #error unsupport LONG_BIT
70450517e57Snonaka #endif	/* LONG_BIT */
70550517e57Snonaka 
70650517e57Snonaka static void
vmbus_event_flags_proc(struct vmbus_softc * sc,volatile u_long * revents,int maxrow)70750517e57Snonaka vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *revents,
70850517e57Snonaka     int maxrow)
70950517e57Snonaka {
71050517e57Snonaka 	struct vmbus_channel *ch;
71150517e57Snonaka 	u_long pending;
71250517e57Snonaka 	uint32_t chanid, chanid_base;
71350517e57Snonaka 	int row, chanid_ofs;
71450517e57Snonaka 
71550517e57Snonaka 	for (row = 0; row < maxrow; row++) {
71650517e57Snonaka 		if (revents[row] == 0)
71750517e57Snonaka 			continue;
71850517e57Snonaka 
71950517e57Snonaka 		pending = atomic_swap_ulong(&revents[row], 0);
720*fdd3eadbSnonaka 		pending &= ~sc->sc_evtmask[row];
721*fdd3eadbSnonaka 		chanid_base = row * VMBUS_EVTFLAG_LEN;
72250517e57Snonaka 
72350517e57Snonaka 		while ((chanid_ofs = ffsl(pending)) != 0) {
72450517e57Snonaka 			chanid_ofs--;	/* NOTE: ffs is 1-based */
72550517e57Snonaka 			pending &= ~(1UL << chanid_ofs);
72650517e57Snonaka 
72750517e57Snonaka 			chanid = chanid_base + chanid_ofs;
72850517e57Snonaka 			/* vmbus channel protocol message */
72950517e57Snonaka 			if (chanid == 0)
73050517e57Snonaka 				continue;
73150517e57Snonaka 
732*fdd3eadbSnonaka 			ch = sc->sc_chanmap[chanid];
733*fdd3eadbSnonaka 			if (__predict_false(ch == NULL)) {
734*fdd3eadbSnonaka 				/* Channel is closed. */
73550517e57Snonaka 				continue;
73650517e57Snonaka 			}
737*fdd3eadbSnonaka 			__insn_barrier();
73850517e57Snonaka 			if (ch->ch_state != VMBUS_CHANSTATE_OPENED) {
73950517e57Snonaka 				device_printf(sc->sc_dev,
74050517e57Snonaka 				    "channel %d is not active\n", chanid);
74150517e57Snonaka 				continue;
74250517e57Snonaka 			}
74350517e57Snonaka 			ch->ch_evcnt.ev_count++;
74450517e57Snonaka 			vmbus_channel_schedule(ch);
74550517e57Snonaka 		}
74650517e57Snonaka 	}
74750517e57Snonaka }
74850517e57Snonaka 
74950517e57Snonaka static void
vmbus_event_proc(void * arg,struct cpu_info * ci)75050517e57Snonaka vmbus_event_proc(void *arg, struct cpu_info *ci)
75150517e57Snonaka {
75250517e57Snonaka 	struct vmbus_softc *sc = arg;
75350517e57Snonaka 	struct vmbus_evtflags *evt;
75450517e57Snonaka 
75550517e57Snonaka 	/*
75650517e57Snonaka 	 * On Host with Win8 or above, the event page can be
75750517e57Snonaka 	 * checked directly to get the id of the channel
75850517e57Snonaka 	 * that has the pending interrupt.
75950517e57Snonaka 	 */
76050517e57Snonaka 	evt = (struct vmbus_evtflags *)sc->sc_percpu[cpu_index(ci)].siep +
76150517e57Snonaka 	    VMBUS_SINT_MESSAGE;
76250517e57Snonaka 
76350517e57Snonaka 	vmbus_event_flags_proc(sc, evt->evt_flags,
76450517e57Snonaka 	    __arraycount(evt->evt_flags));
76550517e57Snonaka }
76650517e57Snonaka 
76750517e57Snonaka static void
vmbus_event_proc_compat(void * arg,struct cpu_info * ci)76850517e57Snonaka vmbus_event_proc_compat(void *arg, struct cpu_info *ci)
76950517e57Snonaka {
77050517e57Snonaka 	struct vmbus_softc *sc = arg;
77150517e57Snonaka 	struct vmbus_evtflags *evt;
77250517e57Snonaka 
77350517e57Snonaka 	evt = (struct vmbus_evtflags *)sc->sc_percpu[cpu_index(ci)].siep +
77450517e57Snonaka 	    VMBUS_SINT_MESSAGE;
77550517e57Snonaka 
77650517e57Snonaka 	if (test_bit(0, &evt->evt_flags[0])) {
77750517e57Snonaka 		clear_bit(0, &evt->evt_flags[0]);
77850517e57Snonaka 		/*
77950517e57Snonaka 		 * receive size is 1/2 page and divide that by 4 bytes
78050517e57Snonaka 		 */
78150517e57Snonaka 		vmbus_event_flags_proc(sc, sc->sc_revents,
78250517e57Snonaka 		    VMBUS_CHAN_MAX_COMPAT / VMBUS_EVTFLAG_LEN);
78350517e57Snonaka 	}
78450517e57Snonaka }
78550517e57Snonaka 
78650517e57Snonaka static void
vmbus_message_proc(void * arg,struct cpu_info * ci)78750517e57Snonaka vmbus_message_proc(void *arg, struct cpu_info *ci)
78850517e57Snonaka {
78950517e57Snonaka 	struct vmbus_softc *sc = arg;
79050517e57Snonaka 	struct vmbus_message *msg;
79150517e57Snonaka 
79250517e57Snonaka 	msg = (struct vmbus_message *)sc->sc_percpu[cpu_index(ci)].simp +
79350517e57Snonaka 	    VMBUS_SINT_MESSAGE;
79450517e57Snonaka 	if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
79550517e57Snonaka 		if (__predict_true(!cold))
79650517e57Snonaka 			softint_schedule_cpu(sc->sc_msg_sih, ci);
79750517e57Snonaka 		else
79850517e57Snonaka 			vmbus_message_softintr(sc);
79950517e57Snonaka 	}
80050517e57Snonaka }
80150517e57Snonaka 
80250517e57Snonaka static void
vmbus_message_softintr(void * arg)80350517e57Snonaka vmbus_message_softintr(void *arg)
80450517e57Snonaka {
80550517e57Snonaka 	struct vmbus_softc *sc = arg;
80650517e57Snonaka 	struct vmbus_message *msg;
80750517e57Snonaka 	struct vmbus_chanmsg_hdr *hdr;
80850517e57Snonaka 	uint32_t type;
80950517e57Snonaka 	cpuid_t cpu;
81050517e57Snonaka 
81150517e57Snonaka 	cpu = cpu_index(curcpu());
81250517e57Snonaka 
81350517e57Snonaka 	for (;;) {
81450517e57Snonaka 		msg = (struct vmbus_message *)sc->sc_percpu[cpu].simp +
81550517e57Snonaka 		    VMBUS_SINT_MESSAGE;
81650517e57Snonaka 		if (msg->msg_type == HYPERV_MSGTYPE_NONE)
81750517e57Snonaka 			break;
81850517e57Snonaka 
81950517e57Snonaka 		hdr = (struct vmbus_chanmsg_hdr *)msg->msg_data;
82050517e57Snonaka 		type = hdr->chm_type;
82150517e57Snonaka 		if (type >= VMBUS_CHANMSG_COUNT) {
82250517e57Snonaka 			device_printf(sc->sc_dev,
82350517e57Snonaka 			    "unhandled message type %u flags %#x\n", type,
82450517e57Snonaka 			    msg->msg_flags);
82550517e57Snonaka 		} else {
82650517e57Snonaka 			if (vmbus_msg_dispatch[type].hmd_handler) {
82750517e57Snonaka 				vmbus_msg_dispatch[type].hmd_handler(sc, hdr);
82850517e57Snonaka 			} else {
82950517e57Snonaka 				device_printf(sc->sc_dev,
83050517e57Snonaka 				    "unhandled message type %u\n", type);
83150517e57Snonaka 			}
83250517e57Snonaka 		}
83350517e57Snonaka 
83450517e57Snonaka 		msg->msg_type = HYPERV_MSGTYPE_NONE;
83550517e57Snonaka 		membar_sync();
83650517e57Snonaka 		if (msg->msg_flags & VMBUS_MSGFLAG_PENDING)
83750517e57Snonaka 			hyperv_send_eom();
83850517e57Snonaka 	}
83950517e57Snonaka }
84050517e57Snonaka 
84150517e57Snonaka static void
vmbus_channel_response(struct vmbus_softc * sc,struct vmbus_chanmsg_hdr * rsphdr)84250517e57Snonaka vmbus_channel_response(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *rsphdr)
84350517e57Snonaka {
84450517e57Snonaka 	struct vmbus_msg *msg;
84550517e57Snonaka 	struct vmbus_chanmsg_hdr *reqhdr;
84650517e57Snonaka 	int req;
84750517e57Snonaka 
84850517e57Snonaka 	req = vmbus_msg_dispatch[rsphdr->chm_type].hmd_request;
84950517e57Snonaka 	mutex_enter(&sc->sc_req_lock);
85050517e57Snonaka 	TAILQ_FOREACH(msg, &sc->sc_reqs, msg_entry) {
85150517e57Snonaka 		reqhdr = (struct vmbus_chanmsg_hdr *)&msg->msg_req.hc_data;
85250517e57Snonaka 		if (reqhdr->chm_type == req) {
85350517e57Snonaka 			TAILQ_REMOVE(&sc->sc_reqs, msg, msg_entry);
85450517e57Snonaka 			break;
85550517e57Snonaka 		}
85650517e57Snonaka 	}
85750517e57Snonaka 	mutex_exit(&sc->sc_req_lock);
85850517e57Snonaka 	if (msg != NULL) {
85950517e57Snonaka 		memcpy(msg->msg_rsp, rsphdr, msg->msg_rsplen);
86050517e57Snonaka 		mutex_enter(&sc->sc_rsp_lock);
86150517e57Snonaka 		TAILQ_INSERT_TAIL(&sc->sc_rsps, msg, msg_entry);
86250517e57Snonaka 		mutex_exit(&sc->sc_rsp_lock);
86350517e57Snonaka 		wakeup(msg);
86450517e57Snonaka 	}
86550517e57Snonaka }
86650517e57Snonaka 
86750517e57Snonaka static void
vmbus_channel_offer(struct vmbus_softc * sc,struct vmbus_chanmsg_hdr * hdr)86850517e57Snonaka vmbus_channel_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
86950517e57Snonaka {
870b74d80dfSnonaka 	struct vmbus_chanmsg_choffer *co;
87150517e57Snonaka 
872b74d80dfSnonaka 	co = kmem_intr_alloc(sizeof(*co), KM_NOSLEEP);
873b74d80dfSnonaka 	if (co == NULL) {
874b74d80dfSnonaka 		device_printf(sc->sc_dev,
875b74d80dfSnonaka 		    "failed to allocate an offer object\n");
876b74d80dfSnonaka 		return;
877b74d80dfSnonaka 	}
878b74d80dfSnonaka 
879b74d80dfSnonaka 	memcpy(co, hdr, sizeof(*co));
880b74d80dfSnonaka 	vmbus_chevq_enqueue(sc, VMBUS_CHEV_TYPE_OFFER, co);
88150517e57Snonaka }
88250517e57Snonaka 
88350517e57Snonaka static void
vmbus_channel_rescind(struct vmbus_softc * sc,struct vmbus_chanmsg_hdr * hdr)88450517e57Snonaka vmbus_channel_rescind(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
88550517e57Snonaka {
886b74d80dfSnonaka 	struct vmbus_chanmsg_chrescind *cr;
88750517e57Snonaka 
888b74d80dfSnonaka 	cr = kmem_intr_alloc(sizeof(*cr), KM_NOSLEEP);
889b74d80dfSnonaka 	if (cr == NULL) {
890b74d80dfSnonaka 		device_printf(sc->sc_dev,
891b74d80dfSnonaka 		    "failed to allocate an rescind object\n");
892b74d80dfSnonaka 		return;
893b74d80dfSnonaka 	}
894b74d80dfSnonaka 
895b74d80dfSnonaka 	memcpy(cr, hdr, sizeof(*cr));
896b74d80dfSnonaka 	vmbus_chevq_enqueue(sc, VMBUS_CHEV_TYPE_RESCIND, cr);
89750517e57Snonaka }
89850517e57Snonaka 
89950517e57Snonaka static void
vmbus_channel_delivered(struct vmbus_softc * sc,struct vmbus_chanmsg_hdr * hdr)90050517e57Snonaka vmbus_channel_delivered(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
90150517e57Snonaka {
90250517e57Snonaka 
90350517e57Snonaka 	atomic_or_32(&sc->sc_flags, VMBUS_SCFLAG_OFFERS_DELIVERED);
904c9d5b091Snonaka 	wakeup(&sc->sc_devq);
90550517e57Snonaka }
90650517e57Snonaka 
90750517e57Snonaka static void
hyperv_guid_sprint(struct hyperv_guid * guid,char * str,size_t size)90850517e57Snonaka hyperv_guid_sprint(struct hyperv_guid *guid, char *str, size_t size)
90950517e57Snonaka {
91050517e57Snonaka 	static const struct {
91150517e57Snonaka 		const struct hyperv_guid *guid;
91250517e57Snonaka 		const char *ident;
91350517e57Snonaka 	} map[] = {
91450517e57Snonaka 		{ &hyperv_guid_network,		"network" },
91550517e57Snonaka 		{ &hyperv_guid_ide,		"ide" },
91650517e57Snonaka 		{ &hyperv_guid_scsi,		"scsi" },
91750517e57Snonaka 		{ &hyperv_guid_shutdown,	"shutdown" },
91850517e57Snonaka 		{ &hyperv_guid_timesync,	"timesync" },
91950517e57Snonaka 		{ &hyperv_guid_heartbeat,	"heartbeat" },
92050517e57Snonaka 		{ &hyperv_guid_kvp,		"kvp" },
92150517e57Snonaka 		{ &hyperv_guid_vss,		"vss" },
92250517e57Snonaka 		{ &hyperv_guid_dynmem,		"dynamic-memory" },
92350517e57Snonaka 		{ &hyperv_guid_mouse,		"mouse" },
92450517e57Snonaka 		{ &hyperv_guid_kbd,		"keyboard" },
92550517e57Snonaka 		{ &hyperv_guid_video,		"video" },
92650517e57Snonaka 		{ &hyperv_guid_fc,		"fiber-channel" },
92750517e57Snonaka 		{ &hyperv_guid_fcopy,		"file-copy" },
92850517e57Snonaka 		{ &hyperv_guid_pcie,		"pcie-passthrough" },
92950517e57Snonaka 		{ &hyperv_guid_netdir,		"network-direct" },
93050517e57Snonaka 		{ &hyperv_guid_rdesktop,	"remote-desktop" },
93150517e57Snonaka 		{ &hyperv_guid_avma1,		"avma-1" },
93250517e57Snonaka 		{ &hyperv_guid_avma2,		"avma-2" },
93350517e57Snonaka 		{ &hyperv_guid_avma3,		"avma-3" },
93450517e57Snonaka 		{ &hyperv_guid_avma4,		"avma-4" },
93550517e57Snonaka 	};
93650517e57Snonaka 	int i;
93750517e57Snonaka 
93850517e57Snonaka 	for (i = 0; i < __arraycount(map); i++) {
93950517e57Snonaka 		if (memcmp(guid, map[i].guid, sizeof(*guid)) == 0) {
94050517e57Snonaka 			strlcpy(str, map[i].ident, size);
94150517e57Snonaka 			return;
94250517e57Snonaka 		}
94350517e57Snonaka 	}
94450517e57Snonaka 	hyperv_guid2str(guid, str, size);
94550517e57Snonaka }
94650517e57Snonaka 
94750517e57Snonaka static int
vmbus_channel_scan(struct vmbus_softc * sc)94850517e57Snonaka vmbus_channel_scan(struct vmbus_softc *sc)
94950517e57Snonaka {
95050517e57Snonaka 	struct vmbus_chanmsg_hdr hdr;
95150517e57Snonaka 	struct vmbus_chanmsg_choffer rsp;
95250517e57Snonaka 
953b74d80dfSnonaka 	TAILQ_INIT(&sc->sc_prichans);
954b74d80dfSnonaka 	mutex_init(&sc->sc_prichan_lock, MUTEX_DEFAULT, IPL_NET);
955c9d5b091Snonaka 	TAILQ_INIT(&sc->sc_channels);
956c9d5b091Snonaka 	mutex_init(&sc->sc_channel_lock, MUTEX_DEFAULT, IPL_NET);
957c9d5b091Snonaka 
958b74d80dfSnonaka 	/*
959b74d80dfSnonaka 	 * This queue serializes vmbus channel offer and rescind messages.
960b74d80dfSnonaka 	 */
961b74d80dfSnonaka 	SIMPLEQ_INIT(&sc->sc_chevq);
962b74d80dfSnonaka 	mutex_init(&sc->sc_chevq_lock, MUTEX_DEFAULT, IPL_NET);
963b74d80dfSnonaka 	cv_init(&sc->sc_chevq_cv, "hvchevcv");
964b74d80dfSnonaka 	if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
965b74d80dfSnonaka 	    vmbus_chevq_thread, sc, NULL, "hvchevq") != 0) {
966b74d80dfSnonaka 		DPRINTF("%s: failed to create prich chevq thread\n",
967b74d80dfSnonaka 		    device_xname(sc->sc_dev));
968b74d80dfSnonaka 		return -1;
969b74d80dfSnonaka 	}
970b74d80dfSnonaka 
971b74d80dfSnonaka 	/*
972b74d80dfSnonaka 	 * This queue serializes vmbus devices' attach and detach
973b74d80dfSnonaka 	 * for channel offer and rescind messages.
974b74d80dfSnonaka 	 */
975c9d5b091Snonaka 	SIMPLEQ_INIT(&sc->sc_devq);
976c9d5b091Snonaka 	mutex_init(&sc->sc_devq_lock, MUTEX_DEFAULT, IPL_NET);
977c9d5b091Snonaka 	cv_init(&sc->sc_devq_cv, "hvdevqcv");
978c9d5b091Snonaka 	if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
979b74d80dfSnonaka 	    vmbus_devq_thread, sc, NULL, "hvdevq") != 0) {
980b74d80dfSnonaka 		DPRINTF("%s: failed to create prich devq thread\n",
981b74d80dfSnonaka 		    device_xname(sc->sc_dev));
982b74d80dfSnonaka 		return -1;
983b74d80dfSnonaka 	}
984b74d80dfSnonaka 
985b74d80dfSnonaka 	/*
986b74d80dfSnonaka 	 * This queue handles sub-channel detach, so that vmbus
987b74d80dfSnonaka 	 * device's detach running in sc_devq can drain its sub-channels.
988b74d80dfSnonaka 	 */
989b74d80dfSnonaka 	SIMPLEQ_INIT(&sc->sc_subch_devq);
990b74d80dfSnonaka 	mutex_init(&sc->sc_subch_devq_lock, MUTEX_DEFAULT, IPL_NET);
991b74d80dfSnonaka 	cv_init(&sc->sc_subch_devq_cv, "hvsdvqcv");
992b74d80dfSnonaka 	if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
993b74d80dfSnonaka 	    vmbus_subchannel_devq_thread, sc, NULL, "hvsdevq") != 0) {
994b74d80dfSnonaka 		DPRINTF("%s: failed to create subch devq thread\n",
995c9d5b091Snonaka 		    device_xname(sc->sc_dev));
996c9d5b091Snonaka 		return -1;
997c9d5b091Snonaka 	}
99850517e57Snonaka 
99950517e57Snonaka 	memset(&hdr, 0, sizeof(hdr));
100050517e57Snonaka 	hdr.chm_type = VMBUS_CHANMSG_CHREQUEST;
100150517e57Snonaka 
100250517e57Snonaka 	if (vmbus_cmd(sc, &hdr, sizeof(hdr), &rsp, sizeof(rsp),
1003*fdd3eadbSnonaka 	    HCF_NOREPLY | HCF_NOSLEEP)) {
100450517e57Snonaka 		DPRINTF("%s: CHREQUEST failed\n", device_xname(sc->sc_dev));
100550517e57Snonaka 		return -1;
100650517e57Snonaka 	}
100750517e57Snonaka 
1008fd5152a9Snonaka 	while (!ISSET(sc->sc_flags, VMBUS_SCFLAG_OFFERS_DELIVERED))
1009fd5152a9Snonaka 		tsleep(&sc->sc_devq, PRIBIO, "hvscan", 1);
101050517e57Snonaka 
1011b74d80dfSnonaka 	mutex_enter(&sc->sc_chevq_lock);
1012b74d80dfSnonaka 	vmbus_process_chevq(sc);
1013b74d80dfSnonaka 	mutex_exit(&sc->sc_chevq_lock);
1014c9d5b091Snonaka 	mutex_enter(&sc->sc_devq_lock);
1015c9d5b091Snonaka 	vmbus_process_devq(sc);
1016c9d5b091Snonaka 	mutex_exit(&sc->sc_devq_lock);
101750517e57Snonaka 
101850517e57Snonaka 	return 0;
101950517e57Snonaka }
102050517e57Snonaka 
102150517e57Snonaka static struct vmbus_channel *
vmbus_channel_alloc(struct vmbus_softc * sc)102250517e57Snonaka vmbus_channel_alloc(struct vmbus_softc *sc)
102350517e57Snonaka {
102450517e57Snonaka 	struct vmbus_channel *ch;
102550517e57Snonaka 
1026b74d80dfSnonaka 	ch = kmem_zalloc(sizeof(*ch), KM_SLEEP);
102750517e57Snonaka 
102850517e57Snonaka 	ch->ch_monprm = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_monprm_dma,
1029*fdd3eadbSnonaka 	    sizeof(*ch->ch_monprm), 8, 0, 1);
103050517e57Snonaka 	if (ch->ch_monprm == NULL) {
103150517e57Snonaka 		device_printf(sc->sc_dev, "monprm alloc failed\n");
103250517e57Snonaka 		kmem_free(ch, sizeof(*ch));
103350517e57Snonaka 		return NULL;
103450517e57Snonaka 	}
103550517e57Snonaka 
103650517e57Snonaka 	ch->ch_refs = 1;
103750517e57Snonaka 	ch->ch_sc = sc;
1038*fdd3eadbSnonaka 	mutex_init(&ch->ch_event_lock, MUTEX_DEFAULT, IPL_NET);
1039*fdd3eadbSnonaka 	cv_init(&ch->ch_event_cv, "hvevwait");
104050517e57Snonaka 	mutex_init(&ch->ch_subchannel_lock, MUTEX_DEFAULT, IPL_NET);
1041*fdd3eadbSnonaka 	cv_init(&ch->ch_subchannel_cv, "hvsubch");
104250517e57Snonaka 	TAILQ_INIT(&ch->ch_subchannels);
104350517e57Snonaka 
104450517e57Snonaka 	ch->ch_state = VMBUS_CHANSTATE_CLOSED;
104550517e57Snonaka 
104650517e57Snonaka 	return ch;
104750517e57Snonaka }
104850517e57Snonaka 
104950517e57Snonaka static void
vmbus_channel_free(struct vmbus_channel * ch)105050517e57Snonaka vmbus_channel_free(struct vmbus_channel *ch)
105150517e57Snonaka {
105250517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
105350517e57Snonaka 
105450517e57Snonaka 	KASSERTMSG(TAILQ_EMPTY(&ch->ch_subchannels) &&
105550517e57Snonaka 	    ch->ch_subchannel_count == 0, "still owns sub-channels");
105650517e57Snonaka 	KASSERTMSG(ch->ch_state == 0 || ch->ch_state == VMBUS_CHANSTATE_CLOSED,
105750517e57Snonaka 	    "free busy channel");
105850517e57Snonaka 	KASSERTMSG(ch->ch_refs == 0, "channel %u: invalid refcnt %d",
105950517e57Snonaka 	    ch->ch_id, ch->ch_refs);
106050517e57Snonaka 
106150517e57Snonaka 	hyperv_dma_free(sc->sc_dmat, &ch->ch_monprm_dma);
1062*fdd3eadbSnonaka 	mutex_destroy(&ch->ch_event_lock);
1063*fdd3eadbSnonaka 	cv_destroy(&ch->ch_event_cv);
106450517e57Snonaka 	mutex_destroy(&ch->ch_subchannel_lock);
1065*fdd3eadbSnonaka 	cv_destroy(&ch->ch_subchannel_cv);
106650517e57Snonaka 	/* XXX ch_evcnt */
1067c9d5b091Snonaka 	if (ch->ch_taskq != NULL)
106850517e57Snonaka 		softint_disestablish(ch->ch_taskq);
106950517e57Snonaka 	kmem_free(ch, sizeof(*ch));
107050517e57Snonaka }
107150517e57Snonaka 
107250517e57Snonaka static int
vmbus_channel_add(struct vmbus_channel * nch)107350517e57Snonaka vmbus_channel_add(struct vmbus_channel *nch)
107450517e57Snonaka {
107550517e57Snonaka 	struct vmbus_softc *sc = nch->ch_sc;
107650517e57Snonaka 	struct vmbus_channel *ch;
1077*fdd3eadbSnonaka 	int refs __diagused;
107850517e57Snonaka 
107950517e57Snonaka 	if (nch->ch_id == 0) {
108050517e57Snonaka 		device_printf(sc->sc_dev, "got channel 0 offer, discard\n");
108150517e57Snonaka 		return EINVAL;
108250517e57Snonaka 	} else if (nch->ch_id >= sc->sc_channel_max) {
108350517e57Snonaka 		device_printf(sc->sc_dev, "invalid channel %u offer\n",
108450517e57Snonaka 		    nch->ch_id);
108550517e57Snonaka 		return EINVAL;
108650517e57Snonaka 	}
108750517e57Snonaka 
1088b74d80dfSnonaka 	mutex_enter(&sc->sc_prichan_lock);
1089b74d80dfSnonaka 	TAILQ_FOREACH(ch, &sc->sc_prichans, ch_prientry) {
109050517e57Snonaka 		if (!memcmp(&ch->ch_type, &nch->ch_type, sizeof(ch->ch_type)) &&
109150517e57Snonaka 		    !memcmp(&ch->ch_inst, &nch->ch_inst, sizeof(ch->ch_inst)))
109250517e57Snonaka 			break;
109350517e57Snonaka 	}
109450517e57Snonaka 	if (VMBUS_CHAN_ISPRIMARY(nch)) {
109550517e57Snonaka 		if (ch == NULL) {
1096b74d80dfSnonaka 			TAILQ_INSERT_TAIL(&sc->sc_prichans, nch, ch_prientry);
1097b74d80dfSnonaka 			mutex_exit(&sc->sc_prichan_lock);
109850517e57Snonaka 			goto done;
109950517e57Snonaka 		} else {
1100b74d80dfSnonaka 			mutex_exit(&sc->sc_prichan_lock);
110150517e57Snonaka 			device_printf(sc->sc_dev,
110250517e57Snonaka 			    "duplicated primary channel%u\n", nch->ch_id);
110350517e57Snonaka 			return EINVAL;
110450517e57Snonaka 		}
110550517e57Snonaka 	} else {
110650517e57Snonaka 		if (ch == NULL) {
1107b74d80dfSnonaka 			mutex_exit(&sc->sc_prichan_lock);
110850517e57Snonaka 			device_printf(sc->sc_dev, "no primary channel%u\n",
110950517e57Snonaka 			    nch->ch_id);
111050517e57Snonaka 			return EINVAL;
111150517e57Snonaka 		}
111250517e57Snonaka 	}
1113b74d80dfSnonaka 	mutex_exit(&sc->sc_prichan_lock);
111450517e57Snonaka 
111550517e57Snonaka 	KASSERT(!VMBUS_CHAN_ISPRIMARY(nch));
111650517e57Snonaka 	KASSERT(ch != NULL);
111750517e57Snonaka 
1118c9d5b091Snonaka 	refs = atomic_inc_uint_nv(&nch->ch_refs);
1119c9d5b091Snonaka 	KASSERT(refs == 2);
112050517e57Snonaka 
112150517e57Snonaka 	nch->ch_primary_channel = ch;
112250517e57Snonaka 	nch->ch_dev = ch->ch_dev;
112350517e57Snonaka 
112450517e57Snonaka 	mutex_enter(&ch->ch_subchannel_lock);
112550517e57Snonaka 	TAILQ_INSERT_TAIL(&ch->ch_subchannels, nch, ch_subentry);
112650517e57Snonaka 	ch->ch_subchannel_count++;
1127*fdd3eadbSnonaka 	cv_signal(&ch->ch_subchannel_cv);
112850517e57Snonaka 	mutex_exit(&ch->ch_subchannel_lock);
112950517e57Snonaka 
113050517e57Snonaka done:
1131b74d80dfSnonaka 	mutex_enter(&sc->sc_channel_lock);
1132b74d80dfSnonaka 	TAILQ_INSERT_TAIL(&sc->sc_channels, nch, ch_entry);
1133b74d80dfSnonaka 	mutex_exit(&sc->sc_channel_lock);
1134b74d80dfSnonaka 
113550517e57Snonaka 	vmbus_channel_cpu_default(nch);
113650517e57Snonaka 
113750517e57Snonaka 	return 0;
113850517e57Snonaka }
113950517e57Snonaka 
114050517e57Snonaka void
vmbus_channel_cpu_set(struct vmbus_channel * ch,int cpu)114150517e57Snonaka vmbus_channel_cpu_set(struct vmbus_channel *ch, int cpu)
114250517e57Snonaka {
114350517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
114450517e57Snonaka 
114550517e57Snonaka 	KASSERTMSG(cpu >= 0 && cpu < ncpu, "invalid cpu %d", cpu);
114650517e57Snonaka 
114750517e57Snonaka 	if (sc->sc_proto == VMBUS_VERSION_WS2008 ||
114850517e57Snonaka 	    sc->sc_proto == VMBUS_VERSION_WIN7) {
114950517e57Snonaka 		/* Only cpu0 is supported */
115050517e57Snonaka 		cpu = 0;
115150517e57Snonaka 	}
115250517e57Snonaka 
115350517e57Snonaka 	ch->ch_cpuid = cpu;
115482065befSnonaka 	ch->ch_vcpu = hyperv_get_vcpuid(cpu);
1155*fdd3eadbSnonaka 
1156*fdd3eadbSnonaka 	aprint_debug_dev(ch->ch_dev != NULL ? ch->ch_dev : sc->sc_dev,
1157*fdd3eadbSnonaka 	    "channel %u assigned to cpu%u [vcpu%u]\n",
1158*fdd3eadbSnonaka 	    ch->ch_id, ch->ch_cpuid, ch->ch_vcpu);
115950517e57Snonaka }
116050517e57Snonaka 
116150517e57Snonaka void
vmbus_channel_cpu_rr(struct vmbus_channel * ch)116250517e57Snonaka vmbus_channel_cpu_rr(struct vmbus_channel *ch)
116350517e57Snonaka {
116450517e57Snonaka 	static uint32_t vmbus_channel_nextcpu;
116550517e57Snonaka 	int cpu;
116650517e57Snonaka 
1167c9d5b091Snonaka 	cpu = atomic_inc_32_nv(&vmbus_channel_nextcpu) % ncpu;
116850517e57Snonaka 	vmbus_channel_cpu_set(ch, cpu);
116950517e57Snonaka }
117050517e57Snonaka 
117150517e57Snonaka static void
vmbus_channel_cpu_default(struct vmbus_channel * ch)117250517e57Snonaka vmbus_channel_cpu_default(struct vmbus_channel *ch)
117350517e57Snonaka {
117450517e57Snonaka 
117550517e57Snonaka         /*
117650517e57Snonaka 	 * By default, pin the channel to cpu0.  Devices having
117750517e57Snonaka 	 * special channel-cpu mapping requirement should call
117850517e57Snonaka 	 * vmbus_channel_cpu_{set,rr}().
117950517e57Snonaka 	 */
118050517e57Snonaka 	vmbus_channel_cpu_set(ch, 0);
118150517e57Snonaka }
118250517e57Snonaka 
1183c9d5b091Snonaka bool
vmbus_channel_is_revoked(struct vmbus_channel * ch)1184c9d5b091Snonaka vmbus_channel_is_revoked(struct vmbus_channel *ch)
1185c9d5b091Snonaka {
1186c9d5b091Snonaka 
1187c9d5b091Snonaka 	return (ch->ch_flags & CHF_REVOKED) ? true : false;
1188c9d5b091Snonaka }
1189c9d5b091Snonaka 
119050517e57Snonaka static void
vmbus_process_offer(struct vmbus_softc * sc,struct vmbus_chanmsg_choffer * co)1191c9d5b091Snonaka vmbus_process_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_choffer *co)
119250517e57Snonaka {
119350517e57Snonaka 	struct vmbus_channel *ch;
119450517e57Snonaka 
119550517e57Snonaka 	ch = vmbus_channel_alloc(sc);
119650517e57Snonaka 	if (ch == NULL) {
119750517e57Snonaka 		device_printf(sc->sc_dev, "allocate channel %u failed\n",
1198c9d5b091Snonaka 		    co->chm_chanid);
119950517e57Snonaka 		return;
120050517e57Snonaka 	}
120150517e57Snonaka 
120250517e57Snonaka 	/*
120350517e57Snonaka 	 * By default we setup state to enable batched reading.
120450517e57Snonaka 	 * A specific service can choose to disable this prior
120550517e57Snonaka 	 * to opening the channel.
120650517e57Snonaka 	 */
120750517e57Snonaka 	ch->ch_flags |= CHF_BATCHED;
120850517e57Snonaka 
1209c9d5b091Snonaka 	hyperv_guid_sprint(&co->chm_chtype, ch->ch_ident,
121050517e57Snonaka 	    sizeof(ch->ch_ident));
121150517e57Snonaka 
121250517e57Snonaka 	ch->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
121350517e57Snonaka 	if (sc->sc_proto > VMBUS_VERSION_WS2008)
1214c9d5b091Snonaka 		ch->ch_monprm->mp_connid = co->chm_connid;
121550517e57Snonaka 
1216c9d5b091Snonaka 	if (co->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
1217c9d5b091Snonaka 		ch->ch_mgroup = co->chm_montrig / VMBUS_MONTRIG_LEN;
1218c9d5b091Snonaka 		ch->ch_mindex = co->chm_montrig % VMBUS_MONTRIG_LEN;
121950517e57Snonaka 		ch->ch_flags |= CHF_MONITOR;
122050517e57Snonaka 	}
122150517e57Snonaka 
1222c9d5b091Snonaka 	ch->ch_id = co->chm_chanid;
1223c9d5b091Snonaka 	ch->ch_subidx = co->chm_subidx;
122450517e57Snonaka 
1225c9d5b091Snonaka 	memcpy(&ch->ch_type, &co->chm_chtype, sizeof(ch->ch_type));
1226c9d5b091Snonaka 	memcpy(&ch->ch_inst, &co->chm_chinst, sizeof(ch->ch_inst));
122750517e57Snonaka 
122850517e57Snonaka 	if (vmbus_channel_add(ch) != 0) {
1229c9d5b091Snonaka 		atomic_dec_uint(&ch->ch_refs);
123050517e57Snonaka 		vmbus_channel_free(ch);
123150517e57Snonaka 		return;
123250517e57Snonaka 	}
123350517e57Snonaka 
123450517e57Snonaka 	ch->ch_state = VMBUS_CHANSTATE_OFFERED;
123550517e57Snonaka 
1236c9d5b091Snonaka 	vmbus_devq_enqueue(sc, VMBUS_DEV_TYPE_ATTACH, ch);
1237c9d5b091Snonaka 
123850517e57Snonaka #ifdef HYPERV_DEBUG
123950517e57Snonaka 	printf("%s: channel %u: \"%s\"", device_xname(sc->sc_dev), ch->ch_id,
124050517e57Snonaka 	    ch->ch_ident);
124150517e57Snonaka 	if (ch->ch_flags & CHF_MONITOR)
1242c9d5b091Snonaka 		printf(", monitor %u\n", co->chm_montrig);
124350517e57Snonaka 	else
124450517e57Snonaka 		printf("\n");
124550517e57Snonaka #endif
124650517e57Snonaka }
124750517e57Snonaka 
1248c9d5b091Snonaka static void
vmbus_process_rescind(struct vmbus_softc * sc,struct vmbus_chanmsg_chrescind * cr)1249c9d5b091Snonaka vmbus_process_rescind(struct vmbus_softc *sc,
1250c9d5b091Snonaka     struct vmbus_chanmsg_chrescind *cr)
1251c9d5b091Snonaka {
1252c9d5b091Snonaka 	struct vmbus_channel *ch;
1253c9d5b091Snonaka 
1254c9d5b091Snonaka 	if (cr->chm_chanid > VMBUS_CHAN_MAX) {
1255c9d5b091Snonaka 		device_printf(sc->sc_dev, "invalid revoked channel%u\n",
1256c9d5b091Snonaka 		    cr->chm_chanid);
1257c9d5b091Snonaka 		return;
1258c9d5b091Snonaka 	}
1259c9d5b091Snonaka 
1260c9d5b091Snonaka 	mutex_enter(&sc->sc_channel_lock);
1261c9d5b091Snonaka 	ch = vmbus_channel_lookup(sc, cr->chm_chanid);
1262c9d5b091Snonaka 	if (ch == NULL) {
1263c9d5b091Snonaka 		mutex_exit(&sc->sc_channel_lock);
1264c9d5b091Snonaka 		device_printf(sc->sc_dev, "channel%u is not offered\n",
1265c9d5b091Snonaka 		    cr->chm_chanid);
1266c9d5b091Snonaka 		return;
1267c9d5b091Snonaka 	}
1268c9d5b091Snonaka 	TAILQ_REMOVE(&sc->sc_channels, ch, ch_entry);
1269c9d5b091Snonaka 	mutex_exit(&sc->sc_channel_lock);
1270c9d5b091Snonaka 
1271b74d80dfSnonaka 	if (VMBUS_CHAN_ISPRIMARY(ch)) {
1272b74d80dfSnonaka 		mutex_enter(&sc->sc_prichan_lock);
1273b74d80dfSnonaka 		TAILQ_REMOVE(&sc->sc_prichans, ch, ch_prientry);
1274b74d80dfSnonaka 		mutex_exit(&sc->sc_prichan_lock);
1275b74d80dfSnonaka 	}
1276b74d80dfSnonaka 
1277c9d5b091Snonaka 	KASSERTMSG(!(ch->ch_flags & CHF_REVOKED),
1278c9d5b091Snonaka 	    "channel%u has already been revoked", ch->ch_id);
1279c9d5b091Snonaka 	atomic_or_uint(&ch->ch_flags, CHF_REVOKED);
1280c9d5b091Snonaka 
1281c9d5b091Snonaka 	vmbus_channel_detach(ch);
1282c9d5b091Snonaka }
1283c9d5b091Snonaka 
128450517e57Snonaka static int
vmbus_channel_release(struct vmbus_channel * ch)128550517e57Snonaka vmbus_channel_release(struct vmbus_channel *ch)
128650517e57Snonaka {
128750517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
128850517e57Snonaka 	struct vmbus_chanmsg_chfree cmd;
128950517e57Snonaka 	int rv;
129050517e57Snonaka 
129150517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
129250517e57Snonaka 	cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CHFREE;
129350517e57Snonaka 	cmd.chm_chanid = ch->ch_id;
129450517e57Snonaka 
129550517e57Snonaka 	rv = vmbus_cmd(sc, &cmd, sizeof(cmd), NULL, 0,
1296*fdd3eadbSnonaka 	    HCF_NOREPLY | HCF_SLEEPOK);
129750517e57Snonaka 	if (rv) {
129850517e57Snonaka 		DPRINTF("%s: CHFREE failed with %d\n", device_xname(sc->sc_dev),
129950517e57Snonaka 		    rv);
130050517e57Snonaka 	}
130150517e57Snonaka 	return rv;
130250517e57Snonaka }
130350517e57Snonaka 
130450517e57Snonaka struct vmbus_channel **
vmbus_subchannel_get(struct vmbus_channel * prich,int subchan_cnt)1305*fdd3eadbSnonaka vmbus_subchannel_get(struct vmbus_channel *prich, int subchan_cnt)
130650517e57Snonaka {
1307b74d80dfSnonaka 	struct vmbus_softc *sc = prich->ch_sc;
130850517e57Snonaka 	struct vmbus_channel **ret, *ch;
1309b74d80dfSnonaka 	int i, s;
131050517e57Snonaka 
1311*fdd3eadbSnonaka 	KASSERTMSG(subchan_cnt > 0,
1312*fdd3eadbSnonaka 	    "invalid sub-channel count %d", subchan_cnt);
131350517e57Snonaka 
1314*fdd3eadbSnonaka 	ret = kmem_zalloc(sizeof(struct vmbus_channel *) * subchan_cnt,
1315*fdd3eadbSnonaka 	    KM_SLEEP);
131650517e57Snonaka 
131750517e57Snonaka 	mutex_enter(&prich->ch_subchannel_lock);
131850517e57Snonaka 
1319*fdd3eadbSnonaka 	while (prich->ch_subchannel_count < subchan_cnt) {
1320b74d80dfSnonaka 		if (cold) {
1321b74d80dfSnonaka 			mutex_exit(&prich->ch_subchannel_lock);
1322b74d80dfSnonaka 			delay(1000);
1323b74d80dfSnonaka 			s = splnet();
1324b74d80dfSnonaka 			hyperv_intr();
1325b74d80dfSnonaka 			splx(s);
1326b74d80dfSnonaka 			mutex_enter(&sc->sc_chevq_lock);
1327b74d80dfSnonaka 			vmbus_process_chevq(sc);
1328b74d80dfSnonaka 			mutex_exit(&sc->sc_chevq_lock);
1329b74d80dfSnonaka 			mutex_enter(&prich->ch_subchannel_lock);
1330b74d80dfSnonaka 		} else {
1331b74d80dfSnonaka 			mtsleep(prich, PRIBIO, "hvsubch", 1,
133250517e57Snonaka 			    &prich->ch_subchannel_lock);
1333b74d80dfSnonaka 		}
1334b74d80dfSnonaka 	}
133550517e57Snonaka 
133650517e57Snonaka 	i = 0;
133750517e57Snonaka 	TAILQ_FOREACH(ch, &prich->ch_subchannels, ch_subentry) {
133850517e57Snonaka 		ret[i] = ch;	/* XXX inc refs */
133950517e57Snonaka 
1340*fdd3eadbSnonaka 		if (++i == subchan_cnt)
134150517e57Snonaka 			break;
134250517e57Snonaka 	}
134350517e57Snonaka 
1344*fdd3eadbSnonaka 	KASSERTMSG(i == subchan_cnt, "invalid subchan count %d, should be %d",
1345*fdd3eadbSnonaka 	    prich->ch_subchannel_count, subchan_cnt);
1346b74d80dfSnonaka 
134750517e57Snonaka 	mutex_exit(&prich->ch_subchannel_lock);
134850517e57Snonaka 
134950517e57Snonaka 	return ret;
135050517e57Snonaka }
135150517e57Snonaka 
135250517e57Snonaka void
vmbus_subchannel_rel(struct vmbus_channel ** subch,int cnt)1353*fdd3eadbSnonaka vmbus_subchannel_rel(struct vmbus_channel **subch, int cnt)
135450517e57Snonaka {
135550517e57Snonaka 
135650517e57Snonaka 	kmem_free(subch, sizeof(struct vmbus_channel *) * cnt);
135750517e57Snonaka }
135850517e57Snonaka 
1359*fdd3eadbSnonaka void
vmbus_subchannel_drain(struct vmbus_channel * prich)1360*fdd3eadbSnonaka vmbus_subchannel_drain(struct vmbus_channel *prich)
136150517e57Snonaka {
1362*fdd3eadbSnonaka 	int s;
1363*fdd3eadbSnonaka 
1364*fdd3eadbSnonaka 	mutex_enter(&prich->ch_subchannel_lock);
1365*fdd3eadbSnonaka 	while (prich->ch_subchannel_count > 0) {
1366*fdd3eadbSnonaka 		if (cold) {
1367*fdd3eadbSnonaka 			mutex_exit(&prich->ch_subchannel_lock);
1368*fdd3eadbSnonaka 			delay(1000);
1369*fdd3eadbSnonaka 			s = splnet();
1370*fdd3eadbSnonaka 			hyperv_intr();
1371*fdd3eadbSnonaka 			splx(s);
1372*fdd3eadbSnonaka 			mutex_enter(&prich->ch_subchannel_lock);
1373*fdd3eadbSnonaka 		} else {
1374*fdd3eadbSnonaka 			cv_wait(&prich->ch_subchannel_cv,
1375*fdd3eadbSnonaka 			    &prich->ch_subchannel_lock);
1376*fdd3eadbSnonaka 		}
1377*fdd3eadbSnonaka 	}
1378*fdd3eadbSnonaka 	mutex_exit(&prich->ch_subchannel_lock);
1379*fdd3eadbSnonaka }
1380*fdd3eadbSnonaka 
1381*fdd3eadbSnonaka static struct vmbus_channel *
vmbus_channel_lookup(struct vmbus_softc * sc,uint32_t chanid)1382*fdd3eadbSnonaka vmbus_channel_lookup(struct vmbus_softc *sc, uint32_t chanid)
1383*fdd3eadbSnonaka {
1384*fdd3eadbSnonaka 	struct vmbus_channel *ch = NULL;
138550517e57Snonaka 
138650517e57Snonaka 	TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
1387*fdd3eadbSnonaka 		if (ch->ch_id == chanid)
138850517e57Snonaka 			return ch;
138950517e57Snonaka 	}
139050517e57Snonaka 	return NULL;
139150517e57Snonaka }
139250517e57Snonaka 
139350517e57Snonaka static int
vmbus_channel_ring_create(struct vmbus_channel * ch,uint32_t buflen)139450517e57Snonaka vmbus_channel_ring_create(struct vmbus_channel *ch, uint32_t buflen)
139550517e57Snonaka {
139650517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
139750517e57Snonaka 
139850517e57Snonaka 	buflen = roundup(buflen, PAGE_SIZE) + sizeof(struct vmbus_bufring);
139950517e57Snonaka 	ch->ch_ring_size = 2 * buflen;
1400c9d5b091Snonaka 	/* page aligned memory */
140150517e57Snonaka 	ch->ch_ring = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_ring_dma,
1402*fdd3eadbSnonaka 	    ch->ch_ring_size, PAGE_SIZE, 0, 1);
140350517e57Snonaka 	if (ch->ch_ring == NULL) {
140450517e57Snonaka 		device_printf(sc->sc_dev,
140550517e57Snonaka 		    "failed to allocate channel ring\n");
140650517e57Snonaka 		return ENOMEM;
140750517e57Snonaka 	}
140850517e57Snonaka 
140950517e57Snonaka 	memset(&ch->ch_wrd, 0, sizeof(ch->ch_wrd));
141050517e57Snonaka 	ch->ch_wrd.rd_ring = (struct vmbus_bufring *)ch->ch_ring;
141150517e57Snonaka 	ch->ch_wrd.rd_size = buflen;
141250517e57Snonaka 	ch->ch_wrd.rd_dsize = buflen - sizeof(struct vmbus_bufring);
141350517e57Snonaka 	mutex_init(&ch->ch_wrd.rd_lock, MUTEX_DEFAULT, IPL_NET);
141450517e57Snonaka 
141550517e57Snonaka 	memset(&ch->ch_rrd, 0, sizeof(ch->ch_rrd));
141650517e57Snonaka 	ch->ch_rrd.rd_ring = (struct vmbus_bufring *)((uint8_t *)ch->ch_ring +
141750517e57Snonaka 	    buflen);
141850517e57Snonaka 	ch->ch_rrd.rd_size = buflen;
141950517e57Snonaka 	ch->ch_rrd.rd_dsize = buflen - sizeof(struct vmbus_bufring);
142050517e57Snonaka 	mutex_init(&ch->ch_rrd.rd_lock, MUTEX_DEFAULT, IPL_NET);
142150517e57Snonaka 
142250517e57Snonaka 	if (vmbus_handle_alloc(ch, &ch->ch_ring_dma, ch->ch_ring_size,
142350517e57Snonaka 	    &ch->ch_ring_gpadl)) {
142450517e57Snonaka 		device_printf(sc->sc_dev,
142550517e57Snonaka 		    "failed to obtain a PA handle for the ring\n");
142650517e57Snonaka 		vmbus_channel_ring_destroy(ch);
142750517e57Snonaka 		return ENOMEM;
142850517e57Snonaka 	}
142950517e57Snonaka 
143050517e57Snonaka 	return 0;
143150517e57Snonaka }
143250517e57Snonaka 
143350517e57Snonaka static void
vmbus_channel_ring_destroy(struct vmbus_channel * ch)143450517e57Snonaka vmbus_channel_ring_destroy(struct vmbus_channel *ch)
143550517e57Snonaka {
143650517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
143750517e57Snonaka 
143850517e57Snonaka 	hyperv_dma_free(sc->sc_dmat, &ch->ch_ring_dma);
143950517e57Snonaka 	ch->ch_ring = NULL;
144050517e57Snonaka 	vmbus_handle_free(ch, ch->ch_ring_gpadl);
144150517e57Snonaka 
144250517e57Snonaka 	mutex_destroy(&ch->ch_wrd.rd_lock);
144350517e57Snonaka 	memset(&ch->ch_wrd, 0, sizeof(ch->ch_wrd));
144450517e57Snonaka 	mutex_destroy(&ch->ch_rrd.rd_lock);
144550517e57Snonaka 	memset(&ch->ch_rrd, 0, sizeof(ch->ch_rrd));
144650517e57Snonaka }
144750517e57Snonaka 
144850517e57Snonaka int
vmbus_channel_open(struct vmbus_channel * ch,size_t buflen,void * udata,size_t udatalen,void (* handler)(void *),void * arg)144950517e57Snonaka vmbus_channel_open(struct vmbus_channel *ch, size_t buflen, void *udata,
145050517e57Snonaka     size_t udatalen, void (*handler)(void *), void *arg)
145150517e57Snonaka {
145250517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
145350517e57Snonaka 	struct vmbus_chanmsg_chopen cmd;
145450517e57Snonaka 	struct vmbus_chanmsg_chopen_resp rsp;
145550517e57Snonaka 	int rv = EINVAL;
145650517e57Snonaka 
145750517e57Snonaka 	if (ch->ch_ring == NULL &&
145850517e57Snonaka 	    (rv = vmbus_channel_ring_create(ch, buflen))) {
145950517e57Snonaka 		DPRINTF("%s: failed to create channel ring\n",
146050517e57Snonaka 		    device_xname(sc->sc_dev));
146150517e57Snonaka 		return rv;
146250517e57Snonaka 	}
146350517e57Snonaka 
1464*fdd3eadbSnonaka 	__insn_barrier();
1465*fdd3eadbSnonaka 	sc->sc_chanmap[ch->ch_id] = ch;
1466*fdd3eadbSnonaka 
146750517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
146850517e57Snonaka 	cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CHOPEN;
146950517e57Snonaka 	cmd.chm_openid = ch->ch_id;
147050517e57Snonaka 	cmd.chm_chanid = ch->ch_id;
147150517e57Snonaka 	cmd.chm_gpadl = ch->ch_ring_gpadl;
147250517e57Snonaka 	cmd.chm_txbr_pgcnt = atop(ch->ch_wrd.rd_size);
147350517e57Snonaka 	cmd.chm_vcpuid = ch->ch_vcpu;
147450517e57Snonaka 	if (udata && udatalen > 0)
147550517e57Snonaka 		memcpy(cmd.chm_udata, udata, udatalen);
147650517e57Snonaka 
147750517e57Snonaka 	memset(&rsp, 0, sizeof(rsp));
147850517e57Snonaka 
147950517e57Snonaka 	ch->ch_handler = handler;
148050517e57Snonaka 	ch->ch_ctx = arg;
148150517e57Snonaka 	ch->ch_state = VMBUS_CHANSTATE_OPENED;
148250517e57Snonaka 
1483*fdd3eadbSnonaka 	rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp), HCF_NOSLEEP);
148450517e57Snonaka 	if (rv) {
1485*fdd3eadbSnonaka 		sc->sc_chanmap[ch->ch_id] = NULL;
148650517e57Snonaka 		vmbus_channel_ring_destroy(ch);
148750517e57Snonaka 		DPRINTF("%s: CHOPEN failed with %d\n", device_xname(sc->sc_dev),
148850517e57Snonaka 		    rv);
148950517e57Snonaka 		ch->ch_handler = NULL;
149050517e57Snonaka 		ch->ch_ctx = NULL;
149150517e57Snonaka 		ch->ch_state = VMBUS_CHANSTATE_OFFERED;
149250517e57Snonaka 		return rv;
149350517e57Snonaka 	}
149450517e57Snonaka 	return 0;
149550517e57Snonaka }
149650517e57Snonaka 
149750517e57Snonaka static void
vmbus_channel_detach(struct vmbus_channel * ch)149850517e57Snonaka vmbus_channel_detach(struct vmbus_channel *ch)
149950517e57Snonaka {
150050517e57Snonaka 	u_int refs;
150150517e57Snonaka 
1502c9d5b091Snonaka 	KASSERTMSG(ch->ch_refs > 0, "channel%u: invalid refcnt %d",
1503c9d5b091Snonaka 	    ch->ch_id, ch->ch_refs);
150450517e57Snonaka 
1505ef3476fbSriastradh 	membar_release();
1506c9d5b091Snonaka 	refs = atomic_dec_uint_nv(&ch->ch_refs);
1507c9d5b091Snonaka 	if (refs == 0) {
1508ef3476fbSriastradh 		membar_acquire();
1509c9d5b091Snonaka 		/* Detach the target channel. */
1510c9d5b091Snonaka 		vmbus_devq_enqueue(ch->ch_sc, VMBUS_DEV_TYPE_DETACH, ch);
151150517e57Snonaka 	}
151250517e57Snonaka }
151350517e57Snonaka 
151450517e57Snonaka static int
vmbus_channel_close_internal(struct vmbus_channel * ch)151550517e57Snonaka vmbus_channel_close_internal(struct vmbus_channel *ch)
151650517e57Snonaka {
151750517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
151850517e57Snonaka 	struct vmbus_chanmsg_chclose cmd;
151950517e57Snonaka 	int rv;
152050517e57Snonaka 
1521*fdd3eadbSnonaka 	sc->sc_chanmap[ch->ch_id] = NULL;
1522*fdd3eadbSnonaka 
152350517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
152450517e57Snonaka 	cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CHCLOSE;
152550517e57Snonaka 	cmd.chm_chanid = ch->ch_id;
152650517e57Snonaka 
152750517e57Snonaka 	ch->ch_state = VMBUS_CHANSTATE_CLOSING;
152850517e57Snonaka 	rv = vmbus_cmd(sc, &cmd, sizeof(cmd), NULL, 0,
1529*fdd3eadbSnonaka 	    HCF_NOREPLY | HCF_NOSLEEP);
153050517e57Snonaka 	if (rv) {
153150517e57Snonaka 		DPRINTF("%s: CHCLOSE failed with %d\n",
153250517e57Snonaka 		    device_xname(sc->sc_dev), rv);
153350517e57Snonaka 		return rv;
153450517e57Snonaka 	}
153550517e57Snonaka 	ch->ch_state = VMBUS_CHANSTATE_CLOSED;
153650517e57Snonaka 	vmbus_channel_ring_destroy(ch);
153750517e57Snonaka 	return 0;
153850517e57Snonaka }
153950517e57Snonaka 
154050517e57Snonaka int
vmbus_channel_close_direct(struct vmbus_channel * ch)154150517e57Snonaka vmbus_channel_close_direct(struct vmbus_channel *ch)
154250517e57Snonaka {
154350517e57Snonaka 	int rv;
154450517e57Snonaka 
154550517e57Snonaka 	rv = vmbus_channel_close_internal(ch);
154650517e57Snonaka 	if (!VMBUS_CHAN_ISPRIMARY(ch))
154750517e57Snonaka 		vmbus_channel_detach(ch);
154850517e57Snonaka 	return rv;
154950517e57Snonaka }
155050517e57Snonaka 
155150517e57Snonaka int
vmbus_channel_close(struct vmbus_channel * ch)155250517e57Snonaka vmbus_channel_close(struct vmbus_channel *ch)
155350517e57Snonaka {
155450517e57Snonaka 	struct vmbus_channel **subch;
155550517e57Snonaka 	int i, cnt, rv;
155650517e57Snonaka 
155750517e57Snonaka 	if (!VMBUS_CHAN_ISPRIMARY(ch))
155850517e57Snonaka 		return 0;
155950517e57Snonaka 
156050517e57Snonaka 	cnt = ch->ch_subchannel_count;
156150517e57Snonaka 	if (cnt > 0) {
156250517e57Snonaka 		subch = vmbus_subchannel_get(ch, cnt);
156350517e57Snonaka 		for (i = 0; i < ch->ch_subchannel_count; i++) {
156450517e57Snonaka 			rv = vmbus_channel_close_internal(subch[i]);
156550517e57Snonaka 			(void) rv;	/* XXX */
156650517e57Snonaka 			vmbus_channel_detach(ch);
156750517e57Snonaka 		}
1568*fdd3eadbSnonaka 		vmbus_subchannel_rel(subch, cnt);
156950517e57Snonaka 	}
157050517e57Snonaka 
157150517e57Snonaka 	return vmbus_channel_close_internal(ch);
157250517e57Snonaka }
157350517e57Snonaka 
157450517e57Snonaka static inline void
vmbus_channel_setevent(struct vmbus_softc * sc,struct vmbus_channel * ch)157550517e57Snonaka vmbus_channel_setevent(struct vmbus_softc *sc, struct vmbus_channel *ch)
157650517e57Snonaka {
157750517e57Snonaka 	struct vmbus_mon_trig *mtg;
157850517e57Snonaka 
157950517e57Snonaka 	/* Each uint32_t represents 32 channels */
158050517e57Snonaka 	set_bit(ch->ch_id, sc->sc_wevents);
158150517e57Snonaka 	if (ch->ch_flags & CHF_MONITOR) {
158250517e57Snonaka 		mtg = &sc->sc_monitor[1]->mnf_trigs[ch->ch_mgroup];
158350517e57Snonaka 		set_bit(ch->ch_mindex, &mtg->mt_pending);
158450517e57Snonaka 	} else
158550517e57Snonaka 		vmbus_intr_signal(sc, hyperv_dma_get_paddr(&ch->ch_monprm_dma));
158650517e57Snonaka }
158750517e57Snonaka 
158850517e57Snonaka static void
vmbus_channel_intr(void * arg)158950517e57Snonaka vmbus_channel_intr(void *arg)
159050517e57Snonaka {
159150517e57Snonaka 	struct vmbus_channel *ch = arg;
159250517e57Snonaka 
159350517e57Snonaka 	if (vmbus_channel_ready(ch))
159450517e57Snonaka 		ch->ch_handler(ch->ch_ctx);
159550517e57Snonaka 
159650517e57Snonaka 	if (vmbus_channel_unpause(ch) == 0)
159750517e57Snonaka 		return;
159850517e57Snonaka 
159950517e57Snonaka 	vmbus_channel_pause(ch);
160050517e57Snonaka 	vmbus_channel_schedule(ch);
160150517e57Snonaka }
160250517e57Snonaka 
160350517e57Snonaka int
vmbus_channel_setdeferred(struct vmbus_channel * ch,const char * name)160450517e57Snonaka vmbus_channel_setdeferred(struct vmbus_channel *ch, const char *name)
160550517e57Snonaka {
160650517e57Snonaka 
160750517e57Snonaka 	ch->ch_taskq = softint_establish(SOFTINT_NET | SOFTINT_MPSAFE,
160850517e57Snonaka 	    vmbus_channel_intr, ch);
160950517e57Snonaka 	if (ch->ch_taskq == NULL)
161050517e57Snonaka 		return -1;
161150517e57Snonaka 	return 0;
161250517e57Snonaka }
161350517e57Snonaka 
161450517e57Snonaka void
vmbus_channel_schedule(struct vmbus_channel * ch)161550517e57Snonaka vmbus_channel_schedule(struct vmbus_channel *ch)
161650517e57Snonaka {
161750517e57Snonaka 
161850517e57Snonaka 	if (ch->ch_handler) {
161950517e57Snonaka 		if (!cold && (ch->ch_flags & CHF_BATCHED)) {
162050517e57Snonaka 			vmbus_channel_pause(ch);
162150517e57Snonaka 			softint_schedule(ch->ch_taskq);
162250517e57Snonaka 		} else
162350517e57Snonaka 			ch->ch_handler(ch->ch_ctx);
162450517e57Snonaka 	}
162550517e57Snonaka }
162650517e57Snonaka 
162750517e57Snonaka static __inline void
vmbus_ring_put(struct vmbus_ring_data * wrd,uint8_t * data,uint32_t datalen)162850517e57Snonaka vmbus_ring_put(struct vmbus_ring_data *wrd, uint8_t *data, uint32_t datalen)
162950517e57Snonaka {
163050517e57Snonaka 	int left = MIN(datalen, wrd->rd_dsize - wrd->rd_prod);
163150517e57Snonaka 
163250517e57Snonaka 	memcpy(&wrd->rd_ring->br_data[wrd->rd_prod], data, left);
163350517e57Snonaka 	memcpy(&wrd->rd_ring->br_data[0], data + left, datalen - left);
163450517e57Snonaka 	wrd->rd_prod += datalen;
163550517e57Snonaka 	if (wrd->rd_prod >= wrd->rd_dsize)
163650517e57Snonaka 		wrd->rd_prod -= wrd->rd_dsize;
163750517e57Snonaka }
163850517e57Snonaka 
163950517e57Snonaka static inline void
vmbus_ring_get(struct vmbus_ring_data * rrd,uint8_t * data,uint32_t datalen,int peek)164050517e57Snonaka vmbus_ring_get(struct vmbus_ring_data *rrd, uint8_t *data, uint32_t datalen,
164150517e57Snonaka     int peek)
164250517e57Snonaka {
164350517e57Snonaka 	int left = MIN(datalen, rrd->rd_dsize - rrd->rd_cons);
164450517e57Snonaka 
164550517e57Snonaka 	memcpy(data, &rrd->rd_ring->br_data[rrd->rd_cons], left);
164650517e57Snonaka 	memcpy(data + left, &rrd->rd_ring->br_data[0], datalen - left);
164750517e57Snonaka 	if (!peek) {
164850517e57Snonaka 		rrd->rd_cons += datalen;
164950517e57Snonaka 		if (rrd->rd_cons >= rrd->rd_dsize)
165050517e57Snonaka 			rrd->rd_cons -= rrd->rd_dsize;
165150517e57Snonaka 	}
165250517e57Snonaka }
165350517e57Snonaka 
165450517e57Snonaka static __inline void
vmbus_ring_avail(struct vmbus_ring_data * rd,uint32_t * towrite,uint32_t * toread)165550517e57Snonaka vmbus_ring_avail(struct vmbus_ring_data *rd, uint32_t *towrite,
165650517e57Snonaka     uint32_t *toread)
165750517e57Snonaka {
165850517e57Snonaka 	uint32_t ridx = rd->rd_ring->br_rindex;
165950517e57Snonaka 	uint32_t widx = rd->rd_ring->br_windex;
166050517e57Snonaka 	uint32_t r, w;
166150517e57Snonaka 
166250517e57Snonaka 	if (widx >= ridx)
166350517e57Snonaka 		w = rd->rd_dsize - (widx - ridx);
166450517e57Snonaka 	else
166550517e57Snonaka 		w = ridx - widx;
166650517e57Snonaka 	r = rd->rd_dsize - w;
166750517e57Snonaka 	if (towrite)
166850517e57Snonaka 		*towrite = w;
166950517e57Snonaka 	if (toread)
167050517e57Snonaka 		*toread = r;
167150517e57Snonaka }
167250517e57Snonaka 
1673*fdd3eadbSnonaka static bool
vmbus_ring_is_empty(struct vmbus_ring_data * rd)1674*fdd3eadbSnonaka vmbus_ring_is_empty(struct vmbus_ring_data *rd)
1675*fdd3eadbSnonaka {
1676*fdd3eadbSnonaka 
1677*fdd3eadbSnonaka 	return rd->rd_ring->br_rindex == rd->rd_ring->br_windex;
1678*fdd3eadbSnonaka }
1679*fdd3eadbSnonaka 
168050517e57Snonaka static int
vmbus_ring_write(struct vmbus_ring_data * wrd,struct iovec * iov,int iov_cnt,int * needsig)168150517e57Snonaka vmbus_ring_write(struct vmbus_ring_data *wrd, struct iovec *iov, int iov_cnt,
168250517e57Snonaka     int *needsig)
168350517e57Snonaka {
168450517e57Snonaka 	uint64_t indices = 0;
168550517e57Snonaka 	uint32_t avail, oprod, datalen = sizeof(indices);
168650517e57Snonaka 	int i;
168750517e57Snonaka 
168850517e57Snonaka 	for (i = 0; i < iov_cnt; i++)
168950517e57Snonaka 		datalen += iov[i].iov_len;
169050517e57Snonaka 
169150517e57Snonaka 	KASSERT(datalen <= wrd->rd_dsize);
169250517e57Snonaka 
169350517e57Snonaka 	vmbus_ring_avail(wrd, &avail, NULL);
169450517e57Snonaka 	if (avail <= datalen) {
169550517e57Snonaka 		DPRINTF("%s: avail %u datalen %u\n", __func__, avail, datalen);
169650517e57Snonaka 		return EAGAIN;
169750517e57Snonaka 	}
169850517e57Snonaka 
169950517e57Snonaka 	oprod = wrd->rd_prod;
170050517e57Snonaka 
170150517e57Snonaka 	for (i = 0; i < iov_cnt; i++)
170250517e57Snonaka 		vmbus_ring_put(wrd, iov[i].iov_base, iov[i].iov_len);
170350517e57Snonaka 
170450517e57Snonaka 	indices = (uint64_t)oprod << 32;
170550517e57Snonaka 	vmbus_ring_put(wrd, (uint8_t *)&indices, sizeof(indices));
170650517e57Snonaka 
170750517e57Snonaka 	membar_sync();
170850517e57Snonaka 	wrd->rd_ring->br_windex = wrd->rd_prod;
170950517e57Snonaka 	membar_sync();
171050517e57Snonaka 
171150517e57Snonaka 	/* Signal when the ring transitions from being empty to non-empty */
171250517e57Snonaka 	if (wrd->rd_ring->br_imask == 0 &&
171350517e57Snonaka 	    wrd->rd_ring->br_rindex == oprod)
171450517e57Snonaka 		*needsig = 1;
171550517e57Snonaka 	else
171650517e57Snonaka 		*needsig = 0;
171750517e57Snonaka 
171850517e57Snonaka 	return 0;
171950517e57Snonaka }
172050517e57Snonaka 
172150517e57Snonaka int
vmbus_channel_send(struct vmbus_channel * ch,void * data,uint32_t datalen,uint64_t rid,int type,uint32_t flags)172250517e57Snonaka vmbus_channel_send(struct vmbus_channel *ch, void *data, uint32_t datalen,
172350517e57Snonaka     uint64_t rid, int type, uint32_t flags)
172450517e57Snonaka {
172550517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
172650517e57Snonaka 	struct vmbus_chanpkt cp;
172750517e57Snonaka 	struct iovec iov[3];
172850517e57Snonaka 	uint32_t pktlen, pktlen_aligned;
172950517e57Snonaka 	uint64_t zeropad = 0;
173050517e57Snonaka 	int rv, needsig = 0;
173150517e57Snonaka 
173250517e57Snonaka 	pktlen = sizeof(cp) + datalen;
173350517e57Snonaka 	pktlen_aligned = roundup(pktlen, sizeof(uint64_t));
173450517e57Snonaka 
173550517e57Snonaka 	cp.cp_hdr.cph_type = type;
173650517e57Snonaka 	cp.cp_hdr.cph_flags = flags;
173750517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_hlen, sizeof(cp));
173850517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_tlen, pktlen_aligned);
173950517e57Snonaka 	cp.cp_hdr.cph_tid = rid;
174050517e57Snonaka 
174150517e57Snonaka 	iov[0].iov_base = &cp;
174250517e57Snonaka 	iov[0].iov_len = sizeof(cp);
174350517e57Snonaka 
174450517e57Snonaka 	iov[1].iov_base = data;
174550517e57Snonaka 	iov[1].iov_len = datalen;
174650517e57Snonaka 
174750517e57Snonaka 	iov[2].iov_base = &zeropad;
174850517e57Snonaka 	iov[2].iov_len = pktlen_aligned - pktlen;
174950517e57Snonaka 
175050517e57Snonaka 	mutex_enter(&ch->ch_wrd.rd_lock);
175150517e57Snonaka 	rv = vmbus_ring_write(&ch->ch_wrd, iov, 3, &needsig);
175250517e57Snonaka 	mutex_exit(&ch->ch_wrd.rd_lock);
175350517e57Snonaka 	if (rv == 0 && needsig)
175450517e57Snonaka 		vmbus_channel_setevent(sc, ch);
175550517e57Snonaka 
175650517e57Snonaka 	return rv;
175750517e57Snonaka }
175850517e57Snonaka 
175950517e57Snonaka int
vmbus_channel_send_sgl(struct vmbus_channel * ch,struct vmbus_gpa * sgl,uint32_t nsge,void * data,uint32_t datalen,uint64_t rid)176050517e57Snonaka vmbus_channel_send_sgl(struct vmbus_channel *ch, struct vmbus_gpa *sgl,
176150517e57Snonaka     uint32_t nsge, void *data, uint32_t datalen, uint64_t rid)
176250517e57Snonaka {
176350517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
176450517e57Snonaka 	struct vmbus_chanpkt_sglist cp;
176550517e57Snonaka 	struct iovec iov[4];
176650517e57Snonaka 	uint32_t buflen, pktlen, pktlen_aligned;
176750517e57Snonaka 	uint64_t zeropad = 0;
176850517e57Snonaka 	int rv, needsig = 0;
176950517e57Snonaka 
177050517e57Snonaka 	buflen = sizeof(struct vmbus_gpa) * nsge;
177150517e57Snonaka 	pktlen = sizeof(cp) + datalen + buflen;
177250517e57Snonaka 	pktlen_aligned = roundup(pktlen, sizeof(uint64_t));
177350517e57Snonaka 
177450517e57Snonaka 	cp.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
177550517e57Snonaka 	cp.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
177650517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_hlen, sizeof(cp) + buflen);
177750517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_tlen, pktlen_aligned);
177850517e57Snonaka 	cp.cp_hdr.cph_tid = rid;
177950517e57Snonaka 	cp.cp_gpa_cnt = nsge;
178050517e57Snonaka 	cp.cp_rsvd = 0;
178150517e57Snonaka 
178250517e57Snonaka 	iov[0].iov_base = &cp;
178350517e57Snonaka 	iov[0].iov_len = sizeof(cp);
178450517e57Snonaka 
178550517e57Snonaka 	iov[1].iov_base = sgl;
178650517e57Snonaka 	iov[1].iov_len = buflen;
178750517e57Snonaka 
178850517e57Snonaka 	iov[2].iov_base = data;
178950517e57Snonaka 	iov[2].iov_len = datalen;
179050517e57Snonaka 
179150517e57Snonaka 	iov[3].iov_base = &zeropad;
179250517e57Snonaka 	iov[3].iov_len = pktlen_aligned - pktlen;
179350517e57Snonaka 
179450517e57Snonaka 	mutex_enter(&ch->ch_wrd.rd_lock);
179550517e57Snonaka 	rv = vmbus_ring_write(&ch->ch_wrd, iov, 4, &needsig);
179650517e57Snonaka 	mutex_exit(&ch->ch_wrd.rd_lock);
179750517e57Snonaka 	if (rv == 0 && needsig)
179850517e57Snonaka 		vmbus_channel_setevent(sc, ch);
179950517e57Snonaka 
180050517e57Snonaka 	return rv;
180150517e57Snonaka }
180250517e57Snonaka 
180350517e57Snonaka int
vmbus_channel_send_prpl(struct vmbus_channel * ch,struct vmbus_gpa_range * prpl,uint32_t nprp,void * data,uint32_t datalen,uint64_t rid)180450517e57Snonaka vmbus_channel_send_prpl(struct vmbus_channel *ch, struct vmbus_gpa_range *prpl,
180550517e57Snonaka     uint32_t nprp, void *data, uint32_t datalen, uint64_t rid)
180650517e57Snonaka {
180750517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
180850517e57Snonaka 	struct vmbus_chanpkt_prplist cp;
180950517e57Snonaka 	struct iovec iov[4];
181050517e57Snonaka 	uint32_t buflen, pktlen, pktlen_aligned;
181150517e57Snonaka 	uint64_t zeropad = 0;
181250517e57Snonaka 	int rv, needsig = 0;
181350517e57Snonaka 
181450517e57Snonaka 	buflen = sizeof(struct vmbus_gpa_range) * (nprp + 1);
181550517e57Snonaka 	pktlen = sizeof(cp) + datalen + buflen;
181650517e57Snonaka 	pktlen_aligned = roundup(pktlen, sizeof(uint64_t));
181750517e57Snonaka 
181850517e57Snonaka 	cp.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
181950517e57Snonaka 	cp.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
182050517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_hlen, sizeof(cp) + buflen);
182150517e57Snonaka 	VMBUS_CHANPKT_SETLEN(cp.cp_hdr.cph_tlen, pktlen_aligned);
182250517e57Snonaka 	cp.cp_hdr.cph_tid = rid;
182350517e57Snonaka 	cp.cp_range_cnt = 1;
182450517e57Snonaka 	cp.cp_rsvd = 0;
182550517e57Snonaka 
182650517e57Snonaka 	iov[0].iov_base = &cp;
182750517e57Snonaka 	iov[0].iov_len = sizeof(cp);
182850517e57Snonaka 
182950517e57Snonaka 	iov[1].iov_base = prpl;
183050517e57Snonaka 	iov[1].iov_len = buflen;
183150517e57Snonaka 
183250517e57Snonaka 	iov[2].iov_base = data;
183350517e57Snonaka 	iov[2].iov_len = datalen;
183450517e57Snonaka 
183550517e57Snonaka 	iov[3].iov_base = &zeropad;
183650517e57Snonaka 	iov[3].iov_len = pktlen_aligned - pktlen;
183750517e57Snonaka 
183850517e57Snonaka 	mutex_enter(&ch->ch_wrd.rd_lock);
183950517e57Snonaka 	rv = vmbus_ring_write(&ch->ch_wrd, iov, 4, &needsig);
184050517e57Snonaka 	mutex_exit(&ch->ch_wrd.rd_lock);
184150517e57Snonaka 	if (rv == 0 && needsig)
184250517e57Snonaka 		vmbus_channel_setevent(sc, ch);
184350517e57Snonaka 
184450517e57Snonaka 	return rv;
184550517e57Snonaka }
184650517e57Snonaka 
184750517e57Snonaka static int
vmbus_ring_peek(struct vmbus_ring_data * rrd,void * data,uint32_t datalen)184850517e57Snonaka vmbus_ring_peek(struct vmbus_ring_data *rrd, void *data, uint32_t datalen)
184950517e57Snonaka {
185050517e57Snonaka 	uint32_t avail;
185150517e57Snonaka 
185250517e57Snonaka 	KASSERT(datalen <= rrd->rd_dsize);
185350517e57Snonaka 
185450517e57Snonaka 	vmbus_ring_avail(rrd, NULL, &avail);
185550517e57Snonaka 	if (avail < datalen)
185650517e57Snonaka 		return EAGAIN;
185750517e57Snonaka 
185850517e57Snonaka 	vmbus_ring_get(rrd, (uint8_t *)data, datalen, 1);
185950517e57Snonaka 	return 0;
186050517e57Snonaka }
186150517e57Snonaka 
186250517e57Snonaka static int
vmbus_ring_read(struct vmbus_ring_data * rrd,void * data,uint32_t datalen,uint32_t offset)186350517e57Snonaka vmbus_ring_read(struct vmbus_ring_data *rrd, void *data, uint32_t datalen,
186450517e57Snonaka     uint32_t offset)
186550517e57Snonaka {
186650517e57Snonaka 	uint64_t indices;
186750517e57Snonaka 	uint32_t avail;
186850517e57Snonaka 
186950517e57Snonaka 	KASSERT(datalen <= rrd->rd_dsize);
187050517e57Snonaka 
187150517e57Snonaka 	vmbus_ring_avail(rrd, NULL, &avail);
187250517e57Snonaka 	if (avail < datalen) {
187350517e57Snonaka 		DPRINTF("%s: avail %u datalen %u\n", __func__, avail, datalen);
187450517e57Snonaka 		return EAGAIN;
187550517e57Snonaka 	}
187650517e57Snonaka 
187750517e57Snonaka 	if (offset) {
187850517e57Snonaka 		rrd->rd_cons += offset;
187950517e57Snonaka 		if (rrd->rd_cons >= rrd->rd_dsize)
188050517e57Snonaka 			rrd->rd_cons -= rrd->rd_dsize;
188150517e57Snonaka 	}
188250517e57Snonaka 
188350517e57Snonaka 	vmbus_ring_get(rrd, (uint8_t *)data, datalen, 0);
188450517e57Snonaka 	vmbus_ring_get(rrd, (uint8_t *)&indices, sizeof(indices), 0);
188550517e57Snonaka 
188650517e57Snonaka 	membar_sync();
188750517e57Snonaka 	rrd->rd_ring->br_rindex = rrd->rd_cons;
188850517e57Snonaka 
188950517e57Snonaka 	return 0;
189050517e57Snonaka }
189150517e57Snonaka 
189250517e57Snonaka int
vmbus_channel_recv(struct vmbus_channel * ch,void * data,uint32_t datalen,uint32_t * rlen,uint64_t * rid,int raw)189350517e57Snonaka vmbus_channel_recv(struct vmbus_channel *ch, void *data, uint32_t datalen,
189450517e57Snonaka     uint32_t *rlen, uint64_t *rid, int raw)
189550517e57Snonaka {
189650517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
189750517e57Snonaka 	struct vmbus_chanpkt_hdr cph;
189850517e57Snonaka 	uint32_t offset, pktlen;
189950517e57Snonaka 	int rv;
190050517e57Snonaka 
190150517e57Snonaka 	*rlen = 0;
190250517e57Snonaka 
190350517e57Snonaka 	mutex_enter(&ch->ch_rrd.rd_lock);
190450517e57Snonaka 
190550517e57Snonaka 	if ((rv = vmbus_ring_peek(&ch->ch_rrd, &cph, sizeof(cph))) != 0) {
190650517e57Snonaka 		mutex_exit(&ch->ch_rrd.rd_lock);
190750517e57Snonaka 		return rv;
190850517e57Snonaka 	}
190950517e57Snonaka 
191050517e57Snonaka 	offset = raw ? 0 : VMBUS_CHANPKT_GETLEN(cph.cph_hlen);
191150517e57Snonaka 	pktlen = VMBUS_CHANPKT_GETLEN(cph.cph_tlen) - offset;
191250517e57Snonaka 	if (pktlen > datalen) {
191350517e57Snonaka 		mutex_exit(&ch->ch_rrd.rd_lock);
191450517e57Snonaka 		device_printf(sc->sc_dev, "%s: pktlen %u datalen %u\n",
191550517e57Snonaka 		    __func__, pktlen, datalen);
191650517e57Snonaka 		return EINVAL;
191750517e57Snonaka 	}
191850517e57Snonaka 
191950517e57Snonaka 	rv = vmbus_ring_read(&ch->ch_rrd, data, pktlen, offset);
192050517e57Snonaka 	if (rv == 0) {
192150517e57Snonaka 		*rlen = pktlen;
192250517e57Snonaka 		*rid = cph.cph_tid;
192350517e57Snonaka 	}
192450517e57Snonaka 
192550517e57Snonaka 	mutex_exit(&ch->ch_rrd.rd_lock);
192650517e57Snonaka 
192750517e57Snonaka 	return rv;
192850517e57Snonaka }
192950517e57Snonaka 
193050517e57Snonaka static inline void
vmbus_ring_mask(struct vmbus_ring_data * rd)193150517e57Snonaka vmbus_ring_mask(struct vmbus_ring_data *rd)
193250517e57Snonaka {
193350517e57Snonaka 
193450517e57Snonaka 	membar_sync();
193550517e57Snonaka 	rd->rd_ring->br_imask = 1;
193650517e57Snonaka 	membar_sync();
193750517e57Snonaka }
193850517e57Snonaka 
193950517e57Snonaka static inline void
vmbus_ring_unmask(struct vmbus_ring_data * rd)194050517e57Snonaka vmbus_ring_unmask(struct vmbus_ring_data *rd)
194150517e57Snonaka {
194250517e57Snonaka 
194350517e57Snonaka 	membar_sync();
194450517e57Snonaka 	rd->rd_ring->br_imask = 0;
194550517e57Snonaka 	membar_sync();
194650517e57Snonaka }
194750517e57Snonaka 
1948*fdd3eadbSnonaka void
vmbus_channel_pause(struct vmbus_channel * ch)194950517e57Snonaka vmbus_channel_pause(struct vmbus_channel *ch)
195050517e57Snonaka {
195150517e57Snonaka 
1952*fdd3eadbSnonaka 	atomic_or_ulong(&ch->ch_sc->sc_evtmask[ch->ch_id / VMBUS_EVTFLAG_LEN],
1953*fdd3eadbSnonaka 	    __BIT(ch->ch_id % VMBUS_EVTFLAG_LEN));
195450517e57Snonaka 	vmbus_ring_mask(&ch->ch_rrd);
195550517e57Snonaka }
195650517e57Snonaka 
1957*fdd3eadbSnonaka uint32_t
vmbus_channel_unpause(struct vmbus_channel * ch)195850517e57Snonaka vmbus_channel_unpause(struct vmbus_channel *ch)
195950517e57Snonaka {
196050517e57Snonaka 	uint32_t avail;
196150517e57Snonaka 
1962*fdd3eadbSnonaka 	atomic_and_ulong(&ch->ch_sc->sc_evtmask[ch->ch_id / VMBUS_EVTFLAG_LEN],
1963*fdd3eadbSnonaka 	    ~__BIT(ch->ch_id % VMBUS_EVTFLAG_LEN));
196450517e57Snonaka 	vmbus_ring_unmask(&ch->ch_rrd);
196550517e57Snonaka 	vmbus_ring_avail(&ch->ch_rrd, NULL, &avail);
196650517e57Snonaka 
196750517e57Snonaka 	return avail;
196850517e57Snonaka }
196950517e57Snonaka 
1970*fdd3eadbSnonaka uint32_t
vmbus_channel_ready(struct vmbus_channel * ch)197150517e57Snonaka vmbus_channel_ready(struct vmbus_channel *ch)
197250517e57Snonaka {
197350517e57Snonaka 	uint32_t avail;
197450517e57Snonaka 
197550517e57Snonaka 	vmbus_ring_avail(&ch->ch_rrd, NULL, &avail);
197650517e57Snonaka 
197750517e57Snonaka 	return avail;
197850517e57Snonaka }
197950517e57Snonaka 
1980*fdd3eadbSnonaka bool
vmbus_channel_tx_empty(struct vmbus_channel * ch)1981*fdd3eadbSnonaka vmbus_channel_tx_empty(struct vmbus_channel *ch)
1982*fdd3eadbSnonaka {
1983*fdd3eadbSnonaka 
1984*fdd3eadbSnonaka 	return vmbus_ring_is_empty(&ch->ch_wrd);
1985*fdd3eadbSnonaka }
1986*fdd3eadbSnonaka 
1987*fdd3eadbSnonaka bool
vmbus_channel_rx_empty(struct vmbus_channel * ch)1988*fdd3eadbSnonaka vmbus_channel_rx_empty(struct vmbus_channel *ch)
1989*fdd3eadbSnonaka {
1990*fdd3eadbSnonaka 
1991*fdd3eadbSnonaka 	return vmbus_ring_is_empty(&ch->ch_rrd);
1992*fdd3eadbSnonaka }
1993*fdd3eadbSnonaka 
199450517e57Snonaka /* How many PFNs can be referenced by the header */
199550517e57Snonaka #define VMBUS_NPFNHDR	((VMBUS_MSG_DSIZE_MAX -	\
199650517e57Snonaka 	  sizeof(struct vmbus_chanmsg_gpadl_conn)) / sizeof(uint64_t))
199750517e57Snonaka 
199850517e57Snonaka /* How many PFNs can be referenced by the body */
199950517e57Snonaka #define VMBUS_NPFNBODY	((VMBUS_MSG_DSIZE_MAX -	\
200050517e57Snonaka 	  sizeof(struct vmbus_chanmsg_gpadl_subconn)) / sizeof(uint64_t))
200150517e57Snonaka 
200250517e57Snonaka int
vmbus_handle_alloc(struct vmbus_channel * ch,const struct hyperv_dma * dma,uint32_t buflen,uint32_t * handle)200350517e57Snonaka vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
200450517e57Snonaka     uint32_t buflen, uint32_t *handle)
200550517e57Snonaka {
200650517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
200750517e57Snonaka 	struct vmbus_chanmsg_gpadl_conn *hdr;
200850517e57Snonaka 	struct vmbus_chanmsg_gpadl_subconn *cmd;
200950517e57Snonaka 	struct vmbus_chanmsg_gpadl_connresp rsp;
201050517e57Snonaka 	struct vmbus_msg *msg;
201150517e57Snonaka 	int i, j, last, left, rv;
201250517e57Snonaka 	int bodylen = 0, ncmds = 0, pfn = 0;
201350517e57Snonaka 	uint64_t *frames;
201450517e57Snonaka 	paddr_t pa;
201550517e57Snonaka 	uint8_t *body;
201650517e57Snonaka 	/* Total number of pages to reference */
201750517e57Snonaka 	int total = atop(buflen);
201850517e57Snonaka 	/* Number of pages that will fit the header */
201950517e57Snonaka 	int inhdr = MIN(total, VMBUS_NPFNHDR);
202050517e57Snonaka 
202150517e57Snonaka 	KASSERT((buflen & PAGE_MASK) == 0);
202250517e57Snonaka 	KASSERT(buflen == (uint32_t)dma->map->dm_mapsize);
202350517e57Snonaka 
2024*fdd3eadbSnonaka 	msg = pool_cache_get_paddr(sc->sc_msgpool, PR_WAITOK, &pa);
202550517e57Snonaka 
202650517e57Snonaka 	/* Prepare array of frame addresses */
2027*fdd3eadbSnonaka 	frames = kmem_zalloc(total * sizeof(*frames), KM_SLEEP);
202850517e57Snonaka 	for (i = 0, j = 0; i < dma->map->dm_nsegs && j < total; i++) {
202950517e57Snonaka 		bus_dma_segment_t *seg = &dma->map->dm_segs[i];
203050517e57Snonaka 		bus_addr_t addr = seg->ds_addr;
203150517e57Snonaka 
203250517e57Snonaka 		KASSERT((addr & PAGE_MASK) == 0);
203350517e57Snonaka 		KASSERT((seg->ds_len & PAGE_MASK) == 0);
203450517e57Snonaka 
203550517e57Snonaka 		while (addr < seg->ds_addr + seg->ds_len && j < total) {
203650517e57Snonaka 			frames[j++] = atop(addr);
203750517e57Snonaka 			addr += PAGE_SIZE;
203850517e57Snonaka 		}
203950517e57Snonaka 	}
204050517e57Snonaka 
204150517e57Snonaka 	memset(msg, 0, sizeof(*msg));
204250517e57Snonaka 	msg->msg_req.hc_dsize = sizeof(struct vmbus_chanmsg_gpadl_conn) +
204350517e57Snonaka 	    inhdr * sizeof(uint64_t);
204450517e57Snonaka 	hdr = (struct vmbus_chanmsg_gpadl_conn *)msg->msg_req.hc_data;
204550517e57Snonaka 	msg->msg_rsp = &rsp;
204650517e57Snonaka 	msg->msg_rsplen = sizeof(rsp);
2047*fdd3eadbSnonaka 	msg->msg_flags = MSGF_NOSLEEP;
204850517e57Snonaka 
204950517e57Snonaka 	left = total - inhdr;
205050517e57Snonaka 
205150517e57Snonaka 	/* Allocate additional gpadl_body structures if required */
205250517e57Snonaka 	if (left > 0) {
2053a08e08e6Snonaka 		ncmds = howmany(left, VMBUS_NPFNBODY);
205450517e57Snonaka 		bodylen = ncmds * VMBUS_MSG_DSIZE_MAX;
2055*fdd3eadbSnonaka 		body = kmem_zalloc(bodylen, KM_SLEEP);
205650517e57Snonaka 	}
205750517e57Snonaka 
2058c9d5b091Snonaka 	*handle = atomic_inc_32_nv(&sc->sc_handle);
205950517e57Snonaka 
206050517e57Snonaka 	hdr->chm_hdr.chm_type = VMBUS_CHANMSG_GPADL_CONN;
206150517e57Snonaka 	hdr->chm_chanid = ch->ch_id;
206250517e57Snonaka 	hdr->chm_gpadl = *handle;
206350517e57Snonaka 
206450517e57Snonaka 	/* Single range for a contiguous buffer */
206550517e57Snonaka 	hdr->chm_range_cnt = 1;
206650517e57Snonaka 	hdr->chm_range_len = sizeof(struct vmbus_gpa_range) + total *
206750517e57Snonaka 	    sizeof(uint64_t);
206850517e57Snonaka 	hdr->chm_range.gpa_ofs = 0;
206950517e57Snonaka 	hdr->chm_range.gpa_len = buflen;
207050517e57Snonaka 
207150517e57Snonaka 	/* Fit as many pages as possible into the header */
207250517e57Snonaka 	for (i = 0; i < inhdr; i++)
207350517e57Snonaka 		hdr->chm_range.gpa_page[i] = frames[pfn++];
207450517e57Snonaka 
207550517e57Snonaka 	for (i = 0; i < ncmds; i++) {
207650517e57Snonaka 		cmd = (struct vmbus_chanmsg_gpadl_subconn *)(body +
207750517e57Snonaka 		    VMBUS_MSG_DSIZE_MAX * i);
207850517e57Snonaka 		cmd->chm_hdr.chm_type = VMBUS_CHANMSG_GPADL_SUBCONN;
207950517e57Snonaka 		cmd->chm_gpadl = *handle;
208050517e57Snonaka 		last = MIN(left, VMBUS_NPFNBODY);
208150517e57Snonaka 		for (j = 0; j < last; j++)
208250517e57Snonaka 			cmd->chm_gpa_page[j] = frames[pfn++];
208350517e57Snonaka 		left -= last;
208450517e57Snonaka 	}
208550517e57Snonaka 
208650517e57Snonaka 	rv = vmbus_start(sc, msg, pa);
208750517e57Snonaka 	if (rv != 0) {
208850517e57Snonaka 		DPRINTF("%s: GPADL_CONN failed\n", device_xname(sc->sc_dev));
208950517e57Snonaka 		goto out;
209050517e57Snonaka 	}
209150517e57Snonaka 	for (i = 0; i < ncmds; i++) {
209250517e57Snonaka 		int cmdlen = sizeof(*cmd);
209350517e57Snonaka 		cmd = (struct vmbus_chanmsg_gpadl_subconn *)(body +
209450517e57Snonaka 		    VMBUS_MSG_DSIZE_MAX * i);
209550517e57Snonaka 		/* Last element can be short */
209650517e57Snonaka 		if (i == ncmds - 1)
209750517e57Snonaka 			cmdlen += last * sizeof(uint64_t);
209850517e57Snonaka 		else
209950517e57Snonaka 			cmdlen += VMBUS_NPFNBODY * sizeof(uint64_t);
2100*fdd3eadbSnonaka 		rv = vmbus_cmd(sc, cmd, cmdlen, NULL, 0,
2101*fdd3eadbSnonaka 		    HCF_NOREPLY | HCF_NOSLEEP);
210250517e57Snonaka 		if (rv != 0) {
210350517e57Snonaka 			DPRINTF("%s: GPADL_SUBCONN (iteration %d/%d) failed "
210450517e57Snonaka 			    "with %d\n", device_xname(sc->sc_dev), i, ncmds,
210550517e57Snonaka 			    rv);
210650517e57Snonaka 			goto out;
210750517e57Snonaka 		}
210850517e57Snonaka 	}
210950517e57Snonaka 	rv = vmbus_reply(sc, msg);
211050517e57Snonaka 	if (rv != 0) {
211150517e57Snonaka 		DPRINTF("%s: GPADL allocation failed with %d\n",
211250517e57Snonaka 		    device_xname(sc->sc_dev), rv);
211350517e57Snonaka 	}
211450517e57Snonaka 
211550517e57Snonaka  out:
211650517e57Snonaka 	if (bodylen > 0)
211750517e57Snonaka 		kmem_free(body, bodylen);
211850517e57Snonaka 	kmem_free(frames, total * sizeof(*frames));
211950517e57Snonaka 	pool_cache_put_paddr(sc->sc_msgpool, msg, pa);
212050517e57Snonaka 	if (rv)
212150517e57Snonaka 		return rv;
212250517e57Snonaka 
212350517e57Snonaka 	KASSERT(*handle == rsp.chm_gpadl);
212450517e57Snonaka 
212550517e57Snonaka 	return 0;
212650517e57Snonaka }
212750517e57Snonaka 
212850517e57Snonaka void
vmbus_handle_free(struct vmbus_channel * ch,uint32_t handle)212950517e57Snonaka vmbus_handle_free(struct vmbus_channel *ch, uint32_t handle)
213050517e57Snonaka {
213150517e57Snonaka 	struct vmbus_softc *sc = ch->ch_sc;
213250517e57Snonaka 	struct vmbus_chanmsg_gpadl_disconn cmd;
213350517e57Snonaka 	struct vmbus_chanmsg_gpadl_disconn rsp;
213450517e57Snonaka 	int rv;
213550517e57Snonaka 
213650517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
213750517e57Snonaka 	cmd.chm_hdr.chm_type = VMBUS_CHANMSG_GPADL_DISCONN;
213850517e57Snonaka 	cmd.chm_chanid = ch->ch_id;
213950517e57Snonaka 	cmd.chm_gpadl = handle;
214050517e57Snonaka 
2141*fdd3eadbSnonaka 	rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp), HCF_NOSLEEP);
214250517e57Snonaka 	if (rv) {
214350517e57Snonaka 		DPRINTF("%s: GPADL_DISCONN failed with %d\n",
214450517e57Snonaka 		    device_xname(sc->sc_dev), rv);
214550517e57Snonaka 	}
214650517e57Snonaka }
214750517e57Snonaka 
2148c9d5b091Snonaka  static void
vmbus_chevq_enqueue(struct vmbus_softc * sc,int type,void * arg)2149b74d80dfSnonaka vmbus_chevq_enqueue(struct vmbus_softc *sc, int type, void *arg)
2150b74d80dfSnonaka {
2151b74d80dfSnonaka 	struct vmbus_chev *vce;
2152b74d80dfSnonaka 
2153b74d80dfSnonaka 	vce = kmem_intr_alloc(sizeof(*vce), KM_NOSLEEP);
2154b74d80dfSnonaka 	if (vce == NULL) {
2155b74d80dfSnonaka 		device_printf(sc->sc_dev, "failed to allocate chev\n");
2156b74d80dfSnonaka 		return;
2157b74d80dfSnonaka 	}
2158b74d80dfSnonaka 
2159b74d80dfSnonaka 	vce->vce_type = type;
2160b74d80dfSnonaka 	vce->vce_arg = arg;
2161b74d80dfSnonaka 
2162b74d80dfSnonaka 	mutex_enter(&sc->sc_chevq_lock);
2163b74d80dfSnonaka 	SIMPLEQ_INSERT_TAIL(&sc->sc_chevq, vce, vce_entry);
2164b74d80dfSnonaka 	cv_broadcast(&sc->sc_chevq_cv);
2165b74d80dfSnonaka 	mutex_exit(&sc->sc_chevq_lock);
2166b74d80dfSnonaka }
2167b74d80dfSnonaka 
2168b74d80dfSnonaka static void
vmbus_process_chevq(void * arg)2169b74d80dfSnonaka vmbus_process_chevq(void *arg)
2170b74d80dfSnonaka {
2171b74d80dfSnonaka 	struct vmbus_softc *sc = arg;
2172b74d80dfSnonaka 	struct vmbus_chev *vce;
2173b74d80dfSnonaka 	struct vmbus_chanmsg_choffer *co;
2174b74d80dfSnonaka 	struct vmbus_chanmsg_chrescind *cr;
2175b74d80dfSnonaka 
2176b74d80dfSnonaka 	KASSERT(mutex_owned(&sc->sc_chevq_lock));
2177b74d80dfSnonaka 
2178b74d80dfSnonaka 	while (!SIMPLEQ_EMPTY(&sc->sc_chevq)) {
2179b74d80dfSnonaka 		vce = SIMPLEQ_FIRST(&sc->sc_chevq);
2180b74d80dfSnonaka 		SIMPLEQ_REMOVE_HEAD(&sc->sc_chevq, vce_entry);
2181b74d80dfSnonaka 		mutex_exit(&sc->sc_chevq_lock);
2182b74d80dfSnonaka 
2183b74d80dfSnonaka 		switch (vce->vce_type) {
2184b74d80dfSnonaka 		case VMBUS_CHEV_TYPE_OFFER:
2185b74d80dfSnonaka 			co = vce->vce_arg;
2186b74d80dfSnonaka 			vmbus_process_offer(sc, co);
2187b74d80dfSnonaka 			kmem_free(co, sizeof(*co));
2188b74d80dfSnonaka 			break;
2189b74d80dfSnonaka 
2190b74d80dfSnonaka 		case VMBUS_CHEV_TYPE_RESCIND:
2191b74d80dfSnonaka 			cr = vce->vce_arg;
2192b74d80dfSnonaka 			vmbus_process_rescind(sc, cr);
2193b74d80dfSnonaka 			kmem_free(cr, sizeof(*cr));
2194b74d80dfSnonaka 			break;
2195b74d80dfSnonaka 
2196b74d80dfSnonaka 		default:
2197b74d80dfSnonaka 			DPRINTF("%s: unknown chevq type %d\n",
2198b74d80dfSnonaka 			    device_xname(sc->sc_dev), vce->vce_type);
2199b74d80dfSnonaka 			break;
2200b74d80dfSnonaka 		}
2201b74d80dfSnonaka 		kmem_free(vce, sizeof(*vce));
2202b74d80dfSnonaka 
2203b74d80dfSnonaka 		mutex_enter(&sc->sc_chevq_lock);
2204b74d80dfSnonaka 	}
2205b74d80dfSnonaka }
2206b74d80dfSnonaka 
2207b74d80dfSnonaka static void
vmbus_chevq_thread(void * arg)2208b74d80dfSnonaka vmbus_chevq_thread(void *arg)
2209b74d80dfSnonaka {
2210b74d80dfSnonaka 	struct vmbus_softc *sc = arg;
2211b74d80dfSnonaka 
2212b74d80dfSnonaka 	mutex_enter(&sc->sc_chevq_lock);
2213b74d80dfSnonaka 	for (;;) {
2214b74d80dfSnonaka 		if (SIMPLEQ_EMPTY(&sc->sc_chevq)) {
2215b74d80dfSnonaka 			cv_wait(&sc->sc_chevq_cv, &sc->sc_chevq_lock);
2216b74d80dfSnonaka 			continue;
2217b74d80dfSnonaka 		}
2218b74d80dfSnonaka 
2219b74d80dfSnonaka 		vmbus_process_chevq(sc);
2220b74d80dfSnonaka 	}
2221b74d80dfSnonaka 	mutex_exit(&sc->sc_chevq_lock);
2222b74d80dfSnonaka 
2223b74d80dfSnonaka 	kthread_exit(0);
2224b74d80dfSnonaka }
2225b74d80dfSnonaka 
2226b74d80dfSnonaka static void
vmbus_devq_enqueue(struct vmbus_softc * sc,int type,struct vmbus_channel * ch)2227c9d5b091Snonaka vmbus_devq_enqueue(struct vmbus_softc *sc, int type, struct vmbus_channel *ch)
2228c9d5b091Snonaka {
2229c9d5b091Snonaka 	struct vmbus_dev *vd;
2230c9d5b091Snonaka 
2231b74d80dfSnonaka 	vd = kmem_zalloc(sizeof(*vd), KM_SLEEP);
2232c9d5b091Snonaka 	if (vd == NULL) {
2233c9d5b091Snonaka 		device_printf(sc->sc_dev, "failed to allocate devq\n");
2234c9d5b091Snonaka 		return;
2235c9d5b091Snonaka 	}
2236c9d5b091Snonaka 
2237c9d5b091Snonaka 	vd->vd_type = type;
2238c9d5b091Snonaka 	vd->vd_chan = ch;
2239c9d5b091Snonaka 
2240b74d80dfSnonaka 	if (VMBUS_CHAN_ISPRIMARY(ch)) {
2241c9d5b091Snonaka 		mutex_enter(&sc->sc_devq_lock);
2242c9d5b091Snonaka 		SIMPLEQ_INSERT_TAIL(&sc->sc_devq, vd, vd_entry);
2243c9d5b091Snonaka 		cv_broadcast(&sc->sc_devq_cv);
2244c9d5b091Snonaka 		mutex_exit(&sc->sc_devq_lock);
2245b74d80dfSnonaka 	} else {
2246b74d80dfSnonaka 		mutex_enter(&sc->sc_subch_devq_lock);
2247b74d80dfSnonaka 		SIMPLEQ_INSERT_TAIL(&sc->sc_subch_devq, vd, vd_entry);
2248b74d80dfSnonaka 		cv_broadcast(&sc->sc_subch_devq_cv);
2249b74d80dfSnonaka 		mutex_exit(&sc->sc_subch_devq_lock);
2250b74d80dfSnonaka 	}
2251c9d5b091Snonaka }
2252c9d5b091Snonaka 
2253c9d5b091Snonaka static void
vmbus_process_devq(void * arg)2254c9d5b091Snonaka vmbus_process_devq(void *arg)
2255c9d5b091Snonaka {
2256c9d5b091Snonaka 	struct vmbus_softc *sc = arg;
2257c9d5b091Snonaka 	struct vmbus_dev *vd;
2258b74d80dfSnonaka 	struct vmbus_channel *ch;
2259b74d80dfSnonaka 	struct vmbus_attach_args vaa;
2260c9d5b091Snonaka 
2261c9d5b091Snonaka 	KASSERT(mutex_owned(&sc->sc_devq_lock));
2262c9d5b091Snonaka 
2263c9d5b091Snonaka 	while (!SIMPLEQ_EMPTY(&sc->sc_devq)) {
2264c9d5b091Snonaka 		vd = SIMPLEQ_FIRST(&sc->sc_devq);
2265c9d5b091Snonaka 		SIMPLEQ_REMOVE_HEAD(&sc->sc_devq, vd_entry);
2266c9d5b091Snonaka 		mutex_exit(&sc->sc_devq_lock);
2267c9d5b091Snonaka 
2268c9d5b091Snonaka 		switch (vd->vd_type) {
2269c9d5b091Snonaka 		case VMBUS_DEV_TYPE_ATTACH:
2270c9d5b091Snonaka 			ch = vd->vd_chan;
2271c9d5b091Snonaka 			vaa.aa_type = &ch->ch_type;
2272c9d5b091Snonaka 			vaa.aa_inst = &ch->ch_inst;
2273c9d5b091Snonaka 			vaa.aa_ident = ch->ch_ident;
2274c9d5b091Snonaka 			vaa.aa_chan = ch;
2275c9d5b091Snonaka 			vaa.aa_iot = sc->sc_iot;
2276c9d5b091Snonaka 			vaa.aa_memt = sc->sc_memt;
22772685996bSthorpej 			ch->ch_dev = config_found(sc->sc_dev,
2278c7fb772bSthorpej 			    &vaa, vmbus_attach_print, CFARGS_NONE);
2279c9d5b091Snonaka 			break;
2280c9d5b091Snonaka 
2281c9d5b091Snonaka 		case VMBUS_DEV_TYPE_DETACH:
2282c9d5b091Snonaka 			ch = vd->vd_chan;
2283c9d5b091Snonaka 			if (ch->ch_dev != NULL) {
2284c9d5b091Snonaka 				config_detach(ch->ch_dev, DETACH_FORCE);
2285c9d5b091Snonaka 				ch->ch_dev = NULL;
2286c9d5b091Snonaka 			}
2287c9d5b091Snonaka 			vmbus_channel_release(ch);
2288c9d5b091Snonaka 			vmbus_channel_free(ch);
2289c9d5b091Snonaka 			break;
2290c9d5b091Snonaka 
2291c9d5b091Snonaka 		default:
2292b74d80dfSnonaka 			DPRINTF("%s: unknown devq type %d\n",
2293c9d5b091Snonaka 			    device_xname(sc->sc_dev), vd->vd_type);
2294c9d5b091Snonaka 			break;
2295c9d5b091Snonaka 		}
2296c9d5b091Snonaka 		kmem_free(vd, sizeof(*vd));
2297c9d5b091Snonaka 
2298c9d5b091Snonaka 		mutex_enter(&sc->sc_devq_lock);
2299c9d5b091Snonaka 	}
2300c9d5b091Snonaka }
2301c9d5b091Snonaka 
2302c9d5b091Snonaka static void
vmbus_devq_thread(void * arg)2303c9d5b091Snonaka vmbus_devq_thread(void *arg)
2304c9d5b091Snonaka {
2305c9d5b091Snonaka 	struct vmbus_softc *sc = arg;
2306c9d5b091Snonaka 
2307c9d5b091Snonaka 	mutex_enter(&sc->sc_devq_lock);
2308c9d5b091Snonaka 	for (;;) {
2309c9d5b091Snonaka 		if (SIMPLEQ_EMPTY(&sc->sc_devq)) {
2310c9d5b091Snonaka 			cv_wait(&sc->sc_devq_cv, &sc->sc_devq_lock);
2311c9d5b091Snonaka 			continue;
2312c9d5b091Snonaka 		}
2313c9d5b091Snonaka 
2314c9d5b091Snonaka 		vmbus_process_devq(sc);
2315c9d5b091Snonaka 	}
2316c9d5b091Snonaka 	mutex_exit(&sc->sc_devq_lock);
2317c9d5b091Snonaka 
2318c9d5b091Snonaka 	kthread_exit(0);
2319c9d5b091Snonaka }
2320c9d5b091Snonaka 
2321b74d80dfSnonaka static void
vmbus_subchannel_devq_thread(void * arg)2322b74d80dfSnonaka vmbus_subchannel_devq_thread(void *arg)
2323b74d80dfSnonaka {
2324b74d80dfSnonaka 	struct vmbus_softc *sc = arg;
2325b74d80dfSnonaka 	struct vmbus_dev *vd;
2326b74d80dfSnonaka 	struct vmbus_channel *ch, *prich;
2327b74d80dfSnonaka 
2328b74d80dfSnonaka 	mutex_enter(&sc->sc_subch_devq_lock);
2329b74d80dfSnonaka 	for (;;) {
2330b74d80dfSnonaka 		if (SIMPLEQ_EMPTY(&sc->sc_subch_devq)) {
2331b74d80dfSnonaka 			cv_wait(&sc->sc_subch_devq_cv, &sc->sc_subch_devq_lock);
2332b74d80dfSnonaka 			continue;
2333b74d80dfSnonaka 		}
2334b74d80dfSnonaka 
2335b74d80dfSnonaka 		while (!SIMPLEQ_EMPTY(&sc->sc_subch_devq)) {
2336b74d80dfSnonaka 			vd = SIMPLEQ_FIRST(&sc->sc_subch_devq);
2337b74d80dfSnonaka 			SIMPLEQ_REMOVE_HEAD(&sc->sc_subch_devq, vd_entry);
2338b74d80dfSnonaka 			mutex_exit(&sc->sc_subch_devq_lock);
2339b74d80dfSnonaka 
2340b74d80dfSnonaka 			switch (vd->vd_type) {
2341b74d80dfSnonaka 			case VMBUS_DEV_TYPE_ATTACH:
2342b74d80dfSnonaka 				/* Nothing to do */
2343b74d80dfSnonaka 				break;
2344b74d80dfSnonaka 
2345b74d80dfSnonaka 			case VMBUS_DEV_TYPE_DETACH:
2346b74d80dfSnonaka 				ch = vd->vd_chan;
2347b74d80dfSnonaka 
2348b74d80dfSnonaka 				vmbus_channel_release(ch);
2349b74d80dfSnonaka 
2350b74d80dfSnonaka 				prich = ch->ch_primary_channel;
2351b74d80dfSnonaka 				mutex_enter(&prich->ch_subchannel_lock);
2352b74d80dfSnonaka 				TAILQ_REMOVE(&prich->ch_subchannels, ch,
2353b74d80dfSnonaka 				    ch_subentry);
2354b74d80dfSnonaka 				prich->ch_subchannel_count--;
2355b74d80dfSnonaka 				mutex_exit(&prich->ch_subchannel_lock);
2356b74d80dfSnonaka 				wakeup(prich);
2357b74d80dfSnonaka 
2358b74d80dfSnonaka 				vmbus_channel_free(ch);
2359b74d80dfSnonaka 				break;
2360b74d80dfSnonaka 
2361b74d80dfSnonaka 			default:
2362b74d80dfSnonaka 				DPRINTF("%s: unknown devq type %d\n",
2363b74d80dfSnonaka 				    device_xname(sc->sc_dev), vd->vd_type);
2364b74d80dfSnonaka 				break;
2365b74d80dfSnonaka 			}
2366b74d80dfSnonaka 
2367b74d80dfSnonaka 			kmem_free(vd, sizeof(*vd));
2368b74d80dfSnonaka 
2369b74d80dfSnonaka 			mutex_enter(&sc->sc_subch_devq_lock);
2370b74d80dfSnonaka 		}
2371b74d80dfSnonaka 	}
2372b74d80dfSnonaka 	mutex_exit(&sc->sc_subch_devq_lock);
2373b74d80dfSnonaka 
2374b74d80dfSnonaka 	kthread_exit(0);
2375b74d80dfSnonaka }
2376b74d80dfSnonaka 
2377b74d80dfSnonaka 
237850517e57Snonaka static int
vmbus_attach_print(void * aux,const char * name)237950517e57Snonaka vmbus_attach_print(void *aux, const char *name)
238050517e57Snonaka {
238150517e57Snonaka 	struct vmbus_attach_args *aa = aux;
238250517e57Snonaka 
238350517e57Snonaka 	if (name)
238450517e57Snonaka 		printf("\"%s\" at %s", aa->aa_ident, name);
238550517e57Snonaka 
238650517e57Snonaka 	return UNCONF;
238750517e57Snonaka }
238850517e57Snonaka 
238950517e57Snonaka MODULE(MODULE_CLASS_DRIVER, vmbus, "hyperv");
239050517e57Snonaka 
239150517e57Snonaka #ifdef _MODULE
239250517e57Snonaka #include "ioconf.c"
239350517e57Snonaka #endif
239450517e57Snonaka 
239550517e57Snonaka static int
vmbus_modcmd(modcmd_t cmd,void * aux)239650517e57Snonaka vmbus_modcmd(modcmd_t cmd, void *aux)
239750517e57Snonaka {
239850517e57Snonaka 	int rv = 0;
239950517e57Snonaka 
240050517e57Snonaka 	switch (cmd) {
240150517e57Snonaka 	case MODULE_CMD_INIT:
240250517e57Snonaka #ifdef _MODULE
240350517e57Snonaka 		rv = config_init_component(cfdriver_ioconf_vmbus,
240450517e57Snonaka 		    cfattach_ioconf_vmbus, cfdata_ioconf_vmbus);
240550517e57Snonaka #endif
240650517e57Snonaka 		break;
240750517e57Snonaka 
240850517e57Snonaka 	case MODULE_CMD_FINI:
240950517e57Snonaka #ifdef _MODULE
241050517e57Snonaka 		rv = config_fini_component(cfdriver_ioconf_vmbus,
241150517e57Snonaka 		    cfattach_ioconf_vmbus, cfdata_ioconf_vmbus);
241250517e57Snonaka #endif
241350517e57Snonaka 		break;
241450517e57Snonaka 
241550517e57Snonaka 	default:
241650517e57Snonaka 		rv = ENOTTY;
241750517e57Snonaka 		break;
241850517e57Snonaka 	}
241950517e57Snonaka 
242050517e57Snonaka 	return rv;
242150517e57Snonaka }
2422