hn_nvs.c revision 285236
1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2010-2012 Citrix Inc.
4250199Sgrehan * Copyright (c) 2012 NetApp 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.
27266794Smarius *
28266794Smarius * $FreeBSD: stable/10/sys/dev/hyperv/netvsc/hv_net_vsc.c 285236 2015-07-07 04:15:22Z whu $
29250199Sgrehan */
30250199Sgrehan
31250199Sgrehan/**
32250199Sgrehan * HyperV vmbus network VSC (virtual services client) module
33250199Sgrehan *
34250199Sgrehan */
35250199Sgrehan
36250199Sgrehan
37250199Sgrehan#include <sys/param.h>
38250199Sgrehan#include <sys/kernel.h>
39250199Sgrehan#include <sys/socket.h>
40250199Sgrehan#include <sys/lock.h>
41250199Sgrehan#include <net/if.h>
42250199Sgrehan#include <net/if_arp.h>
43250199Sgrehan#include <machine/bus.h>
44250199Sgrehan#include <machine/atomic.h>
45250199Sgrehan
46250199Sgrehan#include <dev/hyperv/include/hyperv.h>
47250199Sgrehan#include "hv_net_vsc.h"
48250199Sgrehan#include "hv_rndis.h"
49250199Sgrehan#include "hv_rndis_filter.h"
50250199Sgrehan
51285236SwhuMALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver");
52250199Sgrehan
53250199Sgrehan/*
54250199Sgrehan * Forward declarations
55250199Sgrehan */
56250199Sgrehanstatic void hv_nv_on_channel_callback(void *context);
57250199Sgrehanstatic int  hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device);
58250199Sgrehanstatic int  hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device);
59250199Sgrehanstatic int  hv_nv_destroy_send_buffer(netvsc_dev *net_dev);
60250199Sgrehanstatic int  hv_nv_destroy_rx_buffer(netvsc_dev *net_dev);
61250199Sgrehanstatic int  hv_nv_connect_to_vsp(struct hv_device *device);
62285236Swhustatic void hv_nv_on_send_completion(netvsc_dev *net_dev,
63285236Swhu    struct hv_device *device, hv_vm_packet_descriptor *pkt);
64285236Swhustatic void hv_nv_on_receive(netvsc_dev *net_dev,
65285236Swhu    struct hv_device *device, hv_vm_packet_descriptor *pkt);
66250199Sgrehan
67250199Sgrehan/*
68250199Sgrehan *
69250199Sgrehan */
70250199Sgrehanstatic inline netvsc_dev *
71250199Sgrehanhv_nv_alloc_net_device(struct hv_device *device)
72250199Sgrehan{
73250199Sgrehan	netvsc_dev *net_dev;
74250199Sgrehan	hn_softc_t *sc = device_get_softc(device->device);
75250199Sgrehan
76285236Swhu	net_dev = malloc(sizeof(netvsc_dev), M_NETVSC, M_NOWAIT | M_ZERO);
77250199Sgrehan	if (net_dev == NULL) {
78250199Sgrehan		return (NULL);
79250199Sgrehan	}
80250199Sgrehan
81250199Sgrehan	net_dev->dev = device;
82250199Sgrehan	net_dev->destroy = FALSE;
83250199Sgrehan	sc->net_dev = net_dev;
84250199Sgrehan
85250199Sgrehan	return (net_dev);
86250199Sgrehan}
87250199Sgrehan
88250199Sgrehan/*
89250199Sgrehan *
90250199Sgrehan */
91250199Sgrehanstatic inline netvsc_dev *
92250199Sgrehanhv_nv_get_outbound_net_device(struct hv_device *device)
93250199Sgrehan{
94250199Sgrehan	hn_softc_t *sc = device_get_softc(device->device);
95250199Sgrehan	netvsc_dev *net_dev = sc->net_dev;;
96250199Sgrehan
97250199Sgrehan	if ((net_dev != NULL) && net_dev->destroy) {
98250199Sgrehan		return (NULL);
99250199Sgrehan	}
100250199Sgrehan
101250199Sgrehan	return (net_dev);
102250199Sgrehan}
103250199Sgrehan
104250199Sgrehan/*
105250199Sgrehan *
106250199Sgrehan */
107250199Sgrehanstatic inline netvsc_dev *
108250199Sgrehanhv_nv_get_inbound_net_device(struct hv_device *device)
109250199Sgrehan{
110250199Sgrehan	hn_softc_t *sc = device_get_softc(device->device);
111250199Sgrehan	netvsc_dev *net_dev = sc->net_dev;;
112250199Sgrehan
113250199Sgrehan	if (net_dev == NULL) {
114250199Sgrehan		return (net_dev);
115250199Sgrehan	}
116250199Sgrehan	/*
117250199Sgrehan	 * When the device is being destroyed; we only
118250199Sgrehan	 * permit incoming packets if and only if there
119250199Sgrehan	 * are outstanding sends.
120250199Sgrehan	 */
121250199Sgrehan	if (net_dev->destroy && net_dev->num_outstanding_sends == 0) {
122250199Sgrehan		return (NULL);
123250199Sgrehan	}
124250199Sgrehan
125250199Sgrehan	return (net_dev);
126250199Sgrehan}
127250199Sgrehan
128285236Swhuint
129285236Swhuhv_nv_get_next_send_section(netvsc_dev *net_dev)
130285236Swhu{
131285236Swhu	unsigned long bitsmap_words = net_dev->bitsmap_words;
132285236Swhu	unsigned long *bitsmap = net_dev->send_section_bitsmap;
133285236Swhu	unsigned long idx;
134285236Swhu	int ret = NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
135285236Swhu	int i;
136285236Swhu
137285236Swhu	for (i = 0; i < bitsmap_words; i++) {
138285236Swhu		idx = ffs(~bitsmap[i]);
139285236Swhu		if (0 == idx)
140285236Swhu			continue;
141285236Swhu
142285236Swhu		idx--;
143285236Swhu		if (i * BITS_PER_LONG + idx >= net_dev->send_section_count)
144285236Swhu			return (ret);
145285236Swhu
146285236Swhu		if (synch_test_and_set_bit(idx, &bitsmap[i]))
147285236Swhu			continue;
148285236Swhu
149285236Swhu		ret = i * BITS_PER_LONG + idx;
150285236Swhu		break;
151285236Swhu	}
152285236Swhu
153285236Swhu	return (ret);
154285236Swhu}
155285236Swhu
156250199Sgrehan/*
157250199Sgrehan * Net VSC initialize receive buffer with net VSP
158250199Sgrehan *
159250199Sgrehan * Net VSP:  Network virtual services client, also known as the
160250199Sgrehan *     Hyper-V extensible switch and the synthetic data path.
161250199Sgrehan */
162250199Sgrehanstatic int
163250199Sgrehanhv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device)
164250199Sgrehan{
165250199Sgrehan	netvsc_dev *net_dev;
166250199Sgrehan	nvsp_msg *init_pkt;
167250199Sgrehan	int ret = 0;
168250199Sgrehan
169250199Sgrehan	net_dev = hv_nv_get_outbound_net_device(device);
170250199Sgrehan	if (!net_dev) {
171250199Sgrehan		return (ENODEV);
172250199Sgrehan	}
173250199Sgrehan
174285236Swhu	net_dev->rx_buf = contigmalloc(net_dev->rx_buf_size, M_NETVSC,
175250199Sgrehan	    M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
176250199Sgrehan
177250199Sgrehan	/*
178250199Sgrehan	 * Establish the GPADL handle for this buffer on this channel.
179250199Sgrehan	 * Note:  This call uses the vmbus connection rather than the
180250199Sgrehan	 * channel to establish the gpadl handle.
181250199Sgrehan	 * GPADL:  Guest physical address descriptor list.
182250199Sgrehan	 */
183250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(
184250199Sgrehan		device->channel, net_dev->rx_buf,
185250199Sgrehan		net_dev->rx_buf_size, &net_dev->rx_buf_gpadl_handle);
186250199Sgrehan	if (ret != 0) {
187250199Sgrehan		goto cleanup;
188250199Sgrehan	}
189250199Sgrehan
190250199Sgrehan	/* sema_wait(&ext->channel_init_sema); KYS CHECK */
191250199Sgrehan
192250199Sgrehan	/* Notify the NetVsp of the gpadl handle */
193250199Sgrehan	init_pkt = &net_dev->channel_init_packet;
194250199Sgrehan
195250199Sgrehan	memset(init_pkt, 0, sizeof(nvsp_msg));
196250199Sgrehan
197250199Sgrehan	init_pkt->hdr.msg_type = nvsp_msg_1_type_send_rx_buf;
198250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle =
199250199Sgrehan	    net_dev->rx_buf_gpadl_handle;
200250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_rx_buf.id =
201250199Sgrehan	    NETVSC_RECEIVE_BUFFER_ID;
202250199Sgrehan
203250199Sgrehan	/* Send the gpadl notification request */
204250199Sgrehan
205250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, init_pkt,
206266794Smarius	    sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt,
207250199Sgrehan	    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
208250199Sgrehan	    HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
209250199Sgrehan	if (ret != 0) {
210250199Sgrehan		goto cleanup;
211250199Sgrehan	}
212250199Sgrehan
213250199Sgrehan	sema_wait(&net_dev->channel_init_sema);
214250199Sgrehan
215250199Sgrehan	/* Check the response */
216250199Sgrehan	if (init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.status
217250199Sgrehan	    != nvsp_status_success) {
218250199Sgrehan		ret = EINVAL;
219250199Sgrehan		goto cleanup;
220250199Sgrehan	}
221250199Sgrehan
222250199Sgrehan	net_dev->rx_section_count =
223250199Sgrehan	    init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.num_sections;
224250199Sgrehan
225250199Sgrehan	net_dev->rx_sections = malloc(net_dev->rx_section_count *
226285236Swhu	    sizeof(nvsp_1_rx_buf_section), M_NETVSC, M_NOWAIT);
227250199Sgrehan	if (net_dev->rx_sections == NULL) {
228250199Sgrehan		ret = EINVAL;
229250199Sgrehan		goto cleanup;
230250199Sgrehan	}
231250199Sgrehan	memcpy(net_dev->rx_sections,
232250199Sgrehan	    init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.sections,
233250199Sgrehan	    net_dev->rx_section_count * sizeof(nvsp_1_rx_buf_section));
234250199Sgrehan
235250199Sgrehan
236250199Sgrehan	/*
237250199Sgrehan	 * For first release, there should only be 1 section that represents
238250199Sgrehan	 * the entire receive buffer
239250199Sgrehan	 */
240250199Sgrehan	if (net_dev->rx_section_count != 1
241250199Sgrehan	    || net_dev->rx_sections->offset != 0) {
242250199Sgrehan		ret = EINVAL;
243250199Sgrehan		goto cleanup;
244250199Sgrehan	}
245250199Sgrehan
246250199Sgrehan	goto exit;
247250199Sgrehan
248250199Sgrehancleanup:
249250199Sgrehan	hv_nv_destroy_rx_buffer(net_dev);
250250199Sgrehan
251250199Sgrehanexit:
252250199Sgrehan	return (ret);
253250199Sgrehan}
254250199Sgrehan
255250199Sgrehan/*
256250199Sgrehan * Net VSC initialize send buffer with net VSP
257250199Sgrehan */
258250199Sgrehanstatic int
259250199Sgrehanhv_nv_init_send_buffer_with_net_vsp(struct hv_device *device)
260250199Sgrehan{
261250199Sgrehan	netvsc_dev *net_dev;
262250199Sgrehan	nvsp_msg *init_pkt;
263250199Sgrehan	int ret = 0;
264250199Sgrehan
265250199Sgrehan	net_dev = hv_nv_get_outbound_net_device(device);
266250199Sgrehan	if (!net_dev) {
267250199Sgrehan		return (ENODEV);
268250199Sgrehan	}
269250199Sgrehan
270285236Swhu	net_dev->send_buf  = contigmalloc(net_dev->send_buf_size, M_NETVSC,
271250199Sgrehan	    M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
272250199Sgrehan	if (net_dev->send_buf == NULL) {
273250199Sgrehan		ret = ENOMEM;
274250199Sgrehan		goto cleanup;
275250199Sgrehan	}
276250199Sgrehan
277250199Sgrehan	/*
278250199Sgrehan	 * Establish the gpadl handle for this buffer on this channel.
279250199Sgrehan	 * Note:  This call uses the vmbus connection rather than the
280250199Sgrehan	 * channel to establish the gpadl handle.
281250199Sgrehan	 */
282250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(device->channel,
283285236Swhu  	    net_dev->send_buf, net_dev->send_buf_size,
284250199Sgrehan	    &net_dev->send_buf_gpadl_handle);
285250199Sgrehan	if (ret != 0) {
286250199Sgrehan		goto cleanup;
287250199Sgrehan	}
288250199Sgrehan
289250199Sgrehan	/* Notify the NetVsp of the gpadl handle */
290250199Sgrehan
291250199Sgrehan	init_pkt = &net_dev->channel_init_packet;
292250199Sgrehan
293250199Sgrehan	memset(init_pkt, 0, sizeof(nvsp_msg));
294250199Sgrehan
295250199Sgrehan	init_pkt->hdr.msg_type = nvsp_msg_1_type_send_send_buf;
296250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle =
297250199Sgrehan	    net_dev->send_buf_gpadl_handle;
298250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_rx_buf.id =
299250199Sgrehan	    NETVSC_SEND_BUFFER_ID;
300250199Sgrehan
301250199Sgrehan	/* Send the gpadl notification request */
302250199Sgrehan
303250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, init_pkt,
304285236Swhu  	    sizeof(nvsp_msg), (uint64_t)init_pkt,
305250199Sgrehan	    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
306250199Sgrehan	    HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
307250199Sgrehan	if (ret != 0) {
308250199Sgrehan		goto cleanup;
309250199Sgrehan	}
310250199Sgrehan
311250199Sgrehan	sema_wait(&net_dev->channel_init_sema);
312250199Sgrehan
313250199Sgrehan	/* Check the response */
314250199Sgrehan	if (init_pkt->msgs.vers_1_msgs.send_send_buf_complete.status
315250199Sgrehan	    != nvsp_status_success) {
316250199Sgrehan		ret = EINVAL;
317250199Sgrehan		goto cleanup;
318250199Sgrehan	}
319250199Sgrehan
320250199Sgrehan	net_dev->send_section_size =
321250199Sgrehan	    init_pkt->msgs.vers_1_msgs.send_send_buf_complete.section_size;
322285236Swhu	net_dev->send_section_count =
323285236Swhu	    net_dev->send_buf_size / net_dev->send_section_size;
324285236Swhu	net_dev->bitsmap_words = howmany(net_dev->send_section_count,
325285236Swhu	    BITS_PER_LONG);
326285236Swhu	net_dev->send_section_bitsmap =
327285236Swhu	    malloc(net_dev->bitsmap_words * sizeof(long), M_NETVSC,
328285236Swhu	    M_NOWAIT | M_ZERO);
329285236Swhu	if (NULL == net_dev->send_section_bitsmap) {
330285236Swhu		ret = ENOMEM;
331285236Swhu		goto cleanup;
332285236Swhu	}
333250199Sgrehan
334250199Sgrehan	goto exit;
335250199Sgrehan
336250199Sgrehancleanup:
337250199Sgrehan	hv_nv_destroy_send_buffer(net_dev);
338250199Sgrehan
339250199Sgrehanexit:
340250199Sgrehan	return (ret);
341250199Sgrehan}
342250199Sgrehan
343250199Sgrehan/*
344250199Sgrehan * Net VSC destroy receive buffer
345250199Sgrehan */
346250199Sgrehanstatic int
347250199Sgrehanhv_nv_destroy_rx_buffer(netvsc_dev *net_dev)
348250199Sgrehan{
349250199Sgrehan	nvsp_msg *revoke_pkt;
350250199Sgrehan	int ret = 0;
351250199Sgrehan
352250199Sgrehan	/*
353250199Sgrehan	 * If we got a section count, it means we received a
354250199Sgrehan	 * send_rx_buf_complete msg
355250199Sgrehan	 * (ie sent nvsp_msg_1_type_send_rx_buf msg) therefore,
356250199Sgrehan	 * we need to send a revoke msg here
357250199Sgrehan	 */
358250199Sgrehan	if (net_dev->rx_section_count) {
359250199Sgrehan		/* Send the revoke receive buffer */
360250199Sgrehan		revoke_pkt = &net_dev->revoke_packet;
361250199Sgrehan		memset(revoke_pkt, 0, sizeof(nvsp_msg));
362250199Sgrehan
363250199Sgrehan		revoke_pkt->hdr.msg_type = nvsp_msg_1_type_revoke_rx_buf;
364250199Sgrehan		revoke_pkt->msgs.vers_1_msgs.revoke_rx_buf.id =
365250199Sgrehan		    NETVSC_RECEIVE_BUFFER_ID;
366250199Sgrehan
367250199Sgrehan		ret = hv_vmbus_channel_send_packet(net_dev->dev->channel,
368250199Sgrehan		    revoke_pkt, sizeof(nvsp_msg),
369266794Smarius		    (uint64_t)(uintptr_t)revoke_pkt,
370250199Sgrehan		    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0);
371250199Sgrehan
372250199Sgrehan		/*
373250199Sgrehan		 * If we failed here, we might as well return and have a leak
374250199Sgrehan		 * rather than continue and a bugchk
375250199Sgrehan		 */
376250199Sgrehan		if (ret != 0) {
377250199Sgrehan			return (ret);
378250199Sgrehan		}
379250199Sgrehan	}
380250199Sgrehan
381250199Sgrehan	/* Tear down the gpadl on the vsp end */
382250199Sgrehan	if (net_dev->rx_buf_gpadl_handle) {
383250199Sgrehan		ret = hv_vmbus_channel_teardown_gpdal(net_dev->dev->channel,
384250199Sgrehan		    net_dev->rx_buf_gpadl_handle);
385250199Sgrehan		/*
386250199Sgrehan		 * If we failed here, we might as well return and have a leak
387250199Sgrehan		 * rather than continue and a bugchk
388250199Sgrehan		 */
389250199Sgrehan		if (ret != 0) {
390250199Sgrehan			return (ret);
391250199Sgrehan		}
392250199Sgrehan		net_dev->rx_buf_gpadl_handle = 0;
393250199Sgrehan	}
394250199Sgrehan
395250199Sgrehan	if (net_dev->rx_buf) {
396250199Sgrehan		/* Free up the receive buffer */
397285236Swhu		contigfree(net_dev->rx_buf, net_dev->rx_buf_size, M_NETVSC);
398250199Sgrehan		net_dev->rx_buf = NULL;
399250199Sgrehan	}
400250199Sgrehan
401250199Sgrehan	if (net_dev->rx_sections) {
402285236Swhu		free(net_dev->rx_sections, M_NETVSC);
403250199Sgrehan		net_dev->rx_sections = NULL;
404250199Sgrehan		net_dev->rx_section_count = 0;
405250199Sgrehan	}
406250199Sgrehan
407250199Sgrehan	return (ret);
408250199Sgrehan}
409250199Sgrehan
410250199Sgrehan/*
411250199Sgrehan * Net VSC destroy send buffer
412250199Sgrehan */
413250199Sgrehanstatic int
414250199Sgrehanhv_nv_destroy_send_buffer(netvsc_dev *net_dev)
415250199Sgrehan{
416250199Sgrehan	nvsp_msg *revoke_pkt;
417250199Sgrehan	int ret = 0;
418250199Sgrehan
419250199Sgrehan	/*
420250199Sgrehan	 * If we got a section count, it means we received a
421250199Sgrehan	 * send_rx_buf_complete msg
422250199Sgrehan	 * (ie sent nvsp_msg_1_type_send_rx_buf msg) therefore,
423250199Sgrehan	 * we need to send a revoke msg here
424250199Sgrehan	 */
425250199Sgrehan	if (net_dev->send_section_size) {
426250199Sgrehan		/* Send the revoke send buffer */
427250199Sgrehan		revoke_pkt = &net_dev->revoke_packet;
428250199Sgrehan		memset(revoke_pkt, 0, sizeof(nvsp_msg));
429250199Sgrehan
430250199Sgrehan		revoke_pkt->hdr.msg_type =
431250199Sgrehan		    nvsp_msg_1_type_revoke_send_buf;
432250199Sgrehan		revoke_pkt->msgs.vers_1_msgs.revoke_send_buf.id =
433250199Sgrehan		    NETVSC_SEND_BUFFER_ID;
434250199Sgrehan
435250199Sgrehan		ret = hv_vmbus_channel_send_packet(net_dev->dev->channel,
436250199Sgrehan		    revoke_pkt, sizeof(nvsp_msg),
437266794Smarius		    (uint64_t)(uintptr_t)revoke_pkt,
438250199Sgrehan		    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0);
439250199Sgrehan		/*
440250199Sgrehan		 * If we failed here, we might as well return and have a leak
441250199Sgrehan		 * rather than continue and a bugchk
442250199Sgrehan		 */
443250199Sgrehan		if (ret != 0) {
444250199Sgrehan			return (ret);
445250199Sgrehan		}
446250199Sgrehan	}
447250199Sgrehan
448250199Sgrehan	/* Tear down the gpadl on the vsp end */
449250199Sgrehan	if (net_dev->send_buf_gpadl_handle) {
450250199Sgrehan		ret = hv_vmbus_channel_teardown_gpdal(net_dev->dev->channel,
451250199Sgrehan		    net_dev->send_buf_gpadl_handle);
452250199Sgrehan
453250199Sgrehan		/*
454250199Sgrehan		 * If we failed here, we might as well return and have a leak
455250199Sgrehan		 * rather than continue and a bugchk
456250199Sgrehan		 */
457250199Sgrehan		if (ret != 0) {
458250199Sgrehan			return (ret);
459250199Sgrehan		}
460250199Sgrehan		net_dev->send_buf_gpadl_handle = 0;
461250199Sgrehan	}
462250199Sgrehan
463250199Sgrehan	if (net_dev->send_buf) {
464250199Sgrehan		/* Free up the receive buffer */
465285236Swhu		contigfree(net_dev->send_buf, net_dev->send_buf_size, M_NETVSC);
466250199Sgrehan		net_dev->send_buf = NULL;
467250199Sgrehan	}
468250199Sgrehan
469285236Swhu	if (net_dev->send_section_bitsmap) {
470285236Swhu		free(net_dev->send_section_bitsmap, M_NETVSC);
471285236Swhu	}
472285236Swhu
473250199Sgrehan	return (ret);
474250199Sgrehan}
475250199Sgrehan
476250199Sgrehan
477250199Sgrehan/*
478250199Sgrehan * Attempt to negotiate the caller-specified NVSP version
479250199Sgrehan *
480250199Sgrehan * For NVSP v2, Server 2008 R2 does not set
481250199Sgrehan * init_pkt->msgs.init_msgs.init_compl.negotiated_prot_vers
482250199Sgrehan * to the negotiated version, so we cannot rely on that.
483250199Sgrehan */
484250199Sgrehanstatic int
485250199Sgrehanhv_nv_negotiate_nvsp_protocol(struct hv_device *device, netvsc_dev *net_dev,
486285236Swhu    uint32_t nvsp_ver)
487250199Sgrehan{
488250199Sgrehan	nvsp_msg *init_pkt;
489250199Sgrehan	int ret;
490250199Sgrehan
491250199Sgrehan	init_pkt = &net_dev->channel_init_packet;
492250199Sgrehan	memset(init_pkt, 0, sizeof(nvsp_msg));
493250199Sgrehan	init_pkt->hdr.msg_type = nvsp_msg_type_init;
494250199Sgrehan
495250199Sgrehan	/*
496250199Sgrehan	 * Specify parameter as the only acceptable protocol version
497250199Sgrehan	 */
498250199Sgrehan	init_pkt->msgs.init_msgs.init.p1.protocol_version = nvsp_ver;
499250199Sgrehan	init_pkt->msgs.init_msgs.init.protocol_version_2 = nvsp_ver;
500250199Sgrehan
501250199Sgrehan	/* Send the init request */
502250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, init_pkt,
503266794Smarius	    sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt,
504250199Sgrehan	    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
505250199Sgrehan	    HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
506250199Sgrehan	if (ret != 0)
507250199Sgrehan		return (-1);
508250199Sgrehan
509250199Sgrehan	sema_wait(&net_dev->channel_init_sema);
510250199Sgrehan
511250199Sgrehan	if (init_pkt->msgs.init_msgs.init_compl.status != nvsp_status_success)
512250199Sgrehan		return (EINVAL);
513250199Sgrehan
514250199Sgrehan	return (0);
515250199Sgrehan}
516250199Sgrehan
517250199Sgrehan/*
518250199Sgrehan * Send NDIS version 2 config packet containing MTU.
519250199Sgrehan *
520250199Sgrehan * Not valid for NDIS version 1.
521250199Sgrehan */
522250199Sgrehanstatic int
523250199Sgrehanhv_nv_send_ndis_config(struct hv_device *device, uint32_t mtu)
524250199Sgrehan{
525250199Sgrehan	netvsc_dev *net_dev;
526250199Sgrehan	nvsp_msg *init_pkt;
527250199Sgrehan	int ret;
528250199Sgrehan
529250199Sgrehan	net_dev = hv_nv_get_outbound_net_device(device);
530250199Sgrehan	if (!net_dev)
531250199Sgrehan		return (-ENODEV);
532250199Sgrehan
533250199Sgrehan	/*
534250199Sgrehan	 * Set up configuration packet, write MTU
535250199Sgrehan	 * Indicate we are capable of handling VLAN tags
536250199Sgrehan	 */
537250199Sgrehan	init_pkt = &net_dev->channel_init_packet;
538250199Sgrehan	memset(init_pkt, 0, sizeof(nvsp_msg));
539250199Sgrehan	init_pkt->hdr.msg_type = nvsp_msg_2_type_send_ndis_config;
540250199Sgrehan	init_pkt->msgs.vers_2_msgs.send_ndis_config.mtu = mtu;
541250199Sgrehan	init_pkt->
542250199Sgrehan		msgs.vers_2_msgs.send_ndis_config.capabilities.u1.u2.ieee8021q
543250199Sgrehan		= 1;
544250199Sgrehan
545250199Sgrehan	/* Send the configuration packet */
546250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, init_pkt,
547266794Smarius	    sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt,
548250199Sgrehan	    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0);
549250199Sgrehan	if (ret != 0)
550250199Sgrehan		return (-EINVAL);
551250199Sgrehan
552250199Sgrehan	return (0);
553250199Sgrehan}
554250199Sgrehan
555250199Sgrehan/*
556250199Sgrehan * Net VSC connect to VSP
557250199Sgrehan */
558250199Sgrehanstatic int
559250199Sgrehanhv_nv_connect_to_vsp(struct hv_device *device)
560250199Sgrehan{
561250199Sgrehan	netvsc_dev *net_dev;
562250199Sgrehan	nvsp_msg *init_pkt;
563250199Sgrehan	uint32_t ndis_version;
564285236Swhu	uint32_t protocol_list[] = { NVSP_PROTOCOL_VERSION_1,
565285236Swhu	    NVSP_PROTOCOL_VERSION_2,
566285236Swhu	    NVSP_PROTOCOL_VERSION_4,
567285236Swhu	    NVSP_PROTOCOL_VERSION_5 };
568285236Swhu	int i;
569285236Swhu	int protocol_number = nitems(protocol_list);
570250199Sgrehan	int ret = 0;
571250199Sgrehan	device_t dev = device->device;
572250199Sgrehan	hn_softc_t *sc = device_get_softc(dev);
573250199Sgrehan	struct ifnet *ifp = sc->arpcom.ac_ifp;
574250199Sgrehan
575250199Sgrehan	net_dev = hv_nv_get_outbound_net_device(device);
576250199Sgrehan	if (!net_dev) {
577250199Sgrehan		return (ENODEV);
578250199Sgrehan	}
579250199Sgrehan
580250199Sgrehan	/*
581285236Swhu	 * Negotiate the NVSP version.  Try the latest NVSP first.
582250199Sgrehan	 */
583285236Swhu	for (i = protocol_number - 1; i >= 0; i--) {
584285236Swhu		if (hv_nv_negotiate_nvsp_protocol(device, net_dev,
585285236Swhu		    protocol_list[i]) == 0) {
586285236Swhu			net_dev->nvsp_version = protocol_list[i];
587285236Swhu			if (bootverbose)
588285236Swhu				device_printf(dev, "Netvsc: got version 0x%x\n",
589285236Swhu				    net_dev->nvsp_version);
590285236Swhu			break;
591250199Sgrehan		}
592250199Sgrehan	}
593250199Sgrehan
594285236Swhu	if (i < 0) {
595285236Swhu		if (bootverbose)
596285236Swhu			device_printf(dev, "failed to negotiate a valid "
597285236Swhu			    "protocol.\n");
598285236Swhu		return (EPROTO);
599285236Swhu	}
600285236Swhu
601250199Sgrehan	/*
602250199Sgrehan	 * Set the MTU if supported by this NVSP protocol version
603250199Sgrehan	 * This needs to be right after the NVSP init message per Haiyang
604250199Sgrehan	 */
605285236Swhu	if (net_dev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
606250199Sgrehan		ret = hv_nv_send_ndis_config(device, ifp->if_mtu);
607250199Sgrehan
608250199Sgrehan	/*
609250199Sgrehan	 * Send the NDIS version
610250199Sgrehan	 */
611250199Sgrehan	init_pkt = &net_dev->channel_init_packet;
612250199Sgrehan
613250199Sgrehan	memset(init_pkt, 0, sizeof(nvsp_msg));
614250199Sgrehan
615285236Swhu	if (net_dev->nvsp_version <= NVSP_PROTOCOL_VERSION_4) {
616285236Swhu		ndis_version = NDIS_VERSION_6_1;
617285236Swhu	} else {
618285236Swhu		ndis_version = NDIS_VERSION_6_30;
619285236Swhu	}
620250199Sgrehan
621250199Sgrehan	init_pkt->hdr.msg_type = nvsp_msg_1_type_send_ndis_vers;
622250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_major_vers =
623250199Sgrehan	    (ndis_version & 0xFFFF0000) >> 16;
624250199Sgrehan	init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_minor_vers =
625250199Sgrehan	    ndis_version & 0xFFFF;
626250199Sgrehan
627250199Sgrehan	/* Send the init request */
628250199Sgrehan
629250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, init_pkt,
630266794Smarius	    sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt,
631250199Sgrehan	    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0);
632250199Sgrehan	if (ret != 0) {
633250199Sgrehan		goto cleanup;
634250199Sgrehan	}
635250199Sgrehan	/*
636250199Sgrehan	 * TODO:  BUGBUG - We have to wait for the above msg since the netvsp
637250199Sgrehan	 * uses KMCL which acknowledges packet (completion packet)
638250199Sgrehan	 * since our Vmbus always set the
639250199Sgrehan	 * HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag
640250199Sgrehan	 */
641250199Sgrehan	/* sema_wait(&NetVscChannel->channel_init_sema); */
642250199Sgrehan
643250199Sgrehan	/* Post the big receive buffer to NetVSP */
644250199Sgrehan	ret = hv_nv_init_rx_buffer_with_net_vsp(device);
645250199Sgrehan	if (ret == 0)
646250199Sgrehan		ret = hv_nv_init_send_buffer_with_net_vsp(device);
647250199Sgrehan
648250199Sgrehancleanup:
649250199Sgrehan	return (ret);
650250199Sgrehan}
651250199Sgrehan
652250199Sgrehan/*
653250199Sgrehan * Net VSC disconnect from VSP
654250199Sgrehan */
655250199Sgrehanstatic void
656250199Sgrehanhv_nv_disconnect_from_vsp(netvsc_dev *net_dev)
657250199Sgrehan{
658250199Sgrehan	hv_nv_destroy_rx_buffer(net_dev);
659250199Sgrehan	hv_nv_destroy_send_buffer(net_dev);
660250199Sgrehan}
661250199Sgrehan
662250199Sgrehan/*
663250199Sgrehan * Net VSC on device add
664250199Sgrehan *
665250199Sgrehan * Callback when the device belonging to this driver is added
666250199Sgrehan */
667250199Sgrehannetvsc_dev *
668250199Sgrehanhv_nv_on_device_add(struct hv_device *device, void *additional_info)
669250199Sgrehan{
670250199Sgrehan	netvsc_dev *net_dev;
671285236Swhu	int ret = 0;
672250199Sgrehan
673250199Sgrehan	net_dev = hv_nv_alloc_net_device(device);
674250199Sgrehan	if (!net_dev)
675250199Sgrehan		goto cleanup;
676250199Sgrehan
677250199Sgrehan	/* Initialize the NetVSC channel extension */
678250199Sgrehan	net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
679250199Sgrehan
680250199Sgrehan	net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE;
681250199Sgrehan
682250199Sgrehan	sema_init(&net_dev->channel_init_sema, 0, "netdev_sema");
683250199Sgrehan
684250199Sgrehan	/*
685250199Sgrehan	 * Open the channel
686250199Sgrehan	 */
687250199Sgrehan	ret = hv_vmbus_channel_open(device->channel,
688250199Sgrehan	    NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE,
689250199Sgrehan	    NULL, 0, hv_nv_on_channel_callback, device);
690250199Sgrehan	if (ret != 0)
691250199Sgrehan		goto cleanup;
692250199Sgrehan
693250199Sgrehan	/*
694250199Sgrehan	 * Connect with the NetVsp
695250199Sgrehan	 */
696250199Sgrehan	ret = hv_nv_connect_to_vsp(device);
697250199Sgrehan	if (ret != 0)
698250199Sgrehan		goto close;
699250199Sgrehan
700250199Sgrehan	return (net_dev);
701250199Sgrehan
702250199Sgrehanclose:
703250199Sgrehan	/* Now, we can close the channel safely */
704250199Sgrehan
705250199Sgrehan	hv_vmbus_channel_close(device->channel);
706250199Sgrehan
707250199Sgrehancleanup:
708250199Sgrehan	/*
709250199Sgrehan	 * Free the packet buffers on the netvsc device packet queue.
710250199Sgrehan	 * Release other resources.
711250199Sgrehan	 */
712250199Sgrehan	if (net_dev) {
713250199Sgrehan		sema_destroy(&net_dev->channel_init_sema);
714285236Swhu		free(net_dev, M_NETVSC);
715250199Sgrehan	}
716250199Sgrehan
717250199Sgrehan	return (NULL);
718250199Sgrehan}
719250199Sgrehan
720250199Sgrehan/*
721250199Sgrehan * Net VSC on device remove
722250199Sgrehan */
723250199Sgrehanint
724250199Sgrehanhv_nv_on_device_remove(struct hv_device *device, boolean_t destroy_channel)
725250199Sgrehan{
726250199Sgrehan	hn_softc_t *sc = device_get_softc(device->device);
727250199Sgrehan	netvsc_dev *net_dev = sc->net_dev;;
728250199Sgrehan
729250199Sgrehan	/* Stop outbound traffic ie sends and receives completions */
730250199Sgrehan	mtx_lock(&device->channel->inbound_lock);
731250199Sgrehan	net_dev->destroy = TRUE;
732250199Sgrehan	mtx_unlock(&device->channel->inbound_lock);
733250199Sgrehan
734250199Sgrehan	/* Wait for all send completions */
735250199Sgrehan	while (net_dev->num_outstanding_sends) {
736250199Sgrehan		DELAY(100);
737250199Sgrehan	}
738250199Sgrehan
739250199Sgrehan	hv_nv_disconnect_from_vsp(net_dev);
740250199Sgrehan
741250199Sgrehan	/* At this point, no one should be accessing net_dev except in here */
742250199Sgrehan
743250199Sgrehan	/* Now, we can close the channel safely */
744250199Sgrehan
745250199Sgrehan	if (!destroy_channel) {
746250199Sgrehan		device->channel->state =
747250199Sgrehan		    HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE;
748250199Sgrehan	}
749250199Sgrehan
750250199Sgrehan	hv_vmbus_channel_close(device->channel);
751250199Sgrehan
752250199Sgrehan	sema_destroy(&net_dev->channel_init_sema);
753285236Swhu	free(net_dev, M_NETVSC);
754250199Sgrehan
755250199Sgrehan	return (0);
756250199Sgrehan}
757250199Sgrehan
758250199Sgrehan/*
759250199Sgrehan * Net VSC on send completion
760250199Sgrehan */
761285236Swhustatic void
762285236Swhuhv_nv_on_send_completion(netvsc_dev *net_dev,
763285236Swhu    struct hv_device *device, hv_vm_packet_descriptor *pkt)
764250199Sgrehan{
765250199Sgrehan	nvsp_msg *nvsp_msg_pkt;
766250199Sgrehan	netvsc_packet *net_vsc_pkt;
767250199Sgrehan
768250199Sgrehan	nvsp_msg_pkt =
769250199Sgrehan	    (nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3));
770250199Sgrehan
771250199Sgrehan	if (nvsp_msg_pkt->hdr.msg_type == nvsp_msg_type_init_complete
772250199Sgrehan		|| nvsp_msg_pkt->hdr.msg_type
773250199Sgrehan			== nvsp_msg_1_type_send_rx_buf_complete
774250199Sgrehan		|| nvsp_msg_pkt->hdr.msg_type
775250199Sgrehan			== nvsp_msg_1_type_send_send_buf_complete) {
776250199Sgrehan		/* Copy the response back */
777250199Sgrehan		memcpy(&net_dev->channel_init_packet, nvsp_msg_pkt,
778285236Swhu		    sizeof(nvsp_msg));
779250199Sgrehan		sema_post(&net_dev->channel_init_sema);
780250199Sgrehan	} else if (nvsp_msg_pkt->hdr.msg_type ==
781285236Swhu		    nvsp_msg_1_type_send_rndis_pkt_complete) {
782250199Sgrehan		/* Get the send context */
783250199Sgrehan		net_vsc_pkt =
784250199Sgrehan		    (netvsc_packet *)(unsigned long)pkt->transaction_id;
785285236Swhu		if (NULL != net_vsc_pkt) {
786285236Swhu			if (net_vsc_pkt->send_buf_section_idx !=
787285236Swhu			    NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX) {
788285236Swhu				synch_change_bit(net_vsc_pkt->send_buf_section_idx,
789285236Swhu				    net_dev->send_section_bitsmap);
790285236Swhu			}
791285236Swhu
792285236Swhu			/* Notify the layer above us */
793285236Swhu			net_vsc_pkt->compl.send.on_send_completion(
794285236Swhu			    net_vsc_pkt->compl.send.send_completion_context);
795250199Sgrehan
796285236Swhu		}
797250199Sgrehan
798250199Sgrehan		atomic_subtract_int(&net_dev->num_outstanding_sends, 1);
799250199Sgrehan	}
800250199Sgrehan}
801250199Sgrehan
802250199Sgrehan/*
803250199Sgrehan * Net VSC on send
804250199Sgrehan * Sends a packet on the specified Hyper-V device.
805250199Sgrehan * Returns 0 on success, non-zero on failure.
806250199Sgrehan */
807250199Sgrehanint
808250199Sgrehanhv_nv_on_send(struct hv_device *device, netvsc_packet *pkt)
809250199Sgrehan{
810250199Sgrehan	netvsc_dev *net_dev;
811250199Sgrehan	nvsp_msg send_msg;
812250199Sgrehan	int ret;
813250199Sgrehan
814250199Sgrehan	net_dev = hv_nv_get_outbound_net_device(device);
815250199Sgrehan	if (!net_dev)
816250199Sgrehan		return (ENODEV);
817250199Sgrehan
818250199Sgrehan	send_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt;
819250199Sgrehan	if (pkt->is_data_pkt) {
820250199Sgrehan		/* 0 is RMC_DATA */
821250199Sgrehan		send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 0;
822250199Sgrehan	} else {
823250199Sgrehan		/* 1 is RMC_CONTROL */
824250199Sgrehan		send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 1;
825250199Sgrehan	}
826250199Sgrehan
827250199Sgrehan	send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_idx =
828285236Swhu	    pkt->send_buf_section_idx;
829285236Swhu	send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size =
830285236Swhu	    pkt->send_buf_section_size;
831250199Sgrehan
832250199Sgrehan	if (pkt->page_buf_count) {
833250199Sgrehan		ret = hv_vmbus_channel_send_packet_pagebuffer(device->channel,
834250199Sgrehan		    pkt->page_buffers, pkt->page_buf_count,
835266794Smarius		    &send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt);
836250199Sgrehan	} else {
837250199Sgrehan		ret = hv_vmbus_channel_send_packet(device->channel,
838266794Smarius		    &send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt,
839250199Sgrehan		    HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
840250199Sgrehan		    HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
841250199Sgrehan	}
842250199Sgrehan
843250199Sgrehan	/* Record outstanding send only if send_packet() succeeded */
844250199Sgrehan	if (ret == 0)
845250199Sgrehan		atomic_add_int(&net_dev->num_outstanding_sends, 1);
846250199Sgrehan
847250199Sgrehan	return (ret);
848250199Sgrehan}
849250199Sgrehan
850250199Sgrehan/*
851250199Sgrehan * Net VSC on receive
852250199Sgrehan *
853250199Sgrehan * In the FreeBSD Hyper-V virtual world, this function deals exclusively
854250199Sgrehan * with virtual addresses.
855250199Sgrehan */
856285236Swhustatic void
857285236Swhuhv_nv_on_receive(netvsc_dev *net_dev, struct hv_device *device,
858285236Swhu    hv_vm_packet_descriptor *pkt)
859250199Sgrehan{
860250199Sgrehan	hv_vm_transfer_page_packet_header *vm_xfer_page_pkt;
861250199Sgrehan	nvsp_msg *nvsp_msg_pkt;
862285236Swhu	netvsc_packet vsc_pkt;
863285236Swhu	netvsc_packet *net_vsc_pkt = &vsc_pkt;
864285236Swhu	device_t dev = device->device;
865250199Sgrehan	int count = 0;
866250199Sgrehan	int i = 0;
867285236Swhu	int status = nvsp_status_success;
868250199Sgrehan
869250199Sgrehan	/*
870250199Sgrehan	 * All inbound packets other than send completion should be
871250199Sgrehan	 * xfer page packet.
872250199Sgrehan	 */
873285236Swhu	if (pkt->type != HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES) {
874285236Swhu		device_printf(dev, "packet type %d is invalid!\n", pkt->type);
875250199Sgrehan		return;
876285236Swhu	}
877250199Sgrehan
878250199Sgrehan	nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt
879250199Sgrehan		+ (pkt->data_offset8 << 3));
880250199Sgrehan
881250199Sgrehan	/* Make sure this is a valid nvsp packet */
882285236Swhu	if (nvsp_msg_pkt->hdr.msg_type != nvsp_msg_1_type_send_rndis_pkt) {
883285236Swhu		device_printf(dev, "packet hdr type %d is invalid!\n",
884285236Swhu		    pkt->type);
885250199Sgrehan		return;
886285236Swhu	}
887250199Sgrehan
888250199Sgrehan	vm_xfer_page_pkt = (hv_vm_transfer_page_packet_header *)pkt;
889250199Sgrehan
890285236Swhu	if (vm_xfer_page_pkt->transfer_page_set_id !=
891285236Swhu	    NETVSC_RECEIVE_BUFFER_ID) {
892285236Swhu		device_printf(dev, "transfer_page_set_id %d is invalid!\n",
893285236Swhu		    vm_xfer_page_pkt->transfer_page_set_id);
894250199Sgrehan		return;
895250199Sgrehan	}
896250199Sgrehan
897285236Swhu	count = vm_xfer_page_pkt->range_count;
898285236Swhu	net_vsc_pkt->device = device;
899250199Sgrehan
900285236Swhu	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
901285236Swhu	for (i = 0; i < count; i++) {
902285236Swhu		net_vsc_pkt->status = nvsp_status_success;
903285236Swhu		net_vsc_pkt->data = (void *)((unsigned long)net_dev->rx_buf +
904285236Swhu		    vm_xfer_page_pkt->ranges[i].byte_offset);
905285236Swhu		net_vsc_pkt->tot_data_buf_len =
906285236Swhu		    vm_xfer_page_pkt->ranges[i].byte_count;
907250199Sgrehan
908285236Swhu		hv_rf_on_receive(net_dev, device, net_vsc_pkt);
909285236Swhu		if (net_vsc_pkt->status != nvsp_status_success) {
910285236Swhu			status = nvsp_status_failure;
911285236Swhu		}
912250199Sgrehan	}
913285236Swhu
914250199Sgrehan	/*
915285236Swhu	 * Moved completion call back here so that all received
916285236Swhu	 * messages (not just data messages) will trigger a response
917285236Swhu	 * message back to the host.
918250199Sgrehan	 */
919285236Swhu	hv_nv_on_receive_completion(device, vm_xfer_page_pkt->d.transaction_id,
920285236Swhu	    status);
921250199Sgrehan}
922250199Sgrehan
923250199Sgrehan/*
924285236Swhu * Net VSC on receive completion
925285236Swhu *
926285236Swhu * Send a receive completion packet to RNDIS device (ie NetVsp)
927250199Sgrehan */
928285236Swhuvoid
929285236Swhuhv_nv_on_receive_completion(struct hv_device *device, uint64_t tid,
930285236Swhu    uint32_t status)
931250199Sgrehan{
932250199Sgrehan	nvsp_msg rx_comp_msg;
933250199Sgrehan	int retries = 0;
934250199Sgrehan	int ret = 0;
935250199Sgrehan
936250199Sgrehan	rx_comp_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt_complete;
937250199Sgrehan
938250199Sgrehan	/* Pass in the status */
939250199Sgrehan	rx_comp_msg.msgs.vers_1_msgs.send_rndis_pkt_complete.status =
940285236Swhu	    status;
941250199Sgrehan
942250199Sgrehanretry_send_cmplt:
943250199Sgrehan	/* Send the completion */
944250199Sgrehan	ret = hv_vmbus_channel_send_packet(device->channel, &rx_comp_msg,
945250199Sgrehan	    sizeof(nvsp_msg), tid, HV_VMBUS_PACKET_TYPE_COMPLETION, 0);
946250199Sgrehan	if (ret == 0) {
947250199Sgrehan		/* success */
948250199Sgrehan		/* no-op */
949250199Sgrehan	} else if (ret == EAGAIN) {
950250199Sgrehan		/* no more room... wait a bit and attempt to retry 3 times */
951250199Sgrehan		retries++;
952250199Sgrehan
953250199Sgrehan		if (retries < 4) {
954250199Sgrehan			DELAY(100);
955250199Sgrehan			goto retry_send_cmplt;
956250199Sgrehan		}
957250199Sgrehan	}
958250199Sgrehan}
959250199Sgrehan
960250199Sgrehan/*
961250199Sgrehan * Net VSC on channel callback
962250199Sgrehan */
963250199Sgrehanstatic void
964250199Sgrehanhv_nv_on_channel_callback(void *context)
965250199Sgrehan{
966250199Sgrehan	struct hv_device *device = (struct hv_device *)context;
967250199Sgrehan	netvsc_dev *net_dev;
968285236Swhu	device_t dev = device->device;
969250199Sgrehan	uint32_t bytes_rxed;
970250199Sgrehan	uint64_t request_id;
971285236Swhu 	hv_vm_packet_descriptor *desc;
972250199Sgrehan	uint8_t *buffer;
973285236Swhu	int bufferlen = NETVSC_PACKET_SIZE;
974285236Swhu	int ret = 0;
975250199Sgrehan
976285236Swhu	net_dev = hv_nv_get_inbound_net_device(device);
977285236Swhu	if (net_dev == NULL)
978250199Sgrehan		return;
979250199Sgrehan
980285236Swhu	buffer = net_dev->callback_buf;
981250199Sgrehan
982250199Sgrehan	do {
983250199Sgrehan		ret = hv_vmbus_channel_recv_packet_raw(device->channel,
984250199Sgrehan		    buffer, bufferlen, &bytes_rxed, &request_id);
985250199Sgrehan		if (ret == 0) {
986250199Sgrehan			if (bytes_rxed > 0) {
987250199Sgrehan				desc = (hv_vm_packet_descriptor *)buffer;
988250199Sgrehan				switch (desc->type) {
989250199Sgrehan				case HV_VMBUS_PACKET_TYPE_COMPLETION:
990285236Swhu					hv_nv_on_send_completion(net_dev, device, desc);
991250199Sgrehan					break;
992250199Sgrehan				case HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES:
993285236Swhu					hv_nv_on_receive(net_dev, device, desc);
994250199Sgrehan					break;
995250199Sgrehan				default:
996285236Swhu					device_printf(dev,
997285236Swhu					    "hv_cb recv unknow type %d "
998285236Swhu					    " packet\n", desc->type);
999250199Sgrehan					break;
1000250199Sgrehan				}
1001250199Sgrehan			} else {
1002250199Sgrehan				break;
1003250199Sgrehan			}
1004250199Sgrehan		} else if (ret == ENOBUFS) {
1005250199Sgrehan			/* Handle large packet */
1006285236Swhu			if (bufferlen > NETVSC_PACKET_SIZE) {
1007285236Swhu				free(buffer, M_NETVSC);
1008285236Swhu				buffer = NULL;
1009285236Swhu			}
1010285236Swhu
1011285236Swhu			/* alloc new buffer */
1012285236Swhu			buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT);
1013250199Sgrehan			if (buffer == NULL) {
1014285236Swhu				device_printf(dev,
1015285236Swhu				    "hv_cb malloc buffer failed, len=%u\n",
1016285236Swhu				    bytes_rxed);
1017285236Swhu				bufferlen = 0;
1018250199Sgrehan				break;
1019250199Sgrehan			}
1020250199Sgrehan			bufferlen = bytes_rxed;
1021250199Sgrehan		}
1022250199Sgrehan	} while (1);
1023250199Sgrehan
1024285236Swhu	if (bufferlen > NETVSC_PACKET_SIZE)
1025285236Swhu		free(buffer, M_NETVSC);
1026250199Sgrehan}
1027