vmbus_chan.c revision 302892
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 302892 2016-07-15 08:40:22Z sephe $");
31256276Sdim
32250199Sgrehan#include <sys/param.h>
33296028Ssephe#include <sys/kernel.h>
34250199Sgrehan#include <sys/malloc.h>
35250199Sgrehan#include <sys/systm.h>
36250199Sgrehan#include <sys/mbuf.h>
37250199Sgrehan#include <sys/lock.h>
38250199Sgrehan#include <sys/mutex.h>
39296181Ssephe#include <sys/sysctl.h>
40301588Ssephe
41301588Ssephe#include <machine/atomic.h>
42250199Sgrehan#include <machine/bus.h>
43301588Ssephe
44250199Sgrehan#include <vm/vm.h>
45250199Sgrehan#include <vm/vm_param.h>
46250199Sgrehan#include <vm/pmap.h>
47250199Sgrehan
48302872Ssephe#include <dev/hyperv/include/hyperv_busdma.h>
49300102Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
50302619Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
51301588Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h>
52300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h>
53250199Sgrehan
54302731Ssephestatic void 	vmbus_chan_send_event(hv_vmbus_channel* channel);
55302692Ssephestatic void	vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
56302692Ssephe		    const struct hv_vmbus_channel *);
57302864Ssephe
58302713Ssephestatic void	vmbus_chan_task(void *, int);
59302713Ssephestatic void	vmbus_chan_task_nobatch(void *, int);
60302864Ssephestatic void	vmbus_chan_detach_task(void *, int);
61250199Sgrehan
62302864Ssephestatic void	vmbus_chan_msgproc_choffer(struct vmbus_softc *,
63302864Ssephe		    const struct vmbus_message *);
64302864Ssephestatic void	vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
65302864Ssephe		    const struct vmbus_message *);
66302864Ssephe
67302864Ssephe/*
68302864Ssephe * Vmbus channel message processing.
69302864Ssephe */
70302864Ssephestatic const vmbus_chanmsg_proc_t
71302864Ssephevmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
72302864Ssephe	VMBUS_CHANMSG_PROC(CHOFFER,	vmbus_chan_msgproc_choffer),
73302864Ssephe	VMBUS_CHANMSG_PROC(CHRESCIND,	vmbus_chan_msgproc_chrescind),
74302864Ssephe
75302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
76302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
77302864Ssephe	VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
78302864Ssephe};
79302864Ssephe
80250199Sgrehan/**
81250199Sgrehan *  @brief Trigger an event notification on the specified channel
82250199Sgrehan */
83250199Sgrehanstatic void
84302731Ssephevmbus_chan_send_event(hv_vmbus_channel *channel)
85250199Sgrehan{
86302618Ssephe	struct vmbus_softc *sc = channel->vmbus_sc;
87302693Ssephe	uint32_t chanid = channel->ch_id;
88302618Ssephe
89302618Ssephe	atomic_set_long(&sc->vmbus_tx_evtflags[chanid >> VMBUS_EVTFLAG_SHIFT],
90302618Ssephe	    1UL << (chanid & VMBUS_EVTFLAG_MASK));
91302618Ssephe
92302695Ssephe	if (channel->ch_flags & VMBUS_CHAN_FLAG_HASMNF) {
93302731Ssephe		atomic_set_int(
94302731Ssephe		&sc->vmbus_mnf2->mnf_trigs[channel->ch_montrig_idx].mt_pending,
95302731Ssephe		channel->ch_montrig_mask);
96250199Sgrehan	} else {
97302726Ssephe		hypercall_signal_event(channel->ch_monprm_dma.hv_paddr);
98250199Sgrehan	}
99250199Sgrehan}
100250199Sgrehan
101296289Ssephestatic int
102302892Ssephevmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS)
103296289Ssephe{
104296289Ssephe	struct hv_vmbus_channel *chan = arg1;
105302892Ssephe	int mnf = 0;
106296289Ssephe
107302695Ssephe	if (chan->ch_flags & VMBUS_CHAN_FLAG_HASMNF)
108302892Ssephe		mnf = 1;
109302892Ssephe	return sysctl_handle_int(oidp, &mnf, 0, req);
110296289Ssephe}
111296289Ssephe
112296181Ssephestatic void
113302892Ssephevmbus_chan_sysctl_create(struct hv_vmbus_channel *chan)
114296181Ssephe{
115302892Ssephe	struct sysctl_oid *ch_tree, *chid_tree, *br_tree;
116296181Ssephe	struct sysctl_ctx_list *ctx;
117296181Ssephe	uint32_t ch_id;
118296181Ssephe	char name[16];
119296181Ssephe
120302892Ssephe	/*
121302892Ssephe	 * Add sysctl nodes related to this channel to this
122302892Ssephe	 * channel's sysctl ctx, so that they can be destroyed
123302892Ssephe	 * independently upon close of this channel, which can
124302892Ssephe	 * happen even if the device is not detached.
125302892Ssephe	 */
126302892Ssephe	ctx = &chan->ch_sysctl_ctx;
127302633Ssephe	sysctl_ctx_init(ctx);
128302892Ssephe
129302892Ssephe	/*
130302892Ssephe	 * Create dev.NAME.UNIT.channel tree.
131302892Ssephe	 */
132302892Ssephe	ch_tree = SYSCTL_ADD_NODE(ctx,
133302892Ssephe	    SYSCTL_CHILDREN(device_get_sysctl_tree(chan->ch_dev)),
134302892Ssephe	    OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
135302892Ssephe	if (ch_tree == NULL)
136302892Ssephe		return;
137302892Ssephe
138302892Ssephe	/*
139302892Ssephe	 * Create dev.NAME.UNIT.channel.CHANID tree.
140302892Ssephe	 */
141302892Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan))
142302892Ssephe		ch_id = chan->ch_id;
143302892Ssephe	else
144302892Ssephe		ch_id = chan->ch_prichan->ch_id;
145296181Ssephe	snprintf(name, sizeof(name), "%d", ch_id);
146302892Ssephe	chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
147302892Ssephe	    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
148302892Ssephe	if (chid_tree == NULL)
149302892Ssephe		return;
150296181Ssephe
151302892Ssephe	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
152302892Ssephe		/*
153302892Ssephe		 * Create dev.NAME.UNIT.channel.CHANID.sub tree.
154302892Ssephe		 */
155302892Ssephe		ch_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree),
156302892Ssephe		    OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
157302892Ssephe		if (ch_tree == NULL)
158302892Ssephe			return;
159296188Ssephe
160302892Ssephe		/*
161302892Ssephe		 * Create dev.NAME.UNIT.channel.CHANID.sub.SUBIDX tree.
162302892Ssephe		 *
163302892Ssephe		 * NOTE:
164302892Ssephe		 * chid_tree is changed to this new sysctl tree.
165302892Ssephe		 */
166302892Ssephe		snprintf(name, sizeof(name), "%d", chan->ch_subidx);
167302892Ssephe		chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
168302892Ssephe		    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
169302892Ssephe		if (chid_tree == NULL)
170302892Ssephe			return;
171302892Ssephe
172302892Ssephe		SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
173302892Ssephe		    "chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id");
174296181Ssephe	}
175296188Ssephe
176302892Ssephe	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
177302892Ssephe	    "cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id");
178302892Ssephe	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
179302892Ssephe	    "mnf", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
180302892Ssephe	    chan, 0, vmbus_chan_sysctl_mnf, "I",
181302892Ssephe	    "has monitor notification facilities");
182302892Ssephe
183302892Ssephe	/*
184302892Ssephe	 * Create sysctl tree for RX bufring.
185302892Ssephe	 */
186302892Ssephe	br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
187302892Ssephe	    "in", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
188302892Ssephe	if (br_tree != NULL) {
189302892Ssephe		hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
190302892Ssephe		    &chan->inbound, "inbound ring buffer stats");
191302892Ssephe	}
192302892Ssephe
193302892Ssephe	/*
194302892Ssephe	 * Create sysctl tree for TX bufring.
195302892Ssephe	 */
196302892Ssephe	br_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
197302892Ssephe	    "out", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
198302892Ssephe	if (br_tree != NULL) {
199302892Ssephe		hv_ring_buffer_stat(ctx, SYSCTL_CHILDREN(br_tree),
200302892Ssephe		    &chan->outbound, "outbound ring buffer stats");
201302892Ssephe	}
202296181Ssephe}
203296290Ssephe
204250199Sgrehan/**
205250199Sgrehan * @brief Open the specified channel
206250199Sgrehan */
207250199Sgrehanint
208250199Sgrehanhv_vmbus_channel_open(
209250199Sgrehan	hv_vmbus_channel*		new_channel,
210250199Sgrehan	uint32_t			send_ring_buffer_size,
211250199Sgrehan	uint32_t			recv_ring_buffer_size,
212250199Sgrehan	void*				user_data,
213250199Sgrehan	uint32_t			user_data_len,
214302874Ssephe	vmbus_chan_callback_t		cb,
215302874Ssephe	void				*cbarg)
216250199Sgrehan{
217302607Ssephe	struct vmbus_softc *sc = new_channel->vmbus_sc;
218302607Ssephe	const struct vmbus_chanmsg_chopen_resp *resp;
219302607Ssephe	const struct vmbus_message *msg;
220302607Ssephe	struct vmbus_chanmsg_chopen *req;
221302607Ssephe	struct vmbus_msghc *mh;
222302607Ssephe	uint32_t status;
223250199Sgrehan	int ret = 0;
224302872Ssephe	uint8_t *br;
225250199Sgrehan
226302607Ssephe	if (user_data_len > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
227302607Ssephe		device_printf(sc->vmbus_dev,
228302607Ssephe		    "invalid udata len %u for chan%u\n",
229302693Ssephe		    user_data_len, new_channel->ch_id);
230302607Ssephe		return EINVAL;
231302607Ssephe	}
232302872Ssephe	KASSERT((send_ring_buffer_size & PAGE_MASK) == 0,
233302872Ssephe	    ("send bufring size is not multiple page"));
234302872Ssephe	KASSERT((recv_ring_buffer_size & PAGE_MASK) == 0,
235302872Ssephe	    ("recv bufring size is not multiple page"));
236302607Ssephe
237302812Ssephe	if (atomic_testandset_int(&new_channel->ch_stflags,
238302812Ssephe	    VMBUS_CHAN_ST_OPENED_SHIFT))
239302812Ssephe		panic("double-open chan%u", new_channel->ch_id);
240282212Swhu
241302874Ssephe	new_channel->ch_cb = cb;
242302874Ssephe	new_channel->ch_cbarg = cbarg;
243250199Sgrehan
244302692Ssephe	vmbus_chan_update_evtflagcnt(sc, new_channel);
245300102Ssephe
246302874Ssephe	new_channel->ch_tq = VMBUS_PCPU_GET(new_channel->vmbus_sc, event_tq,
247302873Ssephe	    new_channel->ch_cpuid);
248302713Ssephe	if (new_channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) {
249302874Ssephe		TASK_INIT(&new_channel->ch_task, 0, vmbus_chan_task,
250302874Ssephe		    new_channel);
251302713Ssephe	} else {
252302874Ssephe		TASK_INIT(&new_channel->ch_task, 0, vmbus_chan_task_nobatch,
253302874Ssephe		    new_channel);
254302713Ssephe	}
255294886Ssephe
256302872Ssephe	/*
257302872Ssephe	 * Allocate the TX+RX bufrings.
258302872Ssephe	 * XXX should use ch_dev dtag
259302872Ssephe	 */
260302872Ssephe	br = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
261302872Ssephe	    PAGE_SIZE, 0, send_ring_buffer_size + recv_ring_buffer_size,
262302872Ssephe	    &new_channel->ch_bufring_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
263302872Ssephe	if (br == NULL) {
264302872Ssephe		device_printf(sc->vmbus_dev, "bufring allocation failed\n");
265302812Ssephe		ret = ENOMEM;
266302812Ssephe		goto failed;
267302812Ssephe	}
268302872Ssephe	new_channel->ch_bufring = br;
269250199Sgrehan
270302872Ssephe	/* TX bufring comes first */
271302872Ssephe	hv_vmbus_ring_buffer_init(&new_channel->outbound,
272302872Ssephe	    br, send_ring_buffer_size);
273302872Ssephe	/* RX bufring immediately follows TX bufring */
274302872Ssephe	hv_vmbus_ring_buffer_init(&new_channel->inbound,
275302872Ssephe	    br + send_ring_buffer_size, recv_ring_buffer_size);
276250199Sgrehan
277296290Ssephe	/* Create sysctl tree for this channel */
278302892Ssephe	vmbus_chan_sysctl_create(new_channel);
279296181Ssephe
280302872Ssephe	/*
281302872Ssephe	 * Connect the bufrings, both RX and TX, to this channel.
282250199Sgrehan	 */
283302872Ssephe	ret = vmbus_chan_gpadl_connect(new_channel,
284302872Ssephe		new_channel->ch_bufring_dma.hv_paddr,
285250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
286302872Ssephe		&new_channel->ch_bufring_gpadl);
287302872Ssephe	if (ret != 0) {
288302872Ssephe		device_printf(sc->vmbus_dev,
289302872Ssephe		    "failed to connect bufring GPADL to chan%u\n",
290302872Ssephe		    new_channel->ch_id);
291302872Ssephe		goto failed;
292302872Ssephe	}
293250199Sgrehan
294302607Ssephe	/*
295302607Ssephe	 * Open channel w/ the bufring GPADL on the target CPU.
296250199Sgrehan	 */
297302607Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
298302607Ssephe	if (mh == NULL) {
299302607Ssephe		device_printf(sc->vmbus_dev,
300302607Ssephe		    "can not get msg hypercall for chopen(chan%u)\n",
301302693Ssephe		    new_channel->ch_id);
302302812Ssephe		ret = ENXIO;
303302812Ssephe		goto failed;
304302607Ssephe	}
305250199Sgrehan
306302607Ssephe	req = vmbus_msghc_dataptr(mh);
307302607Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHOPEN;
308302693Ssephe	req->chm_chanid = new_channel->ch_id;
309302693Ssephe	req->chm_openid = new_channel->ch_id;
310302872Ssephe	req->chm_gpadl = new_channel->ch_bufring_gpadl;
311302873Ssephe	req->chm_vcpuid = new_channel->ch_vcpuid;
312302607Ssephe	req->chm_rxbr_pgofs = send_ring_buffer_size >> PAGE_SHIFT;
313250199Sgrehan	if (user_data_len)
314302607Ssephe		memcpy(req->chm_udata, user_data, user_data_len);
315250199Sgrehan
316302607Ssephe	ret = vmbus_msghc_exec(sc, mh);
317302607Ssephe	if (ret != 0) {
318302607Ssephe		device_printf(sc->vmbus_dev,
319302607Ssephe		    "chopen(chan%u) msg hypercall exec failed: %d\n",
320302693Ssephe		    new_channel->ch_id, ret);
321302607Ssephe		vmbus_msghc_put(sc, mh);
322302812Ssephe		goto failed;
323302607Ssephe	}
324250199Sgrehan
325302607Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
326302607Ssephe	resp = (const struct vmbus_chanmsg_chopen_resp *)msg->msg_data;
327302607Ssephe	status = resp->chm_status;
328250199Sgrehan
329302607Ssephe	vmbus_msghc_put(sc, mh);
330250199Sgrehan
331302607Ssephe	if (status == 0) {
332302607Ssephe		if (bootverbose) {
333302607Ssephe			device_printf(sc->vmbus_dev, "chan%u opened\n",
334302693Ssephe			    new_channel->ch_id);
335302607Ssephe		}
336302812Ssephe		return 0;
337250199Sgrehan	}
338302812Ssephe
339302812Ssephe	device_printf(sc->vmbus_dev, "failed to open chan%u\n",
340302812Ssephe	    new_channel->ch_id);
341302812Ssephe	ret = ENXIO;
342302812Ssephe
343302812Ssephefailed:
344302872Ssephe	if (new_channel->ch_bufring_gpadl) {
345302890Ssephe		vmbus_chan_gpadl_disconnect(new_channel,
346302872Ssephe		    new_channel->ch_bufring_gpadl);
347302872Ssephe		new_channel->ch_bufring_gpadl = 0;
348302872Ssephe	}
349302872Ssephe	if (new_channel->ch_bufring != NULL) {
350302872Ssephe		hyperv_dmamem_free(&new_channel->ch_bufring_dma,
351302872Ssephe		    new_channel->ch_bufring);
352302872Ssephe		new_channel->ch_bufring = NULL;
353302872Ssephe	}
354302812Ssephe	atomic_clear_int(&new_channel->ch_stflags, VMBUS_CHAN_ST_OPENED);
355302812Ssephe	return ret;
356250199Sgrehan}
357250199Sgrehan
358302609Ssepheint
359302871Ssephevmbus_chan_gpadl_connect(struct hv_vmbus_channel *chan, bus_addr_t paddr,
360302871Ssephe    int size, uint32_t *gpadl0)
361302871Ssephe{
362302871Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
363302609Ssephe	struct vmbus_msghc *mh;
364302609Ssephe	struct vmbus_chanmsg_gpadl_conn *req;
365302609Ssephe	const struct vmbus_message *msg;
366302609Ssephe	size_t reqsz;
367302609Ssephe	uint32_t gpadl, status;
368302609Ssephe	int page_count, range_len, i, cnt, error;
369302871Ssephe	uint64_t page_id;
370250199Sgrehan
371302609Ssephe	/*
372302609Ssephe	 * Preliminary checks.
373302609Ssephe	 */
374250199Sgrehan
375302609Ssephe	KASSERT((size & PAGE_MASK) == 0,
376302871Ssephe	    ("invalid GPA size %d, not multiple page size", size));
377250199Sgrehan	page_count = size >> PAGE_SHIFT;
378250199Sgrehan
379302609Ssephe	KASSERT((paddr & PAGE_MASK) == 0,
380302609Ssephe	    ("GPA is not page aligned %jx", (uintmax_t)paddr));
381302609Ssephe	page_id = paddr >> PAGE_SHIFT;
382250199Sgrehan
383302609Ssephe	range_len = __offsetof(struct vmbus_gpa_range, gpa_page[page_count]);
384302609Ssephe	/*
385302609Ssephe	 * We don't support multiple GPA ranges.
386302609Ssephe	 */
387302609Ssephe	if (range_len > UINT16_MAX) {
388302609Ssephe		device_printf(sc->vmbus_dev, "GPA too large, %d pages\n",
389302609Ssephe		    page_count);
390302609Ssephe		return EOPNOTSUPP;
391250199Sgrehan	}
392250199Sgrehan
393302609Ssephe	/*
394302609Ssephe	 * Allocate GPADL id.
395302609Ssephe	 */
396302630Ssephe	gpadl = vmbus_gpadl_alloc(sc);
397302609Ssephe	*gpadl0 = gpadl;
398250199Sgrehan
399302609Ssephe	/*
400302609Ssephe	 * Connect this GPADL to the target channel.
401302609Ssephe	 *
402302609Ssephe	 * NOTE:
403302609Ssephe	 * Since each message can only hold small set of page
404302609Ssephe	 * addresses, several messages may be required to
405302609Ssephe	 * complete the connection.
406302609Ssephe	 */
407302609Ssephe	if (page_count > VMBUS_CHANMSG_GPADL_CONN_PGMAX)
408302609Ssephe		cnt = VMBUS_CHANMSG_GPADL_CONN_PGMAX;
409302609Ssephe	else
410302609Ssephe		cnt = page_count;
411302609Ssephe	page_count -= cnt;
412250199Sgrehan
413302609Ssephe	reqsz = __offsetof(struct vmbus_chanmsg_gpadl_conn,
414302609Ssephe	    chm_range.gpa_page[cnt]);
415302609Ssephe	mh = vmbus_msghc_get(sc, reqsz);
416302609Ssephe	if (mh == NULL) {
417302609Ssephe		device_printf(sc->vmbus_dev,
418302609Ssephe		    "can not get msg hypercall for gpadl->chan%u\n",
419302871Ssephe		    chan->ch_id);
420302609Ssephe		return EIO;
421250199Sgrehan	}
422250199Sgrehan
423302609Ssephe	req = vmbus_msghc_dataptr(mh);
424302609Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_CONN;
425302871Ssephe	req->chm_chanid = chan->ch_id;
426302609Ssephe	req->chm_gpadl = gpadl;
427302609Ssephe	req->chm_range_len = range_len;
428302609Ssephe	req->chm_range_cnt = 1;
429302609Ssephe	req->chm_range.gpa_len = size;
430302609Ssephe	req->chm_range.gpa_ofs = 0;
431302609Ssephe	for (i = 0; i < cnt; ++i)
432302609Ssephe		req->chm_range.gpa_page[i] = page_id++;
433250199Sgrehan
434302609Ssephe	error = vmbus_msghc_exec(sc, mh);
435302609Ssephe	if (error) {
436302609Ssephe		device_printf(sc->vmbus_dev,
437302609Ssephe		    "gpadl->chan%u msg hypercall exec failed: %d\n",
438302871Ssephe		    chan->ch_id, error);
439302609Ssephe		vmbus_msghc_put(sc, mh);
440302609Ssephe		return error;
441302609Ssephe	}
442250199Sgrehan
443302609Ssephe	while (page_count > 0) {
444302609Ssephe		struct vmbus_chanmsg_gpadl_subconn *subreq;
445250199Sgrehan
446302609Ssephe		if (page_count > VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX)
447302609Ssephe			cnt = VMBUS_CHANMSG_GPADL_SUBCONN_PGMAX;
448302609Ssephe		else
449302609Ssephe			cnt = page_count;
450302609Ssephe		page_count -= cnt;
451250199Sgrehan
452302609Ssephe		reqsz = __offsetof(struct vmbus_chanmsg_gpadl_subconn,
453302609Ssephe		    chm_gpa_page[cnt]);
454302609Ssephe		vmbus_msghc_reset(mh, reqsz);
455250199Sgrehan
456302609Ssephe		subreq = vmbus_msghc_dataptr(mh);
457302609Ssephe		subreq->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_SUBCONN;
458302609Ssephe		subreq->chm_gpadl = gpadl;
459302609Ssephe		for (i = 0; i < cnt; ++i)
460302609Ssephe			subreq->chm_gpa_page[i] = page_id++;
461250199Sgrehan
462302609Ssephe		vmbus_msghc_exec_noresult(mh);
463250199Sgrehan	}
464302609Ssephe	KASSERT(page_count == 0, ("invalid page count %d", page_count));
465250199Sgrehan
466302609Ssephe	msg = vmbus_msghc_wait_result(sc, mh);
467302609Ssephe	status = ((const struct vmbus_chanmsg_gpadl_connresp *)
468302609Ssephe	    msg->msg_data)->chm_status;
469250199Sgrehan
470302609Ssephe	vmbus_msghc_put(sc, mh);
471250199Sgrehan
472302609Ssephe	if (status != 0) {
473302609Ssephe		device_printf(sc->vmbus_dev, "gpadl->chan%u failed: "
474302871Ssephe		    "status %u\n", chan->ch_id, status);
475302609Ssephe		return EIO;
476302632Ssephe	} else {
477302632Ssephe		if (bootverbose) {
478302632Ssephe			device_printf(sc->vmbus_dev, "gpadl->chan%u "
479302871Ssephe			    "succeeded\n", chan->ch_id);
480302632Ssephe		}
481302609Ssephe	}
482302609Ssephe	return 0;
483250199Sgrehan}
484250199Sgrehan
485302611Ssephe/*
486302611Ssephe * Disconnect the GPA from the target channel
487250199Sgrehan */
488250199Sgrehanint
489302890Ssephevmbus_chan_gpadl_disconnect(struct hv_vmbus_channel *chan, uint32_t gpadl)
490250199Sgrehan{
491302611Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
492302611Ssephe	struct vmbus_msghc *mh;
493302611Ssephe	struct vmbus_chanmsg_gpadl_disconn *req;
494302611Ssephe	int error;
495250199Sgrehan
496302611Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
497302611Ssephe	if (mh == NULL) {
498302611Ssephe		device_printf(sc->vmbus_dev,
499302611Ssephe		    "can not get msg hypercall for gpa x->chan%u\n",
500302693Ssephe		    chan->ch_id);
501302611Ssephe		return EBUSY;
502250199Sgrehan	}
503250199Sgrehan
504302611Ssephe	req = vmbus_msghc_dataptr(mh);
505302611Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_GPADL_DISCONN;
506302693Ssephe	req->chm_chanid = chan->ch_id;
507302611Ssephe	req->chm_gpadl = gpadl;
508250199Sgrehan
509302611Ssephe	error = vmbus_msghc_exec(sc, mh);
510302611Ssephe	if (error) {
511302611Ssephe		device_printf(sc->vmbus_dev,
512302611Ssephe		    "gpa x->chan%u msg hypercall exec failed: %d\n",
513302693Ssephe		    chan->ch_id, error);
514302611Ssephe		vmbus_msghc_put(sc, mh);
515302611Ssephe		return error;
516302611Ssephe	}
517250199Sgrehan
518302611Ssephe	vmbus_msghc_wait_result(sc, mh);
519302611Ssephe	/* Discard result; no useful information */
520302611Ssephe	vmbus_msghc_put(sc, mh);
521250199Sgrehan
522302611Ssephe	return 0;
523250199Sgrehan}
524250199Sgrehan
525282212Swhustatic void
526302891Ssephevmbus_chan_close_internal(struct hv_vmbus_channel *chan)
527250199Sgrehan{
528302891Ssephe	struct vmbus_softc *sc = chan->vmbus_sc;
529302610Ssephe	struct vmbus_msghc *mh;
530302610Ssephe	struct vmbus_chanmsg_chclose *req;
531302891Ssephe	struct taskqueue *tq = chan->ch_tq;
532302610Ssephe	int error;
533250199Sgrehan
534302812Ssephe	/* TODO: stringent check */
535302891Ssephe	atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED);
536302812Ssephe
537302891Ssephe	/*
538302891Ssephe	 * Free this channel's sysctl tree attached to its device's
539302891Ssephe	 * sysctl tree.
540302891Ssephe	 */
541302891Ssephe	sysctl_ctx_free(&chan->ch_sysctl_ctx);
542282212Swhu
543282212Swhu	/*
544302891Ssephe	 * Set ch_tq to NULL to avoid more requests be scheduled.
545302891Ssephe	 * XXX pretty broken; need rework.
546294886Ssephe	 */
547302891Ssephe	chan->ch_tq = NULL;
548302891Ssephe	taskqueue_drain(tq, &chan->ch_task);
549302891Ssephe	chan->ch_cb = NULL;
550250199Sgrehan
551302891Ssephe	/*
552302891Ssephe	 * Close this channel.
553250199Sgrehan	 */
554302610Ssephe	mh = vmbus_msghc_get(sc, sizeof(*req));
555302610Ssephe	if (mh == NULL) {
556302610Ssephe		device_printf(sc->vmbus_dev,
557302610Ssephe		    "can not get msg hypercall for chclose(chan%u)\n",
558302891Ssephe		    chan->ch_id);
559302610Ssephe		return;
560302610Ssephe	}
561250199Sgrehan
562302610Ssephe	req = vmbus_msghc_dataptr(mh);
563302610Ssephe	req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHCLOSE;
564302891Ssephe	req->chm_chanid = chan->ch_id;
565250199Sgrehan
566302610Ssephe	error = vmbus_msghc_exec_noresult(mh);
567302610Ssephe	vmbus_msghc_put(sc, mh);
568302610Ssephe
569302610Ssephe	if (error) {
570302610Ssephe		device_printf(sc->vmbus_dev,
571302610Ssephe		    "chclose(chan%u) msg hypercall exec failed: %d\n",
572302891Ssephe		    chan->ch_id, error);
573302610Ssephe		return;
574302610Ssephe	} else if (bootverbose) {
575302891Ssephe		device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id);
576302610Ssephe	}
577302610Ssephe
578302891Ssephe	/*
579302891Ssephe	 * Disconnect the TX+RX bufrings from this channel.
580302891Ssephe	 */
581302891Ssephe	if (chan->ch_bufring_gpadl) {
582302891Ssephe		vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
583302891Ssephe		chan->ch_bufring_gpadl = 0;
584250199Sgrehan	}
585250199Sgrehan
586302891Ssephe	/*
587302891Ssephe	 * Destroy the TX+RX bufrings.
588302891Ssephe	 */
589302891Ssephe	hv_ring_buffer_cleanup(&chan->outbound);
590302891Ssephe	hv_ring_buffer_cleanup(&chan->inbound);
591302891Ssephe	if (chan->ch_bufring != NULL) {
592302891Ssephe		hyperv_dmamem_free(&chan->ch_bufring_dma, chan->ch_bufring);
593302891Ssephe		chan->ch_bufring = NULL;
594302872Ssephe	}
595282212Swhu}
596250199Sgrehan
597302818Ssephe/*
598302818Ssephe * Caller should make sure that all sub-channels have
599302818Ssephe * been added to 'chan' and all to-be-closed channels
600302818Ssephe * are not being opened.
601282212Swhu */
602282212Swhuvoid
603302818Ssephehv_vmbus_channel_close(struct hv_vmbus_channel *chan)
604282212Swhu{
605302818Ssephe	int subchan_cnt;
606282212Swhu
607302818Ssephe	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
608282212Swhu		/*
609302818Ssephe		 * Sub-channel is closed when its primary channel
610302818Ssephe		 * is closed; done.
611282212Swhu		 */
612282212Swhu		return;
613282212Swhu	}
614282212Swhu
615250199Sgrehan	/*
616302818Ssephe	 * Close all sub-channels, if any.
617250199Sgrehan	 */
618302819Ssephe	subchan_cnt = chan->ch_subchan_cnt;
619302818Ssephe	if (subchan_cnt > 0) {
620302818Ssephe		struct hv_vmbus_channel **subchan;
621302818Ssephe		int i;
622302818Ssephe
623302890Ssephe		subchan = vmbus_subchan_get(chan, subchan_cnt);
624302818Ssephe		for (i = 0; i < subchan_cnt; ++i)
625302891Ssephe			vmbus_chan_close_internal(subchan[i]);
626302890Ssephe		vmbus_subchan_rel(subchan, subchan_cnt);
627250199Sgrehan	}
628302818Ssephe
629302818Ssephe	/* Then close the primary channel. */
630302891Ssephe	vmbus_chan_close_internal(chan);
631250199Sgrehan}
632250199Sgrehan
633250199Sgrehanint
634302882Ssephevmbus_chan_send(struct hv_vmbus_channel *chan, uint16_t type, uint16_t flags,
635302882Ssephe    void *data, int dlen, uint64_t xactid)
636250199Sgrehan{
637302875Ssephe	struct vmbus_chanpkt pkt;
638302881Ssephe	int pktlen, pad_pktlen, hlen, error;
639302881Ssephe	uint64_t pad = 0;
640302881Ssephe	struct iovec iov[3];
641302881Ssephe	boolean_t send_evt;
642250199Sgrehan
643302881Ssephe	hlen = sizeof(pkt);
644302881Ssephe	pktlen = hlen + dlen;
645302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
646250199Sgrehan
647302875Ssephe	pkt.cp_hdr.cph_type = type;
648302875Ssephe	pkt.cp_hdr.cph_flags = flags;
649302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
650302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
651302881Ssephe	pkt.cp_hdr.cph_xactid = xactid;
652250199Sgrehan
653302875Ssephe	iov[0].iov_base = &pkt;
654302881Ssephe	iov[0].iov_len = hlen;
655302881Ssephe	iov[1].iov_base = data;
656302881Ssephe	iov[1].iov_len = dlen;
657302881Ssephe	iov[2].iov_base = &pad;
658302881Ssephe	iov[2].iov_len = pad_pktlen - pktlen;
659250199Sgrehan
660302881Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 3, &send_evt);
661302881Ssephe	if (!error && send_evt)
662302881Ssephe		vmbus_chan_send_event(chan);
663302881Ssephe	return error;
664250199Sgrehan}
665250199Sgrehan
666250199Sgrehanint
667302876Ssephevmbus_chan_send_sglist(struct hv_vmbus_channel *chan,
668302876Ssephe    struct vmbus_gpa sg[], int sglen, void *data, int dlen, uint64_t xactid)
669250199Sgrehan{
670302876Ssephe	struct vmbus_chanpkt_sglist pkt;
671302876Ssephe	int pktlen, pad_pktlen, hlen, error;
672302876Ssephe	struct iovec iov[4];
673302876Ssephe	boolean_t send_evt;
674302876Ssephe	uint64_t pad = 0;
675250199Sgrehan
676302876Ssephe	KASSERT(sglen < VMBUS_CHAN_SGLIST_MAX,
677302876Ssephe	    ("invalid sglist len %d", sglen));
678250199Sgrehan
679302876Ssephe	hlen = __offsetof(struct vmbus_chanpkt_sglist, cp_gpa[sglen]);
680302876Ssephe	pktlen = hlen + dlen;
681302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
682250199Sgrehan
683302880Ssephe	pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
684302879Ssephe	pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
685302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
686302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
687302876Ssephe	pkt.cp_hdr.cph_xactid = xactid;
688302876Ssephe	pkt.cp_rsvd = 0;
689302876Ssephe	pkt.cp_gpa_cnt = sglen;
690250199Sgrehan
691302876Ssephe	iov[0].iov_base = &pkt;
692302876Ssephe	iov[0].iov_len = sizeof(pkt);
693302876Ssephe	iov[1].iov_base = sg;
694302876Ssephe	iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;
695302876Ssephe	iov[2].iov_base = data;
696302876Ssephe	iov[2].iov_len = dlen;
697302876Ssephe	iov[3].iov_base = &pad;
698302876Ssephe	iov[3].iov_len = pad_pktlen - pktlen;
699250199Sgrehan
700302876Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
701302876Ssephe	if (!error && send_evt)
702302876Ssephe		vmbus_chan_send_event(chan);
703302876Ssephe	return error;
704250199Sgrehan}
705250199Sgrehan
706250199Sgrehanint
707302878Ssephevmbus_chan_send_prplist(struct hv_vmbus_channel *chan,
708302878Ssephe    struct vmbus_gpa_range *prp, int prp_cnt, void *data, int dlen,
709302878Ssephe    uint64_t xactid)
710250199Sgrehan{
711302878Ssephe	struct vmbus_chanpkt_prplist pkt;
712302878Ssephe	int pktlen, pad_pktlen, hlen, error;
713302878Ssephe	struct iovec iov[4];
714302878Ssephe	boolean_t send_evt;
715302878Ssephe	uint64_t pad = 0;
716250199Sgrehan
717302878Ssephe	KASSERT(prp_cnt < VMBUS_CHAN_PRPLIST_MAX,
718302878Ssephe	    ("invalid prplist entry count %d", prp_cnt));
719250199Sgrehan
720302878Ssephe	hlen = __offsetof(struct vmbus_chanpkt_prplist,
721302878Ssephe	    cp_range[0].gpa_page[prp_cnt]);
722302878Ssephe	pktlen = hlen + dlen;
723302884Ssephe	pad_pktlen = VMBUS_CHANPKT_TOTLEN(pktlen);
724250199Sgrehan
725302880Ssephe	pkt.cp_hdr.cph_type = VMBUS_CHANPKT_TYPE_GPA;
726302879Ssephe	pkt.cp_hdr.cph_flags = VMBUS_CHANPKT_FLAG_RC;
727302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_hlen, hlen);
728302884Ssephe	VMBUS_CHANPKT_SETLEN(pkt.cp_hdr.cph_tlen, pad_pktlen);
729302878Ssephe	pkt.cp_hdr.cph_xactid = xactid;
730302878Ssephe	pkt.cp_rsvd = 0;
731302878Ssephe	pkt.cp_range_cnt = 1;
732250199Sgrehan
733302878Ssephe	iov[0].iov_base = &pkt;
734302878Ssephe	iov[0].iov_len = sizeof(pkt);
735302878Ssephe	iov[1].iov_base = prp;
736302878Ssephe	iov[1].iov_len = __offsetof(struct vmbus_gpa_range, gpa_page[prp_cnt]);
737302878Ssephe	iov[2].iov_base = data;
738302878Ssephe	iov[2].iov_len = dlen;
739302878Ssephe	iov[3].iov_base = &pad;
740302878Ssephe	iov[3].iov_len = pad_pktlen - pktlen;
741250199Sgrehan
742302878Ssephe	error = hv_ring_buffer_write(&chan->outbound, iov, 4, &send_evt);
743302878Ssephe	if (!error && send_evt)
744302878Ssephe		vmbus_chan_send_event(chan);
745302878Ssephe	return error;
746250199Sgrehan}
747250199Sgrehan
748250199Sgrehanint
749302885Ssephevmbus_chan_recv(struct hv_vmbus_channel *chan, void *data, int *dlen0,
750302885Ssephe    uint64_t *xactid)
751250199Sgrehan{
752302885Ssephe	struct vmbus_chanpkt_hdr pkt;
753302885Ssephe	int error, dlen, hlen;
754250199Sgrehan
755302885Ssephe	error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
756302885Ssephe	if (error)
757302885Ssephe		return error;
758250199Sgrehan
759302885Ssephe	hlen = VMBUS_CHANPKT_GETLEN(pkt.cph_hlen);
760302885Ssephe	dlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen) - hlen;
761250199Sgrehan
762302885Ssephe	if (*dlen0 < dlen) {
763302886Ssephe		/* Return the size of this packet's data. */
764302885Ssephe		*dlen0 = dlen;
765302885Ssephe		return ENOBUFS;
766302885Ssephe	}
767250199Sgrehan
768302885Ssephe	*xactid = pkt.cph_xactid;
769302885Ssephe	*dlen0 = dlen;
770250199Sgrehan
771302886Ssephe	/* Skip packet header */
772302885Ssephe	error = hv_ring_buffer_read(&chan->inbound, data, dlen, hlen);
773302885Ssephe	KASSERT(!error, ("hv_ring_buffer_read failed"));
774250199Sgrehan
775302885Ssephe	return 0;
776250199Sgrehan}
777250199Sgrehan
778250199Sgrehanint
779302886Ssephevmbus_chan_recv_pkt(struct hv_vmbus_channel *chan,
780302886Ssephe    struct vmbus_chanpkt_hdr *pkt0, int *pktlen0)
781250199Sgrehan{
782302886Ssephe	struct vmbus_chanpkt_hdr pkt;
783302886Ssephe	int error, pktlen;
784250199Sgrehan
785302886Ssephe	error = hv_ring_buffer_peek(&chan->inbound, &pkt, sizeof(pkt));
786302886Ssephe	if (error)
787302886Ssephe		return error;
788250199Sgrehan
789302886Ssephe	pktlen = VMBUS_CHANPKT_GETLEN(pkt.cph_tlen);
790302886Ssephe	if (*pktlen0 < pktlen) {
791302886Ssephe		/* Return the size of this packet. */
792302886Ssephe		*pktlen0 = pktlen;
793302886Ssephe		return ENOBUFS;
794302886Ssephe	}
795302886Ssephe	*pktlen0 = pktlen;
796250199Sgrehan
797302886Ssephe	/* Include packet header */
798302886Ssephe	error = hv_ring_buffer_read(&chan->inbound, pkt0, pktlen, 0);
799302886Ssephe	KASSERT(!error, ("hv_ring_buffer_read failed"));
800250199Sgrehan
801302886Ssephe	return 0;
802250199Sgrehan}
803294886Ssephe
804294886Ssephestatic void
805302713Ssephevmbus_chan_task(void *xchan, int pending __unused)
806294886Ssephe{
807302713Ssephe	struct hv_vmbus_channel *chan = xchan;
808302874Ssephe	vmbus_chan_callback_t cb = chan->ch_cb;
809302874Ssephe	void *cbarg = chan->ch_cbarg;
810294886Ssephe
811302710Ssephe	/*
812302710Ssephe	 * Optimize host to guest signaling by ensuring:
813302710Ssephe	 * 1. While reading the channel, we disable interrupts from
814302710Ssephe	 *    host.
815302710Ssephe	 * 2. Ensure that we process all posted messages from the host
816302710Ssephe	 *    before returning from this callback.
817302710Ssephe	 * 3. Once we return, enable signaling from the host. Once this
818302710Ssephe	 *    state is set we check to see if additional packets are
819302710Ssephe	 *    available to read. In this case we repeat the process.
820302713Ssephe	 *
821302713Ssephe	 * NOTE: Interrupt has been disabled in the ISR.
822302710Ssephe	 */
823302713Ssephe	for (;;) {
824302713Ssephe		uint32_t left;
825294886Ssephe
826302874Ssephe		cb(cbarg);
827294886Ssephe
828302713Ssephe		left = hv_ring_buffer_read_end(&chan->inbound);
829302713Ssephe		if (left == 0) {
830302713Ssephe			/* No more data in RX bufring; done */
831302713Ssephe			break;
832302713Ssephe		}
833302713Ssephe		hv_ring_buffer_read_begin(&chan->inbound);
834302713Ssephe	}
835294886Ssephe}
836302692Ssephe
837302713Ssephestatic void
838302713Ssephevmbus_chan_task_nobatch(void *xchan, int pending __unused)
839302713Ssephe{
840302713Ssephe	struct hv_vmbus_channel *chan = xchan;
841302713Ssephe
842302874Ssephe	chan->ch_cb(chan->ch_cbarg);
843302713Ssephe}
844302713Ssephe
845302692Ssephestatic __inline void
846302692Ssephevmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
847302692Ssephe    int flag_cnt)
848302692Ssephe{
849302692Ssephe	int f;
850302692Ssephe
851302692Ssephe	for (f = 0; f < flag_cnt; ++f) {
852302806Ssephe		uint32_t chid_base;
853302692Ssephe		u_long flags;
854302806Ssephe		int chid_ofs;
855302692Ssephe
856302692Ssephe		if (event_flags[f] == 0)
857302692Ssephe			continue;
858302692Ssephe
859302692Ssephe		flags = atomic_swap_long(&event_flags[f], 0);
860302806Ssephe		chid_base = f << VMBUS_EVTFLAG_SHIFT;
861302692Ssephe
862302806Ssephe		while ((chid_ofs = ffsl(flags)) != 0) {
863302692Ssephe			struct hv_vmbus_channel *channel;
864302692Ssephe
865302806Ssephe			--chid_ofs; /* NOTE: ffsl is 1-based */
866302806Ssephe			flags &= ~(1UL << chid_ofs);
867302692Ssephe
868302806Ssephe			channel = sc->vmbus_chmap[chid_base + chid_ofs];
869302692Ssephe
870302692Ssephe			/* if channel is closed or closing */
871302874Ssephe			if (channel == NULL || channel->ch_tq == NULL)
872302692Ssephe				continue;
873302692Ssephe
874302709Ssephe			if (channel->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
875302692Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
876302874Ssephe			taskqueue_enqueue(channel->ch_tq, &channel->ch_task);
877302692Ssephe		}
878302692Ssephe	}
879302692Ssephe}
880302692Ssephe
881302692Ssephevoid
882302692Ssephevmbus_event_proc(struct vmbus_softc *sc, int cpu)
883302692Ssephe{
884302692Ssephe	struct vmbus_evtflags *eventf;
885302692Ssephe
886302692Ssephe	/*
887302692Ssephe	 * On Host with Win8 or above, the event page can be checked directly
888302692Ssephe	 * to get the id of the channel that has the pending interrupt.
889302692Ssephe	 */
890302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
891302692Ssephe	vmbus_event_flags_proc(sc, eventf->evt_flags,
892302692Ssephe	    VMBUS_PCPU_GET(sc, event_flags_cnt, cpu));
893302692Ssephe}
894302692Ssephe
895302692Ssephevoid
896302692Ssephevmbus_event_proc_compat(struct vmbus_softc *sc, int cpu)
897302692Ssephe{
898302692Ssephe	struct vmbus_evtflags *eventf;
899302692Ssephe
900302692Ssephe	eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE;
901302692Ssephe	if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) {
902302692Ssephe		vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags,
903302692Ssephe		    VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT);
904302692Ssephe	}
905302692Ssephe}
906302692Ssephe
907302692Ssephestatic void
908302692Ssephevmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
909302692Ssephe    const struct hv_vmbus_channel *chan)
910302692Ssephe{
911302692Ssephe	volatile int *flag_cnt_ptr;
912302692Ssephe	int flag_cnt;
913302692Ssephe
914302693Ssephe	flag_cnt = (chan->ch_id / VMBUS_EVTFLAG_LEN) + 1;
915302873Ssephe	flag_cnt_ptr = VMBUS_PCPU_PTR(sc, event_flags_cnt, chan->ch_cpuid);
916302692Ssephe
917302692Ssephe	for (;;) {
918302692Ssephe		int old_flag_cnt;
919302692Ssephe
920302692Ssephe		old_flag_cnt = *flag_cnt_ptr;
921302692Ssephe		if (old_flag_cnt >= flag_cnt)
922302692Ssephe			break;
923302692Ssephe		if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
924302692Ssephe			if (bootverbose) {
925302692Ssephe				device_printf(sc->vmbus_dev,
926302692Ssephe				    "channel%u update cpu%d flag_cnt to %d\n",
927302873Ssephe				    chan->ch_id, chan->ch_cpuid, flag_cnt);
928302692Ssephe			}
929302692Ssephe			break;
930302692Ssephe		}
931302692Ssephe	}
932302692Ssephe}
933302864Ssephe
934302864Ssephestatic struct hv_vmbus_channel *
935302864Ssephevmbus_chan_alloc(struct vmbus_softc *sc)
936302864Ssephe{
937302864Ssephe	struct hv_vmbus_channel *chan;
938302864Ssephe
939302864Ssephe	chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
940302864Ssephe
941302864Ssephe	chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
942302864Ssephe	    HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
943302864Ssephe	    &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
944302864Ssephe	if (chan->ch_monprm == NULL) {
945302864Ssephe		device_printf(sc->vmbus_dev, "monprm alloc failed\n");
946302864Ssephe		free(chan, M_DEVBUF);
947302864Ssephe		return NULL;
948302864Ssephe	}
949302864Ssephe
950302864Ssephe	chan->vmbus_sc = sc;
951302864Ssephe	mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
952302864Ssephe	TAILQ_INIT(&chan->ch_subchans);
953302864Ssephe	TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
954302864Ssephe
955302864Ssephe	return chan;
956302864Ssephe}
957302864Ssephe
958302864Ssephestatic void
959302864Ssephevmbus_chan_free(struct hv_vmbus_channel *chan)
960302864Ssephe{
961302864Ssephe	/* TODO: assert sub-channel list is empty */
962302864Ssephe	/* TODO: asset no longer on the primary channel's sub-channel list */
963302864Ssephe	/* TODO: asset no longer on the vmbus channel list */
964302864Ssephe	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
965302864Ssephe	mtx_destroy(&chan->ch_subchan_lock);
966302864Ssephe	free(chan, M_DEVBUF);
967302864Ssephe}
968302864Ssephe
969302864Ssephestatic int
970302864Ssephevmbus_chan_add(struct hv_vmbus_channel *newchan)
971302864Ssephe{
972302864Ssephe	struct vmbus_softc *sc = newchan->vmbus_sc;
973302864Ssephe	struct hv_vmbus_channel *prichan;
974302864Ssephe
975302864Ssephe	if (newchan->ch_id == 0) {
976302864Ssephe		/*
977302864Ssephe		 * XXX
978302864Ssephe		 * Chan0 will neither be processed nor should be offered;
979302864Ssephe		 * skip it.
980302864Ssephe		 */
981302864Ssephe		device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
982302864Ssephe		return EINVAL;
983302864Ssephe	} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
984302864Ssephe		device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
985302864Ssephe		    newchan->ch_id);
986302864Ssephe		return EINVAL;
987302864Ssephe	}
988302864Ssephe	sc->vmbus_chmap[newchan->ch_id] = newchan;
989302864Ssephe
990302864Ssephe	if (bootverbose) {
991302864Ssephe		device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
992302864Ssephe		    newchan->ch_id, newchan->ch_subidx);
993302864Ssephe	}
994302864Ssephe
995302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
996302864Ssephe	TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
997302864Ssephe		/*
998302864Ssephe		 * Sub-channel will have the same type GUID and instance
999302864Ssephe		 * GUID as its primary channel.
1000302864Ssephe		 */
1001302864Ssephe		if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
1002302864Ssephe		    sizeof(struct hyperv_guid)) == 0 &&
1003302864Ssephe		    memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
1004302864Ssephe		    sizeof(struct hyperv_guid)) == 0)
1005302864Ssephe			break;
1006302864Ssephe	}
1007302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(newchan)) {
1008302864Ssephe		if (prichan == NULL) {
1009302864Ssephe			/* Install the new primary channel */
1010302864Ssephe			TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
1011302864Ssephe			    ch_prilink);
1012302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1013302864Ssephe			return 0;
1014302864Ssephe		} else {
1015302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1016302864Ssephe			device_printf(sc->vmbus_dev, "duplicated primary "
1017302864Ssephe			    "chan%u\n", newchan->ch_id);
1018302864Ssephe			return EINVAL;
1019302864Ssephe		}
1020302864Ssephe	} else { /* Sub-channel */
1021302864Ssephe		if (prichan == NULL) {
1022302864Ssephe			mtx_unlock(&sc->vmbus_prichan_lock);
1023302864Ssephe			device_printf(sc->vmbus_dev, "no primary chan for "
1024302864Ssephe			    "chan%u\n", newchan->ch_id);
1025302864Ssephe			return EINVAL;
1026302864Ssephe		}
1027302864Ssephe		/*
1028302864Ssephe		 * Found the primary channel for this sub-channel and
1029302864Ssephe		 * move on.
1030302864Ssephe		 *
1031302864Ssephe		 * XXX refcnt prichan
1032302864Ssephe		 */
1033302864Ssephe	}
1034302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1035302864Ssephe
1036302864Ssephe	/*
1037302864Ssephe	 * This is a sub-channel; link it with the primary channel.
1038302864Ssephe	 */
1039302864Ssephe	KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
1040302864Ssephe	    ("new channel is not sub-channel"));
1041302864Ssephe	KASSERT(prichan != NULL, ("no primary channel"));
1042302864Ssephe
1043302864Ssephe	newchan->ch_prichan = prichan;
1044302864Ssephe	newchan->ch_dev = prichan->ch_dev;
1045302864Ssephe
1046302864Ssephe	mtx_lock(&prichan->ch_subchan_lock);
1047302864Ssephe	TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
1048302864Ssephe	/*
1049302864Ssephe	 * Bump up sub-channel count and notify anyone that is
1050302864Ssephe	 * interested in this sub-channel, after this sub-channel
1051302864Ssephe	 * is setup.
1052302864Ssephe	 */
1053302864Ssephe	prichan->ch_subchan_cnt++;
1054302864Ssephe	mtx_unlock(&prichan->ch_subchan_lock);
1055302864Ssephe	wakeup(prichan);
1056302864Ssephe
1057302864Ssephe	return 0;
1058302864Ssephe}
1059302864Ssephe
1060302864Ssephevoid
1061302890Ssephevmbus_chan_cpu_set(struct hv_vmbus_channel *chan, int cpu)
1062302864Ssephe{
1063302864Ssephe	KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
1064302864Ssephe
1065302864Ssephe	if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
1066302864Ssephe	    chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
1067302864Ssephe		/* Only cpu0 is supported */
1068302864Ssephe		cpu = 0;
1069302864Ssephe	}
1070302864Ssephe
1071302873Ssephe	chan->ch_cpuid = cpu;
1072302873Ssephe	chan->ch_vcpuid = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
1073302864Ssephe
1074302864Ssephe	if (bootverbose) {
1075302864Ssephe		printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
1076302873Ssephe		    chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid);
1077302864Ssephe	}
1078302864Ssephe}
1079302864Ssephe
1080302864Ssephevoid
1081302890Ssephevmbus_chan_cpu_rr(struct hv_vmbus_channel *chan)
1082302864Ssephe{
1083302864Ssephe	static uint32_t vmbus_chan_nextcpu;
1084302864Ssephe	int cpu;
1085302864Ssephe
1086302864Ssephe	cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
1087302890Ssephe	vmbus_chan_cpu_set(chan, cpu);
1088302864Ssephe}
1089302864Ssephe
1090302864Ssephestatic void
1091302864Ssephevmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
1092302864Ssephe{
1093302864Ssephe	/*
1094302864Ssephe	 * By default, pin the channel to cpu0.  Devices having
1095302864Ssephe	 * special channel-cpu mapping requirement should call
1096302890Ssephe	 * vmbus_chan_cpu_{set,rr}().
1097302864Ssephe	 */
1098302890Ssephe	vmbus_chan_cpu_set(chan, 0);
1099302864Ssephe}
1100302864Ssephe
1101302864Ssephestatic void
1102302864Ssephevmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
1103302864Ssephe    const struct vmbus_message *msg)
1104302864Ssephe{
1105302864Ssephe	const struct vmbus_chanmsg_choffer *offer;
1106302864Ssephe	struct hv_vmbus_channel *chan;
1107302864Ssephe	int error;
1108302864Ssephe
1109302864Ssephe	offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
1110302864Ssephe
1111302864Ssephe	chan = vmbus_chan_alloc(sc);
1112302864Ssephe	if (chan == NULL) {
1113302864Ssephe		device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
1114302864Ssephe		    offer->chm_chanid);
1115302864Ssephe		return;
1116302864Ssephe	}
1117302864Ssephe
1118302864Ssephe	chan->ch_id = offer->chm_chanid;
1119302864Ssephe	chan->ch_subidx = offer->chm_subidx;
1120302864Ssephe	chan->ch_guid_type = offer->chm_chtype;
1121302864Ssephe	chan->ch_guid_inst = offer->chm_chinst;
1122302864Ssephe
1123302864Ssephe	/* Batch reading is on by default */
1124302864Ssephe	chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
1125302864Ssephe
1126302864Ssephe	chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
1127302864Ssephe	if (sc->vmbus_version != VMBUS_VERSION_WS2008)
1128302864Ssephe		chan->ch_monprm->mp_connid = offer->chm_connid;
1129302864Ssephe
1130302864Ssephe	if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
1131302864Ssephe		/*
1132302864Ssephe		 * Setup MNF stuffs.
1133302864Ssephe		 */
1134302864Ssephe		chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
1135302864Ssephe		chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
1136302864Ssephe		if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
1137302864Ssephe			panic("invalid monitor trigger %u", offer->chm_montrig);
1138302864Ssephe		chan->ch_montrig_mask =
1139302864Ssephe		    1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
1140302864Ssephe	}
1141302864Ssephe
1142302864Ssephe	/* Select default cpu for this channel. */
1143302864Ssephe	vmbus_chan_cpu_default(chan);
1144302864Ssephe
1145302864Ssephe	error = vmbus_chan_add(chan);
1146302864Ssephe	if (error) {
1147302864Ssephe		device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
1148302864Ssephe		    chan->ch_id, error);
1149302864Ssephe		vmbus_chan_free(chan);
1150302864Ssephe		return;
1151302864Ssephe	}
1152302864Ssephe
1153302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1154302864Ssephe		/*
1155302864Ssephe		 * Add device for this primary channel.
1156302864Ssephe		 *
1157302864Ssephe		 * NOTE:
1158302864Ssephe		 * Error is ignored here; don't have much to do if error
1159302864Ssephe		 * really happens.
1160302864Ssephe		 */
1161302868Ssephe		vmbus_add_child(chan);
1162302864Ssephe	}
1163302864Ssephe}
1164302864Ssephe
1165302864Ssephe/*
1166302864Ssephe * XXX pretty broken; need rework.
1167302864Ssephe */
1168302864Ssephestatic void
1169302864Ssephevmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
1170302864Ssephe    const struct vmbus_message *msg)
1171302864Ssephe{
1172302864Ssephe	const struct vmbus_chanmsg_chrescind *note;
1173302864Ssephe	struct hv_vmbus_channel *chan;
1174302864Ssephe
1175302864Ssephe	note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
1176302864Ssephe	if (note->chm_chanid > VMBUS_CHAN_MAX) {
1177302864Ssephe		device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
1178302864Ssephe		    note->chm_chanid);
1179302864Ssephe		return;
1180302864Ssephe	}
1181302864Ssephe
1182302864Ssephe	if (bootverbose) {
1183302864Ssephe		device_printf(sc->vmbus_dev, "chan%u rescinded\n",
1184302864Ssephe		    note->chm_chanid);
1185302864Ssephe	}
1186302864Ssephe
1187302864Ssephe	chan = sc->vmbus_chmap[note->chm_chanid];
1188302864Ssephe	if (chan == NULL)
1189302864Ssephe		return;
1190302864Ssephe	sc->vmbus_chmap[note->chm_chanid] = NULL;
1191302864Ssephe
1192302864Ssephe	taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
1193302864Ssephe}
1194302864Ssephe
1195302864Ssephestatic void
1196302864Ssephevmbus_chan_detach_task(void *xchan, int pending __unused)
1197302864Ssephe{
1198302864Ssephe	struct hv_vmbus_channel *chan = xchan;
1199302864Ssephe
1200302864Ssephe	if (VMBUS_CHAN_ISPRIMARY(chan)) {
1201302864Ssephe		/* Only primary channel owns the device */
1202302868Ssephe		vmbus_delete_child(chan);
1203302864Ssephe		/* NOTE: DO NOT free primary channel for now */
1204302864Ssephe	} else {
1205302864Ssephe		struct vmbus_softc *sc = chan->vmbus_sc;
1206302864Ssephe		struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
1207302864Ssephe		struct vmbus_chanmsg_chfree *req;
1208302864Ssephe		struct vmbus_msghc *mh;
1209302864Ssephe		int error;
1210302864Ssephe
1211302864Ssephe		mh = vmbus_msghc_get(sc, sizeof(*req));
1212302864Ssephe		if (mh == NULL) {
1213302864Ssephe			device_printf(sc->vmbus_dev,
1214302864Ssephe			    "can not get msg hypercall for chfree(chan%u)\n",
1215302864Ssephe			    chan->ch_id);
1216302864Ssephe			goto remove;
1217302864Ssephe		}
1218302864Ssephe
1219302864Ssephe		req = vmbus_msghc_dataptr(mh);
1220302864Ssephe		req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
1221302864Ssephe		req->chm_chanid = chan->ch_id;
1222302864Ssephe
1223302864Ssephe		error = vmbus_msghc_exec_noresult(mh);
1224302864Ssephe		vmbus_msghc_put(sc, mh);
1225302864Ssephe
1226302864Ssephe		if (error) {
1227302864Ssephe			device_printf(sc->vmbus_dev,
1228302864Ssephe			    "chfree(chan%u) failed: %d",
1229302864Ssephe			    chan->ch_id, error);
1230302864Ssephe			/* NOTE: Move on! */
1231302864Ssephe		} else {
1232302864Ssephe			if (bootverbose) {
1233302864Ssephe				device_printf(sc->vmbus_dev, "chan%u freed\n",
1234302864Ssephe				    chan->ch_id);
1235302864Ssephe			}
1236302864Ssephe		}
1237302864Ssepheremove:
1238302864Ssephe		mtx_lock(&pri_chan->ch_subchan_lock);
1239302864Ssephe		TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
1240302864Ssephe		KASSERT(pri_chan->ch_subchan_cnt > 0,
1241302864Ssephe		    ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
1242302864Ssephe		pri_chan->ch_subchan_cnt--;
1243302864Ssephe		mtx_unlock(&pri_chan->ch_subchan_lock);
1244302864Ssephe		wakeup(pri_chan);
1245302864Ssephe
1246302864Ssephe		vmbus_chan_free(chan);
1247302864Ssephe	}
1248302864Ssephe}
1249302864Ssephe
1250302864Ssephe/*
1251302864Ssephe * Detach all devices and destroy the corresponding primary channels.
1252302864Ssephe */
1253302864Ssephevoid
1254302864Ssephevmbus_chan_destroy_all(struct vmbus_softc *sc)
1255302864Ssephe{
1256302864Ssephe	struct hv_vmbus_channel *chan;
1257302864Ssephe
1258302864Ssephe	mtx_lock(&sc->vmbus_prichan_lock);
1259302864Ssephe	while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
1260302864Ssephe		KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
1261302864Ssephe		TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
1262302864Ssephe		mtx_unlock(&sc->vmbus_prichan_lock);
1263302864Ssephe
1264302868Ssephe		vmbus_delete_child(chan);
1265302864Ssephe		vmbus_chan_free(chan);
1266302864Ssephe
1267302864Ssephe		mtx_lock(&sc->vmbus_prichan_lock);
1268302864Ssephe	}
1269302864Ssephe	bzero(sc->vmbus_chmap,
1270302864Ssephe	    sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
1271302864Ssephe	mtx_unlock(&sc->vmbus_prichan_lock);
1272302864Ssephe}
1273302864Ssephe
1274302864Ssephe/**
1275302864Ssephe * @brief Select the best outgoing channel
1276302864Ssephe *
1277302864Ssephe * The channel whose vcpu binding is closest to the currect vcpu will
1278302864Ssephe * be selected.
1279302864Ssephe * If no multi-channel, always select primary channel
1280302864Ssephe *
1281302864Ssephe * @param primary - primary channel
1282302864Ssephe */
1283302864Ssephestruct hv_vmbus_channel *
1284302864Ssephevmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
1285302864Ssephe{
1286302864Ssephe	hv_vmbus_channel *new_channel = NULL;
1287302864Ssephe	hv_vmbus_channel *outgoing_channel = primary;
1288302864Ssephe	int old_cpu_distance = 0;
1289302864Ssephe	int new_cpu_distance = 0;
1290302864Ssephe	int cur_vcpu = 0;
1291302864Ssephe	int smp_pro_id = PCPU_GET(cpuid);
1292302864Ssephe
1293302864Ssephe	if (TAILQ_EMPTY(&primary->ch_subchans)) {
1294302864Ssephe		return outgoing_channel;
1295302864Ssephe	}
1296302864Ssephe
1297302864Ssephe	if (smp_pro_id >= MAXCPU) {
1298302864Ssephe		return outgoing_channel;
1299302864Ssephe	}
1300302864Ssephe
1301302864Ssephe	cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
1302302864Ssephe
1303302864Ssephe	/* XXX need lock */
1304302864Ssephe	TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
1305302864Ssephe		if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
1306302864Ssephe			continue;
1307302864Ssephe		}
1308302864Ssephe
1309302873Ssephe		if (new_channel->ch_vcpuid == cur_vcpu){
1310302864Ssephe			return new_channel;
1311302864Ssephe		}
1312302864Ssephe
1313302873Ssephe		old_cpu_distance = ((outgoing_channel->ch_vcpuid > cur_vcpu) ?
1314302873Ssephe		    (outgoing_channel->ch_vcpuid - cur_vcpu) :
1315302873Ssephe		    (cur_vcpu - outgoing_channel->ch_vcpuid));
1316302864Ssephe
1317302873Ssephe		new_cpu_distance = ((new_channel->ch_vcpuid > cur_vcpu) ?
1318302873Ssephe		    (new_channel->ch_vcpuid - cur_vcpu) :
1319302873Ssephe		    (cur_vcpu - new_channel->ch_vcpuid));
1320302864Ssephe
1321302864Ssephe		if (old_cpu_distance < new_cpu_distance) {
1322302864Ssephe			continue;
1323302864Ssephe		}
1324302864Ssephe
1325302864Ssephe		outgoing_channel = new_channel;
1326302864Ssephe	}
1327302864Ssephe
1328302864Ssephe	return(outgoing_channel);
1329302864Ssephe}
1330302864Ssephe
1331302864Ssephestruct hv_vmbus_channel **
1332302890Ssephevmbus_subchan_get(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
1333302864Ssephe{
1334302864Ssephe	struct hv_vmbus_channel **ret, *chan;
1335302864Ssephe	int i;
1336302864Ssephe
1337302864Ssephe	ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
1338302864Ssephe	    M_WAITOK);
1339302864Ssephe
1340302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1341302864Ssephe
1342302864Ssephe	while (pri_chan->ch_subchan_cnt < subchan_cnt)
1343302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
1344302864Ssephe
1345302864Ssephe	i = 0;
1346302864Ssephe	TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
1347302864Ssephe		/* TODO: refcnt chan */
1348302864Ssephe		ret[i] = chan;
1349302864Ssephe
1350302864Ssephe		++i;
1351302864Ssephe		if (i == subchan_cnt)
1352302864Ssephe			break;
1353302864Ssephe	}
1354302864Ssephe	KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
1355302864Ssephe	    pri_chan->ch_subchan_cnt, subchan_cnt));
1356302864Ssephe
1357302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1358302864Ssephe
1359302864Ssephe	return ret;
1360302864Ssephe}
1361302864Ssephe
1362302864Ssephevoid
1363302890Ssephevmbus_subchan_rel(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
1364302864Ssephe{
1365302864Ssephe
1366302864Ssephe	free(subchan, M_TEMP);
1367302864Ssephe}
1368302864Ssephe
1369302864Ssephevoid
1370302890Ssephevmbus_subchan_drain(struct hv_vmbus_channel *pri_chan)
1371302864Ssephe{
1372302864Ssephe	mtx_lock(&pri_chan->ch_subchan_lock);
1373302864Ssephe	while (pri_chan->ch_subchan_cnt > 0)
1374302864Ssephe		mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
1375302864Ssephe	mtx_unlock(&pri_chan->ch_subchan_lock);
1376302864Ssephe}
1377302864Ssephe
1378302864Ssephevoid
1379302864Ssephevmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1380302864Ssephe{
1381302864Ssephe	vmbus_chanmsg_proc_t msg_proc;
1382302864Ssephe	uint32_t msg_type;
1383302864Ssephe
1384302864Ssephe	msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
1385302864Ssephe	KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
1386302864Ssephe	    ("invalid message type %u", msg_type));
1387302864Ssephe
1388302864Ssephe	msg_proc = vmbus_chan_msgprocs[msg_type];
1389302864Ssephe	if (msg_proc != NULL)
1390302864Ssephe		msg_proc(sc, msg);
1391302864Ssephe}
1392