hn_nvs.c revision 308625
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
29/*
30 * Network Virtualization Service.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/netvsc/hn_nvs.c 308625 2016-11-14 03:36:59Z sephe $");
35
36#include "opt_inet6.h"
37#include "opt_inet.h"
38
39#include <sys/param.h>
40#include <sys/kernel.h>
41#include <sys/limits.h>
42#include <sys/socket.h>
43#include <sys/systm.h>
44#include <sys/taskqueue.h>
45
46#include <net/if.h>
47#include <net/if_var.h>
48#include <net/if_media.h>
49
50#include <netinet/in.h>
51#include <netinet/tcp_lro.h>
52
53#include <dev/hyperv/include/hyperv.h>
54#include <dev/hyperv/include/hyperv_busdma.h>
55#include <dev/hyperv/include/vmbus.h>
56#include <dev/hyperv/include/vmbus_xact.h>
57
58#include <dev/hyperv/netvsc/ndis.h>
59#include <dev/hyperv/netvsc/if_hnreg.h>
60#include <dev/hyperv/netvsc/if_hnvar.h>
61#include <dev/hyperv/netvsc/hn_nvs.h>
62
63static int			hn_nvs_conn_chim(struct hn_softc *);
64static int			hn_nvs_conn_rxbuf(struct hn_softc *);
65static int			hn_nvs_disconn_chim(struct hn_softc *);
66static int			hn_nvs_disconn_rxbuf(struct hn_softc *);
67static int			hn_nvs_conf_ndis(struct hn_softc *, int);
68static int			hn_nvs_init_ndis(struct hn_softc *);
69static int			hn_nvs_doinit(struct hn_softc *, uint32_t);
70static int			hn_nvs_init(struct hn_softc *);
71static const void		*hn_nvs_xact_execute(struct hn_softc *,
72				    struct vmbus_xact *, void *, int,
73				    size_t *, uint32_t);
74static void			hn_nvs_sent_none(struct hn_nvs_sendctx *,
75				    struct hn_softc *, struct vmbus_channel *,
76				    const void *, int);
77
78struct hn_nvs_sendctx		hn_nvs_sendctx_none =
79    HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL);
80
81static const uint32_t		hn_nvs_version[] = {
82	HN_NVS_VERSION_5,
83	HN_NVS_VERSION_4,
84	HN_NVS_VERSION_2,
85	HN_NVS_VERSION_1
86};
87
88static const void *
89hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
90    void *req, int reqlen, size_t *resplen0, uint32_t type)
91{
92	struct hn_nvs_sendctx sndc;
93	size_t resplen, min_resplen = *resplen0;
94	const struct hn_nvs_hdr *hdr;
95	int error;
96
97	KASSERT(min_resplen >= sizeof(*hdr),
98	    ("invalid minimum response len %zu", min_resplen));
99
100	/*
101	 * Execute the xact setup by the caller.
102	 */
103	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
104
105	vmbus_xact_activate(xact);
106	error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC,
107	    req, reqlen, &sndc);
108	if (error) {
109		vmbus_xact_deactivate(xact);
110		return (NULL);
111	}
112	hdr = vmbus_xact_wait(xact, &resplen);
113
114	/*
115	 * Check this NVS response message.
116	 */
117	if (resplen < min_resplen) {
118		if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen);
119		return (NULL);
120	}
121	if (hdr->nvs_type != type) {
122		if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, "
123		    "expect 0x%08x\n", hdr->nvs_type, type);
124		return (NULL);
125	}
126	/* All pass! */
127	*resplen0 = resplen;
128	return (hdr);
129}
130
131static __inline int
132hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
133{
134
135	return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE,
136	    req, reqlen, &hn_nvs_sendctx_none));
137}
138
139static int
140hn_nvs_conn_rxbuf(struct hn_softc *sc)
141{
142	struct vmbus_xact *xact = NULL;
143	struct hn_nvs_rxbuf_conn *conn;
144	const struct hn_nvs_rxbuf_connresp *resp;
145	size_t resp_len;
146	uint32_t status;
147	int error, rxbuf_size;
148
149	/*
150	 * Limit RXBUF size for old NVS.
151	 */
152	if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
153		rxbuf_size = HN_RXBUF_SIZE_COMPAT;
154	else
155		rxbuf_size = HN_RXBUF_SIZE;
156
157	/*
158	 * Connect the RXBUF GPADL to the primary channel.
159	 *
160	 * NOTE:
161	 * Only primary channel has RXBUF connected to it.  Sub-channels
162	 * just share this RXBUF.
163	 */
164	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
165	    sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
166	if (error) {
167		if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n",
168		    error);
169		goto cleanup;
170	}
171
172	/*
173	 * Connect RXBUF to NVS.
174	 */
175
176	xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn));
177	if (xact == NULL) {
178		if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n");
179		error = ENXIO;
180		goto cleanup;
181	}
182	conn = vmbus_xact_req_data(xact);
183	conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN;
184	conn->nvs_gpadl = sc->hn_rxbuf_gpadl;
185	conn->nvs_sig = HN_NVS_RXBUF_SIG;
186
187	resp_len = sizeof(*resp);
188	resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
189	    HN_NVS_TYPE_RXBUF_CONNRESP);
190	if (resp == NULL) {
191		if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n");
192		error = EIO;
193		goto cleanup;
194	}
195
196	status = resp->nvs_status;
197	vmbus_xact_put(xact);
198	xact = NULL;
199
200	if (status != HN_NVS_STATUS_OK) {
201		if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status);
202		error = EIO;
203		goto cleanup;
204	}
205	sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED;
206
207	return (0);
208
209cleanup:
210	if (xact != NULL)
211		vmbus_xact_put(xact);
212	hn_nvs_disconn_rxbuf(sc);
213	return (error);
214}
215
216static int
217hn_nvs_conn_chim(struct hn_softc *sc)
218{
219	struct vmbus_xact *xact = NULL;
220	struct hn_nvs_chim_conn *chim;
221	const struct hn_nvs_chim_connresp *resp;
222	size_t resp_len;
223	uint32_t status, sectsz;
224	int error;
225
226	/*
227	 * Connect chimney sending buffer GPADL to the primary channel.
228	 *
229	 * NOTE:
230	 * Only primary channel has chimney sending buffer connected to it.
231	 * Sub-channels just share this chimney sending buffer.
232	 */
233	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
234  	    sc->hn_chim_dma.hv_paddr, HN_CHIM_SIZE, &sc->hn_chim_gpadl);
235	if (error) {
236		if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error);
237		goto cleanup;
238	}
239
240	/*
241	 * Connect chimney sending buffer to NVS
242	 */
243
244	xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim));
245	if (xact == NULL) {
246		if_printf(sc->hn_ifp, "no xact for nvs chim conn\n");
247		error = ENXIO;
248		goto cleanup;
249	}
250	chim = vmbus_xact_req_data(xact);
251	chim->nvs_type = HN_NVS_TYPE_CHIM_CONN;
252	chim->nvs_gpadl = sc->hn_chim_gpadl;
253	chim->nvs_sig = HN_NVS_CHIM_SIG;
254
255	resp_len = sizeof(*resp);
256	resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
257	    HN_NVS_TYPE_CHIM_CONNRESP);
258	if (resp == NULL) {
259		if_printf(sc->hn_ifp, "exec nvs chim conn failed\n");
260		error = EIO;
261		goto cleanup;
262	}
263
264	status = resp->nvs_status;
265	sectsz = resp->nvs_sectsz;
266	vmbus_xact_put(xact);
267	xact = NULL;
268
269	if (status != HN_NVS_STATUS_OK) {
270		if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status);
271		error = EIO;
272		goto cleanup;
273	}
274	if (sectsz == 0) {
275		if_printf(sc->hn_ifp, "zero chimney sending buffer "
276		    "section size\n");
277		return (0);
278	}
279
280	sc->hn_chim_szmax = sectsz;
281	sc->hn_chim_cnt = HN_CHIM_SIZE / sc->hn_chim_szmax;
282	if (HN_CHIM_SIZE % sc->hn_chim_szmax != 0) {
283		if_printf(sc->hn_ifp, "chimney sending sections are "
284		    "not properly aligned\n");
285	}
286	if (sc->hn_chim_cnt % LONG_BIT != 0) {
287		if_printf(sc->hn_ifp, "discard %d chimney sending sections\n",
288		    sc->hn_chim_cnt % LONG_BIT);
289	}
290
291	sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT;
292	sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long),
293	    M_DEVBUF, M_WAITOK | M_ZERO);
294
295	/* Done! */
296	sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
297	if (bootverbose) {
298		if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
299		    sc->hn_chim_szmax, sc->hn_chim_cnt);
300	}
301	return (0);
302
303cleanup:
304	if (xact != NULL)
305		vmbus_xact_put(xact);
306	hn_nvs_disconn_chim(sc);
307	return (error);
308}
309
310static int
311hn_nvs_disconn_rxbuf(struct hn_softc *sc)
312{
313	int error;
314
315	if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
316		struct hn_nvs_rxbuf_disconn disconn;
317
318		/*
319		 * Disconnect RXBUF from NVS.
320		 */
321		memset(&disconn, 0, sizeof(disconn));
322		disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN;
323		disconn.nvs_sig = HN_NVS_RXBUF_SIG;
324
325		/* NOTE: No response. */
326		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
327		if (error) {
328			if_printf(sc->hn_ifp,
329			    "send nvs rxbuf disconn failed: %d\n", error);
330			return (error);
331		}
332		sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
333
334		/*
335		 * Wait for the hypervisor to receive this NVS request.
336		 */
337		while (!vmbus_chan_tx_empty(sc->hn_prichan))
338			pause("waittx", 1);
339		/*
340		 * Linger long enough for NVS to disconnect RXBUF.
341		 */
342		pause("lingtx", (200 * hz) / 1000);
343	}
344
345	if (sc->hn_rxbuf_gpadl != 0) {
346		/*
347		 * Disconnect RXBUF from primary channel.
348		 */
349		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
350		    sc->hn_rxbuf_gpadl);
351		if (error) {
352			if_printf(sc->hn_ifp,
353			    "rxbuf gpadl disconn failed: %d\n", error);
354			return (error);
355		}
356		sc->hn_rxbuf_gpadl = 0;
357	}
358	return (0);
359}
360
361static int
362hn_nvs_disconn_chim(struct hn_softc *sc)
363{
364	int error;
365
366	if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
367		struct hn_nvs_chim_disconn disconn;
368
369		/*
370		 * Disconnect chimney sending buffer from NVS.
371		 */
372		memset(&disconn, 0, sizeof(disconn));
373		disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN;
374		disconn.nvs_sig = HN_NVS_CHIM_SIG;
375
376		/* NOTE: No response. */
377		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
378		if (error) {
379			if_printf(sc->hn_ifp,
380			    "send nvs chim disconn failed: %d\n", error);
381			return (error);
382		}
383		sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
384
385		/*
386		 * Wait for the hypervisor to receive this NVS request.
387		 */
388		while (!vmbus_chan_tx_empty(sc->hn_prichan))
389			pause("waittx", 1);
390		/*
391		 * Linger long enough for NVS to disconnect chimney
392		 * sending buffer.
393		 */
394		pause("lingtx", (200 * hz) / 1000);
395	}
396
397	if (sc->hn_chim_gpadl != 0) {
398		/*
399		 * Disconnect chimney sending buffer from primary channel.
400		 */
401		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
402		    sc->hn_chim_gpadl);
403		if (error) {
404			if_printf(sc->hn_ifp,
405			    "chim gpadl disconn failed: %d\n", error);
406			return (error);
407		}
408		sc->hn_chim_gpadl = 0;
409	}
410
411	if (sc->hn_chim_bmap != NULL) {
412		free(sc->hn_chim_bmap, M_DEVBUF);
413		sc->hn_chim_bmap = NULL;
414	}
415	return (0);
416}
417
418static int
419hn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
420{
421	struct vmbus_xact *xact;
422	struct hn_nvs_init *init;
423	const struct hn_nvs_init_resp *resp;
424	size_t resp_len;
425	uint32_t status;
426
427	xact = vmbus_xact_get(sc->hn_xact, sizeof(*init));
428	if (xact == NULL) {
429		if_printf(sc->hn_ifp, "no xact for nvs init\n");
430		return (ENXIO);
431	}
432	init = vmbus_xact_req_data(xact);
433	init->nvs_type = HN_NVS_TYPE_INIT;
434	init->nvs_ver_min = nvs_ver;
435	init->nvs_ver_max = nvs_ver;
436
437	resp_len = sizeof(*resp);
438	resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len,
439	    HN_NVS_TYPE_INIT_RESP);
440	if (resp == NULL) {
441		if_printf(sc->hn_ifp, "exec init failed\n");
442		vmbus_xact_put(xact);
443		return (EIO);
444	}
445
446	status = resp->nvs_status;
447	vmbus_xact_put(xact);
448
449	if (status != HN_NVS_STATUS_OK) {
450		if (bootverbose) {
451			/*
452			 * Caller may try another NVS version, and will log
453			 * error if there are no more NVS versions to try,
454			 * so don't bark out loud here.
455			 */
456			if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n",
457			    nvs_ver);
458		}
459		return (EINVAL);
460	}
461	return (0);
462}
463
464/*
465 * Configure MTU and enable VLAN.
466 */
467static int
468hn_nvs_conf_ndis(struct hn_softc *sc, int mtu)
469{
470	struct hn_nvs_ndis_conf conf;
471	int error;
472
473	memset(&conf, 0, sizeof(conf));
474	conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
475	conf.nvs_mtu = mtu;
476	conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
477
478	/* NOTE: No response. */
479	error = hn_nvs_req_send(sc, &conf, sizeof(conf));
480	if (error) {
481		if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error);
482		return (error);
483	}
484
485	if (bootverbose)
486		if_printf(sc->hn_ifp, "nvs ndis conf done\n");
487	sc->hn_caps |= HN_CAP_MTU | HN_CAP_VLAN;
488	return (0);
489}
490
491static int
492hn_nvs_init_ndis(struct hn_softc *sc)
493{
494	struct hn_nvs_ndis_init ndis;
495	int error;
496
497	memset(&ndis, 0, sizeof(ndis));
498	ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT;
499	ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver);
500	ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver);
501
502	/* NOTE: No response. */
503	error = hn_nvs_req_send(sc, &ndis, sizeof(ndis));
504	if (error)
505		if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error);
506	return (error);
507}
508
509static int
510hn_nvs_init(struct hn_softc *sc)
511{
512	int i, error;
513
514	if (device_is_attached(sc->hn_dev)) {
515		/*
516		 * NVS version and NDIS version MUST NOT be changed.
517		 */
518		if (bootverbose) {
519			if_printf(sc->hn_ifp, "reinit NVS version 0x%x, "
520			    "NDIS version %u.%u\n", sc->hn_nvs_ver,
521			    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
522			    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
523		}
524
525		error = hn_nvs_doinit(sc, sc->hn_nvs_ver);
526		if (error) {
527			if_printf(sc->hn_ifp, "reinit NVS version 0x%x "
528			    "failed: %d\n", sc->hn_nvs_ver, error);
529			return (error);
530		}
531		goto done;
532	}
533
534	/*
535	 * Find the supported NVS version and set NDIS version accordingly.
536	 */
537	for (i = 0; i < nitems(hn_nvs_version); ++i) {
538		error = hn_nvs_doinit(sc, hn_nvs_version[i]);
539		if (!error) {
540			sc->hn_nvs_ver = hn_nvs_version[i];
541
542			/* Set NDIS version according to NVS version. */
543			sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
544			if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
545				sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
546
547			if (bootverbose) {
548				if_printf(sc->hn_ifp, "NVS version 0x%x, "
549				    "NDIS version %u.%u\n", sc->hn_nvs_ver,
550				    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
551				    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
552			}
553			goto done;
554		}
555	}
556	if_printf(sc->hn_ifp, "no NVS available\n");
557	return (ENXIO);
558
559done:
560	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
561		sc->hn_caps |= HN_CAP_HASHVAL;
562	return (0);
563}
564
565int
566hn_nvs_attach(struct hn_softc *sc, int mtu)
567{
568	int error;
569
570	/*
571	 * Initialize NVS.
572	 */
573	error = hn_nvs_init(sc);
574	if (error)
575		return (error);
576
577	if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
578		/*
579		 * Configure NDIS before initializing it.
580		 */
581		error = hn_nvs_conf_ndis(sc, mtu);
582		if (error)
583			return (error);
584	}
585
586	/*
587	 * Initialize NDIS.
588	 */
589	error = hn_nvs_init_ndis(sc);
590	if (error)
591		return (error);
592
593	/*
594	 * Connect RXBUF.
595	 */
596	error = hn_nvs_conn_rxbuf(sc);
597	if (error)
598		return (error);
599
600	/*
601	 * Connect chimney sending buffer.
602	 */
603	error = hn_nvs_conn_chim(sc);
604	if (error)
605		return (error);
606	return (0);
607}
608
609void
610hn_nvs_detach(struct hn_softc *sc)
611{
612
613	/* NOTE: there are no requests to stop the NVS. */
614	hn_nvs_disconn_rxbuf(sc);
615	hn_nvs_disconn_chim(sc);
616}
617
618void
619hn_nvs_sent_xact(struct hn_nvs_sendctx *sndc,
620    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
621    const void *data, int dlen)
622{
623
624	vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen);
625}
626
627static void
628hn_nvs_sent_none(struct hn_nvs_sendctx *sndc __unused,
629    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
630    const void *data __unused, int dlen __unused)
631{
632	/* EMPTY */
633}
634
635int
636hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0)
637{
638	struct vmbus_xact *xact;
639	struct hn_nvs_subch_req *req;
640	const struct hn_nvs_subch_resp *resp;
641	int error, nsubch_req;
642	uint32_t nsubch;
643	size_t resp_len;
644
645	nsubch_req = *nsubch0;
646	KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req));
647
648	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
649	if (xact == NULL) {
650		if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n");
651		return (ENXIO);
652	}
653	req = vmbus_xact_req_data(xact);
654	req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
655	req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
656	req->nvs_nsubch = nsubch_req;
657
658	resp_len = sizeof(*resp);
659	resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
660	    HN_NVS_TYPE_SUBCH_RESP);
661	if (resp == NULL) {
662		if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n");
663		error = EIO;
664		goto done;
665	}
666	if (resp->nvs_status != HN_NVS_STATUS_OK) {
667		if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n",
668		    resp->nvs_status);
669		error = EIO;
670		goto done;
671	}
672
673	nsubch = resp->nvs_nsubch;
674	if (nsubch > nsubch_req) {
675		if_printf(sc->hn_ifp, "%u subchans are allocated, "
676		    "requested %d\n", nsubch, nsubch_req);
677		nsubch = nsubch_req;
678	}
679	*nsubch0 = nsubch;
680	error = 0;
681done:
682	vmbus_xact_put(xact);
683	return (error);
684}
685
686int
687hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
688    struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt)
689{
690
691	return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL,
692	    sndc, gpa, gpa_cnt);
693}
694