vmbus_chan.c revision 302891
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29256276Sdim#include <sys/cdefs.h>
30256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_channel.c 302891 2016-07-15 08:31:53Z sephe $");
31256276Sdim
32250199Sgrehan#include <sys/param.h>
33296028Ssephe#include <sys/kernel.h>
34250199Sgrehan#include <sys/malloc.h>
35250199Sgrehan#include <sys/systm.h>
36250199Sgrehan#include <sys/mbuf.h>
37250199Sgrehan#include <sys/lock.h>
38250199Sgrehan#include <sys/mutex.h>
39296181Ssephe#include <sys/sysctl.h>
40301588Ssephe
41301588Ssephe#include <machine/atomic.h>
42250199Sgrehan#include <machine/bus.h>
43301588Ssephe
44250199Sgrehan#include <vm/vm.h>
45250199Sgrehan#include <vm/vm_param.h>
46250199Sgrehan#include <vm/pmap.h>
47250199Sgrehan
48302872Ssephe#include <dev/hyperv/include/hyperv_busdma.h>
49300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
50302619Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
51301588Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h>
52300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
53250199Sgrehan
54302731Ssephestatic void 	vmbus_chan_send_event(hv_vmbus_channel* channel);
55302692Ssephestatic void	vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
56302692Ssephe		    const struct hv_vmbus_channel *);
57302864Ssephe
58302713Ssephestatic void	vmbus_chan_task(void *, int);
59302713Ssephestatic void	vmbus_chan_task_nobatch(void *, int);
60302864Ssephestatic void	vmbus_chan_detach_task(void *, int);
61250199Sgrehan
62302864Ssephestatic void	vmbus_chan_msgproc_choffer(struct vmbus_softc *,
63302864Ssephe		    const struct vmbus_message *);
64302864Ssephestatic void	vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
65302864Ssephe		    const struct vmbus_message *);
66302864Ssephe
67302864Ssephe/*
68302864Ssephe * Vmbus channel message processing.
69302864Ssephe */
70302864Ssephestatic const vmbus_chanmsg_proc_t
71302864Ssephevmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
72302864Ssephe	VMBUS_CHANMSG_PROC(CHOFFER,	vmbus_chan_msgproc_choffer),
73302864Ssephe	VMBUS_CHANMSG_PROC(CHRESCIND,	vmbus_chan_msgproc_chrescind),
74302864Ssephe
75302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
76302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
77302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
78302864Ssephe};
79302864Ssephe
80250199Sgrehan/**
81250199Sgrehan *  @brief Trigger an event notification on the specified channel
82250199Sgrehan */
83250199Sgrehanstatic void
84302731Ssephevmbus_chan_send_event(hv_vmbus_channel *channel)
85250199Sgrehan{
86302618Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
87302693Ssephe	uint32_t chanid = channel->ch_id;
88302618Ssephe
89302618Ssephe	atomic_set_long(&sc->vmbus_tx_evtflags[chanid >> VMBUS_EVTFLAG_SHIFT],
90302618Ssephe	    1UL << (chanid & VMBUS_EVTFLAG_MASK));
91302618Ssephe
92302695Ssephe	if (channel->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
93302731Ssephe		atomic_set_int(
94302731Ssephe		&sc->vmbus_mnf2->mnf_trigs[channel->ch_montrig_idx].mt_pending,
95302731Ssephe		channel->ch_montrig_mask);
96250199Sgrehan	} else {
97302726Ssephe		hypercall_signal_event(channel->ch_monprm_dma.hv_paddr);
98250199Sgrehan	}
99250199Sgrehan}
100250199Sgrehan
101296289Ssephestatic int
102296289Ssephevmbus_channel_sysctl_monalloc(SYSCTL_HANDLER_ARGS)
103296289Ssephe{
104296289Ssephe	struct hv_vmbus_channel *chan = arg1;
105296289Ssephe	int alloc = 0;
106296289Ssephe
107302695Ssephe	if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF)
108296289Ssephe		alloc = 1;
109296289Ssephe	return sysctl_handle_int(oidp, &alloc, 0, req);
110296289Ssephe}
111296289Ssephe
112296181Ssephestatic void
113296290Ssephevmbus_channel_sysctl_create(hv_vmbus_channel* channel)
114296181Ssephe{
115296181Ssephe	device_t dev;
116296181Ssephe	struct sysctl_oid *devch_sysctl;
117296181Ssephe	struct sysctl_oid *devch_id_sysctl, *devch_sub_sysctl;
118296181Ssephe	struct sysctl_oid *devch_id_in_sysctl, *devch_id_out_sysctl;
119296181Ssephe	struct sysctl_ctx_list *ctx;
120296181Ssephe	uint32_t ch_id;
121296181Ssephe	uint16_t sub_ch_id;
122296181Ssephe	char name[16];
123296181Ssephe
124302819Ssephe	hv_vmbus_channel* primary_ch = channel->ch_prichan;
125296181Ssephe
126296181Ssephe	if (primary_ch == NULL) {
127302706Ssephe		dev = channel->ch_dev;
128302693Ssephe		ch_id = channel->ch_id;
129296181Ssephe	} else {
130302706Ssephe		dev = primary_ch->ch_dev;
131302693Ssephe		ch_id = primary_ch->ch_id;
132302694Ssephe		sub_ch_id = channel->ch_subidx;
133296181Ssephe	}
134302633Ssephe	ctx = &channel->ch_sysctl_ctx;
135302633Ssephe	sysctl_ctx_init(ctx);
136296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel tree */
137296181Ssephe	devch_sysctl = SYSCTL_ADD_NODE(ctx,
138296181Ssephe		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
139298693Ssephe		    OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
140296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel.CHANID tree */
141296181Ssephe	snprintf(name, sizeof(name), "%d", ch_id);
142296181Ssephe	devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
143296181Ssephe	    	    SYSCTL_CHILDREN(devch_sysctl),
144298693Ssephe	    	    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
145296181Ssephe
146296181Ssephe	if (primary_ch != NULL) {
147296181Ssephe		devch_sub_sysctl = SYSCTL_ADD_NODE(ctx,
148296181Ssephe			SYSCTL_CHILDREN(devch_id_sysctl),
149298693Ssephe			OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
150296181Ssephe		snprintf(name, sizeof(name), "%d", sub_ch_id);
151296181Ssephe		devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
152296181Ssephe			SYSCTL_CHILDREN(devch_sub_sysctl),
153298693Ssephe			OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
154296188Ssephe
155296188Ssephe		SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl),
156296188Ssephe		    OID_AUTO, "chanid", CTLFLAG_RD,
157302693Ssephe		    &channel->ch_id, 0, "channel id");
158296181Ssephe	}
159296188Ssephe	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
160302873Ssephe	    "cpu", CTLFLAG_RD, &channel->ch_cpuid, 0, "owner CPU id");
161296289Ssephe	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
162298693Ssephe	    "monitor_allocated", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
163298693Ssephe	    channel, 0, vmbus_channel_sysctl_monalloc, "I",
164296289Ssephe	    "is monitor allocated to this channel");
165296188Ssephe
166296181Ssephe	devch_id_in_sysctl = SYSCTL_ADD_NODE(ctx,
167296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
168296181Ssephe                    OID_AUTO,
169296181Ssephe		    "in",
170298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
171296181Ssephe	devch_id_out_sysctl = SYSCTL_ADD_NODE(ctx,
172296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
173296181Ssephe                    OID_AUTO,
174296181Ssephe		    "out",
175298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
176296181Ssephe	hv_ring_buffer_stat(ctx,
177296181Ssephe		SYSCTL_CHILDREN(devch_id_in_sysctl),
178296181Ssephe		&(channel->inbound),
179296181Ssephe		"inbound ring buffer stats");
180296181Ssephe	hv_ring_buffer_stat(ctx,
181296181Ssephe		SYSCTL_CHILDREN(devch_id_out_sysctl),
182296181Ssephe		&(channel->outbound),
183296181Ssephe		"outbound ring buffer stats");
184296181Ssephe}
185296290Ssephe
186250199Sgrehan/**
187250199Sgrehan * @brief Open the specified channel
188250199Sgrehan */
189250199Sgrehanint
190250199Sgrehanhv_vmbus_channel_open(
191250199Sgrehan	hv_vmbus_channel*		new_channel,
192250199Sgrehan	uint32_t			send_ring_buffer_size,
193250199Sgrehan	uint32_t			recv_ring_buffer_size,
194250199Sgrehan	void*				user_data,
195250199Sgrehan	uint32_t			user_data_len,
196302874Ssephe	vmbus_chan_callback_t		cb,
197302874Ssephe	void				*cbarg)
198250199Sgrehan{
199302607Ssephe	struct vmbus_softc *sc = new_channel->vmbus_sc;
200302607Ssephe	const struct vmbus_chanmsg_chopen_resp *resp;
201302607Ssephe	const struct vmbus_message *msg;
202302607Ssephe	struct vmbus_chanmsg_chopen *req;
203302607Ssephe	struct vmbus_msghc *mh;
204302607Ssephe	uint32_t status;
205250199Sgrehan	int ret = 0;
206302872Ssephe	uint8_t *br;
207250199Sgrehan
208302607Ssephe	if (user_data_len > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
209302607Ssephe		device_printf(sc->vmbus_dev,
210302607Ssephe		    "invalid udata len %u for chan%u\n",
211302693Ssephe		    user_data_len, new_channel->ch_id);
212302607Ssephe		return EINVAL;
213302607Ssephe	}
214302872Ssephe	KASSERT((send_ring_buffer_size & PAGE_MASK) == 0,
215302872Ssephe	    ("send bufring size is not multiple page"));
216302872Ssephe	KASSERT((recv_ring_buffer_size & PAGE_MASK) == 0,
217302872Ssephe	    ("recv bufring size is not multiple page"));
218302607Ssephe
219302812Ssephe	if (atomic_testandset_int(&new_channel->ch_stflags,
220302812Ssephe	    VMBUS_CHAN_ST_OPENED_SHIFT))
221302812Ssephe		panic("double-open chan%u", new_channel->ch_id);
222282212Swhu
223302874Ssephe	new_channel->ch_cb = cb;
224302874Ssephe	new_channel->ch_cbarg = cbarg;
225250199Sgrehan
226302692Ssephe	vmbus_chan_update_evtflagcnt(sc, new_channel);
227300102Ssephe
228302874Ssephe	new_channel->ch_tq = VMBUS_PCPU_GET(new_channel->vmbus_sc, event_tq,
229302873Ssephe	    new_channel->ch_cpuid);
230302713Ssephe	if (new_channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) {
231302874Ssephe		TASK_INIT(&new_channel->ch_task, 0, vmbus_chan_task,
232302874Ssephe		    new_channel);
233302713Ssephe	} else {
234302874Ssephe		TASK_INIT(&new_channel->ch_task, 0, vmbus_chan_task_nobatch,
235302874Ssephe		    new_channel);
236302713Ssephe	}
237294886Ssephe
238302872Ssephe	/*
239302872Ssephe	 * Allocate the TX+RX bufrings.
240302872Ssephe	 * XXX should use ch_dev dtag
241302872Ssephe	 */
242302872Ssephe	br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
243302872Ssephe	    PAGE_SIZE, 0, send_ring_buffer_size + recv_ring_buffer_size,
244302872Ssephe	    &new_channel->ch_bufring_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
245302872Ssephe	if (br == NULL) {
246302872Ssephe		device_printf(sc->vmbus_dev, "bufring allocation failed\n");
247302812Ssephe		ret = ENOMEM;
248302812Ssephe		goto failed;
249302812Ssephe	}
250302872Ssephe	new_channel->ch_bufring = br;
251250199Sgrehan
252302872Ssephe	/* TX bufring comes first */
253302872Ssephe	hv_vmbus_ring_buffer_init(&new_channel->outbound,
254302872Ssephe	    br, send_ring_buffer_size);
255302872Ssephe	/* RX bufring immediately follows TX bufring */
256302872Ssephe	hv_vmbus_ring_buffer_init(&new_channel->inbound,
257302872Ssephe	    br + send_ring_buffer_size, recv_ring_buffer_size);
258250199Sgrehan
259296290Ssephe	/* Create sysctl tree for this channel */
260296290Ssephe	vmbus_channel_sysctl_create(new_channel);
261296181Ssephe
262302872Ssephe	/*
263302872Ssephe	 * Connect the bufrings, both RX and TX, to this channel.
264250199Sgrehan	 */
265302872Ssephe	ret = vmbus_chan_gpadl_connect(new_channel,
266302872Ssephe		new_channel->ch_bufring_dma.hv_paddr,
267250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
268302872Ssephe		&new_channel->ch_bufring_gpadl);
269302872Ssephe	if (ret != 0) {
270302872Ssephe		device_printf(sc->vmbus_dev,
271302872Ssephe		    "failed to connect bufring GPADL to chan%u\n",
272302872Ssephe		    new_channel->ch_id);
273302872Ssephe		goto failed;
274302872Ssephe	}
275250199Sgrehan
276302607Ssephe	/*
277302607Ssephe	 * Open channel w/ the bufring GPADL on the target CPU.
278250199Sgrehan	 */
279302607Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
280302607Ssephe	if (mh == NULL) {
281302607Ssephe		device_printf(sc->vmbus_dev,
282302607Ssephe		    "can not get msg hypercall for chopen(chan%u)\n",
283302693Ssephe		    new_channel->ch_id);
284302812Ssephe		ret = ENXIO;
285302812Ssephe		goto failed;
286302607Ssephe	}
287250199Sgrehan
288302607Ssephe	req = vmbus_msghc_dataptr(mh);
289302607Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN;
290302693Ssephe	req->chm_chanid = new_channel->ch_id;
291302693Ssephe	req->chm_openid = new_channel->ch_id;
292302872Ssephe	req->chm_gpadl = new_channel->ch_bufring_gpadl;
293302873Ssephe	req->chm_vcpuid = new_channel->ch_vcpuid;
294302607Ssephe	req->chm_rxbr_pgofs = send_ring_buffer_size >> PAGE_SHIFT;
295250199Sgrehan	if (user_data_len)
296302607Ssephe		memcpy(req->chm_udata, user_data, user_data_len);
297250199Sgrehan
298302607Ssephe	ret = vmbus_msghc_exec(sc, mh);
299302607Ssephe	if (ret != 0) {
300302607Ssephe		device_printf(sc->vmbus_dev,
301302607Ssephe		    "chopen(chan%u) msg hypercall exec failed: %d\n",
302302693Ssephe		    new_channel->ch_id, ret);
303302607Ssephe		vmbus_msghc_put(sc, mh);
304302812Ssephe		goto failed;
305302607Ssephe	}
306250199Sgrehan
307302607Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
308302607Ssephe	resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data;
309302607Ssephe	status = resp->chm_status;
310250199Sgrehan
311302607Ssephe	vmbus_msghc_put(sc, mh);
312250199Sgrehan
313302607Ssephe	if (status == 0) {
314302607Ssephe		if (bootverbose) {
315302607Ssephe			device_printf(sc->vmbus_dev, "chan%u opened\n",
316302693Ssephe			    new_channel->ch_id);
317302607Ssephe		}
318302812Ssephe		return 0;
319250199Sgrehan	}
320302812Ssephe
321302812Ssephe	device_printf(sc->vmbus_dev, "failed to open chan%u\n",
322302812Ssephe	    new_channel->ch_id);
323302812Ssephe	ret = ENXIO;
324302812Ssephe
325302812Ssephefailed:
326302872Ssephe	if (new_channel->ch_bufring_gpadl) {
327302890Ssephe		vmbus_chan_gpadl_disconnect(new_channel,
328302872Ssephe		    new_channel->ch_bufring_gpadl);
329302872Ssephe		new_channel->ch_bufring_gpadl = 0;
330302872Ssephe	}
331302872Ssephe	if (new_channel->ch_bufring != NULL) {
332302872Ssephe		hyperv_dmamem_free(&new_channel->ch_bufring_dma,
333302872Ssephe		    new_channel->ch_bufring);
334302872Ssephe		new_channel->ch_bufring = NULL;
335302872Ssephe	}
336302812Ssephe	atomic_clear_int(&new_channel->ch_stflags, VMBUS_CHAN_ST_OPENED);
337302812Ssephe	return ret;
338250199Sgrehan}
339250199Sgrehan
340302609Ssepheint
341302871Ssephevmbus_chan_gpadl_connect(struct hv_vmbus_channel *chan, bus_addr_t paddr,
342302871Ssephe    int size, uint32_t *gpadl0)
343302871Ssephe{
344302871Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
345302609Ssephe	struct vmbus_msghc *mh;
346302609Ssephe	struct vmbus_chanmsg_gpadl_conn *req;
347302609Ssephe	const struct vmbus_message *msg;
348302609Ssephe	size_t reqsz;
349302609Ssephe	uint32_t gpadl, status;
350302609Ssephe	int page_count, range_len, i, cnt, error;
351302871Ssephe	uint64_t page_id;
352250199Sgrehan
353302609Ssephe	/*
354302609Ssephe	 * Preliminary checks.
355302609Ssephe	 */
356250199Sgrehan
357302609Ssephe	KASSERT((size & PAGE_MASK) == 0,
358302871Ssephe	    ("invalid GPA size %d, not multiple page size", size));
359250199Sgrehan	page_count = size >> PAGE_SHIFT;
360250199Sgrehan
361302609Ssephe	KASSERT((paddr & PAGE_MASK) == 0,
362302609Ssephe	    ("GPA is not page aligned %jx", (uintmax_t)paddr));
363302609Ssephe	page_id = paddr >> PAGE_SHIFT;
364250199Sgrehan
365302609Ssephe	range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
366302609Ssephe	/*
367302609Ssephe	 * We don't support multiple GPA ranges.
368302609Ssephe	 */
369302609Ssephe	if (range_len > UINT16_MAX) {
370302609Ssephe		device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
371302609Ssephe		    page_count);
372302609Ssephe		return EOPNOTSUPP;
373250199Sgrehan	}
374250199Sgrehan
375302609Ssephe	/*
376302609Ssephe	 * Allocate GPADL id.
377302609Ssephe	 */
378302630Ssephe	gpadl = vmbus_gpadl_alloc(sc);
379302609Ssephe	*gpadl0 = gpadl;
380250199Sgrehan
381302609Ssephe	/*
382302609Ssephe	 * Connect this GPADL to the target channel.
383302609Ssephe	 *
384302609Ssephe	 * NOTE:
385302609Ssephe	 * Since each message can only hold small set of page
386302609Ssephe	 * addresses, several messages may be required to
387302609Ssephe	 * complete the connection.
388302609Ssephe	 */
389302609Ssephe	if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
390302609Ssephe		cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
391302609Ssephe	else
392302609Ssephe		cnt = page_count;
393302609Ssephe	page_count -= cnt;
394250199Sgrehan
395302609Ssephe	reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
396302609Ssephe	    chm_range.gpa_page[cnt]);
397302609Ssephe	mh = vmbus_msghc_get(sc, reqsz);
398302609Ssephe	if (mh == NULL) {
399302609Ssephe		device_printf(sc->vmbus_dev,
400302609Ssephe		    "can not get msg hypercall for gpadl->chan%u\n",
401302871Ssephe		    chan->ch_id);
402302609Ssephe		return EIO;
403250199Sgrehan	}
404250199Sgrehan
405302609Ssephe	req = vmbus_msghc_dataptr(mh);
406302609Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
407302871Ssephe	req->chm_chanid = chan->ch_id;
408302609Ssephe	req->chm_gpadl = gpadl;
409302609Ssephe	req->chm_range_len = range_len;
410302609Ssephe	req->chm_range_cnt = 1;
411302609Ssephe	req->chm_range.gpa_len = size;
412302609Ssephe	req->chm_range.gpa_ofs = 0;
413302609Ssephe	for (i = 0; i < cnt; ++i)
414302609Ssephe		req->chm_range.gpa_page[i] = page_id++;
415250199Sgrehan
416302609Ssephe	error = vmbus_msghc_exec(sc, mh);
417302609Ssephe	if (error) {
418302609Ssephe		device_printf(sc->vmbus_dev,
419302609Ssephe		    "gpadl->chan%u msg hypercall exec failed: %d\n",
420302871Ssephe		    chan->ch_id, error);
421302609Ssephe		vmbus_msghc_put(sc, mh);
422302609Ssephe		return error;
423302609Ssephe	}
424250199Sgrehan
425302609Ssephe	while (page_count > 0) {
426302609Ssephe		struct vmbus_chanmsg_gpadl_subconn *subreq;
427250199Sgrehan
428302609Ssephe		if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
429302609Ssephe			cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
430302609Ssephe		else
431302609Ssephe			cnt = page_count;
432302609Ssephe		page_count -= cnt;
433250199Sgrehan
434302609Ssephe		reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
435302609Ssephe		    chm_gpa_page[cnt]);
436302609Ssephe		vmbus_msghc_reset(mh, reqsz);
437250199Sgrehan
438302609Ssephe		subreq = vmbus_msghc_dataptr(mh);
439302609Ssephe		subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
440302609Ssephe		subreq->chm_gpadl = gpadl;
441302609Ssephe		for (i = 0; i < cnt; ++i)
442302609Ssephe			subreq->chm_gpa_page[i] = page_id++;
443250199Sgrehan
444302609Ssephe		vmbus_msghc_exec_noresult(mh);
445250199Sgrehan	}
446302609Ssephe	KASSERT(page_count == 0, ("invalid page count %d", page_count));
447250199Sgrehan
448302609Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
449302609Ssephe	status = ((const struct vmbus_chanmsg_gpadl_connresp *)
450302609Ssephe	    msg->msg_data)->chm_status;
451250199Sgrehan
452302609Ssephe	vmbus_msghc_put(sc, mh);
453250199Sgrehan
454302609Ssephe	if (status != 0) {
455302609Ssephe		device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
456302871Ssephe		    "status %u\n", chan->ch_id, status);
457302609Ssephe		return EIO;
458302632Ssephe	} else {
459302632Ssephe		if (bootverbose) {
460302632Ssephe			device_printf(sc->vmbus_dev, "gpadl->chan%u "
461302871Ssephe			    "succeeded\n", chan->ch_id);
462302632Ssephe		}
463302609Ssephe	}
464302609Ssephe	return 0;
465250199Sgrehan}
466250199Sgrehan
467302611Ssephe/*
468302611Ssephe * Disconnect the GPA from the target channel
469250199Sgrehan */
470250199Sgrehanint
471302890Ssephevmbus_chan_gpadl_disconnect(struct hv_vmbus_channel *chan, uint32_t gpadl)
472250199Sgrehan{
473302611Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
474302611Ssephe	struct vmbus_msghc *mh;
475302611Ssephe	struct vmbus_chanmsg_gpadl_disconn *req;
476302611Ssephe	int error;
477250199Sgrehan
478302611Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
479302611Ssephe	if (mh == NULL) {
480302611Ssephe		device_printf(sc->vmbus_dev,
481302611Ssephe		    "can not get msg hypercall for gpa x->chan%u\n",
482302693Ssephe		    chan->ch_id);
483302611Ssephe		return EBUSY;
484250199Sgrehan	}
485250199Sgrehan
486302611Ssephe	req = vmbus_msghc_dataptr(mh);
487302611Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
488302693Ssephe	req->chm_chanid = chan->ch_id;
489302611Ssephe	req->chm_gpadl = gpadl;
490250199Sgrehan
491302611Ssephe	error = vmbus_msghc_exec(sc, mh);
492302611Ssephe	if (error) {
493302611Ssephe		device_printf(sc->vmbus_dev,
494302611Ssephe		    "gpa x->chan%u msg hypercall exec failed: %d\n",
495302693Ssephe		    chan->ch_id, error);
496302611Ssephe		vmbus_msghc_put(sc, mh);
497302611Ssephe		return error;
498302611Ssephe	}
499250199Sgrehan
500302611Ssephe	vmbus_msghc_wait_result(sc, mh);
501302611Ssephe	/* Discard result; no useful information */
502302611Ssephe	vmbus_msghc_put(sc, mh);
503250199Sgrehan
504302611Ssephe	return 0;
505250199Sgrehan}
506250199Sgrehan
507282212Swhustatic void
508302891Ssephevmbus_chan_close_internal(struct hv_vmbus_channel *chan)
509250199Sgrehan{
510302891Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
511302610Ssephe	struct vmbus_msghc *mh;
512302610Ssephe	struct vmbus_chanmsg_chclose *req;
513302891Ssephe	struct taskqueue *tq = chan->ch_tq;
514302610Ssephe	int error;
515250199Sgrehan
516302812Ssephe	/* TODO: stringent check */
517302891Ssephe	atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
518302812Ssephe
519302891Ssephe	/*
520302891Ssephe	 * Free this channel's sysctl tree attached to its device's
521302891Ssephe	 * sysctl tree.
522302891Ssephe	 */
523302891Ssephe	sysctl_ctx_free(&chan->ch_sysctl_ctx);
524282212Swhu
525282212Swhu	/*
526302891Ssephe	 * Set ch_tq to NULL to avoid more requests be scheduled.
527302891Ssephe	 * XXX pretty broken; need rework.
528294886Ssephe	 */
529302891Ssephe	chan->ch_tq = NULL;
530302891Ssephe	taskqueue_drain(tq, &chan->ch_task);
531302891Ssephe	chan->ch_cb = NULL;
532250199Sgrehan
533302891Ssephe	/*
534302891Ssephe	 * Close this channel.
535250199Sgrehan	 */
536302610Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
537302610Ssephe	if (mh == NULL) {
538302610Ssephe		device_printf(sc->vmbus_dev,
539302610Ssephe		    "can not get msg hypercall for chclose(chan%u)\n",
540302891Ssephe		    chan->ch_id);
541302610Ssephe		return;
542302610Ssephe	}
543250199Sgrehan
544302610Ssephe	req = vmbus_msghc_dataptr(mh);
545302610Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
546302891Ssephe	req->chm_chanid = chan->ch_id;
547250199Sgrehan
548302610Ssephe	error = vmbus_msghc_exec_noresult(mh);
549302610Ssephe	vmbus_msghc_put(sc, mh);
550302610Ssephe
551302610Ssephe	if (error) {
552302610Ssephe		device_printf(sc->vmbus_dev,
553302610Ssephe		    "chclose(chan%u) msg hypercall exec failed: %d\n",
554302891Ssephe		    chan->ch_id, error);
555302610Ssephe		return;
556302610Ssephe	} else if (bootverbose) {
557302891Ssephe		device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id);
558302610Ssephe	}
559302610Ssephe
560302891Ssephe	/*
561302891Ssephe	 * Disconnect the TX+RX bufrings from this channel.
562302891Ssephe	 */
563302891Ssephe	if (chan->ch_bufring_gpadl) {
564302891Ssephe		vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
565302891Ssephe		chan->ch_bufring_gpadl = 0;
566250199Sgrehan	}
567250199Sgrehan
568302891Ssephe	/*
569302891Ssephe	 * Destroy the TX+RX bufrings.
570302891Ssephe	 */
571302891Ssephe	hv_ring_buffer_cleanup(&chan->outbound);
572302891Ssephe	hv_ring_buffer_cleanup(&chan->inbound);
573302891Ssephe	if (chan->ch_bufring != NULL) {
574302891Ssephe		hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
575302891Ssephe		chan->ch_bufring = NULL;
576302872Ssephe	}
577282212Swhu}
578250199Sgrehan
579302818Ssephe/*
580302818Ssephe * Caller should make sure that all sub-channels have
581302818Ssephe * been added to 'chan' and all to-be-closed channels
582302818Ssephe * are not being opened.
583282212Swhu */
584282212Swhuvoid
585302818Ssephehv_vmbus_channel_close(struct hv_vmbus_channel *chan)
586282212Swhu{
587302818Ssephe	int subchan_cnt;
588282212Swhu
589302818Ssephe	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
590282212Swhu		/*
591302818Ssephe		 * Sub-channel is closed when its primary channel
592302818Ssephe		 * is closed; done.
593282212Swhu		 */
594282212Swhu		return;
595282212Swhu	}
596282212Swhu
597250199Sgrehan	/*
598302818Ssephe	 * Close all sub-channels, if any.
599250199Sgrehan	 */
600302819Ssephe	subchan_cnt = chan->ch_subchan_cnt;
601302818Ssephe	if (subchan_cnt > 0) {
602302818Ssephe		struct hv_vmbus_channel **subchan;
603302818Ssephe		int i;
604302818Ssephe
605302890Ssephe		subchan = vmbus_subchan_get(chan, subchan_cnt);
606302818Ssephe		for (i = 0; i < subchan_cnt; ++i)
607302891Ssephe			vmbus_chan_close_internal(subchan[i]);
608302890Ssephe		vmbus_subchan_rel(subchan, subchan_cnt);
609250199Sgrehan	}
610302818Ssephe
611302818Ssephe	/* Then close the primary channel. */
612302891Ssephe	vmbus_chan_close_internal(chan);
613250199Sgrehan}
614250199Sgrehan
615250199Sgrehanint
616302882Ssephevmbus_chan_send(struct hv_vmbus_channel *chan, uint16_t type, uint16_t flags,
617302882Ssephe    void *data, int dlen, uint64_t xactid)
618250199Sgrehan{
619302875Ssephe	struct vmbus_chanpkt pkt;
620302881Ssephe	int pktlen, pad_pktlen, hlen, error;
621302881Ssephe	uint64_t pad = 0;
622302881Ssephe	struct iovec iov[3];
623302881Ssephe	boolean_t send_evt;
624250199Sgrehan
625302881Ssephe	hlen = sizeof(pkt);
626302881Ssephe	pktlen = hlen + dlen;
627302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
628250199Sgrehan
629302875Ssephe	pkt.cp_hdr.cph_type = type;
630302875Ssephe	pkt.cp_hdr.cph_flags = flags;
631302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
632302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
633302881Ssephe	pkt.cp_hdr.cph_xactid = xactid;
634250199Sgrehan
635302875Ssephe	iov[0].iov_base = &pkt;
636302881Ssephe	iov[0].iov_len = hlen;
637302881Ssephe	iov[1].iov_base = data;
638302881Ssephe	iov[1].iov_len = dlen;
639302881Ssephe	iov[2].iov_base = &pad;
640302881Ssephe	iov[2].iov_len = pad_pktlen - pktlen;
641250199Sgrehan
642302881Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 3, &send_evt);
643302881Ssephe	if (!error && send_evt)
644302881Ssephe		vmbus_chan_send_event(chan);
645302881Ssephe	return error;
646250199Sgrehan}
647250199Sgrehan
648250199Sgrehanint
649302876Ssephevmbus_chan_send_sglist(struct hv_vmbus_channel *chan,
650302876Ssephe    struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid)
651250199Sgrehan{
652302876Ssephe	struct vmbus_chanpkt_sglist pkt;
653302876Ssephe	int pktlen, pad_pktlen, hlen, error;
654302876Ssephe	struct iovec iov[4];
655302876Ssephe	boolean_t send_evt;
656302876Ssephe	uint64_t pad = 0;
657250199Sgrehan
658302876Ssephe	KASSERT(sglen < VMBUS_CHAN_SGLIST_MAX,
659302876Ssephe	    ("invalid sglist len %d", sglen));
660250199Sgrehan
661302876Ssephe	hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]);
662302876Ssephe	pktlen = hlen + dlen;
663302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
664250199Sgrehan
665302880Ssephe	pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
666302879Ssephe	pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
667302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
668302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
669302876Ssephe	pkt.cp_hdr.cph_xactid = xactid;
670302876Ssephe	pkt.cp_rsvd = 0;
671302876Ssephe	pkt.cp_gpa_cnt = sglen;
672250199Sgrehan
673302876Ssephe	iov[0].iov_base = &pkt;
674302876Ssephe	iov[0].iov_len = sizeof(pkt);
675302876Ssephe	iov[1].iov_base = sg;
676302876Ssephe	iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;
677302876Ssephe	iov[2].iov_base = data;
678302876Ssephe	iov[2].iov_len = dlen;
679302876Ssephe	iov[3].iov_base = &pad;
680302876Ssephe	iov[3].iov_len = pad_pktlen - pktlen;
681250199Sgrehan
682302876Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
683302876Ssephe	if (!error && send_evt)
684302876Ssephe		vmbus_chan_send_event(chan);
685302876Ssephe	return error;
686250199Sgrehan}
687250199Sgrehan
688250199Sgrehanint
689302878Ssephevmbus_chan_send_prplist(struct hv_vmbus_channel *chan,
690302878Ssephe    struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen,
691302878Ssephe    uint64_t xactid)
692250199Sgrehan{
693302878Ssephe	struct vmbus_chanpkt_prplist pkt;
694302878Ssephe	int pktlen, pad_pktlen, hlen, error;
695302878Ssephe	struct iovec iov[4];
696302878Ssephe	boolean_t send_evt;
697302878Ssephe	uint64_t pad = 0;
698250199Sgrehan
699302878Ssephe	KASSERT(prp_cnt < VMBUS_CHAN_PRPLIST_MAX,
700302878Ssephe	    ("invalid prplist entry count %d", prp_cnt));
701250199Sgrehan
702302878Ssephe	hlen = __offsetof(struct vmbus_chanpkt_prplist,
703302878Ssephe	    cp_range[0].gpa_page[prp_cnt]);
704302878Ssephe	pktlen = hlen + dlen;
705302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
706250199Sgrehan
707302880Ssephe	pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
708302879Ssephe	pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
709302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
710302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
711302878Ssephe	pkt.cp_hdr.cph_xactid = xactid;
712302878Ssephe	pkt.cp_rsvd = 0;
713302878Ssephe	pkt.cp_range_cnt = 1;
714250199Sgrehan
715302878Ssephe	iov[0].iov_base = &pkt;
716302878Ssephe	iov[0].iov_len = sizeof(pkt);
717302878Ssephe	iov[1].iov_base = prp;
718302878Ssephe	iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]);
719302878Ssephe	iov[2].iov_base = data;
720302878Ssephe	iov[2].iov_len = dlen;
721302878Ssephe	iov[3].iov_base = &pad;
722302878Ssephe	iov[3].iov_len = pad_pktlen - pktlen;
723250199Sgrehan
724302878Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
725302878Ssephe	if (!error && send_evt)
726302878Ssephe		vmbus_chan_send_event(chan);
727302878Ssephe	return error;
728250199Sgrehan}
729250199Sgrehan
730250199Sgrehanint
731302885Ssephevmbus_chan_recv(struct hv_vmbus_channel *chan, void *data, int *dlen0,
732302885Ssephe    uint64_t *xactid)
733250199Sgrehan{
734302885Ssephe	struct vmbus_chanpkt_hdr pkt;
735302885Ssephe	int error, dlen, hlen;
736250199Sgrehan
737302885Ssephe	error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
738302885Ssephe	if (error)
739302885Ssephe		return error;
740250199Sgrehan
741302885Ssephe	hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen);
742302885Ssephe	dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen;
743250199Sgrehan
744302885Ssephe	if (*dlen0 < dlen) {
745302886Ssephe		/* Return the size of this packet's data. */
746302885Ssephe		*dlen0 = dlen;
747302885Ssephe		return ENOBUFS;
748302885Ssephe	}
749250199Sgrehan
750302885Ssephe	*xactid = pkt.cph_xactid;
751302885Ssephe	*dlen0 = dlen;
752250199Sgrehan
753302886Ssephe	/* Skip packet header */
754302885Ssephe	error = hv_ring_buffer_read(&chan->inbound, data, dlen, hlen);
755302885Ssephe	KASSERT(!error, ("hv_ring_buffer_read failed"));
756250199Sgrehan
757302885Ssephe	return 0;
758250199Sgrehan}
759250199Sgrehan
760250199Sgrehanint
761302886Ssephevmbus_chan_recv_pkt(struct hv_vmbus_channel *chan,
762302886Ssephe    struct vmbus_chanpkt_hdr *pkt0, int *pktlen0)
763250199Sgrehan{
764302886Ssephe	struct vmbus_chanpkt_hdr pkt;
765302886Ssephe	int error, pktlen;
766250199Sgrehan
767302886Ssephe	error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
768302886Ssephe	if (error)
769302886Ssephe		return error;
770250199Sgrehan
771302886Ssephe	pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen);
772302886Ssephe	if (*pktlen0 < pktlen) {
773302886Ssephe		/* Return the size of this packet. */
774302886Ssephe		*pktlen0 = pktlen;
775302886Ssephe		return ENOBUFS;
776302886Ssephe	}
777302886Ssephe	*pktlen0 = pktlen;
778250199Sgrehan
779302886Ssephe	/* Include packet header */
780302886Ssephe	error = hv_ring_buffer_read(&chan->inbound, pkt0, pktlen, 0);
781302886Ssephe	KASSERT(!error, ("hv_ring_buffer_read failed"));
782250199Sgrehan
783302886Ssephe	return 0;
784250199Sgrehan}
785294886Ssephe
786294886Ssephestatic void
787302713Ssephevmbus_chan_task(void *xchan, int pending __unused)
788294886Ssephe{
789302713Ssephe	struct hv_vmbus_channel *chan = xchan;
790302874Ssephe	vmbus_chan_callback_t cb = chan->ch_cb;
791302874Ssephe	void *cbarg = chan->ch_cbarg;
792294886Ssephe
793302710Ssephe	/*
794302710Ssephe	 * Optimize host to guest signaling by ensuring:
795302710Ssephe	 * 1. While reading the channel, we disable interrupts from
796302710Ssephe	 *    host.
797302710Ssephe	 * 2. Ensure that we process all posted messages from the host
798302710Ssephe	 *    before returning from this callback.
799302710Ssephe	 * 3. Once we return, enable signaling from the host. Once this
800302710Ssephe	 *    state is set we check to see if additional packets are
801302710Ssephe	 *    available to read. In this case we repeat the process.
802302713Ssephe	 *
803302713Ssephe	 * NOTE: Interrupt has been disabled in the ISR.
804302710Ssephe	 */
805302713Ssephe	for (;;) {
806302713Ssephe		uint32_t left;
807294886Ssephe
808302874Ssephe		cb(cbarg);
809294886Ssephe
810302713Ssephe		left = hv_ring_buffer_read_end(&chan->inbound);
811302713Ssephe		if (left == 0) {
812302713Ssephe			/* No more data in RX bufring; done */
813302713Ssephe			break;
814302713Ssephe		}
815302713Ssephe		hv_ring_buffer_read_begin(&chan->inbound);
816302713Ssephe	}
817294886Ssephe}
818302692Ssephe
819302713Ssephestatic void
820302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused)
821302713Ssephe{
822302713Ssephe	struct hv_vmbus_channel *chan = xchan;
823302713Ssephe
824302874Ssephe	chan->ch_cb(chan->ch_cbarg);
825302713Ssephe}
826302713Ssephe
827302692Ssephestatic __inline void
828302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
829302692Ssephe    int flag_cnt)
830302692Ssephe{
831302692Ssephe	int f;
832302692Ssephe
833302692Ssephe	for (f = 0; f < flag_cnt; ++f) {
834302806Ssephe		uint32_t chid_base;
835302692Ssephe		u_long flags;
836302806Ssephe		int chid_ofs;
837302692Ssephe
838302692Ssephe		if (event_flags[f] == 0)
839302692Ssephe			continue;
840302692Ssephe
841302692Ssephe		flags = atomic_swap_long(&event_flags[f], 0);
842302806Ssephe		chid_base = f << VMBUS_EVTFLAG_SHIFT;
843302692Ssephe
844302806Ssephe		while ((chid_ofs = ffsl(flags)) != 0) {
845302692Ssephe			struct hv_vmbus_channel *channel;
846302692Ssephe
847302806Ssephe			--chid_ofs; /* NOTE: ffsl is 1-based */
848302806Ssephe			flags &= ~(1UL << chid_ofs);
849302692Ssephe
850302806Ssephe			channel = sc->vmbus_chmap[chid_base + chid_ofs];
851302692Ssephe
852302692Ssephe			/* if channel is closed or closing */
853302874Ssephe			if (channel == NULL || channel->ch_tq == NULL)
854302692Ssephe				continue;
855302692Ssephe
856302709Ssephe			if (channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
857302692Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
858302874Ssephe			taskqueue_enqueue(channel->ch_tq, &channel->ch_task);
859302692Ssephe		}
860302692Ssephe	}
861302692Ssephe}
862302692Ssephe
863302692Ssephevoid
864302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu)
865302692Ssephe{
866302692Ssephe	struct vmbus_evtflags *eventf;
867302692Ssephe
868302692Ssephe	/*
869302692Ssephe	 * On Host with Win8 or above, the event page can be checked directly
870302692Ssephe	 * to get the id of the channel that has the pending interrupt.
871302692Ssephe	 */
872302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
873302692Ssephe	vmbus_event_flags_proc(sc, eventf->evt_flags,
874302692Ssephe	    VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
875302692Ssephe}
876302692Ssephe
877302692Ssephevoid
878302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
879302692Ssephe{
880302692Ssephe	struct vmbus_evtflags *eventf;
881302692Ssephe
882302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
883302692Ssephe	if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
884302692Ssephe		vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
885302692Ssephe		    VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
886302692Ssephe	}
887302692Ssephe}
888302692Ssephe
889302692Ssephestatic void
890302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
891302692Ssephe    const struct hv_vmbus_channel *chan)
892302692Ssephe{
893302692Ssephe	volatile int *flag_cnt_ptr;
894302692Ssephe	int flag_cnt;
895302692Ssephe
896302693Ssephe	flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
897302873Ssephe	flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid);
898302692Ssephe
899302692Ssephe	for (;;) {
900302692Ssephe		int old_flag_cnt;
901302692Ssephe
902302692Ssephe		old_flag_cnt = *flag_cnt_ptr;
903302692Ssephe		if (old_flag_cnt >= flag_cnt)
904302692Ssephe			break;
905302692Ssephe		if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
906302692Ssephe			if (bootverbose) {
907302692Ssephe				device_printf(sc->vmbus_dev,
908302692Ssephe				    "channel%u update cpu%d flag_cnt to %d\n",
909302873Ssephe				    chan->ch_id, chan->ch_cpuid, flag_cnt);
910302692Ssephe			}
911302692Ssephe			break;
912302692Ssephe		}
913302692Ssephe	}
914302692Ssephe}
915302864Ssephe
916302864Ssephestatic struct hv_vmbus_channel *
917302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc)
918302864Ssephe{
919302864Ssephe	struct hv_vmbus_channel *chan;
920302864Ssephe
921302864Ssephe	chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
922302864Ssephe
923302864Ssephe	chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
924302864Ssephe	    HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
925302864Ssephe	    &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
926302864Ssephe	if (chan->ch_monprm == NULL) {
927302864Ssephe		device_printf(sc->vmbus_dev, "monprm alloc failed\n");
928302864Ssephe		free(chan, M_DEVBUF);
929302864Ssephe		return NULL;
930302864Ssephe	}
931302864Ssephe
932302864Ssephe	chan->vmbus_sc = sc;
933302864Ssephe	mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
934302864Ssephe	TAILQ_INIT(&chan->ch_subchans);
935302864Ssephe	TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
936302864Ssephe
937302864Ssephe	return chan;
938302864Ssephe}
939302864Ssephe
940302864Ssephestatic void
941302864Ssephevmbus_chan_free(struct hv_vmbus_channel *chan)
942302864Ssephe{
943302864Ssephe	/* TODO: assert sub-channel list is empty */
944302864Ssephe	/* TODO: asset no longer on the primary channel's sub-channel list */
945302864Ssephe	/* TODO: asset no longer on the vmbus channel list */
946302864Ssephe	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
947302864Ssephe	mtx_destroy(&chan->ch_subchan_lock);
948302864Ssephe	free(chan, M_DEVBUF);
949302864Ssephe}
950302864Ssephe
951302864Ssephestatic int
952302864Ssephevmbus_chan_add(struct hv_vmbus_channel *newchan)
953302864Ssephe{
954302864Ssephe	struct vmbus_softc *sc = newchan->vmbus_sc;
955302864Ssephe	struct hv_vmbus_channel *prichan;
956302864Ssephe
957302864Ssephe	if (newchan->ch_id == 0) {
958302864Ssephe		/*
959302864Ssephe		 * XXX
960302864Ssephe		 * Chan0 will neither be processed nor should be offered;
961302864Ssephe		 * skip it.
962302864Ssephe		 */
963302864Ssephe		device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
964302864Ssephe		return EINVAL;
965302864Ssephe	} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
966302864Ssephe		device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
967302864Ssephe		    newchan->ch_id);
968302864Ssephe		return EINVAL;
969302864Ssephe	}
970302864Ssephe	sc->vmbus_chmap[newchan->ch_id] = newchan;
971302864Ssephe
972302864Ssephe	if (bootverbose) {
973302864Ssephe		device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
974302864Ssephe		    newchan->ch_id, newchan->ch_subidx);
975302864Ssephe	}
976302864Ssephe
977302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
978302864Ssephe	TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
979302864Ssephe		/*
980302864Ssephe		 * Sub-channel will have the same type GUID and instance
981302864Ssephe		 * GUID as its primary channel.
982302864Ssephe		 */
983302864Ssephe		if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
984302864Ssephe		    sizeof(struct hyperv_guid)) == 0 &&
985302864Ssephe		    memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
986302864Ssephe		    sizeof(struct hyperv_guid)) == 0)
987302864Ssephe			break;
988302864Ssephe	}
989302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(newchan)) {
990302864Ssephe		if (prichan == NULL) {
991302864Ssephe			/* Install the new primary channel */
992302864Ssephe			TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
993302864Ssephe			    ch_prilink);
994302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
995302864Ssephe			return 0;
996302864Ssephe		} else {
997302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
998302864Ssephe			device_printf(sc->vmbus_dev, "duplicated primary "
999302864Ssephe			    "chan%u\n", newchan->ch_id);
1000302864Ssephe			return EINVAL;
1001302864Ssephe		}
1002302864Ssephe	} else { /* Sub-channel */
1003302864Ssephe		if (prichan == NULL) {
1004302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1005302864Ssephe			device_printf(sc->vmbus_dev, "no primary chan for "
1006302864Ssephe			    "chan%u\n", newchan->ch_id);
1007302864Ssephe			return EINVAL;
1008302864Ssephe		}
1009302864Ssephe		/*
1010302864Ssephe		 * Found the primary channel for this sub-channel and
1011302864Ssephe		 * move on.
1012302864Ssephe		 *
1013302864Ssephe		 * XXX refcnt prichan
1014302864Ssephe		 */
1015302864Ssephe	}
1016302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1017302864Ssephe
1018302864Ssephe	/*
1019302864Ssephe	 * This is a sub-channel; link it with the primary channel.
1020302864Ssephe	 */
1021302864Ssephe	KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
1022302864Ssephe	    ("new channel is not sub-channel"));
1023302864Ssephe	KASSERT(prichan != NULL, ("no primary channel"));
1024302864Ssephe
1025302864Ssephe	newchan->ch_prichan = prichan;
1026302864Ssephe	newchan->ch_dev = prichan->ch_dev;
1027302864Ssephe
1028302864Ssephe	mtx_lock(&prichan->ch_subchan_lock);
1029302864Ssephe	TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
1030302864Ssephe	/*
1031302864Ssephe	 * Bump up sub-channel count and notify anyone that is
1032302864Ssephe	 * interested in this sub-channel, after this sub-channel
1033302864Ssephe	 * is setup.
1034302864Ssephe	 */
1035302864Ssephe	prichan->ch_subchan_cnt++;
1036302864Ssephe	mtx_unlock(&prichan->ch_subchan_lock);
1037302864Ssephe	wakeup(prichan);
1038302864Ssephe
1039302864Ssephe	return 0;
1040302864Ssephe}
1041302864Ssephe
1042302864Ssephevoid
1043302890Ssephevmbus_chan_cpu_set(struct hv_vmbus_channel *chan, int cpu)
1044302864Ssephe{
1045302864Ssephe	KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
1046302864Ssephe
1047302864Ssephe	if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
1048302864Ssephe	    chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
1049302864Ssephe		/* Only cpu0 is supported */
1050302864Ssephe		cpu = 0;
1051302864Ssephe	}
1052302864Ssephe
1053302873Ssephe	chan->ch_cpuid = cpu;
1054302873Ssephe	chan->ch_vcpuid = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
1055302864Ssephe
1056302864Ssephe	if (bootverbose) {
1057302864Ssephe		printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
1058302873Ssephe		    chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid);
1059302864Ssephe	}
1060302864Ssephe}
1061302864Ssephe
1062302864Ssephevoid
1063302890Ssephevmbus_chan_cpu_rr(struct hv_vmbus_channel *chan)
1064302864Ssephe{
1065302864Ssephe	static uint32_t vmbus_chan_nextcpu;
1066302864Ssephe	int cpu;
1067302864Ssephe
1068302864Ssephe	cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
1069302890Ssephe	vmbus_chan_cpu_set(chan, cpu);
1070302864Ssephe}
1071302864Ssephe
1072302864Ssephestatic void
1073302864Ssephevmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
1074302864Ssephe{
1075302864Ssephe	/*
1076302864Ssephe	 * By default, pin the channel to cpu0.  Devices having
1077302864Ssephe	 * special channel-cpu mapping requirement should call
1078302890Ssephe	 * vmbus_chan_cpu_{set,rr}().
1079302864Ssephe	 */
1080302890Ssephe	vmbus_chan_cpu_set(chan, 0);
1081302864Ssephe}
1082302864Ssephe
1083302864Ssephestatic void
1084302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
1085302864Ssephe    const struct vmbus_message *msg)
1086302864Ssephe{
1087302864Ssephe	const struct vmbus_chanmsg_choffer *offer;
1088302864Ssephe	struct hv_vmbus_channel *chan;
1089302864Ssephe	int error;
1090302864Ssephe
1091302864Ssephe	offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
1092302864Ssephe
1093302864Ssephe	chan = vmbus_chan_alloc(sc);
1094302864Ssephe	if (chan == NULL) {
1095302864Ssephe		device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
1096302864Ssephe		    offer->chm_chanid);
1097302864Ssephe		return;
1098302864Ssephe	}
1099302864Ssephe
1100302864Ssephe	chan->ch_id = offer->chm_chanid;
1101302864Ssephe	chan->ch_subidx = offer->chm_subidx;
1102302864Ssephe	chan->ch_guid_type = offer->chm_chtype;
1103302864Ssephe	chan->ch_guid_inst = offer->chm_chinst;
1104302864Ssephe
1105302864Ssephe	/* Batch reading is on by default */
1106302864Ssephe	chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
1107302864Ssephe
1108302864Ssephe	chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
1109302864Ssephe	if (sc->vmbus_version != VMBUS_VERSION_WS2008)
1110302864Ssephe		chan->ch_monprm->mp_connid = offer->chm_connid;
1111302864Ssephe
1112302864Ssephe	if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
1113302864Ssephe		/*
1114302864Ssephe		 * Setup MNF stuffs.
1115302864Ssephe		 */
1116302864Ssephe		chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
1117302864Ssephe		chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
1118302864Ssephe		if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
1119302864Ssephe			panic("invalid monitor trigger %u", offer->chm_montrig);
1120302864Ssephe		chan->ch_montrig_mask =
1121302864Ssephe		    1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
1122302864Ssephe	}
1123302864Ssephe
1124302864Ssephe	/* Select default cpu for this channel. */
1125302864Ssephe	vmbus_chan_cpu_default(chan);
1126302864Ssephe
1127302864Ssephe	error = vmbus_chan_add(chan);
1128302864Ssephe	if (error) {
1129302864Ssephe		device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
1130302864Ssephe		    chan->ch_id, error);
1131302864Ssephe		vmbus_chan_free(chan);
1132302864Ssephe		return;
1133302864Ssephe	}
1134302864Ssephe
1135302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1136302864Ssephe		/*
1137302864Ssephe		 * Add device for this primary channel.
1138302864Ssephe		 *
1139302864Ssephe		 * NOTE:
1140302864Ssephe		 * Error is ignored here; don't have much to do if error
1141302864Ssephe		 * really happens.
1142302864Ssephe		 */
1143302868Ssephe		vmbus_add_child(chan);
1144302864Ssephe	}
1145302864Ssephe}
1146302864Ssephe
1147302864Ssephe/*
1148302864Ssephe * XXX pretty broken; need rework.
1149302864Ssephe */
1150302864Ssephestatic void
1151302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
1152302864Ssephe    const struct vmbus_message *msg)
1153302864Ssephe{
1154302864Ssephe	const struct vmbus_chanmsg_chrescind *note;
1155302864Ssephe	struct hv_vmbus_channel *chan;
1156302864Ssephe
1157302864Ssephe	note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
1158302864Ssephe	if (note->chm_chanid > VMBUS_CHAN_MAX) {
1159302864Ssephe		device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
1160302864Ssephe		    note->chm_chanid);
1161302864Ssephe		return;
1162302864Ssephe	}
1163302864Ssephe
1164302864Ssephe	if (bootverbose) {
1165302864Ssephe		device_printf(sc->vmbus_dev, "chan%u rescinded\n",
1166302864Ssephe		    note->chm_chanid);
1167302864Ssephe	}
1168302864Ssephe
1169302864Ssephe	chan = sc->vmbus_chmap[note->chm_chanid];
1170302864Ssephe	if (chan == NULL)
1171302864Ssephe		return;
1172302864Ssephe	sc->vmbus_chmap[note->chm_chanid] = NULL;
1173302864Ssephe
1174302864Ssephe	taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
1175302864Ssephe}
1176302864Ssephe
1177302864Ssephestatic void
1178302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused)
1179302864Ssephe{
1180302864Ssephe	struct hv_vmbus_channel *chan = xchan;
1181302864Ssephe
1182302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1183302864Ssephe		/* Only primary channel owns the device */
1184302868Ssephe		vmbus_delete_child(chan);
1185302864Ssephe		/* NOTE: DO NOT free primary channel for now */
1186302864Ssephe	} else {
1187302864Ssephe		struct vmbus_softc *sc = chan->vmbus_sc;
1188302864Ssephe		struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
1189302864Ssephe		struct vmbus_chanmsg_chfree *req;
1190302864Ssephe		struct vmbus_msghc *mh;
1191302864Ssephe		int error;
1192302864Ssephe
1193302864Ssephe		mh = vmbus_msghc_get(sc, sizeof(*req));
1194302864Ssephe		if (mh == NULL) {
1195302864Ssephe			device_printf(sc->vmbus_dev,
1196302864Ssephe			    "can not get msg hypercall for chfree(chan%u)\n",
1197302864Ssephe			    chan->ch_id);
1198302864Ssephe			goto remove;
1199302864Ssephe		}
1200302864Ssephe
1201302864Ssephe		req = vmbus_msghc_dataptr(mh);
1202302864Ssephe		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
1203302864Ssephe		req->chm_chanid = chan->ch_id;
1204302864Ssephe
1205302864Ssephe		error = vmbus_msghc_exec_noresult(mh);
1206302864Ssephe		vmbus_msghc_put(sc, mh);
1207302864Ssephe
1208302864Ssephe		if (error) {
1209302864Ssephe			device_printf(sc->vmbus_dev,
1210302864Ssephe			    "chfree(chan%u) failed: %d",
1211302864Ssephe			    chan->ch_id, error);
1212302864Ssephe			/* NOTE: Move on! */
1213302864Ssephe		} else {
1214302864Ssephe			if (bootverbose) {
1215302864Ssephe				device_printf(sc->vmbus_dev, "chan%u freed\n",
1216302864Ssephe				    chan->ch_id);
1217302864Ssephe			}
1218302864Ssephe		}
1219302864Ssepheremove:
1220302864Ssephe		mtx_lock(&pri_chan->ch_subchan_lock);
1221302864Ssephe		TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
1222302864Ssephe		KASSERT(pri_chan->ch_subchan_cnt > 0,
1223302864Ssephe		    ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
1224302864Ssephe		pri_chan->ch_subchan_cnt--;
1225302864Ssephe		mtx_unlock(&pri_chan->ch_subchan_lock);
1226302864Ssephe		wakeup(pri_chan);
1227302864Ssephe
1228302864Ssephe		vmbus_chan_free(chan);
1229302864Ssephe	}
1230302864Ssephe}
1231302864Ssephe
1232302864Ssephe/*
1233302864Ssephe * Detach all devices and destroy the corresponding primary channels.
1234302864Ssephe */
1235302864Ssephevoid
1236302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc)
1237302864Ssephe{
1238302864Ssephe	struct hv_vmbus_channel *chan;
1239302864Ssephe
1240302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1241302864Ssephe	while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
1242302864Ssephe		KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
1243302864Ssephe		TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
1244302864Ssephe		mtx_unlock(&sc->vmbus_prichan_lock);
1245302864Ssephe
1246302868Ssephe		vmbus_delete_child(chan);
1247302864Ssephe		vmbus_chan_free(chan);
1248302864Ssephe
1249302864Ssephe		mtx_lock(&sc->vmbus_prichan_lock);
1250302864Ssephe	}
1251302864Ssephe	bzero(sc->vmbus_chmap,
1252302864Ssephe	    sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
1253302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1254302864Ssephe}
1255302864Ssephe
1256302864Ssephe/**
1257302864Ssephe * @brief Select the best outgoing channel
1258302864Ssephe *
1259302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will
1260302864Ssephe * be selected.
1261302864Ssephe * If no multi-channel, always select primary channel
1262302864Ssephe *
1263302864Ssephe * @param primary - primary channel
1264302864Ssephe */
1265302864Ssephestruct hv_vmbus_channel *
1266302864Ssephevmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
1267302864Ssephe{
1268302864Ssephe	hv_vmbus_channel *new_channel = NULL;
1269302864Ssephe	hv_vmbus_channel *outgoing_channel = primary;
1270302864Ssephe	int old_cpu_distance = 0;
1271302864Ssephe	int new_cpu_distance = 0;
1272302864Ssephe	int cur_vcpu = 0;
1273302864Ssephe	int smp_pro_id = PCPU_GET(cpuid);
1274302864Ssephe
1275302864Ssephe	if (TAILQ_EMPTY(&primary->ch_subchans)) {
1276302864Ssephe		return outgoing_channel;
1277302864Ssephe	}
1278302864Ssephe
1279302864Ssephe	if (smp_pro_id >= MAXCPU) {
1280302864Ssephe		return outgoing_channel;
1281302864Ssephe	}
1282302864Ssephe
1283302864Ssephe	cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
1284302864Ssephe
1285302864Ssephe	/* XXX need lock */
1286302864Ssephe	TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
1287302864Ssephe		if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
1288302864Ssephe			continue;
1289302864Ssephe		}
1290302864Ssephe
1291302873Ssephe		if (new_channel->ch_vcpuid == cur_vcpu){
1292302864Ssephe			return new_channel;
1293302864Ssephe		}
1294302864Ssephe
1295302873Ssephe		old_cpu_distance = ((outgoing_channel->ch_vcpuid > cur_vcpu) ?
1296302873Ssephe		    (outgoing_channel->ch_vcpuid - cur_vcpu) :
1297302873Ssephe		    (cur_vcpu - outgoing_channel->ch_vcpuid));
1298302864Ssephe
1299302873Ssephe		new_cpu_distance = ((new_channel->ch_vcpuid > cur_vcpu) ?
1300302873Ssephe		    (new_channel->ch_vcpuid - cur_vcpu) :
1301302873Ssephe		    (cur_vcpu - new_channel->ch_vcpuid));
1302302864Ssephe
1303302864Ssephe		if (old_cpu_distance < new_cpu_distance) {
1304302864Ssephe			continue;
1305302864Ssephe		}
1306302864Ssephe
1307302864Ssephe		outgoing_channel = new_channel;
1308302864Ssephe	}
1309302864Ssephe
1310302864Ssephe	return(outgoing_channel);
1311302864Ssephe}
1312302864Ssephe
1313302864Ssephestruct hv_vmbus_channel **
1314302890Ssephevmbus_subchan_get(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
1315302864Ssephe{
1316302864Ssephe	struct hv_vmbus_channel **ret, *chan;
1317302864Ssephe	int i;
1318302864Ssephe
1319302864Ssephe	ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
1320302864Ssephe	    M_WAITOK);
1321302864Ssephe
1322302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1323302864Ssephe
1324302864Ssephe	while (pri_chan->ch_subchan_cnt < subchan_cnt)
1325302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
1326302864Ssephe
1327302864Ssephe	i = 0;
1328302864Ssephe	TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
1329302864Ssephe		/* TODO: refcnt chan */
1330302864Ssephe		ret[i] = chan;
1331302864Ssephe
1332302864Ssephe		++i;
1333302864Ssephe		if (i == subchan_cnt)
1334302864Ssephe			break;
1335302864Ssephe	}
1336302864Ssephe	KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
1337302864Ssephe	    pri_chan->ch_subchan_cnt, subchan_cnt));
1338302864Ssephe
1339302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1340302864Ssephe
1341302864Ssephe	return ret;
1342302864Ssephe}
1343302864Ssephe
1344302864Ssephevoid
1345302890Ssephevmbus_subchan_rel(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
1346302864Ssephe{
1347302864Ssephe
1348302864Ssephe	free(subchan, M_TEMP);
1349302864Ssephe}
1350302864Ssephe
1351302864Ssephevoid
1352302890Ssephevmbus_subchan_drain(struct hv_vmbus_channel *pri_chan)
1353302864Ssephe{
1354302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1355302864Ssephe	while (pri_chan->ch_subchan_cnt > 0)
1356302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
1357302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1358302864Ssephe}
1359302864Ssephe
1360302864Ssephevoid
1361302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1362302864Ssephe{
1363302864Ssephe	vmbus_chanmsg_proc_t msg_proc;
1364302864Ssephe	uint32_t msg_type;
1365302864Ssephe
1366302864Ssephe	msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
1367302864Ssephe	KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
1368302864Ssephe	    ("invalid message type %u", msg_type));
1369302864Ssephe
1370302864Ssephe	msg_proc = vmbus_chan_msgprocs[msg_type];
1371302864Ssephe	if (msg_proc != NULL)
1372302864Ssephe		msg_proc(sc, msg);
1373302864Ssephe}
1374