hn_nvs.c revision 307250
1/*-
2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2010-2012 Citrix Inc.
4 * Copyright (c) 2012 NetApp Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/10/sys/dev/hyperv/netvsc/hv_net_vsc.c 307250 2016-10-14 02:52:48Z sephe $
29 */
30
31/**
32 * HyperV vmbus network VSC (virtual services client) module
33 *
34 */
35
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/socket.h>
40#include <sys/limits.h>
41#include <sys/lock.h>
42#include <net/if.h>
43#include <net/if_arp.h>
44#include <machine/bus.h>
45#include <machine/atomic.h>
46
47#include <dev/hyperv/include/hyperv.h>
48#include <dev/hyperv/include/vmbus_xact.h>
49#include <dev/hyperv/netvsc/hv_net_vsc.h>
50#include <dev/hyperv/netvsc/hv_rndis_filter.h>
51#include <dev/hyperv/netvsc/if_hnreg.h>
52#include <dev/hyperv/netvsc/if_hnvar.h>
53
54MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver");
55
56/*
57 * Forward declarations
58 */
59static int  hn_nvs_conn_chim(struct hn_softc *sc);
60static int  hn_nvs_conn_rxbuf(struct hn_softc *);
61static int  hn_nvs_disconn_chim(struct hn_softc *sc);
62static int  hn_nvs_disconn_rxbuf(struct hn_softc *sc);
63static void hn_nvs_sent_none(struct hn_send_ctx *sndc,
64    struct hn_softc *, struct vmbus_channel *chan,
65    const void *, int);
66
67struct hn_send_ctx	hn_send_ctx_none =
68    HN_SEND_CTX_INITIALIZER(hn_nvs_sent_none, NULL);
69
70static const uint32_t		hn_nvs_version[] = {
71	HN_NVS_VERSION_5,
72	HN_NVS_VERSION_4,
73	HN_NVS_VERSION_2,
74	HN_NVS_VERSION_1
75};
76
77uint32_t
78hn_chim_alloc(struct hn_softc *sc)
79{
80	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
81	u_long *bmap = sc->hn_chim_bmap;
82	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
83
84	for (i = 0; i < bmap_cnt; ++i) {
85		int idx;
86
87		idx = ffsl(~bmap[i]);
88		if (idx == 0)
89			continue;
90
91		--idx; /* ffsl is 1-based */
92		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
93		    ("invalid i %d and idx %d", i, idx));
94
95		if (atomic_testandset_long(&bmap[i], idx))
96			continue;
97
98		ret = i * LONG_BIT + idx;
99		break;
100	}
101	return (ret);
102}
103
104static const void *
105hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
106    void *req, int reqlen, size_t *resplen0, uint32_t type)
107{
108	struct hn_send_ctx sndc;
109	size_t resplen, min_resplen = *resplen0;
110	const struct hn_nvs_hdr *hdr;
111	int error;
112
113	KASSERT(min_resplen >= sizeof(*hdr),
114	    ("invalid minimum response len %zu", min_resplen));
115
116	/*
117	 * Execute the xact setup by the caller.
118	 */
119	hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact);
120
121	vmbus_xact_activate(xact);
122	error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC,
123	    req, reqlen, &sndc);
124	if (error) {
125		vmbus_xact_deactivate(xact);
126		return (NULL);
127	}
128	hdr = vmbus_xact_wait(xact, &resplen);
129
130	/*
131	 * Check this NVS response message.
132	 */
133	if (resplen < min_resplen) {
134		if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen);
135		return (NULL);
136	}
137	if (hdr->nvs_type != type) {
138		if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, "
139		    "expect 0x%08x\n", hdr->nvs_type, type);
140		return (NULL);
141	}
142	/* All pass! */
143	*resplen0 = resplen;
144	return (hdr);
145}
146
147static __inline int
148hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
149{
150
151	return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE,
152	    req, reqlen, &hn_send_ctx_none));
153}
154
155static int
156hn_nvs_conn_rxbuf(struct hn_softc *sc)
157{
158	struct vmbus_xact *xact = NULL;
159	struct hn_nvs_rxbuf_conn *conn;
160	const struct hn_nvs_rxbuf_connresp *resp;
161	size_t resp_len;
162	uint32_t status;
163	int error, rxbuf_size;
164
165	/*
166	 * Limit RXBUF size for old NVS.
167	 */
168	if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
169		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
170	else
171		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE;
172
173	/*
174	 * Connect the RXBUF GPADL to the primary channel.
175	 *
176	 * NOTE:
177	 * Only primary channel has RXBUF connected to it.  Sub-channels
178	 * just share this RXBUF.
179	 */
180	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
181	    sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
182	if (error) {
183		if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n",
184		    error);
185		goto cleanup;
186	}
187
188	/*
189	 * Connect RXBUF to NVS.
190	 */
191
192	xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn));
193	if (xact == NULL) {
194		if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n");
195		error = ENXIO;
196		goto cleanup;
197	}
198	conn = vmbus_xact_req_data(xact);
199	conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN;
200	conn->nvs_gpadl = sc->hn_rxbuf_gpadl;
201	conn->nvs_sig = HN_NVS_RXBUF_SIG;
202
203	resp_len = sizeof(*resp);
204	resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
205	    HN_NVS_TYPE_RXBUF_CONNRESP);
206	if (resp == NULL) {
207		if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n");
208		error = EIO;
209		goto cleanup;
210	}
211
212	status = resp->nvs_status;
213	vmbus_xact_put(xact);
214	xact = NULL;
215
216	if (status != HN_NVS_STATUS_OK) {
217		if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status);
218		error = EIO;
219		goto cleanup;
220	}
221	sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED;
222
223	return (0);
224
225cleanup:
226	if (xact != NULL)
227		vmbus_xact_put(xact);
228	hn_nvs_disconn_rxbuf(sc);
229	return (error);
230}
231
232static int
233hn_nvs_conn_chim(struct hn_softc *sc)
234{
235	struct vmbus_xact *xact = NULL;
236	struct hn_nvs_chim_conn *chim;
237	const struct hn_nvs_chim_connresp *resp;
238	size_t resp_len;
239	uint32_t status, sectsz;
240	int error;
241
242	/*
243	 * Connect chimney sending buffer GPADL to the primary channel.
244	 *
245	 * NOTE:
246	 * Only primary channel has chimney sending buffer connected to it.
247	 * Sub-channels just share this chimney sending buffer.
248	 */
249	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
250  	    sc->hn_chim_dma.hv_paddr, NETVSC_SEND_BUFFER_SIZE,
251	    &sc->hn_chim_gpadl);
252	if (error) {
253		if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error);
254		goto cleanup;
255	}
256
257	/*
258	 * Connect chimney sending buffer to NVS
259	 */
260
261	xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim));
262	if (xact == NULL) {
263		if_printf(sc->hn_ifp, "no xact for nvs chim conn\n");
264		error = ENXIO;
265		goto cleanup;
266	}
267	chim = vmbus_xact_req_data(xact);
268	chim->nvs_type = HN_NVS_TYPE_CHIM_CONN;
269	chim->nvs_gpadl = sc->hn_chim_gpadl;
270	chim->nvs_sig = HN_NVS_CHIM_SIG;
271
272	resp_len = sizeof(*resp);
273	resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
274	    HN_NVS_TYPE_CHIM_CONNRESP);
275	if (resp == NULL) {
276		if_printf(sc->hn_ifp, "exec nvs chim conn failed\n");
277		error = EIO;
278		goto cleanup;
279	}
280
281	status = resp->nvs_status;
282	sectsz = resp->nvs_sectsz;
283	vmbus_xact_put(xact);
284	xact = NULL;
285
286	if (status != HN_NVS_STATUS_OK) {
287		if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status);
288		error = EIO;
289		goto cleanup;
290	}
291	if (sectsz == 0) {
292		if_printf(sc->hn_ifp, "zero chimney sending buffer "
293		    "section size\n");
294		return (0);
295	}
296
297	sc->hn_chim_szmax = sectsz;
298	sc->hn_chim_cnt = NETVSC_SEND_BUFFER_SIZE / sc->hn_chim_szmax;
299	if (NETVSC_SEND_BUFFER_SIZE % sc->hn_chim_szmax != 0) {
300		if_printf(sc->hn_ifp, "chimney sending sections are "
301		    "not properly aligned\n");
302	}
303	if (sc->hn_chim_cnt % LONG_BIT != 0) {
304		if_printf(sc->hn_ifp, "discard %d chimney sending sections\n",
305		    sc->hn_chim_cnt % LONG_BIT);
306	}
307
308	sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT;
309	sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long),
310	    M_NETVSC, M_WAITOK | M_ZERO);
311
312	/* Done! */
313	sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
314	if (bootverbose) {
315		if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
316		    sc->hn_chim_szmax, sc->hn_chim_cnt);
317	}
318	return (0);
319
320cleanup:
321	if (xact != NULL)
322		vmbus_xact_put(xact);
323	hn_nvs_disconn_chim(sc);
324	return (error);
325}
326
327static int
328hn_nvs_disconn_rxbuf(struct hn_softc *sc)
329{
330	int error;
331
332	if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
333		struct hn_nvs_rxbuf_disconn disconn;
334
335		/*
336		 * Disconnect RXBUF from NVS.
337		 */
338		memset(&disconn, 0, sizeof(disconn));
339		disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN;
340		disconn.nvs_sig = HN_NVS_RXBUF_SIG;
341
342		/* NOTE: No response. */
343		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
344		if (error) {
345			if_printf(sc->hn_ifp,
346			    "send nvs rxbuf disconn failed: %d\n", error);
347			return (error);
348		}
349		sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
350
351		/*
352		 * Wait for the hypervisor to receive this NVS request.
353		 */
354		while (!vmbus_chan_tx_empty(sc->hn_prichan))
355			pause("waittx", 1);
356		/*
357		 * Linger long enough for NVS to disconnect RXBUF.
358		 */
359		pause("lingtx", (200 * hz) / 1000);
360	}
361
362	if (sc->hn_rxbuf_gpadl != 0) {
363		/*
364		 * Disconnect RXBUF from primary channel.
365		 */
366		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
367		    sc->hn_rxbuf_gpadl);
368		if (error) {
369			if_printf(sc->hn_ifp,
370			    "rxbuf gpadl disconn failed: %d\n", error);
371			return (error);
372		}
373		sc->hn_rxbuf_gpadl = 0;
374	}
375	return (0);
376}
377
378static int
379hn_nvs_disconn_chim(struct hn_softc *sc)
380{
381	int error;
382
383	if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
384		struct hn_nvs_chim_disconn disconn;
385
386		/*
387		 * Disconnect chimney sending buffer from NVS.
388		 */
389		memset(&disconn, 0, sizeof(disconn));
390		disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN;
391		disconn.nvs_sig = HN_NVS_CHIM_SIG;
392
393		/* NOTE: No response. */
394		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
395		if (error) {
396			if_printf(sc->hn_ifp,
397			    "send nvs chim disconn failed: %d\n", error);
398			return (error);
399		}
400		sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
401
402		/*
403		 * Wait for the hypervisor to receive this NVS request.
404		 */
405		while (!vmbus_chan_tx_empty(sc->hn_prichan))
406			pause("waittx", 1);
407		/*
408		 * Linger long enough for NVS to disconnect chimney
409		 * sending buffer.
410		 */
411		pause("lingtx", (200 * hz) / 1000);
412	}
413
414	if (sc->hn_chim_gpadl != 0) {
415		/*
416		 * Disconnect chimney sending buffer from primary channel.
417		 */
418		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
419		    sc->hn_chim_gpadl);
420		if (error) {
421			if_printf(sc->hn_ifp,
422			    "chim gpadl disconn failed: %d\n", error);
423			return (error);
424		}
425		sc->hn_chim_gpadl = 0;
426	}
427
428	if (sc->hn_chim_bmap != NULL) {
429		free(sc->hn_chim_bmap, M_NETVSC);
430		sc->hn_chim_bmap = NULL;
431	}
432	return (0);
433}
434
435static int
436hn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
437{
438	struct vmbus_xact *xact;
439	struct hn_nvs_init *init;
440	const struct hn_nvs_init_resp *resp;
441	size_t resp_len;
442	uint32_t status;
443
444	xact = vmbus_xact_get(sc->hn_xact, sizeof(*init));
445	if (xact == NULL) {
446		if_printf(sc->hn_ifp, "no xact for nvs init\n");
447		return (ENXIO);
448	}
449	init = vmbus_xact_req_data(xact);
450	init->nvs_type = HN_NVS_TYPE_INIT;
451	init->nvs_ver_min = nvs_ver;
452	init->nvs_ver_max = nvs_ver;
453
454	resp_len = sizeof(*resp);
455	resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len,
456	    HN_NVS_TYPE_INIT_RESP);
457	if (resp == NULL) {
458		if_printf(sc->hn_ifp, "exec init failed\n");
459		vmbus_xact_put(xact);
460		return (EIO);
461	}
462
463	status = resp->nvs_status;
464	vmbus_xact_put(xact);
465
466	if (status != HN_NVS_STATUS_OK) {
467		if (bootverbose) {
468			/*
469			 * Caller may try another NVS version, and will log
470			 * error if there are no more NVS versions to try,
471			 * so don't bark out loud here.
472			 */
473			if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n",
474			    nvs_ver);
475		}
476		return (EINVAL);
477	}
478	return (0);
479}
480
481/*
482 * Configure MTU and enable VLAN.
483 */
484static int
485hn_nvs_conf_ndis(struct hn_softc *sc, int mtu)
486{
487	struct hn_nvs_ndis_conf conf;
488	int error;
489
490	memset(&conf, 0, sizeof(conf));
491	conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
492	conf.nvs_mtu = mtu;
493	conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
494
495	/* NOTE: No response. */
496	error = hn_nvs_req_send(sc, &conf, sizeof(conf));
497	if (error) {
498		if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error);
499		return (error);
500	}
501
502	if (bootverbose)
503		if_printf(sc->hn_ifp, "nvs ndis conf done\n");
504	sc->hn_caps |= HN_CAP_MTU | HN_CAP_VLAN;
505	return (0);
506}
507
508static int
509hn_nvs_init_ndis(struct hn_softc *sc)
510{
511	struct hn_nvs_ndis_init ndis;
512	int error;
513
514	memset(&ndis, 0, sizeof(ndis));
515	ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT;
516	ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver);
517	ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver);
518
519	/* NOTE: No response. */
520	error = hn_nvs_req_send(sc, &ndis, sizeof(ndis));
521	if (error)
522		if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error);
523	return (error);
524}
525
526static int
527hn_nvs_init(struct hn_softc *sc)
528{
529	int i, error;
530
531	if (device_is_attached(sc->hn_dev)) {
532		/*
533		 * NVS version and NDIS version MUST NOT be changed.
534		 */
535		if (bootverbose) {
536			if_printf(sc->hn_ifp, "reinit NVS version 0x%x, "
537			    "NDIS version %u.%u\n", sc->hn_nvs_ver,
538			    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
539			    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
540		}
541
542		error = hn_nvs_doinit(sc, sc->hn_nvs_ver);
543		if (error) {
544			if_printf(sc->hn_ifp, "reinit NVS version 0x%x "
545			    "failed: %d\n", sc->hn_nvs_ver, error);
546		}
547		return (error);
548	}
549
550	/*
551	 * Find the supported NVS version and set NDIS version accordingly.
552	 */
553	for (i = 0; i < nitems(hn_nvs_version); ++i) {
554		error = hn_nvs_doinit(sc, hn_nvs_version[i]);
555		if (!error) {
556			sc->hn_nvs_ver = hn_nvs_version[i];
557
558			/* Set NDIS version according to NVS version. */
559			sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
560			if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
561				sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
562
563			if (bootverbose) {
564				if_printf(sc->hn_ifp, "NVS version 0x%x, "
565				    "NDIS version %u.%u\n", sc->hn_nvs_ver,
566				    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
567				    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
568			}
569			return (0);
570		}
571	}
572	if_printf(sc->hn_ifp, "no NVS available\n");
573	return (ENXIO);
574}
575
576int
577hn_nvs_attach(struct hn_softc *sc, int mtu)
578{
579	int error;
580
581	/*
582	 * Initialize NVS.
583	 */
584	error = hn_nvs_init(sc);
585	if (error)
586		return (error);
587
588	if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
589		/*
590		 * Configure NDIS before initializing it.
591		 */
592		error = hn_nvs_conf_ndis(sc, mtu);
593		if (error)
594			return (error);
595	}
596
597	/*
598	 * Initialize NDIS.
599	 */
600	error = hn_nvs_init_ndis(sc);
601	if (error)
602		return (error);
603
604	/*
605	 * Connect RXBUF.
606	 */
607	error = hn_nvs_conn_rxbuf(sc);
608	if (error)
609		return (error);
610
611	/*
612	 * Connect chimney sending buffer.
613	 */
614	error = hn_nvs_conn_chim(sc);
615	if (error)
616		return (error);
617	return (0);
618}
619
620void
621hn_nvs_detach(struct hn_softc *sc)
622{
623
624	/* NOTE: there are no requests to stop the NVS. */
625	hn_nvs_disconn_rxbuf(sc);
626	hn_nvs_disconn_chim(sc);
627}
628
629void
630hn_nvs_sent_xact(struct hn_send_ctx *sndc,
631    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
632    const void *data, int dlen)
633{
634
635	vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen);
636}
637
638static void
639hn_nvs_sent_none(struct hn_send_ctx *sndc __unused,
640    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
641    const void *data __unused, int dlen __unused)
642{
643	/* EMPTY */
644}
645
646void
647hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
648{
649	u_long mask;
650	uint32_t idx;
651
652	idx = chim_idx / LONG_BIT;
653	KASSERT(idx < sc->hn_chim_bmap_cnt,
654	    ("invalid chimney index 0x%x", chim_idx));
655
656	mask = 1UL << (chim_idx % LONG_BIT);
657	KASSERT(sc->hn_chim_bmap[idx] & mask,
658	    ("index bitmap 0x%lx, chimney index %u, "
659	     "bitmap idx %d, bitmask 0x%lx",
660	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
661
662	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
663}
664
665/*
666 * Net VSC on send
667 * Sends a packet on the specified Hyper-V device.
668 * Returns 0 on success, non-zero on failure.
669 */
670int
671hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
672    struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt)
673{
674	struct hn_nvs_rndis rndis;
675	int ret;
676
677	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
678	rndis.nvs_rndis_mtype = rndis_mtype;
679	rndis.nvs_chim_idx = sndc->hn_chim_idx;
680	rndis.nvs_chim_sz = sndc->hn_chim_sz;
681
682	if (gpa_cnt) {
683		ret = hn_nvs_send_sglist(chan, gpa, gpa_cnt,
684		    &rndis, sizeof(rndis), sndc);
685	} else {
686		ret = hn_nvs_send(chan, VMBUS_CHANPKT_FLAG_RC,
687		    &rndis, sizeof(rndis), sndc);
688	}
689
690	return (ret);
691}
692
693int
694hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0)
695{
696	struct vmbus_xact *xact;
697	struct hn_nvs_subch_req *req;
698	const struct hn_nvs_subch_resp *resp;
699	int error, nsubch_req;
700	uint32_t nsubch;
701	size_t resp_len;
702
703	nsubch_req = *nsubch0;
704	KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req));
705
706	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
707	if (xact == NULL) {
708		if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n");
709		return (ENXIO);
710	}
711	req = vmbus_xact_req_data(xact);
712	req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
713	req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
714	req->nvs_nsubch = nsubch_req;
715
716	resp_len = sizeof(*resp);
717	resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
718	    HN_NVS_TYPE_SUBCH_RESP);
719	if (resp == NULL) {
720		if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n");
721		error = EIO;
722		goto done;
723	}
724	if (resp->nvs_status != HN_NVS_STATUS_OK) {
725		if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n",
726		    resp->nvs_status);
727		error = EIO;
728		goto done;
729	}
730
731	nsubch = resp->nvs_nsubch;
732	if (nsubch > nsubch_req) {
733		if_printf(sc->hn_ifp, "%u subchans are allocated, "
734		    "requested %d\n", nsubch, nsubch_req);
735		nsubch = nsubch_req;
736	}
737	*nsubch0 = nsubch;
738	error = 0;
739done:
740	vmbus_xact_put(xact);
741	return (error);
742}
743