vmbus_chan.c revision 302868
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 302868 2016-07-15 05:06:15Z 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
48300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
49302619Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
50301588Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h>
51300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
52250199Sgrehan
53302731Ssephestatic void 	vmbus_chan_send_event(hv_vmbus_channel* channel);
54302692Ssephestatic void	vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
55302692Ssephe		    const struct hv_vmbus_channel *);
56302864Ssephe
57302713Ssephestatic void	vmbus_chan_task(void *, int);
58302713Ssephestatic void	vmbus_chan_task_nobatch(void *, int);
59302864Ssephestatic void	vmbus_chan_detach_task(void *, int);
60250199Sgrehan
61302864Ssephestatic void	vmbus_chan_msgproc_choffer(struct vmbus_softc *,
62302864Ssephe		    const struct vmbus_message *);
63302864Ssephestatic void	vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
64302864Ssephe		    const struct vmbus_message *);
65302864Ssephe
66302864Ssephe/*
67302864Ssephe * Vmbus channel message processing.
68302864Ssephe */
69302864Ssephestatic const vmbus_chanmsg_proc_t
70302864Ssephevmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
71302864Ssephe	VMBUS_CHANMSG_PROC(CHOFFER,	vmbus_chan_msgproc_choffer),
72302864Ssephe	VMBUS_CHANMSG_PROC(CHRESCIND,	vmbus_chan_msgproc_chrescind),
73302864Ssephe
74302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
75302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
76302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
77302864Ssephe};
78302864Ssephe
79250199Sgrehan/**
80250199Sgrehan *  @brief Trigger an event notification on the specified channel
81250199Sgrehan */
82250199Sgrehanstatic void
83302731Ssephevmbus_chan_send_event(hv_vmbus_channel *channel)
84250199Sgrehan{
85302618Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
86302693Ssephe	uint32_t chanid = channel->ch_id;
87302618Ssephe
88302618Ssephe	atomic_set_long(&sc->vmbus_tx_evtflags[chanid >> VMBUS_EVTFLAG_SHIFT],
89302618Ssephe	    1UL << (chanid & VMBUS_EVTFLAG_MASK));
90302618Ssephe
91302695Ssephe	if (channel->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
92302731Ssephe		atomic_set_int(
93302731Ssephe		&sc->vmbus_mnf2->mnf_trigs[channel->ch_montrig_idx].mt_pending,
94302731Ssephe		channel->ch_montrig_mask);
95250199Sgrehan	} else {
96302726Ssephe		hypercall_signal_event(channel->ch_monprm_dma.hv_paddr);
97250199Sgrehan	}
98250199Sgrehan}
99250199Sgrehan
100296289Ssephestatic int
101296289Ssephevmbus_channel_sysctl_monalloc(SYSCTL_HANDLER_ARGS)
102296289Ssephe{
103296289Ssephe	struct hv_vmbus_channel *chan = arg1;
104296289Ssephe	int alloc = 0;
105296289Ssephe
106302695Ssephe	if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF)
107296289Ssephe		alloc = 1;
108296289Ssephe	return sysctl_handle_int(oidp, &alloc, 0, req);
109296289Ssephe}
110296289Ssephe
111296181Ssephestatic void
112296290Ssephevmbus_channel_sysctl_create(hv_vmbus_channel* channel)
113296181Ssephe{
114296181Ssephe	device_t dev;
115296181Ssephe	struct sysctl_oid *devch_sysctl;
116296181Ssephe	struct sysctl_oid *devch_id_sysctl, *devch_sub_sysctl;
117296181Ssephe	struct sysctl_oid *devch_id_in_sysctl, *devch_id_out_sysctl;
118296181Ssephe	struct sysctl_ctx_list *ctx;
119296181Ssephe	uint32_t ch_id;
120296181Ssephe	uint16_t sub_ch_id;
121296181Ssephe	char name[16];
122296181Ssephe
123302819Ssephe	hv_vmbus_channel* primary_ch = channel->ch_prichan;
124296181Ssephe
125296181Ssephe	if (primary_ch == NULL) {
126302706Ssephe		dev = channel->ch_dev;
127302693Ssephe		ch_id = channel->ch_id;
128296181Ssephe	} else {
129302706Ssephe		dev = primary_ch->ch_dev;
130302693Ssephe		ch_id = primary_ch->ch_id;
131302694Ssephe		sub_ch_id = channel->ch_subidx;
132296181Ssephe	}
133302633Ssephe	ctx = &channel->ch_sysctl_ctx;
134302633Ssephe	sysctl_ctx_init(ctx);
135296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel tree */
136296181Ssephe	devch_sysctl = SYSCTL_ADD_NODE(ctx,
137296181Ssephe		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
138298693Ssephe		    OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
139296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel.CHANID tree */
140296181Ssephe	snprintf(name, sizeof(name), "%d", ch_id);
141296181Ssephe	devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
142296181Ssephe	    	    SYSCTL_CHILDREN(devch_sysctl),
143298693Ssephe	    	    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
144296181Ssephe
145296181Ssephe	if (primary_ch != NULL) {
146296181Ssephe		devch_sub_sysctl = SYSCTL_ADD_NODE(ctx,
147296181Ssephe			SYSCTL_CHILDREN(devch_id_sysctl),
148298693Ssephe			OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
149296181Ssephe		snprintf(name, sizeof(name), "%d", sub_ch_id);
150296181Ssephe		devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
151296181Ssephe			SYSCTL_CHILDREN(devch_sub_sysctl),
152298693Ssephe			OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
153296188Ssephe
154296188Ssephe		SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl),
155296188Ssephe		    OID_AUTO, "chanid", CTLFLAG_RD,
156302693Ssephe		    &channel->ch_id, 0, "channel id");
157296181Ssephe	}
158296188Ssephe	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
159296188Ssephe	    "cpu", CTLFLAG_RD, &channel->target_cpu, 0, "owner CPU id");
160296289Ssephe	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
161298693Ssephe	    "monitor_allocated", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
162298693Ssephe	    channel, 0, vmbus_channel_sysctl_monalloc, "I",
163296289Ssephe	    "is monitor allocated to this channel");
164296188Ssephe
165296181Ssephe	devch_id_in_sysctl = SYSCTL_ADD_NODE(ctx,
166296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
167296181Ssephe                    OID_AUTO,
168296181Ssephe		    "in",
169298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
170296181Ssephe	devch_id_out_sysctl = SYSCTL_ADD_NODE(ctx,
171296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
172296181Ssephe                    OID_AUTO,
173296181Ssephe		    "out",
174298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
175296181Ssephe	hv_ring_buffer_stat(ctx,
176296181Ssephe		SYSCTL_CHILDREN(devch_id_in_sysctl),
177296181Ssephe		&(channel->inbound),
178296181Ssephe		"inbound ring buffer stats");
179296181Ssephe	hv_ring_buffer_stat(ctx,
180296181Ssephe		SYSCTL_CHILDREN(devch_id_out_sysctl),
181296181Ssephe		&(channel->outbound),
182296181Ssephe		"outbound ring buffer stats");
183296181Ssephe}
184296290Ssephe
185250199Sgrehan/**
186250199Sgrehan * @brief Open the specified channel
187250199Sgrehan */
188250199Sgrehanint
189250199Sgrehanhv_vmbus_channel_open(
190250199Sgrehan	hv_vmbus_channel*		new_channel,
191250199Sgrehan	uint32_t			send_ring_buffer_size,
192250199Sgrehan	uint32_t			recv_ring_buffer_size,
193250199Sgrehan	void*				user_data,
194250199Sgrehan	uint32_t			user_data_len,
195250199Sgrehan	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
196250199Sgrehan	void* 				context)
197250199Sgrehan{
198302607Ssephe	struct vmbus_softc *sc = new_channel->vmbus_sc;
199302607Ssephe	const struct vmbus_chanmsg_chopen_resp *resp;
200302607Ssephe	const struct vmbus_message *msg;
201302607Ssephe	struct vmbus_chanmsg_chopen *req;
202302607Ssephe	struct vmbus_msghc *mh;
203302607Ssephe	uint32_t status;
204250199Sgrehan	int ret = 0;
205250199Sgrehan	void *in, *out;
206250199Sgrehan
207302607Ssephe	if (user_data_len > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
208302607Ssephe		device_printf(sc->vmbus_dev,
209302607Ssephe		    "invalid udata len %u for chan%u\n",
210302693Ssephe		    user_data_len, new_channel->ch_id);
211302607Ssephe		return EINVAL;
212302607Ssephe	}
213302607Ssephe
214302812Ssephe	if (atomic_testandset_int(&new_channel->ch_stflags,
215302812Ssephe	    VMBUS_CHAN_ST_OPENED_SHIFT))
216302812Ssephe		panic("double-open chan%u", new_channel->ch_id);
217282212Swhu
218250199Sgrehan	new_channel->on_channel_callback = pfn_on_channel_callback;
219250199Sgrehan	new_channel->channel_callback_context = context;
220250199Sgrehan
221302692Ssephe	vmbus_chan_update_evtflagcnt(sc, new_channel);
222300102Ssephe
223302557Ssephe	new_channel->rxq = VMBUS_PCPU_GET(new_channel->vmbus_sc, event_tq,
224300646Ssephe	    new_channel->target_cpu);
225302713Ssephe	if (new_channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) {
226302713Ssephe		TASK_INIT(&new_channel->channel_task, 0,
227302713Ssephe		    vmbus_chan_task, new_channel);
228302713Ssephe	} else {
229302713Ssephe		TASK_INIT(&new_channel->channel_task, 0,
230302713Ssephe		    vmbus_chan_task_nobatch, new_channel);
231302713Ssephe	}
232294886Ssephe
233250199Sgrehan	/* Allocate the ring buffer */
234250199Sgrehan	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
235256350Sgrehan	    M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
236250199Sgrehan	KASSERT(out != NULL,
237250199Sgrehan	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
238302812Ssephe	if (out == NULL) {
239302812Ssephe		ret = ENOMEM;
240302812Ssephe		goto failed;
241302812Ssephe	}
242250199Sgrehan
243250199Sgrehan	in = ((uint8_t *) out + send_ring_buffer_size);
244250199Sgrehan
245250199Sgrehan	new_channel->ring_buffer_pages = out;
246256350Sgrehan	new_channel->ring_buffer_page_count = (send_ring_buffer_size +
247256350Sgrehan	    recv_ring_buffer_size) >> PAGE_SHIFT;
248256350Sgrehan	new_channel->ring_buffer_size = send_ring_buffer_size +
249256350Sgrehan	    recv_ring_buffer_size;
250250199Sgrehan
251250199Sgrehan	hv_vmbus_ring_buffer_init(
252250199Sgrehan		&new_channel->outbound,
253250199Sgrehan		out,
254250199Sgrehan		send_ring_buffer_size);
255250199Sgrehan
256250199Sgrehan	hv_vmbus_ring_buffer_init(
257250199Sgrehan		&new_channel->inbound,
258250199Sgrehan		in,
259250199Sgrehan		recv_ring_buffer_size);
260250199Sgrehan
261296290Ssephe	/* Create sysctl tree for this channel */
262296290Ssephe	vmbus_channel_sysctl_create(new_channel);
263296181Ssephe
264250199Sgrehan	/**
265250199Sgrehan	 * Establish the gpadl for the ring buffer
266250199Sgrehan	 */
267250199Sgrehan	new_channel->ring_buffer_gpadl_handle = 0;
268250199Sgrehan
269250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(new_channel,
270250199Sgrehan		new_channel->outbound.ring_buffer,
271250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
272250199Sgrehan		&new_channel->ring_buffer_gpadl_handle);
273250199Sgrehan
274302607Ssephe	/*
275302607Ssephe	 * Open channel w/ the bufring GPADL on the target CPU.
276250199Sgrehan	 */
277302607Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
278302607Ssephe	if (mh == NULL) {
279302607Ssephe		device_printf(sc->vmbus_dev,
280302607Ssephe		    "can not get msg hypercall for chopen(chan%u)\n",
281302693Ssephe		    new_channel->ch_id);
282302812Ssephe		ret = ENXIO;
283302812Ssephe		goto failed;
284302607Ssephe	}
285250199Sgrehan
286302607Ssephe	req = vmbus_msghc_dataptr(mh);
287302607Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN;
288302693Ssephe	req->chm_chanid = new_channel->ch_id;
289302693Ssephe	req->chm_openid = new_channel->ch_id;
290302607Ssephe	req->chm_gpadl = new_channel->ring_buffer_gpadl_handle;
291302607Ssephe	req->chm_vcpuid = new_channel->target_vcpu;
292302607Ssephe	req->chm_rxbr_pgofs = send_ring_buffer_size >> PAGE_SHIFT;
293250199Sgrehan	if (user_data_len)
294302607Ssephe		memcpy(req->chm_udata, user_data, user_data_len);
295250199Sgrehan
296302607Ssephe	ret = vmbus_msghc_exec(sc, mh);
297302607Ssephe	if (ret != 0) {
298302607Ssephe		device_printf(sc->vmbus_dev,
299302607Ssephe		    "chopen(chan%u) msg hypercall exec failed: %d\n",
300302693Ssephe		    new_channel->ch_id, ret);
301302607Ssephe		vmbus_msghc_put(sc, mh);
302302812Ssephe		goto failed;
303302607Ssephe	}
304250199Sgrehan
305302607Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
306302607Ssephe	resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data;
307302607Ssephe	status = resp->chm_status;
308250199Sgrehan
309302607Ssephe	vmbus_msghc_put(sc, mh);
310250199Sgrehan
311302607Ssephe	if (status == 0) {
312302607Ssephe		if (bootverbose) {
313302607Ssephe			device_printf(sc->vmbus_dev, "chan%u opened\n",
314302693Ssephe			    new_channel->ch_id);
315302607Ssephe		}
316302812Ssephe		return 0;
317250199Sgrehan	}
318302812Ssephe
319302812Ssephe	device_printf(sc->vmbus_dev, "failed to open chan%u\n",
320302812Ssephe	    new_channel->ch_id);
321302812Ssephe	ret = ENXIO;
322302812Ssephe
323302812Ssephefailed:
324302812Ssephe	atomic_clear_int(&new_channel->ch_stflags, VMBUS_CHAN_ST_OPENED);
325302812Ssephe	return ret;
326250199Sgrehan}
327250199Sgrehan
328250199Sgrehan/**
329302609Ssephe * @brief Establish a GPADL for the specified buffer
330250199Sgrehan */
331302609Ssepheint
332302609Ssephehv_vmbus_channel_establish_gpadl(struct hv_vmbus_channel *channel,
333302609Ssephe    void *contig_buffer, uint32_t size, uint32_t *gpadl0)
334250199Sgrehan{
335302609Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
336302609Ssephe	struct vmbus_msghc *mh;
337302609Ssephe	struct vmbus_chanmsg_gpadl_conn *req;
338302609Ssephe	const struct vmbus_message *msg;
339302609Ssephe	size_t reqsz;
340302609Ssephe	uint32_t gpadl, status;
341302609Ssephe	int page_count, range_len, i, cnt, error;
342302609Ssephe	uint64_t page_id, paddr;
343250199Sgrehan
344302609Ssephe	/*
345302609Ssephe	 * Preliminary checks.
346302609Ssephe	 */
347250199Sgrehan
348302609Ssephe	KASSERT((size & PAGE_MASK) == 0,
349302609Ssephe	    ("invalid GPA size %u, not multiple page size", size));
350250199Sgrehan	page_count = size >> PAGE_SHIFT;
351250199Sgrehan
352302609Ssephe	paddr = hv_get_phys_addr(contig_buffer);
353302609Ssephe	KASSERT((paddr & PAGE_MASK) == 0,
354302609Ssephe	    ("GPA is not page aligned %jx", (uintmax_t)paddr));
355302609Ssephe	page_id = paddr >> PAGE_SHIFT;
356250199Sgrehan
357302609Ssephe	range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
358302609Ssephe	/*
359302609Ssephe	 * We don't support multiple GPA ranges.
360302609Ssephe	 */
361302609Ssephe	if (range_len > UINT16_MAX) {
362302609Ssephe		device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
363302609Ssephe		    page_count);
364302609Ssephe		return EOPNOTSUPP;
365250199Sgrehan	}
366250199Sgrehan
367302609Ssephe	/*
368302609Ssephe	 * Allocate GPADL id.
369302609Ssephe	 */
370302630Ssephe	gpadl = vmbus_gpadl_alloc(sc);
371302609Ssephe	*gpadl0 = gpadl;
372250199Sgrehan
373302609Ssephe	/*
374302609Ssephe	 * Connect this GPADL to the target channel.
375302609Ssephe	 *
376302609Ssephe	 * NOTE:
377302609Ssephe	 * Since each message can only hold small set of page
378302609Ssephe	 * addresses, several messages may be required to
379302609Ssephe	 * complete the connection.
380302609Ssephe	 */
381302609Ssephe	if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
382302609Ssephe		cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
383302609Ssephe	else
384302609Ssephe		cnt = page_count;
385302609Ssephe	page_count -= cnt;
386250199Sgrehan
387302609Ssephe	reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
388302609Ssephe	    chm_range.gpa_page[cnt]);
389302609Ssephe	mh = vmbus_msghc_get(sc, reqsz);
390302609Ssephe	if (mh == NULL) {
391302609Ssephe		device_printf(sc->vmbus_dev,
392302609Ssephe		    "can not get msg hypercall for gpadl->chan%u\n",
393302693Ssephe		    channel->ch_id);
394302609Ssephe		return EIO;
395250199Sgrehan	}
396250199Sgrehan
397302609Ssephe	req = vmbus_msghc_dataptr(mh);
398302609Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
399302693Ssephe	req->chm_chanid = channel->ch_id;
400302609Ssephe	req->chm_gpadl = gpadl;
401302609Ssephe	req->chm_range_len = range_len;
402302609Ssephe	req->chm_range_cnt = 1;
403302609Ssephe	req->chm_range.gpa_len = size;
404302609Ssephe	req->chm_range.gpa_ofs = 0;
405302609Ssephe	for (i = 0; i < cnt; ++i)
406302609Ssephe		req->chm_range.gpa_page[i] = page_id++;
407250199Sgrehan
408302609Ssephe	error = vmbus_msghc_exec(sc, mh);
409302609Ssephe	if (error) {
410302609Ssephe		device_printf(sc->vmbus_dev,
411302609Ssephe		    "gpadl->chan%u msg hypercall exec failed: %d\n",
412302693Ssephe		    channel->ch_id, error);
413302609Ssephe		vmbus_msghc_put(sc, mh);
414302609Ssephe		return error;
415302609Ssephe	}
416250199Sgrehan
417302609Ssephe	while (page_count > 0) {
418302609Ssephe		struct vmbus_chanmsg_gpadl_subconn *subreq;
419250199Sgrehan
420302609Ssephe		if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
421302609Ssephe			cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
422302609Ssephe		else
423302609Ssephe			cnt = page_count;
424302609Ssephe		page_count -= cnt;
425250199Sgrehan
426302609Ssephe		reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
427302609Ssephe		    chm_gpa_page[cnt]);
428302609Ssephe		vmbus_msghc_reset(mh, reqsz);
429250199Sgrehan
430302609Ssephe		subreq = vmbus_msghc_dataptr(mh);
431302609Ssephe		subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
432302609Ssephe		subreq->chm_gpadl = gpadl;
433302609Ssephe		for (i = 0; i < cnt; ++i)
434302609Ssephe			subreq->chm_gpa_page[i] = page_id++;
435250199Sgrehan
436302609Ssephe		vmbus_msghc_exec_noresult(mh);
437250199Sgrehan	}
438302609Ssephe	KASSERT(page_count == 0, ("invalid page count %d", page_count));
439250199Sgrehan
440302609Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
441302609Ssephe	status = ((const struct vmbus_chanmsg_gpadl_connresp *)
442302609Ssephe	    msg->msg_data)->chm_status;
443250199Sgrehan
444302609Ssephe	vmbus_msghc_put(sc, mh);
445250199Sgrehan
446302609Ssephe	if (status != 0) {
447302609Ssephe		device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
448302693Ssephe		    "status %u\n", channel->ch_id, status);
449302609Ssephe		return EIO;
450302632Ssephe	} else {
451302632Ssephe		if (bootverbose) {
452302632Ssephe			device_printf(sc->vmbus_dev, "gpadl->chan%u "
453302693Ssephe			    "succeeded\n", channel->ch_id);
454302632Ssephe		}
455302609Ssephe	}
456302609Ssephe	return 0;
457250199Sgrehan}
458250199Sgrehan
459302611Ssephe/*
460302611Ssephe * Disconnect the GPA from the target channel
461250199Sgrehan */
462250199Sgrehanint
463302611Ssephehv_vmbus_channel_teardown_gpdal(struct hv_vmbus_channel *chan, uint32_t gpadl)
464250199Sgrehan{
465302611Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
466302611Ssephe	struct vmbus_msghc *mh;
467302611Ssephe	struct vmbus_chanmsg_gpadl_disconn *req;
468302611Ssephe	int error;
469250199Sgrehan
470302611Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
471302611Ssephe	if (mh == NULL) {
472302611Ssephe		device_printf(sc->vmbus_dev,
473302611Ssephe		    "can not get msg hypercall for gpa x->chan%u\n",
474302693Ssephe		    chan->ch_id);
475302611Ssephe		return EBUSY;
476250199Sgrehan	}
477250199Sgrehan
478302611Ssephe	req = vmbus_msghc_dataptr(mh);
479302611Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
480302693Ssephe	req->chm_chanid = chan->ch_id;
481302611Ssephe	req->chm_gpadl = gpadl;
482250199Sgrehan
483302611Ssephe	error = vmbus_msghc_exec(sc, mh);
484302611Ssephe	if (error) {
485302611Ssephe		device_printf(sc->vmbus_dev,
486302611Ssephe		    "gpa x->chan%u msg hypercall exec failed: %d\n",
487302693Ssephe		    chan->ch_id, error);
488302611Ssephe		vmbus_msghc_put(sc, mh);
489302611Ssephe		return error;
490302611Ssephe	}
491250199Sgrehan
492302611Ssephe	vmbus_msghc_wait_result(sc, mh);
493302611Ssephe	/* Discard result; no useful information */
494302611Ssephe	vmbus_msghc_put(sc, mh);
495250199Sgrehan
496302611Ssephe	return 0;
497250199Sgrehan}
498250199Sgrehan
499282212Swhustatic void
500282212Swhuhv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
501250199Sgrehan{
502302610Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
503302610Ssephe	struct vmbus_msghc *mh;
504302610Ssephe	struct vmbus_chanmsg_chclose *req;
505294886Ssephe	struct taskqueue *rxq = channel->rxq;
506302610Ssephe	int error;
507250199Sgrehan
508302812Ssephe	/* TODO: stringent check */
509302812Ssephe	atomic_clear_int(&channel->ch_stflags, VMBUS_CHAN_ST_OPENED);
510302812Ssephe
511302633Ssephe	sysctl_ctx_free(&channel->ch_sysctl_ctx);
512282212Swhu
513282212Swhu	/*
514294886Ssephe	 * set rxq to NULL to avoid more requests be scheduled
515294886Ssephe	 */
516294886Ssephe	channel->rxq = NULL;
517294886Ssephe	taskqueue_drain(rxq, &channel->channel_task);
518250199Sgrehan	channel->on_channel_callback = NULL;
519250199Sgrehan
520250199Sgrehan	/**
521250199Sgrehan	 * Send a closing message
522250199Sgrehan	 */
523250199Sgrehan
524302610Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
525302610Ssephe	if (mh == NULL) {
526302610Ssephe		device_printf(sc->vmbus_dev,
527302610Ssephe		    "can not get msg hypercall for chclose(chan%u)\n",
528302693Ssephe		    channel->ch_id);
529302610Ssephe		return;
530302610Ssephe	}
531250199Sgrehan
532302610Ssephe	req = vmbus_msghc_dataptr(mh);
533302610Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
534302693Ssephe	req->chm_chanid = channel->ch_id;
535250199Sgrehan
536302610Ssephe	error = vmbus_msghc_exec_noresult(mh);
537302610Ssephe	vmbus_msghc_put(sc, mh);
538302610Ssephe
539302610Ssephe	if (error) {
540302610Ssephe		device_printf(sc->vmbus_dev,
541302610Ssephe		    "chclose(chan%u) msg hypercall exec failed: %d\n",
542302693Ssephe		    channel->ch_id, error);
543302610Ssephe		return;
544302610Ssephe	} else if (bootverbose) {
545302610Ssephe		device_printf(sc->vmbus_dev, "close chan%u\n",
546302693Ssephe		    channel->ch_id);
547302610Ssephe	}
548302610Ssephe
549250199Sgrehan	/* Tear down the gpadl for the channel's ring buffer */
550250199Sgrehan	if (channel->ring_buffer_gpadl_handle) {
551250199Sgrehan		hv_vmbus_channel_teardown_gpdal(channel,
552250199Sgrehan			channel->ring_buffer_gpadl_handle);
553250199Sgrehan	}
554250199Sgrehan
555250199Sgrehan	/* TODO: Send a msg to release the childRelId */
556250199Sgrehan
557250199Sgrehan	/* cleanup the ring buffers for this channel */
558250199Sgrehan	hv_ring_buffer_cleanup(&channel->outbound);
559250199Sgrehan	hv_ring_buffer_cleanup(&channel->inbound);
560250199Sgrehan
561256350Sgrehan	contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
562256350Sgrehan	    M_DEVBUF);
563282212Swhu}
564250199Sgrehan
565302818Ssephe/*
566302818Ssephe * Caller should make sure that all sub-channels have
567302818Ssephe * been added to 'chan' and all to-be-closed channels
568302818Ssephe * are not being opened.
569282212Swhu */
570282212Swhuvoid
571302818Ssephehv_vmbus_channel_close(struct hv_vmbus_channel *chan)
572282212Swhu{
573302818Ssephe	int subchan_cnt;
574282212Swhu
575302818Ssephe	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
576282212Swhu		/*
577302818Ssephe		 * Sub-channel is closed when its primary channel
578302818Ssephe		 * is closed; done.
579282212Swhu		 */
580282212Swhu		return;
581282212Swhu	}
582282212Swhu
583250199Sgrehan	/*
584302818Ssephe	 * Close all sub-channels, if any.
585250199Sgrehan	 */
586302819Ssephe	subchan_cnt = chan->ch_subchan_cnt;
587302818Ssephe	if (subchan_cnt > 0) {
588302818Ssephe		struct hv_vmbus_channel **subchan;
589302818Ssephe		int i;
590302818Ssephe
591302818Ssephe		subchan = vmbus_get_subchan(chan, subchan_cnt);
592302818Ssephe		for (i = 0; i < subchan_cnt; ++i)
593302818Ssephe			hv_vmbus_channel_close_internal(subchan[i]);
594302818Ssephe		vmbus_rel_subchan(subchan, subchan_cnt);
595250199Sgrehan	}
596302818Ssephe
597302818Ssephe	/* Then close the primary channel. */
598302818Ssephe	hv_vmbus_channel_close_internal(chan);
599250199Sgrehan}
600250199Sgrehan
601250199Sgrehan/**
602250199Sgrehan * @brief Send the specified buffer on the given channel
603250199Sgrehan */
604250199Sgrehanint
605250199Sgrehanhv_vmbus_channel_send_packet(
606250199Sgrehan	hv_vmbus_channel*	channel,
607250199Sgrehan	void*			buffer,
608250199Sgrehan	uint32_t		buffer_len,
609250199Sgrehan	uint64_t		request_id,
610250199Sgrehan	hv_vmbus_packet_type	type,
611250199Sgrehan	uint32_t		flags)
612250199Sgrehan{
613250199Sgrehan	int			ret = 0;
614250199Sgrehan	hv_vm_packet_descriptor	desc;
615250199Sgrehan	uint32_t		packet_len;
616250199Sgrehan	uint64_t		aligned_data;
617250199Sgrehan	uint32_t		packet_len_aligned;
618282212Swhu	boolean_t		need_sig;
619250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
620250199Sgrehan
621250199Sgrehan	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
622250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
623250199Sgrehan	aligned_data = 0;
624250199Sgrehan
625250199Sgrehan	/* Setup the descriptor */
626250199Sgrehan	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
627250199Sgrehan	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
628250199Sgrehan			    /* in 8-bytes granularity */
629250199Sgrehan	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
630250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
631250199Sgrehan	desc.transaction_id = request_id;
632250199Sgrehan
633250199Sgrehan	buffer_list[0].data = &desc;
634250199Sgrehan	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
635250199Sgrehan
636250199Sgrehan	buffer_list[1].data = buffer;
637250199Sgrehan	buffer_list[1].length = buffer_len;
638250199Sgrehan
639250199Sgrehan	buffer_list[2].data = &aligned_data;
640250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
641250199Sgrehan
642282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
643282212Swhu	    &need_sig);
644250199Sgrehan
645250199Sgrehan	/* TODO: We should determine if this is optional */
646302731Ssephe	if (ret == 0 && need_sig)
647302731Ssephe		vmbus_chan_send_event(channel);
648250199Sgrehan
649250199Sgrehan	return (ret);
650250199Sgrehan}
651250199Sgrehan
652250199Sgrehan/**
653250199Sgrehan * @brief Send a range of single-page buffer packets using
654250199Sgrehan * a GPADL Direct packet type
655250199Sgrehan */
656250199Sgrehanint
657250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer(
658250199Sgrehan	hv_vmbus_channel*	channel,
659250199Sgrehan	hv_vmbus_page_buffer	page_buffers[],
660250199Sgrehan	uint32_t		page_count,
661250199Sgrehan	void*			buffer,
662250199Sgrehan	uint32_t		buffer_len,
663250199Sgrehan	uint64_t		request_id)
664250199Sgrehan{
665250199Sgrehan
666250199Sgrehan	int					ret = 0;
667282212Swhu	boolean_t				need_sig;
668250199Sgrehan	uint32_t				packet_len;
669294705Ssephe	uint32_t				page_buflen;
670250199Sgrehan	uint32_t				packetLen_aligned;
671294705Ssephe	hv_vmbus_sg_buffer_list			buffer_list[4];
672250199Sgrehan	hv_vmbus_channel_packet_page_buffer	desc;
673250199Sgrehan	uint32_t				descSize;
674250199Sgrehan	uint64_t				alignedData = 0;
675250199Sgrehan
676250199Sgrehan	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
677250199Sgrehan		return (EINVAL);
678250199Sgrehan
679250199Sgrehan	/*
680250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
681250199Sgrehan	 *  is the largest size we support
682250199Sgrehan	 */
683294705Ssephe	descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range);
684294705Ssephe	page_buflen = sizeof(hv_vmbus_page_buffer) * page_count;
685294705Ssephe	packet_len = descSize + page_buflen + buffer_len;
686250199Sgrehan	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
687250199Sgrehan
688250199Sgrehan	/* Setup the descriptor */
689250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
690250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
691294705Ssephe	/* in 8-bytes granularity */
692294705Ssephe	desc.data_offset8 = (descSize + page_buflen) >> 3;
693250199Sgrehan	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
694250199Sgrehan	desc.transaction_id = request_id;
695250199Sgrehan	desc.range_count = page_count;
696250199Sgrehan
697250199Sgrehan	buffer_list[0].data = &desc;
698250199Sgrehan	buffer_list[0].length = descSize;
699250199Sgrehan
700294705Ssephe	buffer_list[1].data = page_buffers;
701294705Ssephe	buffer_list[1].length = page_buflen;
702250199Sgrehan
703294705Ssephe	buffer_list[2].data = buffer;
704294705Ssephe	buffer_list[2].length = buffer_len;
705250199Sgrehan
706294705Ssephe	buffer_list[3].data = &alignedData;
707294705Ssephe	buffer_list[3].length = packetLen_aligned - packet_len;
708294705Ssephe
709294705Ssephe	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 4,
710282212Swhu	    &need_sig);
711250199Sgrehan
712250199Sgrehan	/* TODO: We should determine if this is optional */
713302731Ssephe	if (ret == 0 && need_sig)
714302731Ssephe		vmbus_chan_send_event(channel);
715250199Sgrehan
716250199Sgrehan	return (ret);
717250199Sgrehan}
718250199Sgrehan
719250199Sgrehan/**
720250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type
721250199Sgrehan */
722250199Sgrehanint
723250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer(
724250199Sgrehan	hv_vmbus_channel*		channel,
725250199Sgrehan	hv_vmbus_multipage_buffer*	multi_page_buffer,
726250199Sgrehan	void*				buffer,
727250199Sgrehan	uint32_t			buffer_len,
728250199Sgrehan	uint64_t			request_id)
729250199Sgrehan{
730250199Sgrehan
731250199Sgrehan	int			ret = 0;
732250199Sgrehan	uint32_t		desc_size;
733282212Swhu	boolean_t		need_sig;
734250199Sgrehan	uint32_t		packet_len;
735250199Sgrehan	uint32_t		packet_len_aligned;
736250199Sgrehan	uint32_t		pfn_count;
737250199Sgrehan	uint64_t		aligned_data = 0;
738250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
739250199Sgrehan	hv_vmbus_channel_packet_multipage_buffer desc;
740250199Sgrehan
741250199Sgrehan	pfn_count =
742250199Sgrehan	    HV_NUM_PAGES_SPANNED(
743250199Sgrehan		    multi_page_buffer->offset,
744250199Sgrehan		    multi_page_buffer->length);
745250199Sgrehan
746250199Sgrehan	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
747250199Sgrehan	    return (EINVAL);
748250199Sgrehan	/*
749250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
750250199Sgrehan	 * is the largest size we support
751250199Sgrehan	 */
752250199Sgrehan	desc_size =
753250199Sgrehan	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
754250199Sgrehan		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
755250199Sgrehan			sizeof(uint64_t));
756250199Sgrehan	packet_len = desc_size + buffer_len;
757250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
758250199Sgrehan
759250199Sgrehan	/*
760250199Sgrehan	 * Setup the descriptor
761250199Sgrehan	 */
762250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
763250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
764250199Sgrehan	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
765250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
766250199Sgrehan	desc.transaction_id = request_id;
767250199Sgrehan	desc.range_count = 1;
768250199Sgrehan
769250199Sgrehan	desc.range.length = multi_page_buffer->length;
770250199Sgrehan	desc.range.offset = multi_page_buffer->offset;
771250199Sgrehan
772250199Sgrehan	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
773250199Sgrehan		pfn_count * sizeof(uint64_t));
774250199Sgrehan
775250199Sgrehan	buffer_list[0].data = &desc;
776250199Sgrehan	buffer_list[0].length = desc_size;
777250199Sgrehan
778250199Sgrehan	buffer_list[1].data = buffer;
779250199Sgrehan	buffer_list[1].length = buffer_len;
780250199Sgrehan
781250199Sgrehan	buffer_list[2].data = &aligned_data;
782250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
783250199Sgrehan
784282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
785282212Swhu	    &need_sig);
786250199Sgrehan
787250199Sgrehan	/* TODO: We should determine if this is optional */
788302731Ssephe	if (ret == 0 && need_sig)
789302731Ssephe		vmbus_chan_send_event(channel);
790250199Sgrehan
791250199Sgrehan	return (ret);
792250199Sgrehan}
793250199Sgrehan
794250199Sgrehan/**
795250199Sgrehan * @brief Retrieve the user packet on the specified channel
796250199Sgrehan */
797250199Sgrehanint
798250199Sgrehanhv_vmbus_channel_recv_packet(
799250199Sgrehan	hv_vmbus_channel*	channel,
800250199Sgrehan	void*			Buffer,
801250199Sgrehan	uint32_t		buffer_len,
802250199Sgrehan	uint32_t*		buffer_actual_len,
803250199Sgrehan	uint64_t*		request_id)
804250199Sgrehan{
805250199Sgrehan	int			ret;
806250199Sgrehan	uint32_t		user_len;
807250199Sgrehan	uint32_t		packet_len;
808250199Sgrehan	hv_vm_packet_descriptor	desc;
809250199Sgrehan
810250199Sgrehan	*buffer_actual_len = 0;
811250199Sgrehan	*request_id = 0;
812250199Sgrehan
813250199Sgrehan	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
814250199Sgrehan		sizeof(hv_vm_packet_descriptor));
815250199Sgrehan	if (ret != 0)
816250199Sgrehan		return (0);
817250199Sgrehan
818250199Sgrehan	packet_len = desc.length8 << 3;
819250199Sgrehan	user_len = packet_len - (desc.data_offset8 << 3);
820250199Sgrehan
821250199Sgrehan	*buffer_actual_len = user_len;
822250199Sgrehan
823250199Sgrehan	if (user_len > buffer_len)
824250199Sgrehan		return (EINVAL);
825250199Sgrehan
826250199Sgrehan	*request_id = desc.transaction_id;
827250199Sgrehan
828250199Sgrehan	/* Copy over the packet to the user buffer */
829250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
830250199Sgrehan		(desc.data_offset8 << 3));
831250199Sgrehan
832250199Sgrehan	return (0);
833250199Sgrehan}
834250199Sgrehan
835250199Sgrehan/**
836250199Sgrehan * @brief Retrieve the raw packet on the specified channel
837250199Sgrehan */
838250199Sgrehanint
839250199Sgrehanhv_vmbus_channel_recv_packet_raw(
840250199Sgrehan	hv_vmbus_channel*	channel,
841250199Sgrehan	void*			buffer,
842250199Sgrehan	uint32_t		buffer_len,
843250199Sgrehan	uint32_t*		buffer_actual_len,
844250199Sgrehan	uint64_t*		request_id)
845250199Sgrehan{
846250199Sgrehan	int		ret;
847250199Sgrehan	uint32_t	packetLen;
848250199Sgrehan	hv_vm_packet_descriptor	desc;
849250199Sgrehan
850250199Sgrehan	*buffer_actual_len = 0;
851250199Sgrehan	*request_id = 0;
852250199Sgrehan
853250199Sgrehan	ret = hv_ring_buffer_peek(
854250199Sgrehan		&channel->inbound, &desc,
855250199Sgrehan		sizeof(hv_vm_packet_descriptor));
856250199Sgrehan
857250199Sgrehan	if (ret != 0)
858250199Sgrehan	    return (0);
859250199Sgrehan
860250199Sgrehan	packetLen = desc.length8 << 3;
861250199Sgrehan	*buffer_actual_len = packetLen;
862250199Sgrehan
863250199Sgrehan	if (packetLen > buffer_len)
864250199Sgrehan	    return (ENOBUFS);
865250199Sgrehan
866250199Sgrehan	*request_id = desc.transaction_id;
867250199Sgrehan
868250199Sgrehan	/* Copy over the entire packet to the user buffer */
869250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
870250199Sgrehan
871250199Sgrehan	return (0);
872250199Sgrehan}
873294886Ssephe
874294886Ssephestatic void
875302713Ssephevmbus_chan_task(void *xchan, int pending __unused)
876294886Ssephe{
877302713Ssephe	struct hv_vmbus_channel *chan = xchan;
878302713Ssephe	void (*callback)(void *);
879302713Ssephe	void *arg;
880294886Ssephe
881302713Ssephe	arg = chan->channel_callback_context;
882302713Ssephe	callback = chan->on_channel_callback;
883302709Ssephe
884302710Ssephe	/*
885302710Ssephe	 * Optimize host to guest signaling by ensuring:
886302710Ssephe	 * 1. While reading the channel, we disable interrupts from
887302710Ssephe	 *    host.
888302710Ssephe	 * 2. Ensure that we process all posted messages from the host
889302710Ssephe	 *    before returning from this callback.
890302710Ssephe	 * 3. Once we return, enable signaling from the host. Once this
891302710Ssephe	 *    state is set we check to see if additional packets are
892302710Ssephe	 *    available to read. In this case we repeat the process.
893302713Ssephe	 *
894302713Ssephe	 * NOTE: Interrupt has been disabled in the ISR.
895302710Ssephe	 */
896302713Ssephe	for (;;) {
897302713Ssephe		uint32_t left;
898294886Ssephe
899302713Ssephe		callback(arg);
900294886Ssephe
901302713Ssephe		left = hv_ring_buffer_read_end(&chan->inbound);
902302713Ssephe		if (left == 0) {
903302713Ssephe			/* No more data in RX bufring; done */
904302713Ssephe			break;
905302713Ssephe		}
906302713Ssephe		hv_ring_buffer_read_begin(&chan->inbound);
907302713Ssephe	}
908294886Ssephe}
909302692Ssephe
910302713Ssephestatic void
911302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused)
912302713Ssephe{
913302713Ssephe	struct hv_vmbus_channel *chan = xchan;
914302713Ssephe
915302713Ssephe	chan->on_channel_callback(chan->channel_callback_context);
916302713Ssephe}
917302713Ssephe
918302692Ssephestatic __inline void
919302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
920302692Ssephe    int flag_cnt)
921302692Ssephe{
922302692Ssephe	int f;
923302692Ssephe
924302692Ssephe	for (f = 0; f < flag_cnt; ++f) {
925302806Ssephe		uint32_t chid_base;
926302692Ssephe		u_long flags;
927302806Ssephe		int chid_ofs;
928302692Ssephe
929302692Ssephe		if (event_flags[f] == 0)
930302692Ssephe			continue;
931302692Ssephe
932302692Ssephe		flags = atomic_swap_long(&event_flags[f], 0);
933302806Ssephe		chid_base = f << VMBUS_EVTFLAG_SHIFT;
934302692Ssephe
935302806Ssephe		while ((chid_ofs = ffsl(flags)) != 0) {
936302692Ssephe			struct hv_vmbus_channel *channel;
937302692Ssephe
938302806Ssephe			--chid_ofs; /* NOTE: ffsl is 1-based */
939302806Ssephe			flags &= ~(1UL << chid_ofs);
940302692Ssephe
941302806Ssephe			channel = sc->vmbus_chmap[chid_base + chid_ofs];
942302692Ssephe
943302692Ssephe			/* if channel is closed or closing */
944302692Ssephe			if (channel == NULL || channel->rxq == NULL)
945302692Ssephe				continue;
946302692Ssephe
947302709Ssephe			if (channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
948302692Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
949302692Ssephe			taskqueue_enqueue(channel->rxq, &channel->channel_task);
950302692Ssephe		}
951302692Ssephe	}
952302692Ssephe}
953302692Ssephe
954302692Ssephevoid
955302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu)
956302692Ssephe{
957302692Ssephe	struct vmbus_evtflags *eventf;
958302692Ssephe
959302692Ssephe	/*
960302692Ssephe	 * On Host with Win8 or above, the event page can be checked directly
961302692Ssephe	 * to get the id of the channel that has the pending interrupt.
962302692Ssephe	 */
963302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
964302692Ssephe	vmbus_event_flags_proc(sc, eventf->evt_flags,
965302692Ssephe	    VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
966302692Ssephe}
967302692Ssephe
968302692Ssephevoid
969302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
970302692Ssephe{
971302692Ssephe	struct vmbus_evtflags *eventf;
972302692Ssephe
973302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
974302692Ssephe	if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
975302692Ssephe		vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
976302692Ssephe		    VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
977302692Ssephe	}
978302692Ssephe}
979302692Ssephe
980302692Ssephestatic void
981302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
982302692Ssephe    const struct hv_vmbus_channel *chan)
983302692Ssephe{
984302692Ssephe	volatile int *flag_cnt_ptr;
985302692Ssephe	int flag_cnt;
986302692Ssephe
987302693Ssephe	flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
988302692Ssephe	flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->target_cpu);
989302692Ssephe
990302692Ssephe	for (;;) {
991302692Ssephe		int old_flag_cnt;
992302692Ssephe
993302692Ssephe		old_flag_cnt = *flag_cnt_ptr;
994302692Ssephe		if (old_flag_cnt >= flag_cnt)
995302692Ssephe			break;
996302692Ssephe		if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
997302692Ssephe			if (bootverbose) {
998302692Ssephe				device_printf(sc->vmbus_dev,
999302692Ssephe				    "channel%u update cpu%d flag_cnt to %d\n",
1000302693Ssephe				    chan->ch_id,
1001302692Ssephe				    chan->target_cpu, flag_cnt);
1002302692Ssephe			}
1003302692Ssephe			break;
1004302692Ssephe		}
1005302692Ssephe	}
1006302692Ssephe}
1007302864Ssephe
1008302864Ssephestatic struct hv_vmbus_channel *
1009302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc)
1010302864Ssephe{
1011302864Ssephe	struct hv_vmbus_channel *chan;
1012302864Ssephe
1013302864Ssephe	chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
1014302864Ssephe
1015302864Ssephe	chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
1016302864Ssephe	    HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
1017302864Ssephe	    &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
1018302864Ssephe	if (chan->ch_monprm == NULL) {
1019302864Ssephe		device_printf(sc->vmbus_dev, "monprm alloc failed\n");
1020302864Ssephe		free(chan, M_DEVBUF);
1021302864Ssephe		return NULL;
1022302864Ssephe	}
1023302864Ssephe
1024302864Ssephe	chan->vmbus_sc = sc;
1025302864Ssephe	mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
1026302864Ssephe	TAILQ_INIT(&chan->ch_subchans);
1027302864Ssephe	TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
1028302864Ssephe
1029302864Ssephe	return chan;
1030302864Ssephe}
1031302864Ssephe
1032302864Ssephestatic void
1033302864Ssephevmbus_chan_free(struct hv_vmbus_channel *chan)
1034302864Ssephe{
1035302864Ssephe	/* TODO: assert sub-channel list is empty */
1036302864Ssephe	/* TODO: asset no longer on the primary channel's sub-channel list */
1037302864Ssephe	/* TODO: asset no longer on the vmbus channel list */
1038302864Ssephe	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
1039302864Ssephe	mtx_destroy(&chan->ch_subchan_lock);
1040302864Ssephe	free(chan, M_DEVBUF);
1041302864Ssephe}
1042302864Ssephe
1043302864Ssephestatic int
1044302864Ssephevmbus_chan_add(struct hv_vmbus_channel *newchan)
1045302864Ssephe{
1046302864Ssephe	struct vmbus_softc *sc = newchan->vmbus_sc;
1047302864Ssephe	struct hv_vmbus_channel *prichan;
1048302864Ssephe
1049302864Ssephe	if (newchan->ch_id == 0) {
1050302864Ssephe		/*
1051302864Ssephe		 * XXX
1052302864Ssephe		 * Chan0 will neither be processed nor should be offered;
1053302864Ssephe		 * skip it.
1054302864Ssephe		 */
1055302864Ssephe		device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
1056302864Ssephe		return EINVAL;
1057302864Ssephe	} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
1058302864Ssephe		device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
1059302864Ssephe		    newchan->ch_id);
1060302864Ssephe		return EINVAL;
1061302864Ssephe	}
1062302864Ssephe	sc->vmbus_chmap[newchan->ch_id] = newchan;
1063302864Ssephe
1064302864Ssephe	if (bootverbose) {
1065302864Ssephe		device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
1066302864Ssephe		    newchan->ch_id, newchan->ch_subidx);
1067302864Ssephe	}
1068302864Ssephe
1069302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1070302864Ssephe	TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
1071302864Ssephe		/*
1072302864Ssephe		 * Sub-channel will have the same type GUID and instance
1073302864Ssephe		 * GUID as its primary channel.
1074302864Ssephe		 */
1075302864Ssephe		if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
1076302864Ssephe		    sizeof(struct hyperv_guid)) == 0 &&
1077302864Ssephe		    memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
1078302864Ssephe		    sizeof(struct hyperv_guid)) == 0)
1079302864Ssephe			break;
1080302864Ssephe	}
1081302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(newchan)) {
1082302864Ssephe		if (prichan == NULL) {
1083302864Ssephe			/* Install the new primary channel */
1084302864Ssephe			TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
1085302864Ssephe			    ch_prilink);
1086302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1087302864Ssephe			return 0;
1088302864Ssephe		} else {
1089302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1090302864Ssephe			device_printf(sc->vmbus_dev, "duplicated primary "
1091302864Ssephe			    "chan%u\n", newchan->ch_id);
1092302864Ssephe			return EINVAL;
1093302864Ssephe		}
1094302864Ssephe	} else { /* Sub-channel */
1095302864Ssephe		if (prichan == NULL) {
1096302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1097302864Ssephe			device_printf(sc->vmbus_dev, "no primary chan for "
1098302864Ssephe			    "chan%u\n", newchan->ch_id);
1099302864Ssephe			return EINVAL;
1100302864Ssephe		}
1101302864Ssephe		/*
1102302864Ssephe		 * Found the primary channel for this sub-channel and
1103302864Ssephe		 * move on.
1104302864Ssephe		 *
1105302864Ssephe		 * XXX refcnt prichan
1106302864Ssephe		 */
1107302864Ssephe	}
1108302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1109302864Ssephe
1110302864Ssephe	/*
1111302864Ssephe	 * This is a sub-channel; link it with the primary channel.
1112302864Ssephe	 */
1113302864Ssephe	KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
1114302864Ssephe	    ("new channel is not sub-channel"));
1115302864Ssephe	KASSERT(prichan != NULL, ("no primary channel"));
1116302864Ssephe
1117302864Ssephe	newchan->ch_prichan = prichan;
1118302864Ssephe	newchan->ch_dev = prichan->ch_dev;
1119302864Ssephe
1120302864Ssephe	mtx_lock(&prichan->ch_subchan_lock);
1121302864Ssephe	TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
1122302864Ssephe	/*
1123302864Ssephe	 * Bump up sub-channel count and notify anyone that is
1124302864Ssephe	 * interested in this sub-channel, after this sub-channel
1125302864Ssephe	 * is setup.
1126302864Ssephe	 */
1127302864Ssephe	prichan->ch_subchan_cnt++;
1128302864Ssephe	mtx_unlock(&prichan->ch_subchan_lock);
1129302864Ssephe	wakeup(prichan);
1130302864Ssephe
1131302864Ssephe	return 0;
1132302864Ssephe}
1133302864Ssephe
1134302864Ssephevoid
1135302864Ssephevmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
1136302864Ssephe{
1137302864Ssephe	KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
1138302864Ssephe
1139302864Ssephe	if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
1140302864Ssephe	    chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
1141302864Ssephe		/* Only cpu0 is supported */
1142302864Ssephe		cpu = 0;
1143302864Ssephe	}
1144302864Ssephe
1145302864Ssephe	chan->target_cpu = cpu;
1146302864Ssephe	chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
1147302864Ssephe
1148302864Ssephe	if (bootverbose) {
1149302864Ssephe		printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
1150302864Ssephe		    chan->ch_id,
1151302864Ssephe		    chan->target_cpu, chan->target_vcpu);
1152302864Ssephe	}
1153302864Ssephe}
1154302864Ssephe
1155302864Ssephevoid
1156302864Ssephevmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
1157302864Ssephe{
1158302864Ssephe	static uint32_t vmbus_chan_nextcpu;
1159302864Ssephe	int cpu;
1160302864Ssephe
1161302864Ssephe	cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
1162302864Ssephe	vmbus_channel_cpu_set(chan, cpu);
1163302864Ssephe}
1164302864Ssephe
1165302864Ssephestatic void
1166302864Ssephevmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
1167302864Ssephe{
1168302864Ssephe	/*
1169302864Ssephe	 * By default, pin the channel to cpu0.  Devices having
1170302864Ssephe	 * special channel-cpu mapping requirement should call
1171302864Ssephe	 * vmbus_channel_cpu_{set,rr}().
1172302864Ssephe	 */
1173302864Ssephe	vmbus_channel_cpu_set(chan, 0);
1174302864Ssephe}
1175302864Ssephe
1176302864Ssephestatic void
1177302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
1178302864Ssephe    const struct vmbus_message *msg)
1179302864Ssephe{
1180302864Ssephe	const struct vmbus_chanmsg_choffer *offer;
1181302864Ssephe	struct hv_vmbus_channel *chan;
1182302864Ssephe	int error;
1183302864Ssephe
1184302864Ssephe	offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
1185302864Ssephe
1186302864Ssephe	chan = vmbus_chan_alloc(sc);
1187302864Ssephe	if (chan == NULL) {
1188302864Ssephe		device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
1189302864Ssephe		    offer->chm_chanid);
1190302864Ssephe		return;
1191302864Ssephe	}
1192302864Ssephe
1193302864Ssephe	chan->ch_id = offer->chm_chanid;
1194302864Ssephe	chan->ch_subidx = offer->chm_subidx;
1195302864Ssephe	chan->ch_guid_type = offer->chm_chtype;
1196302864Ssephe	chan->ch_guid_inst = offer->chm_chinst;
1197302864Ssephe
1198302864Ssephe	/* Batch reading is on by default */
1199302864Ssephe	chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
1200302864Ssephe
1201302864Ssephe	chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
1202302864Ssephe	if (sc->vmbus_version != VMBUS_VERSION_WS2008)
1203302864Ssephe		chan->ch_monprm->mp_connid = offer->chm_connid;
1204302864Ssephe
1205302864Ssephe	if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
1206302864Ssephe		/*
1207302864Ssephe		 * Setup MNF stuffs.
1208302864Ssephe		 */
1209302864Ssephe		chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
1210302864Ssephe		chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
1211302864Ssephe		if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
1212302864Ssephe			panic("invalid monitor trigger %u", offer->chm_montrig);
1213302864Ssephe		chan->ch_montrig_mask =
1214302864Ssephe		    1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
1215302864Ssephe	}
1216302864Ssephe
1217302864Ssephe	/* Select default cpu for this channel. */
1218302864Ssephe	vmbus_chan_cpu_default(chan);
1219302864Ssephe
1220302864Ssephe	error = vmbus_chan_add(chan);
1221302864Ssephe	if (error) {
1222302864Ssephe		device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
1223302864Ssephe		    chan->ch_id, error);
1224302864Ssephe		vmbus_chan_free(chan);
1225302864Ssephe		return;
1226302864Ssephe	}
1227302864Ssephe
1228302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1229302864Ssephe		/*
1230302864Ssephe		 * Add device for this primary channel.
1231302864Ssephe		 *
1232302864Ssephe		 * NOTE:
1233302864Ssephe		 * Error is ignored here; don't have much to do if error
1234302864Ssephe		 * really happens.
1235302864Ssephe		 */
1236302868Ssephe		vmbus_add_child(chan);
1237302864Ssephe	}
1238302864Ssephe}
1239302864Ssephe
1240302864Ssephe/*
1241302864Ssephe * XXX pretty broken; need rework.
1242302864Ssephe */
1243302864Ssephestatic void
1244302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
1245302864Ssephe    const struct vmbus_message *msg)
1246302864Ssephe{
1247302864Ssephe	const struct vmbus_chanmsg_chrescind *note;
1248302864Ssephe	struct hv_vmbus_channel *chan;
1249302864Ssephe
1250302864Ssephe	note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
1251302864Ssephe	if (note->chm_chanid > VMBUS_CHAN_MAX) {
1252302864Ssephe		device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
1253302864Ssephe		    note->chm_chanid);
1254302864Ssephe		return;
1255302864Ssephe	}
1256302864Ssephe
1257302864Ssephe	if (bootverbose) {
1258302864Ssephe		device_printf(sc->vmbus_dev, "chan%u rescinded\n",
1259302864Ssephe		    note->chm_chanid);
1260302864Ssephe	}
1261302864Ssephe
1262302864Ssephe	chan = sc->vmbus_chmap[note->chm_chanid];
1263302864Ssephe	if (chan == NULL)
1264302864Ssephe		return;
1265302864Ssephe	sc->vmbus_chmap[note->chm_chanid] = NULL;
1266302864Ssephe
1267302864Ssephe	taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
1268302864Ssephe}
1269302864Ssephe
1270302864Ssephestatic void
1271302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused)
1272302864Ssephe{
1273302864Ssephe	struct hv_vmbus_channel *chan = xchan;
1274302864Ssephe
1275302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1276302864Ssephe		/* Only primary channel owns the device */
1277302868Ssephe		vmbus_delete_child(chan);
1278302864Ssephe		/* NOTE: DO NOT free primary channel for now */
1279302864Ssephe	} else {
1280302864Ssephe		struct vmbus_softc *sc = chan->vmbus_sc;
1281302864Ssephe		struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
1282302864Ssephe		struct vmbus_chanmsg_chfree *req;
1283302864Ssephe		struct vmbus_msghc *mh;
1284302864Ssephe		int error;
1285302864Ssephe
1286302864Ssephe		mh = vmbus_msghc_get(sc, sizeof(*req));
1287302864Ssephe		if (mh == NULL) {
1288302864Ssephe			device_printf(sc->vmbus_dev,
1289302864Ssephe			    "can not get msg hypercall for chfree(chan%u)\n",
1290302864Ssephe			    chan->ch_id);
1291302864Ssephe			goto remove;
1292302864Ssephe		}
1293302864Ssephe
1294302864Ssephe		req = vmbus_msghc_dataptr(mh);
1295302864Ssephe		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
1296302864Ssephe		req->chm_chanid = chan->ch_id;
1297302864Ssephe
1298302864Ssephe		error = vmbus_msghc_exec_noresult(mh);
1299302864Ssephe		vmbus_msghc_put(sc, mh);
1300302864Ssephe
1301302864Ssephe		if (error) {
1302302864Ssephe			device_printf(sc->vmbus_dev,
1303302864Ssephe			    "chfree(chan%u) failed: %d",
1304302864Ssephe			    chan->ch_id, error);
1305302864Ssephe			/* NOTE: Move on! */
1306302864Ssephe		} else {
1307302864Ssephe			if (bootverbose) {
1308302864Ssephe				device_printf(sc->vmbus_dev, "chan%u freed\n",
1309302864Ssephe				    chan->ch_id);
1310302864Ssephe			}
1311302864Ssephe		}
1312302864Ssepheremove:
1313302864Ssephe		mtx_lock(&pri_chan->ch_subchan_lock);
1314302864Ssephe		TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
1315302864Ssephe		KASSERT(pri_chan->ch_subchan_cnt > 0,
1316302864Ssephe		    ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
1317302864Ssephe		pri_chan->ch_subchan_cnt--;
1318302864Ssephe		mtx_unlock(&pri_chan->ch_subchan_lock);
1319302864Ssephe		wakeup(pri_chan);
1320302864Ssephe
1321302864Ssephe		vmbus_chan_free(chan);
1322302864Ssephe	}
1323302864Ssephe}
1324302864Ssephe
1325302864Ssephe/*
1326302864Ssephe * Detach all devices and destroy the corresponding primary channels.
1327302864Ssephe */
1328302864Ssephevoid
1329302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc)
1330302864Ssephe{
1331302864Ssephe	struct hv_vmbus_channel *chan;
1332302864Ssephe
1333302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1334302864Ssephe	while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
1335302864Ssephe		KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
1336302864Ssephe		TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
1337302864Ssephe		mtx_unlock(&sc->vmbus_prichan_lock);
1338302864Ssephe
1339302868Ssephe		vmbus_delete_child(chan);
1340302864Ssephe		vmbus_chan_free(chan);
1341302864Ssephe
1342302864Ssephe		mtx_lock(&sc->vmbus_prichan_lock);
1343302864Ssephe	}
1344302864Ssephe	bzero(sc->vmbus_chmap,
1345302864Ssephe	    sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
1346302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1347302864Ssephe}
1348302864Ssephe
1349302864Ssephe/**
1350302864Ssephe * @brief Select the best outgoing channel
1351302864Ssephe *
1352302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will
1353302864Ssephe * be selected.
1354302864Ssephe * If no multi-channel, always select primary channel
1355302864Ssephe *
1356302864Ssephe * @param primary - primary channel
1357302864Ssephe */
1358302864Ssephestruct hv_vmbus_channel *
1359302864Ssephevmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
1360302864Ssephe{
1361302864Ssephe	hv_vmbus_channel *new_channel = NULL;
1362302864Ssephe	hv_vmbus_channel *outgoing_channel = primary;
1363302864Ssephe	int old_cpu_distance = 0;
1364302864Ssephe	int new_cpu_distance = 0;
1365302864Ssephe	int cur_vcpu = 0;
1366302864Ssephe	int smp_pro_id = PCPU_GET(cpuid);
1367302864Ssephe
1368302864Ssephe	if (TAILQ_EMPTY(&primary->ch_subchans)) {
1369302864Ssephe		return outgoing_channel;
1370302864Ssephe	}
1371302864Ssephe
1372302864Ssephe	if (smp_pro_id >= MAXCPU) {
1373302864Ssephe		return outgoing_channel;
1374302864Ssephe	}
1375302864Ssephe
1376302864Ssephe	cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
1377302864Ssephe
1378302864Ssephe	/* XXX need lock */
1379302864Ssephe	TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
1380302864Ssephe		if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
1381302864Ssephe			continue;
1382302864Ssephe		}
1383302864Ssephe
1384302864Ssephe		if (new_channel->target_vcpu == cur_vcpu){
1385302864Ssephe			return new_channel;
1386302864Ssephe		}
1387302864Ssephe
1388302864Ssephe		old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
1389302864Ssephe		    (outgoing_channel->target_vcpu - cur_vcpu) :
1390302864Ssephe		    (cur_vcpu - outgoing_channel->target_vcpu));
1391302864Ssephe
1392302864Ssephe		new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
1393302864Ssephe		    (new_channel->target_vcpu - cur_vcpu) :
1394302864Ssephe		    (cur_vcpu - new_channel->target_vcpu));
1395302864Ssephe
1396302864Ssephe		if (old_cpu_distance < new_cpu_distance) {
1397302864Ssephe			continue;
1398302864Ssephe		}
1399302864Ssephe
1400302864Ssephe		outgoing_channel = new_channel;
1401302864Ssephe	}
1402302864Ssephe
1403302864Ssephe	return(outgoing_channel);
1404302864Ssephe}
1405302864Ssephe
1406302864Ssephestruct hv_vmbus_channel **
1407302864Ssephevmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
1408302864Ssephe{
1409302864Ssephe	struct hv_vmbus_channel **ret, *chan;
1410302864Ssephe	int i;
1411302864Ssephe
1412302864Ssephe	ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
1413302864Ssephe	    M_WAITOK);
1414302864Ssephe
1415302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1416302864Ssephe
1417302864Ssephe	while (pri_chan->ch_subchan_cnt < subchan_cnt)
1418302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
1419302864Ssephe
1420302864Ssephe	i = 0;
1421302864Ssephe	TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
1422302864Ssephe		/* TODO: refcnt chan */
1423302864Ssephe		ret[i] = chan;
1424302864Ssephe
1425302864Ssephe		++i;
1426302864Ssephe		if (i == subchan_cnt)
1427302864Ssephe			break;
1428302864Ssephe	}
1429302864Ssephe	KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
1430302864Ssephe	    pri_chan->ch_subchan_cnt, subchan_cnt));
1431302864Ssephe
1432302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1433302864Ssephe
1434302864Ssephe	return ret;
1435302864Ssephe}
1436302864Ssephe
1437302864Ssephevoid
1438302864Ssephevmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
1439302864Ssephe{
1440302864Ssephe
1441302864Ssephe	free(subchan, M_TEMP);
1442302864Ssephe}
1443302864Ssephe
1444302864Ssephevoid
1445302864Ssephevmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
1446302864Ssephe{
1447302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1448302864Ssephe	while (pri_chan->ch_subchan_cnt > 0)
1449302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
1450302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1451302864Ssephe}
1452302864Ssephe
1453302864Ssephevoid
1454302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1455302864Ssephe{
1456302864Ssephe	vmbus_chanmsg_proc_t msg_proc;
1457302864Ssephe	uint32_t msg_type;
1458302864Ssephe
1459302864Ssephe	msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
1460302864Ssephe	KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
1461302864Ssephe	    ("invalid message type %u", msg_type));
1462302864Ssephe
1463302864Ssephe	msg_proc = vmbus_chan_msgprocs[msg_type];
1464302864Ssephe	if (msg_proc != NULL)
1465302864Ssephe		msg_proc(sc, msg);
1466302864Ssephe}
1467