vmbus_chan.c revision 302871
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 302871 2016-07-15 05:40:34Z 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,
333302871Ssephe    void *contig_buffer, uint32_t size, uint32_t *gpadl)
334250199Sgrehan{
335302871Ssephe	return vmbus_chan_gpadl_connect(channel,
336302871Ssephe	    hv_get_phys_addr(contig_buffer), size, gpadl);
337302871Ssephe}
338302871Ssephe
339302871Ssepheint
340302871Ssephevmbus_chan_gpadl_connect(struct hv_vmbus_channel *chan, bus_addr_t paddr,
341302871Ssephe    int size, uint32_t *gpadl0)
342302871Ssephe{
343302871Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
344302609Ssephe	struct vmbus_msghc *mh;
345302609Ssephe	struct vmbus_chanmsg_gpadl_conn *req;
346302609Ssephe	const struct vmbus_message *msg;
347302609Ssephe	size_t reqsz;
348302609Ssephe	uint32_t gpadl, status;
349302609Ssephe	int page_count, range_len, i, cnt, error;
350302871Ssephe	uint64_t page_id;
351250199Sgrehan
352302609Ssephe	/*
353302609Ssephe	 * Preliminary checks.
354302609Ssephe	 */
355250199Sgrehan
356302609Ssephe	KASSERT((size & PAGE_MASK) == 0,
357302871Ssephe	    ("invalid GPA size %d, not multiple page size", size));
358250199Sgrehan	page_count = size >> PAGE_SHIFT;
359250199Sgrehan
360302609Ssephe	KASSERT((paddr & PAGE_MASK) == 0,
361302609Ssephe	    ("GPA is not page aligned %jx", (uintmax_t)paddr));
362302609Ssephe	page_id = paddr >> PAGE_SHIFT;
363250199Sgrehan
364302609Ssephe	range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
365302609Ssephe	/*
366302609Ssephe	 * We don't support multiple GPA ranges.
367302609Ssephe	 */
368302609Ssephe	if (range_len > UINT16_MAX) {
369302609Ssephe		device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
370302609Ssephe		    page_count);
371302609Ssephe		return EOPNOTSUPP;
372250199Sgrehan	}
373250199Sgrehan
374302609Ssephe	/*
375302609Ssephe	 * Allocate GPADL id.
376302609Ssephe	 */
377302630Ssephe	gpadl = vmbus_gpadl_alloc(sc);
378302609Ssephe	*gpadl0 = gpadl;
379250199Sgrehan
380302609Ssephe	/*
381302609Ssephe	 * Connect this GPADL to the target channel.
382302609Ssephe	 *
383302609Ssephe	 * NOTE:
384302609Ssephe	 * Since each message can only hold small set of page
385302609Ssephe	 * addresses, several messages may be required to
386302609Ssephe	 * complete the connection.
387302609Ssephe	 */
388302609Ssephe	if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
389302609Ssephe		cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
390302609Ssephe	else
391302609Ssephe		cnt = page_count;
392302609Ssephe	page_count -= cnt;
393250199Sgrehan
394302609Ssephe	reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
395302609Ssephe	    chm_range.gpa_page[cnt]);
396302609Ssephe	mh = vmbus_msghc_get(sc, reqsz);
397302609Ssephe	if (mh == NULL) {
398302609Ssephe		device_printf(sc->vmbus_dev,
399302609Ssephe		    "can not get msg hypercall for gpadl->chan%u\n",
400302871Ssephe		    chan->ch_id);
401302609Ssephe		return EIO;
402250199Sgrehan	}
403250199Sgrehan
404302609Ssephe	req = vmbus_msghc_dataptr(mh);
405302609Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
406302871Ssephe	req->chm_chanid = chan->ch_id;
407302609Ssephe	req->chm_gpadl = gpadl;
408302609Ssephe	req->chm_range_len = range_len;
409302609Ssephe	req->chm_range_cnt = 1;
410302609Ssephe	req->chm_range.gpa_len = size;
411302609Ssephe	req->chm_range.gpa_ofs = 0;
412302609Ssephe	for (i = 0; i < cnt; ++i)
413302609Ssephe		req->chm_range.gpa_page[i] = page_id++;
414250199Sgrehan
415302609Ssephe	error = vmbus_msghc_exec(sc, mh);
416302609Ssephe	if (error) {
417302609Ssephe		device_printf(sc->vmbus_dev,
418302609Ssephe		    "gpadl->chan%u msg hypercall exec failed: %d\n",
419302871Ssephe		    chan->ch_id, error);
420302609Ssephe		vmbus_msghc_put(sc, mh);
421302609Ssephe		return error;
422302609Ssephe	}
423250199Sgrehan
424302609Ssephe	while (page_count > 0) {
425302609Ssephe		struct vmbus_chanmsg_gpadl_subconn *subreq;
426250199Sgrehan
427302609Ssephe		if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
428302609Ssephe			cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
429302609Ssephe		else
430302609Ssephe			cnt = page_count;
431302609Ssephe		page_count -= cnt;
432250199Sgrehan
433302609Ssephe		reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
434302609Ssephe		    chm_gpa_page[cnt]);
435302609Ssephe		vmbus_msghc_reset(mh, reqsz);
436250199Sgrehan
437302609Ssephe		subreq = vmbus_msghc_dataptr(mh);
438302609Ssephe		subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
439302609Ssephe		subreq->chm_gpadl = gpadl;
440302609Ssephe		for (i = 0; i < cnt; ++i)
441302609Ssephe			subreq->chm_gpa_page[i] = page_id++;
442250199Sgrehan
443302609Ssephe		vmbus_msghc_exec_noresult(mh);
444250199Sgrehan	}
445302609Ssephe	KASSERT(page_count == 0, ("invalid page count %d", page_count));
446250199Sgrehan
447302609Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
448302609Ssephe	status = ((const struct vmbus_chanmsg_gpadl_connresp *)
449302609Ssephe	    msg->msg_data)->chm_status;
450250199Sgrehan
451302609Ssephe	vmbus_msghc_put(sc, mh);
452250199Sgrehan
453302609Ssephe	if (status != 0) {
454302609Ssephe		device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
455302871Ssephe		    "status %u\n", chan->ch_id, status);
456302609Ssephe		return EIO;
457302632Ssephe	} else {
458302632Ssephe		if (bootverbose) {
459302632Ssephe			device_printf(sc->vmbus_dev, "gpadl->chan%u "
460302871Ssephe			    "succeeded\n", chan->ch_id);
461302632Ssephe		}
462302609Ssephe	}
463302609Ssephe	return 0;
464250199Sgrehan}
465250199Sgrehan
466302611Ssephe/*
467302611Ssephe * Disconnect the GPA from the target channel
468250199Sgrehan */
469250199Sgrehanint
470302611Ssephehv_vmbus_channel_teardown_gpdal(struct hv_vmbus_channel *chan, uint32_t gpadl)
471250199Sgrehan{
472302611Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
473302611Ssephe	struct vmbus_msghc *mh;
474302611Ssephe	struct vmbus_chanmsg_gpadl_disconn *req;
475302611Ssephe	int error;
476250199Sgrehan
477302611Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
478302611Ssephe	if (mh == NULL) {
479302611Ssephe		device_printf(sc->vmbus_dev,
480302611Ssephe		    "can not get msg hypercall for gpa x->chan%u\n",
481302693Ssephe		    chan->ch_id);
482302611Ssephe		return EBUSY;
483250199Sgrehan	}
484250199Sgrehan
485302611Ssephe	req = vmbus_msghc_dataptr(mh);
486302611Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
487302693Ssephe	req->chm_chanid = chan->ch_id;
488302611Ssephe	req->chm_gpadl = gpadl;
489250199Sgrehan
490302611Ssephe	error = vmbus_msghc_exec(sc, mh);
491302611Ssephe	if (error) {
492302611Ssephe		device_printf(sc->vmbus_dev,
493302611Ssephe		    "gpa x->chan%u msg hypercall exec failed: %d\n",
494302693Ssephe		    chan->ch_id, error);
495302611Ssephe		vmbus_msghc_put(sc, mh);
496302611Ssephe		return error;
497302611Ssephe	}
498250199Sgrehan
499302611Ssephe	vmbus_msghc_wait_result(sc, mh);
500302611Ssephe	/* Discard result; no useful information */
501302611Ssephe	vmbus_msghc_put(sc, mh);
502250199Sgrehan
503302611Ssephe	return 0;
504250199Sgrehan}
505250199Sgrehan
506282212Swhustatic void
507282212Swhuhv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
508250199Sgrehan{
509302610Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
510302610Ssephe	struct vmbus_msghc *mh;
511302610Ssephe	struct vmbus_chanmsg_chclose *req;
512294886Ssephe	struct taskqueue *rxq = channel->rxq;
513302610Ssephe	int error;
514250199Sgrehan
515302812Ssephe	/* TODO: stringent check */
516302812Ssephe	atomic_clear_int(&channel->ch_stflags, VMBUS_CHAN_ST_OPENED);
517302812Ssephe
518302633Ssephe	sysctl_ctx_free(&channel->ch_sysctl_ctx);
519282212Swhu
520282212Swhu	/*
521294886Ssephe	 * set rxq to NULL to avoid more requests be scheduled
522294886Ssephe	 */
523294886Ssephe	channel->rxq = NULL;
524294886Ssephe	taskqueue_drain(rxq, &channel->channel_task);
525250199Sgrehan	channel->on_channel_callback = NULL;
526250199Sgrehan
527250199Sgrehan	/**
528250199Sgrehan	 * Send a closing message
529250199Sgrehan	 */
530250199Sgrehan
531302610Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
532302610Ssephe	if (mh == NULL) {
533302610Ssephe		device_printf(sc->vmbus_dev,
534302610Ssephe		    "can not get msg hypercall for chclose(chan%u)\n",
535302693Ssephe		    channel->ch_id);
536302610Ssephe		return;
537302610Ssephe	}
538250199Sgrehan
539302610Ssephe	req = vmbus_msghc_dataptr(mh);
540302610Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
541302693Ssephe	req->chm_chanid = channel->ch_id;
542250199Sgrehan
543302610Ssephe	error = vmbus_msghc_exec_noresult(mh);
544302610Ssephe	vmbus_msghc_put(sc, mh);
545302610Ssephe
546302610Ssephe	if (error) {
547302610Ssephe		device_printf(sc->vmbus_dev,
548302610Ssephe		    "chclose(chan%u) msg hypercall exec failed: %d\n",
549302693Ssephe		    channel->ch_id, error);
550302610Ssephe		return;
551302610Ssephe	} else if (bootverbose) {
552302610Ssephe		device_printf(sc->vmbus_dev, "close chan%u\n",
553302693Ssephe		    channel->ch_id);
554302610Ssephe	}
555302610Ssephe
556250199Sgrehan	/* Tear down the gpadl for the channel's ring buffer */
557250199Sgrehan	if (channel->ring_buffer_gpadl_handle) {
558250199Sgrehan		hv_vmbus_channel_teardown_gpdal(channel,
559250199Sgrehan			channel->ring_buffer_gpadl_handle);
560250199Sgrehan	}
561250199Sgrehan
562250199Sgrehan	/* TODO: Send a msg to release the childRelId */
563250199Sgrehan
564250199Sgrehan	/* cleanup the ring buffers for this channel */
565250199Sgrehan	hv_ring_buffer_cleanup(&channel->outbound);
566250199Sgrehan	hv_ring_buffer_cleanup(&channel->inbound);
567250199Sgrehan
568256350Sgrehan	contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
569256350Sgrehan	    M_DEVBUF);
570282212Swhu}
571250199Sgrehan
572302818Ssephe/*
573302818Ssephe * Caller should make sure that all sub-channels have
574302818Ssephe * been added to 'chan' and all to-be-closed channels
575302818Ssephe * are not being opened.
576282212Swhu */
577282212Swhuvoid
578302818Ssephehv_vmbus_channel_close(struct hv_vmbus_channel *chan)
579282212Swhu{
580302818Ssephe	int subchan_cnt;
581282212Swhu
582302818Ssephe	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
583282212Swhu		/*
584302818Ssephe		 * Sub-channel is closed when its primary channel
585302818Ssephe		 * is closed; done.
586282212Swhu		 */
587282212Swhu		return;
588282212Swhu	}
589282212Swhu
590250199Sgrehan	/*
591302818Ssephe	 * Close all sub-channels, if any.
592250199Sgrehan	 */
593302819Ssephe	subchan_cnt = chan->ch_subchan_cnt;
594302818Ssephe	if (subchan_cnt > 0) {
595302818Ssephe		struct hv_vmbus_channel **subchan;
596302818Ssephe		int i;
597302818Ssephe
598302818Ssephe		subchan = vmbus_get_subchan(chan, subchan_cnt);
599302818Ssephe		for (i = 0; i < subchan_cnt; ++i)
600302818Ssephe			hv_vmbus_channel_close_internal(subchan[i]);
601302818Ssephe		vmbus_rel_subchan(subchan, subchan_cnt);
602250199Sgrehan	}
603302818Ssephe
604302818Ssephe	/* Then close the primary channel. */
605302818Ssephe	hv_vmbus_channel_close_internal(chan);
606250199Sgrehan}
607250199Sgrehan
608250199Sgrehan/**
609250199Sgrehan * @brief Send the specified buffer on the given channel
610250199Sgrehan */
611250199Sgrehanint
612250199Sgrehanhv_vmbus_channel_send_packet(
613250199Sgrehan	hv_vmbus_channel*	channel,
614250199Sgrehan	void*			buffer,
615250199Sgrehan	uint32_t		buffer_len,
616250199Sgrehan	uint64_t		request_id,
617250199Sgrehan	hv_vmbus_packet_type	type,
618250199Sgrehan	uint32_t		flags)
619250199Sgrehan{
620250199Sgrehan	int			ret = 0;
621250199Sgrehan	hv_vm_packet_descriptor	desc;
622250199Sgrehan	uint32_t		packet_len;
623250199Sgrehan	uint64_t		aligned_data;
624250199Sgrehan	uint32_t		packet_len_aligned;
625282212Swhu	boolean_t		need_sig;
626302870Ssephe	struct iovec		iov[3];
627250199Sgrehan
628250199Sgrehan	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
629250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
630250199Sgrehan	aligned_data = 0;
631250199Sgrehan
632250199Sgrehan	/* Setup the descriptor */
633250199Sgrehan	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
634250199Sgrehan	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
635250199Sgrehan			    /* in 8-bytes granularity */
636250199Sgrehan	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
637250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
638250199Sgrehan	desc.transaction_id = request_id;
639250199Sgrehan
640302870Ssephe	iov[0].iov_base = &desc;
641302870Ssephe	iov[0].iov_len = sizeof(hv_vm_packet_descriptor);
642250199Sgrehan
643302870Ssephe	iov[1].iov_base = buffer;
644302870Ssephe	iov[1].iov_len = buffer_len;
645250199Sgrehan
646302870Ssephe	iov[2].iov_base = &aligned_data;
647302870Ssephe	iov[2].iov_len = packet_len_aligned - packet_len;
648250199Sgrehan
649302870Ssephe	ret = hv_ring_buffer_write(&channel->outbound, iov, 3, &need_sig);
650250199Sgrehan
651250199Sgrehan	/* TODO: We should determine if this is optional */
652302731Ssephe	if (ret == 0 && need_sig)
653302731Ssephe		vmbus_chan_send_event(channel);
654250199Sgrehan
655250199Sgrehan	return (ret);
656250199Sgrehan}
657250199Sgrehan
658250199Sgrehan/**
659250199Sgrehan * @brief Send a range of single-page buffer packets using
660250199Sgrehan * a GPADL Direct packet type
661250199Sgrehan */
662250199Sgrehanint
663250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer(
664250199Sgrehan	hv_vmbus_channel*	channel,
665250199Sgrehan	hv_vmbus_page_buffer	page_buffers[],
666250199Sgrehan	uint32_t		page_count,
667250199Sgrehan	void*			buffer,
668250199Sgrehan	uint32_t		buffer_len,
669250199Sgrehan	uint64_t		request_id)
670250199Sgrehan{
671250199Sgrehan
672250199Sgrehan	int					ret = 0;
673282212Swhu	boolean_t				need_sig;
674250199Sgrehan	uint32_t				packet_len;
675294705Ssephe	uint32_t				page_buflen;
676250199Sgrehan	uint32_t				packetLen_aligned;
677302870Ssephe	struct iovec				iov[4];
678250199Sgrehan	hv_vmbus_channel_packet_page_buffer	desc;
679250199Sgrehan	uint32_t				descSize;
680250199Sgrehan	uint64_t				alignedData = 0;
681250199Sgrehan
682250199Sgrehan	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
683250199Sgrehan		return (EINVAL);
684250199Sgrehan
685250199Sgrehan	/*
686250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
687250199Sgrehan	 *  is the largest size we support
688250199Sgrehan	 */
689294705Ssephe	descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range);
690294705Ssephe	page_buflen = sizeof(hv_vmbus_page_buffer) * page_count;
691294705Ssephe	packet_len = descSize + page_buflen + buffer_len;
692250199Sgrehan	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
693250199Sgrehan
694250199Sgrehan	/* Setup the descriptor */
695250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
696250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
697294705Ssephe	/* in 8-bytes granularity */
698294705Ssephe	desc.data_offset8 = (descSize + page_buflen) >> 3;
699250199Sgrehan	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
700250199Sgrehan	desc.transaction_id = request_id;
701250199Sgrehan	desc.range_count = page_count;
702250199Sgrehan
703302870Ssephe	iov[0].iov_base = &desc;
704302870Ssephe	iov[0].iov_len = descSize;
705250199Sgrehan
706302870Ssephe	iov[1].iov_base = page_buffers;
707302870Ssephe	iov[1].iov_len = page_buflen;
708250199Sgrehan
709302870Ssephe	iov[2].iov_base = buffer;
710302870Ssephe	iov[2].iov_len = buffer_len;
711250199Sgrehan
712302870Ssephe	iov[3].iov_base = &alignedData;
713302870Ssephe	iov[3].iov_len = packetLen_aligned - packet_len;
714294705Ssephe
715302870Ssephe	ret = hv_ring_buffer_write(&channel->outbound, iov, 4, &need_sig);
716250199Sgrehan
717250199Sgrehan	/* TODO: We should determine if this is optional */
718302731Ssephe	if (ret == 0 && need_sig)
719302731Ssephe		vmbus_chan_send_event(channel);
720250199Sgrehan
721250199Sgrehan	return (ret);
722250199Sgrehan}
723250199Sgrehan
724250199Sgrehan/**
725250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type
726250199Sgrehan */
727250199Sgrehanint
728250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer(
729250199Sgrehan	hv_vmbus_channel*		channel,
730250199Sgrehan	hv_vmbus_multipage_buffer*	multi_page_buffer,
731250199Sgrehan	void*				buffer,
732250199Sgrehan	uint32_t			buffer_len,
733250199Sgrehan	uint64_t			request_id)
734250199Sgrehan{
735250199Sgrehan
736250199Sgrehan	int			ret = 0;
737250199Sgrehan	uint32_t		desc_size;
738282212Swhu	boolean_t		need_sig;
739250199Sgrehan	uint32_t		packet_len;
740250199Sgrehan	uint32_t		packet_len_aligned;
741250199Sgrehan	uint32_t		pfn_count;
742250199Sgrehan	uint64_t		aligned_data = 0;
743302870Ssephe	struct iovec		iov[3];
744250199Sgrehan	hv_vmbus_channel_packet_multipage_buffer desc;
745250199Sgrehan
746250199Sgrehan	pfn_count =
747250199Sgrehan	    HV_NUM_PAGES_SPANNED(
748250199Sgrehan		    multi_page_buffer->offset,
749250199Sgrehan		    multi_page_buffer->length);
750250199Sgrehan
751250199Sgrehan	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
752250199Sgrehan	    return (EINVAL);
753250199Sgrehan	/*
754250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
755250199Sgrehan	 * is the largest size we support
756250199Sgrehan	 */
757250199Sgrehan	desc_size =
758250199Sgrehan	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
759250199Sgrehan		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
760250199Sgrehan			sizeof(uint64_t));
761250199Sgrehan	packet_len = desc_size + buffer_len;
762250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
763250199Sgrehan
764250199Sgrehan	/*
765250199Sgrehan	 * Setup the descriptor
766250199Sgrehan	 */
767250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
768250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
769250199Sgrehan	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
770250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
771250199Sgrehan	desc.transaction_id = request_id;
772250199Sgrehan	desc.range_count = 1;
773250199Sgrehan
774250199Sgrehan	desc.range.length = multi_page_buffer->length;
775250199Sgrehan	desc.range.offset = multi_page_buffer->offset;
776250199Sgrehan
777250199Sgrehan	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
778250199Sgrehan		pfn_count * sizeof(uint64_t));
779250199Sgrehan
780302870Ssephe	iov[0].iov_base = &desc;
781302870Ssephe	iov[0].iov_len = desc_size;
782250199Sgrehan
783302870Ssephe	iov[1].iov_base = buffer;
784302870Ssephe	iov[1].iov_len = buffer_len;
785250199Sgrehan
786302870Ssephe	iov[2].iov_base = &aligned_data;
787302870Ssephe	iov[2].iov_len = packet_len_aligned - packet_len;
788250199Sgrehan
789302870Ssephe	ret = hv_ring_buffer_write(&channel->outbound, iov, 3, &need_sig);
790250199Sgrehan
791250199Sgrehan	/* TODO: We should determine if this is optional */
792302731Ssephe	if (ret == 0 && need_sig)
793302731Ssephe		vmbus_chan_send_event(channel);
794250199Sgrehan
795250199Sgrehan	return (ret);
796250199Sgrehan}
797250199Sgrehan
798250199Sgrehan/**
799250199Sgrehan * @brief Retrieve the user packet on the specified channel
800250199Sgrehan */
801250199Sgrehanint
802250199Sgrehanhv_vmbus_channel_recv_packet(
803250199Sgrehan	hv_vmbus_channel*	channel,
804250199Sgrehan	void*			Buffer,
805250199Sgrehan	uint32_t		buffer_len,
806250199Sgrehan	uint32_t*		buffer_actual_len,
807250199Sgrehan	uint64_t*		request_id)
808250199Sgrehan{
809250199Sgrehan	int			ret;
810250199Sgrehan	uint32_t		user_len;
811250199Sgrehan	uint32_t		packet_len;
812250199Sgrehan	hv_vm_packet_descriptor	desc;
813250199Sgrehan
814250199Sgrehan	*buffer_actual_len = 0;
815250199Sgrehan	*request_id = 0;
816250199Sgrehan
817250199Sgrehan	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
818250199Sgrehan		sizeof(hv_vm_packet_descriptor));
819250199Sgrehan	if (ret != 0)
820250199Sgrehan		return (0);
821250199Sgrehan
822250199Sgrehan	packet_len = desc.length8 << 3;
823250199Sgrehan	user_len = packet_len - (desc.data_offset8 << 3);
824250199Sgrehan
825250199Sgrehan	*buffer_actual_len = user_len;
826250199Sgrehan
827250199Sgrehan	if (user_len > buffer_len)
828250199Sgrehan		return (EINVAL);
829250199Sgrehan
830250199Sgrehan	*request_id = desc.transaction_id;
831250199Sgrehan
832250199Sgrehan	/* Copy over the packet to the user buffer */
833250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
834250199Sgrehan		(desc.data_offset8 << 3));
835250199Sgrehan
836250199Sgrehan	return (0);
837250199Sgrehan}
838250199Sgrehan
839250199Sgrehan/**
840250199Sgrehan * @brief Retrieve the raw packet on the specified channel
841250199Sgrehan */
842250199Sgrehanint
843250199Sgrehanhv_vmbus_channel_recv_packet_raw(
844250199Sgrehan	hv_vmbus_channel*	channel,
845250199Sgrehan	void*			buffer,
846250199Sgrehan	uint32_t		buffer_len,
847250199Sgrehan	uint32_t*		buffer_actual_len,
848250199Sgrehan	uint64_t*		request_id)
849250199Sgrehan{
850250199Sgrehan	int		ret;
851250199Sgrehan	uint32_t	packetLen;
852250199Sgrehan	hv_vm_packet_descriptor	desc;
853250199Sgrehan
854250199Sgrehan	*buffer_actual_len = 0;
855250199Sgrehan	*request_id = 0;
856250199Sgrehan
857250199Sgrehan	ret = hv_ring_buffer_peek(
858250199Sgrehan		&channel->inbound, &desc,
859250199Sgrehan		sizeof(hv_vm_packet_descriptor));
860250199Sgrehan
861250199Sgrehan	if (ret != 0)
862250199Sgrehan	    return (0);
863250199Sgrehan
864250199Sgrehan	packetLen = desc.length8 << 3;
865250199Sgrehan	*buffer_actual_len = packetLen;
866250199Sgrehan
867250199Sgrehan	if (packetLen > buffer_len)
868250199Sgrehan	    return (ENOBUFS);
869250199Sgrehan
870250199Sgrehan	*request_id = desc.transaction_id;
871250199Sgrehan
872250199Sgrehan	/* Copy over the entire packet to the user buffer */
873250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
874250199Sgrehan
875250199Sgrehan	return (0);
876250199Sgrehan}
877294886Ssephe
878294886Ssephestatic void
879302713Ssephevmbus_chan_task(void *xchan, int pending __unused)
880294886Ssephe{
881302713Ssephe	struct hv_vmbus_channel *chan = xchan;
882302713Ssephe	void (*callback)(void *);
883302713Ssephe	void *arg;
884294886Ssephe
885302713Ssephe	arg = chan->channel_callback_context;
886302713Ssephe	callback = chan->on_channel_callback;
887302709Ssephe
888302710Ssephe	/*
889302710Ssephe	 * Optimize host to guest signaling by ensuring:
890302710Ssephe	 * 1. While reading the channel, we disable interrupts from
891302710Ssephe	 *    host.
892302710Ssephe	 * 2. Ensure that we process all posted messages from the host
893302710Ssephe	 *    before returning from this callback.
894302710Ssephe	 * 3. Once we return, enable signaling from the host. Once this
895302710Ssephe	 *    state is set we check to see if additional packets are
896302710Ssephe	 *    available to read. In this case we repeat the process.
897302713Ssephe	 *
898302713Ssephe	 * NOTE: Interrupt has been disabled in the ISR.
899302710Ssephe	 */
900302713Ssephe	for (;;) {
901302713Ssephe		uint32_t left;
902294886Ssephe
903302713Ssephe		callback(arg);
904294886Ssephe
905302713Ssephe		left = hv_ring_buffer_read_end(&chan->inbound);
906302713Ssephe		if (left == 0) {
907302713Ssephe			/* No more data in RX bufring; done */
908302713Ssephe			break;
909302713Ssephe		}
910302713Ssephe		hv_ring_buffer_read_begin(&chan->inbound);
911302713Ssephe	}
912294886Ssephe}
913302692Ssephe
914302713Ssephestatic void
915302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused)
916302713Ssephe{
917302713Ssephe	struct hv_vmbus_channel *chan = xchan;
918302713Ssephe
919302713Ssephe	chan->on_channel_callback(chan->channel_callback_context);
920302713Ssephe}
921302713Ssephe
922302692Ssephestatic __inline void
923302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
924302692Ssephe    int flag_cnt)
925302692Ssephe{
926302692Ssephe	int f;
927302692Ssephe
928302692Ssephe	for (f = 0; f < flag_cnt; ++f) {
929302806Ssephe		uint32_t chid_base;
930302692Ssephe		u_long flags;
931302806Ssephe		int chid_ofs;
932302692Ssephe
933302692Ssephe		if (event_flags[f] == 0)
934302692Ssephe			continue;
935302692Ssephe
936302692Ssephe		flags = atomic_swap_long(&event_flags[f], 0);
937302806Ssephe		chid_base = f << VMBUS_EVTFLAG_SHIFT;
938302692Ssephe
939302806Ssephe		while ((chid_ofs = ffsl(flags)) != 0) {
940302692Ssephe			struct hv_vmbus_channel *channel;
941302692Ssephe
942302806Ssephe			--chid_ofs; /* NOTE: ffsl is 1-based */
943302806Ssephe			flags &= ~(1UL << chid_ofs);
944302692Ssephe
945302806Ssephe			channel = sc->vmbus_chmap[chid_base + chid_ofs];
946302692Ssephe
947302692Ssephe			/* if channel is closed or closing */
948302692Ssephe			if (channel == NULL || channel->rxq == NULL)
949302692Ssephe				continue;
950302692Ssephe
951302709Ssephe			if (channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
952302692Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
953302692Ssephe			taskqueue_enqueue(channel->rxq, &channel->channel_task);
954302692Ssephe		}
955302692Ssephe	}
956302692Ssephe}
957302692Ssephe
958302692Ssephevoid
959302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu)
960302692Ssephe{
961302692Ssephe	struct vmbus_evtflags *eventf;
962302692Ssephe
963302692Ssephe	/*
964302692Ssephe	 * On Host with Win8 or above, the event page can be checked directly
965302692Ssephe	 * to get the id of the channel that has the pending interrupt.
966302692Ssephe	 */
967302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
968302692Ssephe	vmbus_event_flags_proc(sc, eventf->evt_flags,
969302692Ssephe	    VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
970302692Ssephe}
971302692Ssephe
972302692Ssephevoid
973302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
974302692Ssephe{
975302692Ssephe	struct vmbus_evtflags *eventf;
976302692Ssephe
977302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
978302692Ssephe	if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
979302692Ssephe		vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
980302692Ssephe		    VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
981302692Ssephe	}
982302692Ssephe}
983302692Ssephe
984302692Ssephestatic void
985302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
986302692Ssephe    const struct hv_vmbus_channel *chan)
987302692Ssephe{
988302692Ssephe	volatile int *flag_cnt_ptr;
989302692Ssephe	int flag_cnt;
990302692Ssephe
991302693Ssephe	flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
992302692Ssephe	flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->target_cpu);
993302692Ssephe
994302692Ssephe	for (;;) {
995302692Ssephe		int old_flag_cnt;
996302692Ssephe
997302692Ssephe		old_flag_cnt = *flag_cnt_ptr;
998302692Ssephe		if (old_flag_cnt >= flag_cnt)
999302692Ssephe			break;
1000302692Ssephe		if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
1001302692Ssephe			if (bootverbose) {
1002302692Ssephe				device_printf(sc->vmbus_dev,
1003302692Ssephe				    "channel%u update cpu%d flag_cnt to %d\n",
1004302693Ssephe				    chan->ch_id,
1005302692Ssephe				    chan->target_cpu, flag_cnt);
1006302692Ssephe			}
1007302692Ssephe			break;
1008302692Ssephe		}
1009302692Ssephe	}
1010302692Ssephe}
1011302864Ssephe
1012302864Ssephestatic struct hv_vmbus_channel *
1013302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc)
1014302864Ssephe{
1015302864Ssephe	struct hv_vmbus_channel *chan;
1016302864Ssephe
1017302864Ssephe	chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
1018302864Ssephe
1019302864Ssephe	chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
1020302864Ssephe	    HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
1021302864Ssephe	    &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
1022302864Ssephe	if (chan->ch_monprm == NULL) {
1023302864Ssephe		device_printf(sc->vmbus_dev, "monprm alloc failed\n");
1024302864Ssephe		free(chan, M_DEVBUF);
1025302864Ssephe		return NULL;
1026302864Ssephe	}
1027302864Ssephe
1028302864Ssephe	chan->vmbus_sc = sc;
1029302864Ssephe	mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
1030302864Ssephe	TAILQ_INIT(&chan->ch_subchans);
1031302864Ssephe	TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
1032302864Ssephe
1033302864Ssephe	return chan;
1034302864Ssephe}
1035302864Ssephe
1036302864Ssephestatic void
1037302864Ssephevmbus_chan_free(struct hv_vmbus_channel *chan)
1038302864Ssephe{
1039302864Ssephe	/* TODO: assert sub-channel list is empty */
1040302864Ssephe	/* TODO: asset no longer on the primary channel's sub-channel list */
1041302864Ssephe	/* TODO: asset no longer on the vmbus channel list */
1042302864Ssephe	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
1043302864Ssephe	mtx_destroy(&chan->ch_subchan_lock);
1044302864Ssephe	free(chan, M_DEVBUF);
1045302864Ssephe}
1046302864Ssephe
1047302864Ssephestatic int
1048302864Ssephevmbus_chan_add(struct hv_vmbus_channel *newchan)
1049302864Ssephe{
1050302864Ssephe	struct vmbus_softc *sc = newchan->vmbus_sc;
1051302864Ssephe	struct hv_vmbus_channel *prichan;
1052302864Ssephe
1053302864Ssephe	if (newchan->ch_id == 0) {
1054302864Ssephe		/*
1055302864Ssephe		 * XXX
1056302864Ssephe		 * Chan0 will neither be processed nor should be offered;
1057302864Ssephe		 * skip it.
1058302864Ssephe		 */
1059302864Ssephe		device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
1060302864Ssephe		return EINVAL;
1061302864Ssephe	} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
1062302864Ssephe		device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
1063302864Ssephe		    newchan->ch_id);
1064302864Ssephe		return EINVAL;
1065302864Ssephe	}
1066302864Ssephe	sc->vmbus_chmap[newchan->ch_id] = newchan;
1067302864Ssephe
1068302864Ssephe	if (bootverbose) {
1069302864Ssephe		device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
1070302864Ssephe		    newchan->ch_id, newchan->ch_subidx);
1071302864Ssephe	}
1072302864Ssephe
1073302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1074302864Ssephe	TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
1075302864Ssephe		/*
1076302864Ssephe		 * Sub-channel will have the same type GUID and instance
1077302864Ssephe		 * GUID as its primary channel.
1078302864Ssephe		 */
1079302864Ssephe		if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
1080302864Ssephe		    sizeof(struct hyperv_guid)) == 0 &&
1081302864Ssephe		    memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
1082302864Ssephe		    sizeof(struct hyperv_guid)) == 0)
1083302864Ssephe			break;
1084302864Ssephe	}
1085302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(newchan)) {
1086302864Ssephe		if (prichan == NULL) {
1087302864Ssephe			/* Install the new primary channel */
1088302864Ssephe			TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
1089302864Ssephe			    ch_prilink);
1090302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1091302864Ssephe			return 0;
1092302864Ssephe		} else {
1093302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1094302864Ssephe			device_printf(sc->vmbus_dev, "duplicated primary "
1095302864Ssephe			    "chan%u\n", newchan->ch_id);
1096302864Ssephe			return EINVAL;
1097302864Ssephe		}
1098302864Ssephe	} else { /* Sub-channel */
1099302864Ssephe		if (prichan == NULL) {
1100302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1101302864Ssephe			device_printf(sc->vmbus_dev, "no primary chan for "
1102302864Ssephe			    "chan%u\n", newchan->ch_id);
1103302864Ssephe			return EINVAL;
1104302864Ssephe		}
1105302864Ssephe		/*
1106302864Ssephe		 * Found the primary channel for this sub-channel and
1107302864Ssephe		 * move on.
1108302864Ssephe		 *
1109302864Ssephe		 * XXX refcnt prichan
1110302864Ssephe		 */
1111302864Ssephe	}
1112302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1113302864Ssephe
1114302864Ssephe	/*
1115302864Ssephe	 * This is a sub-channel; link it with the primary channel.
1116302864Ssephe	 */
1117302864Ssephe	KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
1118302864Ssephe	    ("new channel is not sub-channel"));
1119302864Ssephe	KASSERT(prichan != NULL, ("no primary channel"));
1120302864Ssephe
1121302864Ssephe	newchan->ch_prichan = prichan;
1122302864Ssephe	newchan->ch_dev = prichan->ch_dev;
1123302864Ssephe
1124302864Ssephe	mtx_lock(&prichan->ch_subchan_lock);
1125302864Ssephe	TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
1126302864Ssephe	/*
1127302864Ssephe	 * Bump up sub-channel count and notify anyone that is
1128302864Ssephe	 * interested in this sub-channel, after this sub-channel
1129302864Ssephe	 * is setup.
1130302864Ssephe	 */
1131302864Ssephe	prichan->ch_subchan_cnt++;
1132302864Ssephe	mtx_unlock(&prichan->ch_subchan_lock);
1133302864Ssephe	wakeup(prichan);
1134302864Ssephe
1135302864Ssephe	return 0;
1136302864Ssephe}
1137302864Ssephe
1138302864Ssephevoid
1139302864Ssephevmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
1140302864Ssephe{
1141302864Ssephe	KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
1142302864Ssephe
1143302864Ssephe	if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
1144302864Ssephe	    chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
1145302864Ssephe		/* Only cpu0 is supported */
1146302864Ssephe		cpu = 0;
1147302864Ssephe	}
1148302864Ssephe
1149302864Ssephe	chan->target_cpu = cpu;
1150302864Ssephe	chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
1151302864Ssephe
1152302864Ssephe	if (bootverbose) {
1153302864Ssephe		printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
1154302864Ssephe		    chan->ch_id,
1155302864Ssephe		    chan->target_cpu, chan->target_vcpu);
1156302864Ssephe	}
1157302864Ssephe}
1158302864Ssephe
1159302864Ssephevoid
1160302864Ssephevmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
1161302864Ssephe{
1162302864Ssephe	static uint32_t vmbus_chan_nextcpu;
1163302864Ssephe	int cpu;
1164302864Ssephe
1165302864Ssephe	cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
1166302864Ssephe	vmbus_channel_cpu_set(chan, cpu);
1167302864Ssephe}
1168302864Ssephe
1169302864Ssephestatic void
1170302864Ssephevmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
1171302864Ssephe{
1172302864Ssephe	/*
1173302864Ssephe	 * By default, pin the channel to cpu0.  Devices having
1174302864Ssephe	 * special channel-cpu mapping requirement should call
1175302864Ssephe	 * vmbus_channel_cpu_{set,rr}().
1176302864Ssephe	 */
1177302864Ssephe	vmbus_channel_cpu_set(chan, 0);
1178302864Ssephe}
1179302864Ssephe
1180302864Ssephestatic void
1181302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
1182302864Ssephe    const struct vmbus_message *msg)
1183302864Ssephe{
1184302864Ssephe	const struct vmbus_chanmsg_choffer *offer;
1185302864Ssephe	struct hv_vmbus_channel *chan;
1186302864Ssephe	int error;
1187302864Ssephe
1188302864Ssephe	offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
1189302864Ssephe
1190302864Ssephe	chan = vmbus_chan_alloc(sc);
1191302864Ssephe	if (chan == NULL) {
1192302864Ssephe		device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
1193302864Ssephe		    offer->chm_chanid);
1194302864Ssephe		return;
1195302864Ssephe	}
1196302864Ssephe
1197302864Ssephe	chan->ch_id = offer->chm_chanid;
1198302864Ssephe	chan->ch_subidx = offer->chm_subidx;
1199302864Ssephe	chan->ch_guid_type = offer->chm_chtype;
1200302864Ssephe	chan->ch_guid_inst = offer->chm_chinst;
1201302864Ssephe
1202302864Ssephe	/* Batch reading is on by default */
1203302864Ssephe	chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
1204302864Ssephe
1205302864Ssephe	chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
1206302864Ssephe	if (sc->vmbus_version != VMBUS_VERSION_WS2008)
1207302864Ssephe		chan->ch_monprm->mp_connid = offer->chm_connid;
1208302864Ssephe
1209302864Ssephe	if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
1210302864Ssephe		/*
1211302864Ssephe		 * Setup MNF stuffs.
1212302864Ssephe		 */
1213302864Ssephe		chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
1214302864Ssephe		chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
1215302864Ssephe		if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
1216302864Ssephe			panic("invalid monitor trigger %u", offer->chm_montrig);
1217302864Ssephe		chan->ch_montrig_mask =
1218302864Ssephe		    1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
1219302864Ssephe	}
1220302864Ssephe
1221302864Ssephe	/* Select default cpu for this channel. */
1222302864Ssephe	vmbus_chan_cpu_default(chan);
1223302864Ssephe
1224302864Ssephe	error = vmbus_chan_add(chan);
1225302864Ssephe	if (error) {
1226302864Ssephe		device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
1227302864Ssephe		    chan->ch_id, error);
1228302864Ssephe		vmbus_chan_free(chan);
1229302864Ssephe		return;
1230302864Ssephe	}
1231302864Ssephe
1232302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1233302864Ssephe		/*
1234302864Ssephe		 * Add device for this primary channel.
1235302864Ssephe		 *
1236302864Ssephe		 * NOTE:
1237302864Ssephe		 * Error is ignored here; don't have much to do if error
1238302864Ssephe		 * really happens.
1239302864Ssephe		 */
1240302868Ssephe		vmbus_add_child(chan);
1241302864Ssephe	}
1242302864Ssephe}
1243302864Ssephe
1244302864Ssephe/*
1245302864Ssephe * XXX pretty broken; need rework.
1246302864Ssephe */
1247302864Ssephestatic void
1248302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
1249302864Ssephe    const struct vmbus_message *msg)
1250302864Ssephe{
1251302864Ssephe	const struct vmbus_chanmsg_chrescind *note;
1252302864Ssephe	struct hv_vmbus_channel *chan;
1253302864Ssephe
1254302864Ssephe	note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
1255302864Ssephe	if (note->chm_chanid > VMBUS_CHAN_MAX) {
1256302864Ssephe		device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
1257302864Ssephe		    note->chm_chanid);
1258302864Ssephe		return;
1259302864Ssephe	}
1260302864Ssephe
1261302864Ssephe	if (bootverbose) {
1262302864Ssephe		device_printf(sc->vmbus_dev, "chan%u rescinded\n",
1263302864Ssephe		    note->chm_chanid);
1264302864Ssephe	}
1265302864Ssephe
1266302864Ssephe	chan = sc->vmbus_chmap[note->chm_chanid];
1267302864Ssephe	if (chan == NULL)
1268302864Ssephe		return;
1269302864Ssephe	sc->vmbus_chmap[note->chm_chanid] = NULL;
1270302864Ssephe
1271302864Ssephe	taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
1272302864Ssephe}
1273302864Ssephe
1274302864Ssephestatic void
1275302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused)
1276302864Ssephe{
1277302864Ssephe	struct hv_vmbus_channel *chan = xchan;
1278302864Ssephe
1279302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1280302864Ssephe		/* Only primary channel owns the device */
1281302868Ssephe		vmbus_delete_child(chan);
1282302864Ssephe		/* NOTE: DO NOT free primary channel for now */
1283302864Ssephe	} else {
1284302864Ssephe		struct vmbus_softc *sc = chan->vmbus_sc;
1285302864Ssephe		struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
1286302864Ssephe		struct vmbus_chanmsg_chfree *req;
1287302864Ssephe		struct vmbus_msghc *mh;
1288302864Ssephe		int error;
1289302864Ssephe
1290302864Ssephe		mh = vmbus_msghc_get(sc, sizeof(*req));
1291302864Ssephe		if (mh == NULL) {
1292302864Ssephe			device_printf(sc->vmbus_dev,
1293302864Ssephe			    "can not get msg hypercall for chfree(chan%u)\n",
1294302864Ssephe			    chan->ch_id);
1295302864Ssephe			goto remove;
1296302864Ssephe		}
1297302864Ssephe
1298302864Ssephe		req = vmbus_msghc_dataptr(mh);
1299302864Ssephe		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
1300302864Ssephe		req->chm_chanid = chan->ch_id;
1301302864Ssephe
1302302864Ssephe		error = vmbus_msghc_exec_noresult(mh);
1303302864Ssephe		vmbus_msghc_put(sc, mh);
1304302864Ssephe
1305302864Ssephe		if (error) {
1306302864Ssephe			device_printf(sc->vmbus_dev,
1307302864Ssephe			    "chfree(chan%u) failed: %d",
1308302864Ssephe			    chan->ch_id, error);
1309302864Ssephe			/* NOTE: Move on! */
1310302864Ssephe		} else {
1311302864Ssephe			if (bootverbose) {
1312302864Ssephe				device_printf(sc->vmbus_dev, "chan%u freed\n",
1313302864Ssephe				    chan->ch_id);
1314302864Ssephe			}
1315302864Ssephe		}
1316302864Ssepheremove:
1317302864Ssephe		mtx_lock(&pri_chan->ch_subchan_lock);
1318302864Ssephe		TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
1319302864Ssephe		KASSERT(pri_chan->ch_subchan_cnt > 0,
1320302864Ssephe		    ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
1321302864Ssephe		pri_chan->ch_subchan_cnt--;
1322302864Ssephe		mtx_unlock(&pri_chan->ch_subchan_lock);
1323302864Ssephe		wakeup(pri_chan);
1324302864Ssephe
1325302864Ssephe		vmbus_chan_free(chan);
1326302864Ssephe	}
1327302864Ssephe}
1328302864Ssephe
1329302864Ssephe/*
1330302864Ssephe * Detach all devices and destroy the corresponding primary channels.
1331302864Ssephe */
1332302864Ssephevoid
1333302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc)
1334302864Ssephe{
1335302864Ssephe	struct hv_vmbus_channel *chan;
1336302864Ssephe
1337302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1338302864Ssephe	while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
1339302864Ssephe		KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
1340302864Ssephe		TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
1341302864Ssephe		mtx_unlock(&sc->vmbus_prichan_lock);
1342302864Ssephe
1343302868Ssephe		vmbus_delete_child(chan);
1344302864Ssephe		vmbus_chan_free(chan);
1345302864Ssephe
1346302864Ssephe		mtx_lock(&sc->vmbus_prichan_lock);
1347302864Ssephe	}
1348302864Ssephe	bzero(sc->vmbus_chmap,
1349302864Ssephe	    sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
1350302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1351302864Ssephe}
1352302864Ssephe
1353302864Ssephe/**
1354302864Ssephe * @brief Select the best outgoing channel
1355302864Ssephe *
1356302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will
1357302864Ssephe * be selected.
1358302864Ssephe * If no multi-channel, always select primary channel
1359302864Ssephe *
1360302864Ssephe * @param primary - primary channel
1361302864Ssephe */
1362302864Ssephestruct hv_vmbus_channel *
1363302864Ssephevmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
1364302864Ssephe{
1365302864Ssephe	hv_vmbus_channel *new_channel = NULL;
1366302864Ssephe	hv_vmbus_channel *outgoing_channel = primary;
1367302864Ssephe	int old_cpu_distance = 0;
1368302864Ssephe	int new_cpu_distance = 0;
1369302864Ssephe	int cur_vcpu = 0;
1370302864Ssephe	int smp_pro_id = PCPU_GET(cpuid);
1371302864Ssephe
1372302864Ssephe	if (TAILQ_EMPTY(&primary->ch_subchans)) {
1373302864Ssephe		return outgoing_channel;
1374302864Ssephe	}
1375302864Ssephe
1376302864Ssephe	if (smp_pro_id >= MAXCPU) {
1377302864Ssephe		return outgoing_channel;
1378302864Ssephe	}
1379302864Ssephe
1380302864Ssephe	cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
1381302864Ssephe
1382302864Ssephe	/* XXX need lock */
1383302864Ssephe	TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
1384302864Ssephe		if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
1385302864Ssephe			continue;
1386302864Ssephe		}
1387302864Ssephe
1388302864Ssephe		if (new_channel->target_vcpu == cur_vcpu){
1389302864Ssephe			return new_channel;
1390302864Ssephe		}
1391302864Ssephe
1392302864Ssephe		old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
1393302864Ssephe		    (outgoing_channel->target_vcpu - cur_vcpu) :
1394302864Ssephe		    (cur_vcpu - outgoing_channel->target_vcpu));
1395302864Ssephe
1396302864Ssephe		new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
1397302864Ssephe		    (new_channel->target_vcpu - cur_vcpu) :
1398302864Ssephe		    (cur_vcpu - new_channel->target_vcpu));
1399302864Ssephe
1400302864Ssephe		if (old_cpu_distance < new_cpu_distance) {
1401302864Ssephe			continue;
1402302864Ssephe		}
1403302864Ssephe
1404302864Ssephe		outgoing_channel = new_channel;
1405302864Ssephe	}
1406302864Ssephe
1407302864Ssephe	return(outgoing_channel);
1408302864Ssephe}
1409302864Ssephe
1410302864Ssephestruct hv_vmbus_channel **
1411302864Ssephevmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
1412302864Ssephe{
1413302864Ssephe	struct hv_vmbus_channel **ret, *chan;
1414302864Ssephe	int i;
1415302864Ssephe
1416302864Ssephe	ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
1417302864Ssephe	    M_WAITOK);
1418302864Ssephe
1419302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1420302864Ssephe
1421302864Ssephe	while (pri_chan->ch_subchan_cnt < subchan_cnt)
1422302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
1423302864Ssephe
1424302864Ssephe	i = 0;
1425302864Ssephe	TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
1426302864Ssephe		/* TODO: refcnt chan */
1427302864Ssephe		ret[i] = chan;
1428302864Ssephe
1429302864Ssephe		++i;
1430302864Ssephe		if (i == subchan_cnt)
1431302864Ssephe			break;
1432302864Ssephe	}
1433302864Ssephe	KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
1434302864Ssephe	    pri_chan->ch_subchan_cnt, subchan_cnt));
1435302864Ssephe
1436302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1437302864Ssephe
1438302864Ssephe	return ret;
1439302864Ssephe}
1440302864Ssephe
1441302864Ssephevoid
1442302864Ssephevmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
1443302864Ssephe{
1444302864Ssephe
1445302864Ssephe	free(subchan, M_TEMP);
1446302864Ssephe}
1447302864Ssephe
1448302864Ssephevoid
1449302864Ssephevmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
1450302864Ssephe{
1451302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1452302864Ssephe	while (pri_chan->ch_subchan_cnt > 0)
1453302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
1454302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1455302864Ssephe}
1456302864Ssephe
1457302864Ssephevoid
1458302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1459302864Ssephe{
1460302864Ssephe	vmbus_chanmsg_proc_t msg_proc;
1461302864Ssephe	uint32_t msg_type;
1462302864Ssephe
1463302864Ssephe	msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
1464302864Ssephe	KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
1465302864Ssephe	    ("invalid message type %u", msg_type));
1466302864Ssephe
1467302864Ssephe	msg_proc = vmbus_chan_msgprocs[msg_type];
1468302864Ssephe	if (msg_proc != NULL)
1469302864Ssephe		msg_proc(sc, msg);
1470302864Ssephe}
1471