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