]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/netvsc/hn_nvs.c
MFC 308011,308012
[FreeBSD/stable/10.git] / sys / dev / hyperv / netvsc / hn_nvs.c
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$");
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_arp.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
63 static int                      hn_nvs_conn_chim(struct hn_softc *);
64 static int                      hn_nvs_conn_rxbuf(struct hn_softc *);
65 static int                      hn_nvs_disconn_chim(struct hn_softc *);
66 static int                      hn_nvs_disconn_rxbuf(struct hn_softc *);
67 static int                      hn_nvs_conf_ndis(struct hn_softc *, int);
68 static int                      hn_nvs_init_ndis(struct hn_softc *);
69 static int                      hn_nvs_doinit(struct hn_softc *, uint32_t);
70 static int                      hn_nvs_init(struct hn_softc *);
71 static const void               *hn_nvs_xact_execute(struct hn_softc *,
72                                     struct vmbus_xact *, void *, int,
73                                     size_t *, uint32_t);
74 static void                     hn_nvs_sent_none(struct hn_nvs_sendctx *,
75                                     struct hn_softc *, struct vmbus_channel *,
76                                     const void *, int);
77
78 struct hn_nvs_sendctx           hn_nvs_sendctx_none =
79     HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL);
80
81 static 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
88 static const void *
89 hn_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
131 static __inline int
132 hn_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
139 static int 
140 hn_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
209 cleanup:
210         if (xact != NULL)
211                 vmbus_xact_put(xact);
212         hn_nvs_disconn_rxbuf(sc);
213         return (error);
214 }
215
216 static int 
217 hn_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
303 cleanup:
304         if (xact != NULL)
305                 vmbus_xact_put(xact);
306         hn_nvs_disconn_chim(sc);
307         return (error);
308 }
309
310 static int
311 hn_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
361 static int
362 hn_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
418 static int
419 hn_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  */
467 static int
468 hn_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
491 static int
492 hn_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
509 static int
510 hn_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
559 done:
560         if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
561                 sc->hn_caps |= HN_CAP_HASHVAL;
562         return (0);
563 }
564
565 int
566 hn_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
609 void
610 hn_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
618 void
619 hn_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
627 static void
628 hn_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
635 int
636 hn_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;
681 done:
682         vmbus_xact_put(xact);
683         return (error);
684 }
685
686 int
687 hn_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 }