]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hyperv/netvsc/hv_net_vsc.c
MFC 305586,305587
[FreeBSD/FreeBSD.git] / sys / dev / hyperv / netvsc / hv_net_vsc.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  * $FreeBSD$
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_var.h>
44 #include <net/if_arp.h>
45 #include <machine/bus.h>
46 #include <machine/atomic.h>
47
48 #include <dev/hyperv/include/hyperv.h>
49 #include <dev/hyperv/include/vmbus_xact.h>
50 #include <dev/hyperv/netvsc/hv_net_vsc.h>
51 #include <dev/hyperv/netvsc/hv_rndis_filter.h>
52 #include <dev/hyperv/netvsc/if_hnreg.h>
53 #include <dev/hyperv/netvsc/if_hnvar.h>
54
55 MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver");
56
57 /*
58  * Forward declarations
59  */
60 static int  hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc);
61 static int  hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *);
62 static int  hv_nv_destroy_send_buffer(struct hn_softc *sc);
63 static int  hv_nv_destroy_rx_buffer(struct hn_softc *sc);
64 static int  hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu);
65 static void hn_nvs_sent_none(struct hn_send_ctx *sndc,
66     struct hn_softc *, struct vmbus_channel *chan,
67     const void *, int);
68
69 struct hn_send_ctx      hn_send_ctx_none =
70     HN_SEND_CTX_INITIALIZER(hn_nvs_sent_none, NULL);
71
72 static const uint32_t           hn_nvs_version[] = {
73         HN_NVS_VERSION_5,
74         HN_NVS_VERSION_4,
75         HN_NVS_VERSION_2,
76         HN_NVS_VERSION_1
77 };
78
79 uint32_t
80 hn_chim_alloc(struct hn_softc *sc)
81 {
82         int i, bmap_cnt = sc->hn_chim_bmap_cnt;
83         u_long *bmap = sc->hn_chim_bmap;
84         uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
85
86         for (i = 0; i < bmap_cnt; ++i) {
87                 int idx;
88
89                 idx = ffsl(~bmap[i]);
90                 if (idx == 0)
91                         continue;
92
93                 --idx; /* ffsl is 1-based */
94                 KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
95                     ("invalid i %d and idx %d", i, idx));
96
97                 if (atomic_testandset_long(&bmap[i], idx))
98                         continue;
99
100                 ret = i * LONG_BIT + idx;
101                 break;
102         }
103         return (ret);
104 }
105
106 const void *
107 hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
108     void *req, int reqlen, size_t *resplen0, uint32_t type)
109 {
110         struct hn_send_ctx sndc;
111         size_t resplen, min_resplen = *resplen0;
112         const struct hn_nvs_hdr *hdr;
113         int error;
114
115         KASSERT(min_resplen >= sizeof(*hdr),
116             ("invalid minimum response len %zu", min_resplen));
117
118         /*
119          * Execute the xact setup by the caller.
120          */
121         hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact);
122
123         vmbus_xact_activate(xact);
124         error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC,
125             req, reqlen, &sndc);
126         if (error) {
127                 vmbus_xact_deactivate(xact);
128                 return (NULL);
129         }
130         hdr = vmbus_xact_wait(xact, &resplen);
131
132         /*
133          * Check this NVS response message.
134          */
135         if (resplen < min_resplen) {
136                 if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen);
137                 return (NULL);
138         }
139         if (hdr->nvs_type != type) {
140                 if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, "
141                     "expect 0x%08x\n", hdr->nvs_type, type);
142                 return (NULL);
143         }
144         /* All pass! */
145         *resplen0 = resplen;
146         return (hdr);
147 }
148
149 static __inline int
150 hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
151 {
152
153         return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE,
154             req, reqlen, &hn_send_ctx_none));
155 }
156
157 /*
158  * Net VSC initialize receive buffer with net VSP
159  * 
160  * Net VSP:  Network virtual services client, also known as the
161  *     Hyper-V extensible switch and the synthetic data path.
162  */
163 static int 
164 hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
165 {
166         struct vmbus_xact *xact = NULL;
167         struct hn_nvs_rxbuf_conn *conn;
168         const struct hn_nvs_rxbuf_connresp *resp;
169         size_t resp_len;
170         uint32_t status;
171         int error, rxbuf_size;
172
173         /*
174          * Limit RXBUF size for old NVS.
175          */
176         if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
177                 rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
178         else
179                 rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE;
180
181         /*
182          * Connect the RXBUF GPADL to the primary channel.
183          *
184          * NOTE:
185          * Only primary channel has RXBUF connected to it.  Sub-channels
186          * just share this RXBUF.
187          */
188         error = vmbus_chan_gpadl_connect(sc->hn_prichan,
189             sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
190         if (error) {
191                 if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n",
192                     error);
193                 goto cleanup;
194         }
195
196         /*
197          * Connect RXBUF to NVS.
198          */
199
200         xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn));
201         if (xact == NULL) {
202                 if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n");
203                 error = ENXIO;
204                 goto cleanup;
205         }
206         conn = vmbus_xact_req_data(xact);
207         conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN;
208         conn->nvs_gpadl = sc->hn_rxbuf_gpadl;
209         conn->nvs_sig = HN_NVS_RXBUF_SIG;
210
211         resp_len = sizeof(*resp);
212         resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
213             HN_NVS_TYPE_RXBUF_CONNRESP);
214         if (resp == NULL) {
215                 if_printf(sc->hn_ifp, "exec rxbuf conn failed\n");
216                 error = EIO;
217                 goto cleanup;
218         }
219
220         status = resp->nvs_status;
221         vmbus_xact_put(xact);
222         xact = NULL;
223
224         if (status != HN_NVS_STATUS_OK) {
225                 if_printf(sc->hn_ifp, "rxbuf conn failed: %x\n", status);
226                 error = EIO;
227                 goto cleanup;
228         }
229         sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED;
230
231         return (0);
232
233 cleanup:
234         if (xact != NULL)
235                 vmbus_xact_put(xact);
236         hv_nv_destroy_rx_buffer(sc);
237         return (error);
238 }
239
240 /*
241  * Net VSC initialize send buffer with net VSP
242  */
243 static int 
244 hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
245 {
246         struct vmbus_xact *xact = NULL;
247         struct hn_nvs_chim_conn *chim;
248         const struct hn_nvs_chim_connresp *resp;
249         size_t resp_len;
250         uint32_t status, sectsz;
251         int error;
252
253         /*
254          * Connect chimney sending buffer GPADL to the primary channel.
255          *
256          * NOTE:
257          * Only primary channel has chimney sending buffer connected to it.
258          * Sub-channels just share this chimney sending buffer.
259          */
260         error = vmbus_chan_gpadl_connect(sc->hn_prichan,
261             sc->hn_chim_dma.hv_paddr, NETVSC_SEND_BUFFER_SIZE,
262             &sc->hn_chim_gpadl);
263         if (error) {
264                 if_printf(sc->hn_ifp, "chimney sending buffer gpadl "
265                     "connect failed: %d\n", error);
266                 goto cleanup;
267         }
268
269         /*
270          * Connect chimney sending buffer to NVS
271          */
272
273         xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim));
274         if (xact == NULL) {
275                 if_printf(sc->hn_ifp, "no xact for nvs chim conn\n");
276                 error = ENXIO;
277                 goto cleanup;
278         }
279         chim = vmbus_xact_req_data(xact);
280         chim->nvs_type = HN_NVS_TYPE_CHIM_CONN;
281         chim->nvs_gpadl = sc->hn_chim_gpadl;
282         chim->nvs_sig = HN_NVS_CHIM_SIG;
283
284         resp_len = sizeof(*resp);
285         resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
286             HN_NVS_TYPE_CHIM_CONNRESP);
287         if (resp == NULL) {
288                 if_printf(sc->hn_ifp, "exec chim conn failed\n");
289                 error = EIO;
290                 goto cleanup;
291         }
292
293         status = resp->nvs_status;
294         sectsz = resp->nvs_sectsz;
295         vmbus_xact_put(xact);
296         xact = NULL;
297
298         if (status != HN_NVS_STATUS_OK) {
299                 if_printf(sc->hn_ifp, "chim conn failed: %x\n", status);
300                 error = EIO;
301                 goto cleanup;
302         }
303         if (sectsz == 0) {
304                 if_printf(sc->hn_ifp, "zero chimney sending buffer "
305                     "section size\n");
306                 return 0;
307         }
308
309         sc->hn_chim_szmax = sectsz;
310         sc->hn_chim_cnt = NETVSC_SEND_BUFFER_SIZE / sc->hn_chim_szmax;
311         if (NETVSC_SEND_BUFFER_SIZE % sc->hn_chim_szmax != 0) {
312                 if_printf(sc->hn_ifp, "chimney sending sections are "
313                     "not properly aligned\n");
314         }
315         if (sc->hn_chim_cnt % LONG_BIT != 0) {
316                 if_printf(sc->hn_ifp, "discard %d chimney sending sections\n",
317                     sc->hn_chim_cnt % LONG_BIT);
318         }
319
320         sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT;
321         sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long),
322             M_NETVSC, M_WAITOK | M_ZERO);
323
324         /* Done! */
325         sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
326         if (bootverbose) {
327                 if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
328                     sc->hn_chim_szmax, sc->hn_chim_cnt);
329         }
330         return 0;
331
332 cleanup:
333         if (xact != NULL)
334                 vmbus_xact_put(xact);
335         hv_nv_destroy_send_buffer(sc);
336         return (error);
337 }
338
339 /*
340  * Net VSC destroy receive buffer
341  */
342 static int
343 hv_nv_destroy_rx_buffer(struct hn_softc *sc)
344 {
345         int ret = 0;
346
347         if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
348                 struct hn_nvs_rxbuf_disconn disconn;
349
350                 /*
351                  * Disconnect RXBUF from NVS.
352                  */
353                 memset(&disconn, 0, sizeof(disconn));
354                 disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN;
355                 disconn.nvs_sig = HN_NVS_RXBUF_SIG;
356
357                 /* NOTE: No response. */
358                 ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
359                 if (ret != 0) {
360                         if_printf(sc->hn_ifp,
361                             "send rxbuf disconn failed: %d\n", ret);
362                         return (ret);
363                 }
364                 sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
365         }
366                 
367         if (sc->hn_rxbuf_gpadl != 0) {
368                 /*
369                  * Disconnect RXBUF from primary channel.
370                  */
371                 ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
372                     sc->hn_rxbuf_gpadl);
373                 if (ret != 0) {
374                         if_printf(sc->hn_ifp,
375                             "rxbuf disconn failed: %d\n", ret);
376                         return (ret);
377                 }
378                 sc->hn_rxbuf_gpadl = 0;
379         }
380         return (ret);
381 }
382
383 /*
384  * Net VSC destroy send buffer
385  */
386 static int
387 hv_nv_destroy_send_buffer(struct hn_softc *sc)
388 {
389         int ret = 0;
390
391         if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
392                 struct hn_nvs_chim_disconn disconn;
393
394                 /*
395                  * Disconnect chimney sending buffer from NVS.
396                  */
397                 memset(&disconn, 0, sizeof(disconn));
398                 disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN;
399                 disconn.nvs_sig = HN_NVS_CHIM_SIG;
400
401                 /* NOTE: No response. */
402                 ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
403                 if (ret != 0) {
404                         if_printf(sc->hn_ifp,
405                             "send chim disconn failed: %d\n", ret);
406                         return (ret);
407                 }
408                 sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
409         }
410                 
411         if (sc->hn_chim_gpadl != 0) {
412                 /*
413                  * Disconnect chimney sending buffer from primary channel.
414                  */
415                 ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
416                     sc->hn_chim_gpadl);
417                 if (ret != 0) {
418                         if_printf(sc->hn_ifp,
419                             "chim disconn failed: %d\n", ret);
420                         return (ret);
421                 }
422                 sc->hn_chim_gpadl = 0;
423         }
424
425         if (sc->hn_chim_bmap != NULL) {
426                 free(sc->hn_chim_bmap, M_NETVSC);
427                 sc->hn_chim_bmap = NULL;
428         }
429
430         return (ret);
431 }
432
433 static int
434 hn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
435 {
436         struct vmbus_xact *xact;
437         struct hn_nvs_init *init;
438         const struct hn_nvs_init_resp *resp;
439         size_t resp_len;
440         uint32_t status;
441
442         xact = vmbus_xact_get(sc->hn_xact, sizeof(*init));
443         if (xact == NULL) {
444                 if_printf(sc->hn_ifp, "no xact for nvs init\n");
445                 return (ENXIO);
446         }
447         init = vmbus_xact_req_data(xact);
448         init->nvs_type = HN_NVS_TYPE_INIT;
449         init->nvs_ver_min = nvs_ver;
450         init->nvs_ver_max = nvs_ver;
451
452         resp_len = sizeof(*resp);
453         resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len,
454             HN_NVS_TYPE_INIT_RESP);
455         if (resp == NULL) {
456                 if_printf(sc->hn_ifp, "exec init failed\n");
457                 vmbus_xact_put(xact);
458                 return (EIO);
459         }
460
461         status = resp->nvs_status;
462         vmbus_xact_put(xact);
463
464         if (status != HN_NVS_STATUS_OK) {
465                 if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n",
466                     nvs_ver);
467                 return (EINVAL);
468         }
469         return (0);
470 }
471
472 /*
473  * Configure MTU and enable VLAN.
474  */
475 static int
476 hn_nvs_conf_ndis(struct hn_softc *sc, int mtu)
477 {
478         struct hn_nvs_ndis_conf conf;
479         int error;
480
481         memset(&conf, 0, sizeof(conf));
482         conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
483         conf.nvs_mtu = mtu;
484         conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
485
486         /* NOTE: No response. */
487         error = hn_nvs_req_send(sc, &conf, sizeof(conf));
488         if (error)
489                 if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error);
490         return (error);
491 }
492
493 static int
494 hn_nvs_init_ndis(struct hn_softc *sc)
495 {
496         struct hn_nvs_ndis_init ndis;
497         int error;
498
499         memset(&ndis, 0, sizeof(ndis));
500         ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT;
501         ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver);
502         ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver);
503
504         /* NOTE: No response. */
505         error = hn_nvs_req_send(sc, &ndis, sizeof(ndis));
506         if (error)
507                 if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error);
508         return (error);
509 }
510
511 static int
512 hn_nvs_init(struct hn_softc *sc)
513 {
514         int i;
515
516         for (i = 0; i < nitems(hn_nvs_version); ++i) {
517                 int error;
518
519                 error = hn_nvs_doinit(sc, hn_nvs_version[i]);
520                 if (!error) {
521                         sc->hn_nvs_ver = hn_nvs_version[i];
522
523                         /* Set NDIS version according to NVS version. */
524                         sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
525                         if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
526                                 sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
527
528                         if (bootverbose) {
529                                 if_printf(sc->hn_ifp, "NVS version 0x%x, "
530                                     "NDIS version %u.%u\n", sc->hn_nvs_ver,
531                                     HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
532                                     HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
533                         }
534                         return (0);
535                 }
536         }
537         if_printf(sc->hn_ifp, "no NVS available\n");
538         return (ENXIO);
539 }
540
541 static int
542 hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu)
543 {
544         int ret;
545
546         /*
547          * Initialize NVS.
548          */
549         ret = hn_nvs_init(sc);
550         if (ret != 0)
551                 return (ret);
552
553         if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
554                 /*
555                  * Configure NDIS before initializing it.
556                  */
557                 ret = hn_nvs_conf_ndis(sc, mtu);
558                 if (ret != 0)
559                         return (ret);
560         }
561
562         /*
563          * Initialize NDIS.
564          */
565         ret = hn_nvs_init_ndis(sc);
566         if (ret != 0)
567                 return (ret);
568
569         ret = hv_nv_init_rx_buffer_with_net_vsp(sc);
570         if (ret == 0)
571                 ret = hv_nv_init_send_buffer_with_net_vsp(sc);
572         return (ret);
573 }
574
575 /*
576  * Net VSC disconnect from VSP
577  */
578 static void
579 hv_nv_disconnect_from_vsp(struct hn_softc *sc)
580 {
581         hv_nv_destroy_rx_buffer(sc);
582         hv_nv_destroy_send_buffer(sc);
583 }
584
585 /*
586  * Net VSC on device add
587  * 
588  * Callback when the device belonging to this driver is added
589  */
590 int
591 hv_nv_on_device_add(struct hn_softc *sc, int mtu)
592 {
593
594         /*
595          * Connect with the NetVsp
596          */
597         return (hv_nv_connect_to_vsp(sc, mtu));
598 }
599
600 /*
601  * Net VSC on device remove
602  */
603 int
604 hv_nv_on_device_remove(struct hn_softc *sc)
605 {
606         
607         hv_nv_disconnect_from_vsp(sc);
608
609         /* Now, we can close the channel safely */
610
611         vmbus_chan_close(sc->hn_prichan);
612
613         return (0);
614 }
615
616 void
617 hn_nvs_sent_xact(struct hn_send_ctx *sndc,
618     struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
619     const void *data, int dlen)
620 {
621
622         vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen);
623 }
624
625 static void
626 hn_nvs_sent_none(struct hn_send_ctx *sndc __unused,
627     struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
628     const void *data __unused, int dlen __unused)
629 {
630         /* EMPTY */
631 }
632
633 void
634 hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
635 {
636         u_long mask;
637         uint32_t idx;
638
639         idx = chim_idx / LONG_BIT;
640         KASSERT(idx < sc->hn_chim_bmap_cnt,
641             ("invalid chimney index 0x%x", chim_idx));
642
643         mask = 1UL << (chim_idx % LONG_BIT);
644         KASSERT(sc->hn_chim_bmap[idx] & mask,
645             ("index bitmap 0x%lx, chimney index %u, "
646              "bitmap idx %d, bitmask 0x%lx",
647              sc->hn_chim_bmap[idx], chim_idx, idx, mask));
648
649         atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
650 }
651
652 /*
653  * Net VSC on send
654  * Sends a packet on the specified Hyper-V device.
655  * Returns 0 on success, non-zero on failure.
656  */
657 int
658 hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
659     struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt)
660 {
661         struct hn_nvs_rndis rndis;
662         int ret;
663
664         rndis.nvs_type = HN_NVS_TYPE_RNDIS;
665         rndis.nvs_rndis_mtype = rndis_mtype;
666         rndis.nvs_chim_idx = sndc->hn_chim_idx;
667         rndis.nvs_chim_sz = sndc->hn_chim_sz;
668
669         if (gpa_cnt) {
670                 ret = hn_nvs_send_sglist(chan, gpa, gpa_cnt,
671                     &rndis, sizeof(rndis), sndc);
672         } else {
673                 ret = hn_nvs_send(chan, VMBUS_CHANPKT_FLAG_RC,
674                     &rndis, sizeof(rndis), sndc);
675         }
676
677         return (ret);
678 }