hv_netvsc_drv_freebsd.c revision 257513
1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2010-2012 Citrix Inc.
3250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp.
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.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan/*-
30250199Sgrehan * Copyright (c) 2004-2006 Kip Macy
31250199Sgrehan * All rights reserved.
32250199Sgrehan *
33250199Sgrehan * Redistribution and use in source and binary forms, with or without
34250199Sgrehan * modification, are permitted provided that the following conditions
35250199Sgrehan * are met:
36250199Sgrehan * 1. Redistributions of source code must retain the above copyright
37250199Sgrehan *    notice, this list of conditions and the following disclaimer.
38250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
39250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
40250199Sgrehan *    documentation and/or other materials provided with the distribution.
41250199Sgrehan *
42250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43250199Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44250199Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45250199Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46250199Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47250199Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48250199Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49250199Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50250199Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51250199Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52250199Sgrehan * SUCH DAMAGE.
53250199Sgrehan */
54250199Sgrehan
55256363Sgrehan#include <sys/cdefs.h>
56256363Sgrehan__FBSDID("$FreeBSD: stable/10/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c 257513 2013-11-01 17:39:59Z delphij $");
57256363Sgrehan
58250199Sgrehan#include <sys/param.h>
59250199Sgrehan#include <sys/systm.h>
60250199Sgrehan#include <sys/sockio.h>
61250199Sgrehan#include <sys/mbuf.h>
62250199Sgrehan#include <sys/malloc.h>
63250199Sgrehan#include <sys/module.h>
64250199Sgrehan#include <sys/kernel.h>
65250199Sgrehan#include <sys/socket.h>
66250199Sgrehan#include <sys/queue.h>
67250199Sgrehan#include <sys/lock.h>
68250199Sgrehan#include <sys/sx.h>
69250199Sgrehan
70250199Sgrehan#include <net/if.h>
71250199Sgrehan#include <net/if_arp.h>
72250199Sgrehan#include <net/ethernet.h>
73250199Sgrehan#include <net/if_dl.h>
74250199Sgrehan#include <net/if_media.h>
75250199Sgrehan
76250199Sgrehan#include <net/bpf.h>
77250199Sgrehan
78250199Sgrehan#include <net/if_types.h>
79250199Sgrehan#include <net/if_vlan_var.h>
80250199Sgrehan#include <net/if.h>
81250199Sgrehan
82250199Sgrehan#include <netinet/in_systm.h>
83250199Sgrehan#include <netinet/in.h>
84250199Sgrehan#include <netinet/ip.h>
85250199Sgrehan#include <netinet/if_ether.h>
86250199Sgrehan
87250199Sgrehan#include <vm/vm.h>
88250199Sgrehan#include <vm/vm_param.h>
89250199Sgrehan#include <vm/vm_kern.h>
90250199Sgrehan#include <vm/pmap.h>
91250199Sgrehan
92250199Sgrehan#include <machine/bus.h>
93250199Sgrehan#include <machine/resource.h>
94250199Sgrehan#include <machine/frame.h>
95250199Sgrehan#include <machine/vmparam.h>
96250199Sgrehan
97250199Sgrehan#include <sys/bus.h>
98250199Sgrehan#include <sys/rman.h>
99250199Sgrehan#include <sys/mutex.h>
100250199Sgrehan#include <sys/errno.h>
101250199Sgrehan#include <sys/types.h>
102250199Sgrehan#include <machine/atomic.h>
103250199Sgrehan
104250199Sgrehan#include <machine/intr_machdep.h>
105250199Sgrehan
106250199Sgrehan#include <dev/hyperv/include/hyperv.h>
107250199Sgrehan#include "hv_net_vsc.h"
108250199Sgrehan#include "hv_rndis.h"
109250199Sgrehan#include "hv_rndis_filter.h"
110250199Sgrehan
111250199Sgrehan
112250199Sgrehan/* Short for Hyper-V network interface */
113250199Sgrehan#define NETVSC_DEVNAME    "hn"
114250199Sgrehan
115250199Sgrehan/*
116250199Sgrehan * It looks like offset 0 of buf is reserved to hold the softc pointer.
117250199Sgrehan * The sc pointer evidently not needed, and is not presently populated.
118250199Sgrehan * The packet offset is where the netvsc_packet starts in the buffer.
119250199Sgrehan */
120250199Sgrehan#define HV_NV_SC_PTR_OFFSET_IN_BUF         0
121250199Sgrehan#define HV_NV_PACKET_OFFSET_IN_BUF         16
122250199Sgrehan
123250199Sgrehan
124250199Sgrehan/*
125250199Sgrehan * Data types
126250199Sgrehan */
127250199Sgrehan
128250199Sgrehanstruct hv_netvsc_driver_context {
129250199Sgrehan	uint32_t		drv_inited;
130250199Sgrehan};
131250199Sgrehan
132250199Sgrehan/*
133250199Sgrehan * Be aware that this sleepable mutex will exhibit WITNESS errors when
134250199Sgrehan * certain TCP and ARP code paths are taken.  This appears to be a
135250199Sgrehan * well-known condition, as all other drivers checked use a sleeping
136250199Sgrehan * mutex to protect their transmit paths.
137250199Sgrehan * Also Be aware that mutexes do not play well with semaphores, and there
138250199Sgrehan * is a conflicting semaphore in a certain channel code path.
139250199Sgrehan */
140250199Sgrehan#define NV_LOCK_INIT(_sc, _name) \
141250199Sgrehan	    mtx_init(&(_sc)->hn_lock, _name, MTX_NETWORK_LOCK, MTX_DEF)
142250199Sgrehan#define NV_LOCK(_sc)		mtx_lock(&(_sc)->hn_lock)
143250199Sgrehan#define NV_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->hn_lock, MA_OWNED)
144250199Sgrehan#define NV_UNLOCK(_sc)		mtx_unlock(&(_sc)->hn_lock)
145250199Sgrehan#define NV_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->hn_lock)
146250199Sgrehan
147250199Sgrehan
148250199Sgrehan/*
149250199Sgrehan * Globals
150250199Sgrehan */
151250199Sgrehan
152250199Sgrehanint hv_promisc_mode = 0;    /* normal mode by default */
153250199Sgrehan
154250199Sgrehan/* The one and only one */
155250199Sgrehanstatic struct hv_netvsc_driver_context g_netvsc_drv;
156250199Sgrehan
157250199Sgrehan
158250199Sgrehan/*
159250199Sgrehan * Forward declarations
160250199Sgrehan */
161250199Sgrehanstatic void hn_stop(hn_softc_t *sc);
162250199Sgrehanstatic void hn_ifinit_locked(hn_softc_t *sc);
163250199Sgrehanstatic void hn_ifinit(void *xsc);
164250199Sgrehanstatic int  hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
165250199Sgrehanstatic int  hn_start_locked(struct ifnet *ifp);
166250199Sgrehanstatic void hn_start(struct ifnet *ifp);
167250199Sgrehan
168250199Sgrehan
169250199Sgrehan/*
170250199Sgrehan * NetVsc driver initialization
171250199Sgrehan * Note:  Filter init is no longer required
172250199Sgrehan */
173250199Sgrehanstatic int
174250199Sgrehannetvsc_drv_init(void)
175250199Sgrehan{
176250199Sgrehan	return (0);
177250199Sgrehan}
178250199Sgrehan
179250199Sgrehan/*
180250199Sgrehan * NetVsc global initialization entry point
181250199Sgrehan */
182250199Sgrehanstatic void
183250199Sgrehannetvsc_init(void)
184250199Sgrehan{
185250199Sgrehan	printf("Netvsc initializing... ");
186250199Sgrehan
187250199Sgrehan	/*
188250199Sgrehan	 * XXXKYS: cleanup initialization
189250199Sgrehan	 */
190250199Sgrehan	if (!cold && !g_netvsc_drv.drv_inited) {
191250199Sgrehan		g_netvsc_drv.drv_inited = 1;
192250199Sgrehan		netvsc_drv_init();
193250199Sgrehan	} else {
194250199Sgrehan		printf("Already initialized!\n");
195250199Sgrehan	}
196250199Sgrehan}
197250199Sgrehan
198250199Sgrehan/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
199250199Sgrehanstatic const hv_guid g_net_vsc_device_type = {
200250199Sgrehan	.data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
201250199Sgrehan		0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
202250199Sgrehan};
203250199Sgrehan
204250199Sgrehan/*
205250199Sgrehan * Standard probe entry point.
206250199Sgrehan *
207250199Sgrehan */
208250199Sgrehanstatic int
209250199Sgrehannetvsc_probe(device_t dev)
210250199Sgrehan{
211250199Sgrehan	const char *p;
212250199Sgrehan
213250199Sgrehan	p = vmbus_get_type(dev);
214250199Sgrehan	if (!memcmp(p, &g_net_vsc_device_type.data, sizeof(hv_guid))) {
215250199Sgrehan		device_set_desc(dev, "Synthetic Network Interface");
216250199Sgrehan		printf("Netvsc probe... DONE \n");
217250199Sgrehan
218250199Sgrehan		return (0);
219250199Sgrehan	}
220250199Sgrehan
221250199Sgrehan	return (ENXIO);
222250199Sgrehan}
223250199Sgrehan
224250199Sgrehan/*
225250199Sgrehan * Standard attach entry point.
226250199Sgrehan *
227250199Sgrehan * Called when the driver is loaded.  It allocates needed resources,
228250199Sgrehan * and initializes the "hardware" and software.
229250199Sgrehan */
230250199Sgrehanstatic int
231250199Sgrehannetvsc_attach(device_t dev)
232250199Sgrehan{
233250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(dev);
234250199Sgrehan	netvsc_device_info device_info;
235250199Sgrehan	hn_softc_t *sc;
236250199Sgrehan	int unit = device_get_unit(dev);
237250199Sgrehan	struct ifnet *ifp;
238250199Sgrehan	int ret;
239250199Sgrehan
240250199Sgrehan	netvsc_init();
241250199Sgrehan
242250199Sgrehan	sc = device_get_softc(dev);
243250199Sgrehan	if (sc == NULL) {
244250199Sgrehan		return (ENOMEM);
245250199Sgrehan	}
246250199Sgrehan
247250199Sgrehan	bzero(sc, sizeof(hn_softc_t));
248250199Sgrehan	sc->hn_unit = unit;
249250199Sgrehan	sc->hn_dev = dev;
250250199Sgrehan
251250199Sgrehan	NV_LOCK_INIT(sc, "NetVSCLock");
252250199Sgrehan
253250199Sgrehan	sc->hn_dev_obj = device_ctx;
254250199Sgrehan
255250199Sgrehan	ifp = sc->hn_ifp = sc->arpcom.ac_ifp = if_alloc(IFT_ETHER);
256250199Sgrehan	ifp->if_softc = sc;
257250199Sgrehan
258250199Sgrehan	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
259250199Sgrehan	ifp->if_dunit = unit;
260250199Sgrehan	ifp->if_dname = NETVSC_DEVNAME;
261250199Sgrehan
262250199Sgrehan	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
263250199Sgrehan	ifp->if_ioctl = hn_ioctl;
264250199Sgrehan	ifp->if_start = hn_start;
265250199Sgrehan	ifp->if_init = hn_ifinit;
266250199Sgrehan	/* needed by hv_rf_on_device_add() code */
267250199Sgrehan	ifp->if_mtu = ETHERMTU;
268250199Sgrehan	IFQ_SET_MAXLEN(&ifp->if_snd, 512);
269250199Sgrehan	ifp->if_snd.ifq_drv_maxlen = 511;
270250199Sgrehan	IFQ_SET_READY(&ifp->if_snd);
271250199Sgrehan
272250199Sgrehan	/*
273250199Sgrehan	 * Tell upper layers that we support full VLAN capability.
274250199Sgrehan	 */
275250199Sgrehan	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
276250199Sgrehan	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
277250199Sgrehan	ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
278250199Sgrehan
279250199Sgrehan	ret = hv_rf_on_device_add(device_ctx, &device_info);
280250199Sgrehan	if (ret != 0) {
281250199Sgrehan		if_free(ifp);
282250199Sgrehan
283250199Sgrehan		return (ret);
284250199Sgrehan	}
285250199Sgrehan	if (device_info.link_state == 0) {
286250199Sgrehan		sc->hn_carrier = 1;
287250199Sgrehan	}
288250199Sgrehan
289250199Sgrehan	ether_ifattach(ifp, device_info.mac_addr);
290250199Sgrehan
291250199Sgrehan	return (0);
292250199Sgrehan}
293250199Sgrehan
294250199Sgrehan/*
295250199Sgrehan * Standard detach entry point
296250199Sgrehan */
297250199Sgrehanstatic int
298250199Sgrehannetvsc_detach(device_t dev)
299250199Sgrehan{
300250199Sgrehan	struct hv_device *hv_device = vmbus_get_devctx(dev);
301250199Sgrehan
302250199Sgrehan	printf("netvsc_detach\n");
303250199Sgrehan
304250199Sgrehan	/*
305250199Sgrehan	 * XXXKYS:  Need to clean up all our
306250199Sgrehan	 * driver state; this is the driver
307250199Sgrehan	 * unloading.
308250199Sgrehan	 */
309250199Sgrehan
310250199Sgrehan	/*
311250199Sgrehan	 * XXXKYS:  Need to stop outgoing traffic and unregister
312250199Sgrehan	 * the netdevice.
313250199Sgrehan	 */
314250199Sgrehan
315250199Sgrehan	hv_rf_on_device_remove(hv_device, HV_RF_NV_DESTROY_CHANNEL);
316250199Sgrehan
317250199Sgrehan	return (0);
318250199Sgrehan}
319250199Sgrehan
320250199Sgrehan/*
321250199Sgrehan * Standard shutdown entry point
322250199Sgrehan */
323250199Sgrehanstatic int
324250199Sgrehannetvsc_shutdown(device_t dev)
325250199Sgrehan{
326250199Sgrehan	return (0);
327250199Sgrehan}
328250199Sgrehan
329250199Sgrehan/*
330250199Sgrehan * Send completion processing
331250199Sgrehan *
332250199Sgrehan * Note:  It looks like offset 0 of buf is reserved to hold the softc
333250199Sgrehan * pointer.  The sc pointer is not currently needed in this function, and
334250199Sgrehan * it is not presently populated by the TX function.
335250199Sgrehan */
336250199Sgrehanvoid
337250199Sgrehannetvsc_xmit_completion(void *context)
338250199Sgrehan{
339250199Sgrehan	netvsc_packet *packet = (netvsc_packet *)context;
340250199Sgrehan	struct mbuf *mb;
341250199Sgrehan	uint8_t *buf;
342250199Sgrehan
343250199Sgrehan	mb = (struct mbuf *)packet->compl.send.send_completion_tid;
344250199Sgrehan	buf = ((uint8_t *)packet) - HV_NV_PACKET_OFFSET_IN_BUF;
345250199Sgrehan
346250199Sgrehan	free(buf, M_DEVBUF);
347250199Sgrehan
348250199Sgrehan	if (mb != NULL) {
349250199Sgrehan		m_freem(mb);
350250199Sgrehan	}
351250199Sgrehan}
352250199Sgrehan
353250199Sgrehan/*
354250199Sgrehan * Start a transmit of one or more packets
355250199Sgrehan */
356250199Sgrehanstatic int
357250199Sgrehanhn_start_locked(struct ifnet *ifp)
358250199Sgrehan{
359250199Sgrehan	hn_softc_t *sc = ifp->if_softc;
360250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
361250199Sgrehan	uint8_t *buf;
362250199Sgrehan	netvsc_packet *packet;
363250199Sgrehan	struct mbuf *m_head, *m;
364250199Sgrehan	struct mbuf *mc_head = NULL;
365250199Sgrehan	int i;
366250199Sgrehan	int num_frags;
367250199Sgrehan	int len;
368250199Sgrehan	int xlen;
369250199Sgrehan	int rppi_size;
370250199Sgrehan	int retries = 0;
371250199Sgrehan	int ret = 0;
372250199Sgrehan
373250199Sgrehan	while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) {
374250199Sgrehan		IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head);
375250199Sgrehan		if (m_head == NULL) {
376250199Sgrehan			break;
377250199Sgrehan		}
378250199Sgrehan
379250199Sgrehan		len = 0;
380250199Sgrehan		num_frags = 0;
381250199Sgrehan		xlen = 0;
382250199Sgrehan
383250199Sgrehan		/* Walk the mbuf list computing total length and num frags */
384250199Sgrehan		for (m = m_head; m != NULL; m = m->m_next) {
385250199Sgrehan			if (m->m_len != 0) {
386250199Sgrehan				num_frags++;
387250199Sgrehan				len += m->m_len;
388250199Sgrehan			}
389250199Sgrehan		}
390250199Sgrehan
391250199Sgrehan		/*
392250199Sgrehan		 * Reserve the number of pages requested.  Currently,
393250199Sgrehan		 * one page is reserved for the message in the RNDIS
394250199Sgrehan		 * filter packet
395250199Sgrehan		 */
396250199Sgrehan		num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
397250199Sgrehan
398250199Sgrehan		/* If exceeds # page_buffers in netvsc_packet */
399250199Sgrehan		if (num_frags > NETVSC_PACKET_MAXPAGE) {
400250199Sgrehan			m_freem(m);
401250199Sgrehan
402250199Sgrehan			return (EINVAL);
403250199Sgrehan		}
404250199Sgrehan
405250199Sgrehan		rppi_size = 0;
406250199Sgrehan		if (m_head->m_flags & M_VLANTAG) {
407250199Sgrehan			rppi_size = sizeof(rndis_per_packet_info) +
408250199Sgrehan			    sizeof(ndis_8021q_info);
409250199Sgrehan		}
410250199Sgrehan
411250199Sgrehan		/*
412250199Sgrehan		 * Allocate a buffer with space for a netvsc packet plus a
413250199Sgrehan		 * number of reserved areas.  First comes a (currently 16
414250199Sgrehan		 * bytes, currently unused) reserved data area.  Second is
415250199Sgrehan		 * the netvsc_packet, which includes (currently 4) page
416250199Sgrehan		 * buffers.  Third (optional) is a rndis_per_packet_info
417250199Sgrehan		 * struct, but only if a VLAN tag should be inserted into the
418250199Sgrehan		 * Ethernet frame by the Hyper-V infrastructure.  Fourth is
419250199Sgrehan		 * an area reserved for an rndis_filter_packet struct.
420250199Sgrehan		 * Changed malloc to M_NOWAIT to avoid sleep under spin lock.
421250199Sgrehan		 * No longer reserving extra space for page buffers, as they
422250199Sgrehan		 * are already part of the netvsc_packet.
423250199Sgrehan		 */
424250199Sgrehan		buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF +
425250199Sgrehan		    sizeof(netvsc_packet) + rppi_size +
426250199Sgrehan		    sizeof(rndis_filter_packet),
427250199Sgrehan		    M_DEVBUF, M_ZERO | M_NOWAIT);
428250199Sgrehan		if (buf == NULL) {
429250199Sgrehan			m_freem(m);
430250199Sgrehan
431250199Sgrehan			return (ENOMEM);
432250199Sgrehan		}
433250199Sgrehan
434250199Sgrehan		packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF);
435250199Sgrehan		*(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF;
436250199Sgrehan
437250199Sgrehan		/*
438250199Sgrehan		 * extension points to the area reserved for the
439250199Sgrehan		 * rndis_filter_packet, which is placed just after
440250199Sgrehan		 * the netvsc_packet (and rppi struct, if present;
441250199Sgrehan		 * length is updated later).
442250199Sgrehan		 */
443250199Sgrehan		packet->extension = packet + 1;
444250199Sgrehan
445250199Sgrehan		/* Set up the rndis header */
446250199Sgrehan		packet->page_buf_count = num_frags;
447250199Sgrehan
448250199Sgrehan		/* Initialize it from the mbuf */
449250199Sgrehan		packet->tot_data_buf_len = len;
450250199Sgrehan
451250199Sgrehan		/*
452250199Sgrehan		 * If the Hyper-V infrastructure needs to embed a VLAN tag,
453250199Sgrehan		 * initialize netvsc_packet and rppi struct values as needed.
454250199Sgrehan		 */
455250199Sgrehan		if (rppi_size) {
456250199Sgrehan			/* Lower layers need the VLAN TCI */
457250199Sgrehan			packet->vlan_tci = m_head->m_pkthdr.ether_vtag;
458250199Sgrehan		}
459250199Sgrehan
460250199Sgrehan		/*
461250199Sgrehan		 * Fill the page buffers with mbuf info starting at index
462250199Sgrehan		 * HV_RF_NUM_TX_RESERVED_PAGE_BUFS.
463250199Sgrehan		 */
464250199Sgrehan		i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
465250199Sgrehan		for (m = m_head; m != NULL; m = m->m_next) {
466250199Sgrehan			if (m->m_len) {
467250199Sgrehan				vm_offset_t paddr =
468250199Sgrehan				    vtophys(mtod(m, vm_offset_t));
469250199Sgrehan				packet->page_buffers[i].pfn =
470250199Sgrehan				    paddr >> PAGE_SHIFT;
471250199Sgrehan				packet->page_buffers[i].offset =
472250199Sgrehan				    paddr & (PAGE_SIZE - 1);
473250199Sgrehan				packet->page_buffers[i].length = m->m_len;
474250199Sgrehan				i++;
475250199Sgrehan			}
476250199Sgrehan		}
477250199Sgrehan
478250199Sgrehan		/*
479250199Sgrehan		 * If bpf, copy the mbuf chain.  This is less expensive than
480250199Sgrehan		 * it appears; the mbuf clusters are not copied, only their
481250199Sgrehan		 * reference counts are incremented.
482250199Sgrehan		 * Needed to avoid a race condition where the completion
483250199Sgrehan		 * callback is invoked, freeing the mbuf chain, before the
484250199Sgrehan		 * bpf_mtap code has a chance to run.
485250199Sgrehan		 */
486250199Sgrehan		if (ifp->if_bpf) {
487250199Sgrehan			mc_head = m_copypacket(m_head, M_DONTWAIT);
488250199Sgrehan		}
489250199Sgrehanretry_send:
490250199Sgrehan		/* Set the completion routine */
491250199Sgrehan		packet->compl.send.on_send_completion = netvsc_xmit_completion;
492250199Sgrehan		packet->compl.send.send_completion_context = packet;
493250199Sgrehan		packet->compl.send.send_completion_tid = (uint64_t)m_head;
494250199Sgrehan
495250199Sgrehan		/* Removed critical_enter(), does not appear necessary */
496250199Sgrehan		ret = hv_rf_on_send(device_ctx, packet);
497250199Sgrehan
498250199Sgrehan		if (ret == 0) {
499250199Sgrehan			ifp->if_opackets++;
500250199Sgrehan			/* if bpf && mc_head, call bpf_mtap code */
501250199Sgrehan			if (mc_head) {
502250199Sgrehan				ETHER_BPF_MTAP(ifp, mc_head);
503250199Sgrehan			}
504250199Sgrehan		} else {
505250199Sgrehan			retries++;
506250199Sgrehan			if (retries < 4) {
507250199Sgrehan				goto retry_send;
508250199Sgrehan			}
509250199Sgrehan
510250199Sgrehan			IF_PREPEND(&ifp->if_snd, m_head);
511250199Sgrehan			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
512250199Sgrehan
513250199Sgrehan			/*
514250199Sgrehan			 * Null the mbuf pointer so the completion function
515250199Sgrehan			 * does not free the mbuf chain.  We just pushed the
516250199Sgrehan			 * mbuf chain back on the if_snd queue.
517250199Sgrehan			 */
518250199Sgrehan			packet->compl.send.send_completion_tid = 0;
519250199Sgrehan
520250199Sgrehan			/*
521250199Sgrehan			 * Release the resources since we will not get any
522250199Sgrehan			 * send completion
523250199Sgrehan			 */
524250199Sgrehan			netvsc_xmit_completion(packet);
525250199Sgrehan		}
526250199Sgrehan
527250199Sgrehan		/* if bpf && mc_head, free the mbuf chain copy */
528250199Sgrehan		if (mc_head) {
529250199Sgrehan			m_freem(mc_head);
530250199Sgrehan		}
531250199Sgrehan	}
532250199Sgrehan
533250199Sgrehan	return (ret);
534250199Sgrehan}
535250199Sgrehan
536250199Sgrehan/*
537250199Sgrehan * Link up/down notification
538250199Sgrehan */
539250199Sgrehanvoid
540250199Sgrehannetvsc_linkstatus_callback(struct hv_device *device_obj, uint32_t status)
541250199Sgrehan{
542250199Sgrehan	hn_softc_t *sc = device_get_softc(device_obj->device);
543250199Sgrehan
544250199Sgrehan	if (sc == NULL) {
545250199Sgrehan		return;
546250199Sgrehan	}
547250199Sgrehan
548250199Sgrehan	if (status == 1) {
549250199Sgrehan		sc->hn_carrier = 1;
550250199Sgrehan	} else {
551250199Sgrehan		sc->hn_carrier = 0;
552250199Sgrehan	}
553250199Sgrehan}
554250199Sgrehan
555250199Sgrehan/*
556250199Sgrehan * Append the specified data to the indicated mbuf chain,
557250199Sgrehan * Extend the mbuf chain if the new data does not fit in
558250199Sgrehan * existing space.
559250199Sgrehan *
560250199Sgrehan * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
561250199Sgrehan * There should be an equivalent in the kernel mbuf code,
562250199Sgrehan * but there does not appear to be one yet.
563250199Sgrehan *
564250199Sgrehan * Differs from m_append() in that additional mbufs are
565250199Sgrehan * allocated with cluster size MJUMPAGESIZE, and filled
566250199Sgrehan * accordingly.
567250199Sgrehan *
568250199Sgrehan * Return 1 if able to complete the job; otherwise 0.
569250199Sgrehan */
570250199Sgrehanstatic int
571250199Sgrehanhv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
572250199Sgrehan{
573250199Sgrehan	struct mbuf *m, *n;
574250199Sgrehan	int remainder, space;
575250199Sgrehan
576250199Sgrehan	for (m = m0; m->m_next != NULL; m = m->m_next)
577250199Sgrehan		;
578250199Sgrehan	remainder = len;
579250199Sgrehan	space = M_TRAILINGSPACE(m);
580250199Sgrehan	if (space > 0) {
581250199Sgrehan		/*
582250199Sgrehan		 * Copy into available space.
583250199Sgrehan		 */
584250199Sgrehan		if (space > remainder)
585250199Sgrehan			space = remainder;
586250199Sgrehan		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
587250199Sgrehan		m->m_len += space;
588250199Sgrehan		cp += space;
589250199Sgrehan		remainder -= space;
590250199Sgrehan	}
591250199Sgrehan	while (remainder > 0) {
592250199Sgrehan		/*
593250199Sgrehan		 * Allocate a new mbuf; could check space
594250199Sgrehan		 * and allocate a cluster instead.
595250199Sgrehan		 */
596250199Sgrehan		n = m_getjcl(M_DONTWAIT, m->m_type, 0, MJUMPAGESIZE);
597250199Sgrehan		if (n == NULL)
598250199Sgrehan			break;
599250199Sgrehan		n->m_len = min(MJUMPAGESIZE, remainder);
600250199Sgrehan		bcopy(cp, mtod(n, caddr_t), n->m_len);
601250199Sgrehan		cp += n->m_len;
602250199Sgrehan		remainder -= n->m_len;
603250199Sgrehan		m->m_next = n;
604250199Sgrehan		m = n;
605250199Sgrehan	}
606250199Sgrehan	if (m0->m_flags & M_PKTHDR)
607250199Sgrehan		m0->m_pkthdr.len += len - remainder;
608250199Sgrehan
609250199Sgrehan	return (remainder == 0);
610250199Sgrehan}
611250199Sgrehan
612250199Sgrehan
613250199Sgrehan/*
614250199Sgrehan * Called when we receive a data packet from the "wire" on the
615250199Sgrehan * specified device
616250199Sgrehan *
617250199Sgrehan * Note:  This is no longer used as a callback
618250199Sgrehan */
619250199Sgrehanint
620250199Sgrehannetvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet)
621250199Sgrehan{
622250199Sgrehan	hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device);
623250199Sgrehan	struct mbuf *m_new;
624257513Sdelphij	struct ifnet *ifp;
625250199Sgrehan	int size;
626250199Sgrehan	int i;
627250199Sgrehan
628250199Sgrehan	if (sc == NULL) {
629250199Sgrehan		return (0); /* TODO: KYS how can this be! */
630250199Sgrehan	}
631257513Sdelphij
632257513Sdelphij	ifp = sc->hn_ifp;
633250199Sgrehan
634250199Sgrehan	ifp = sc->arpcom.ac_ifp;
635250199Sgrehan
636250199Sgrehan	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
637250199Sgrehan		return (0);
638250199Sgrehan	}
639250199Sgrehan
640250199Sgrehan	/*
641250199Sgrehan	 * Bail out if packet contains more data than configured MTU.
642250199Sgrehan	 */
643250199Sgrehan	if (packet->tot_data_buf_len > (ifp->if_mtu + ETHER_HDR_LEN)) {
644250199Sgrehan		return (0);
645250199Sgrehan	}
646250199Sgrehan
647250199Sgrehan	/*
648250199Sgrehan	 * Get an mbuf with a cluster.  For packets 2K or less,
649250199Sgrehan	 * get a standard 2K cluster.  For anything larger, get a
650250199Sgrehan	 * 4K cluster.  Any buffers larger than 4K can cause problems
651250199Sgrehan	 * if looped around to the Hyper-V TX channel, so avoid them.
652250199Sgrehan	 */
653250199Sgrehan	size = MCLBYTES;
654250199Sgrehan
655250199Sgrehan	if (packet->tot_data_buf_len > MCLBYTES) {
656250199Sgrehan		/* 4096 */
657250199Sgrehan		size = MJUMPAGESIZE;
658250199Sgrehan	}
659250199Sgrehan
660250199Sgrehan	m_new = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size);
661250199Sgrehan
662250199Sgrehan	if (m_new == NULL)
663250199Sgrehan		return (0);
664250199Sgrehan
665250199Sgrehan	/*
666250199Sgrehan	 * Remove trailing junk from RX data buffer.
667250199Sgrehan	 * Fixme:  This will not work for multiple Hyper-V RX buffers.
668250199Sgrehan	 * Fortunately, the channel gathers all RX data into one buffer.
669250199Sgrehan	 *
670250199Sgrehan	 * L2 frame length, with L2 header, not including CRC
671250199Sgrehan	 */
672250199Sgrehan	packet->page_buffers[0].length = packet->tot_data_buf_len;
673250199Sgrehan
674250199Sgrehan	/*
675250199Sgrehan	 * Copy the received packet to one or more mbufs.
676250199Sgrehan	 * The copy is required since the memory pointed to by netvsc_packet
677250199Sgrehan	 * cannot be deallocated
678250199Sgrehan	 */
679250199Sgrehan	for (i=0; i < packet->page_buf_count; i++) {
680250199Sgrehan		/* Shift virtual page number to form virtual page address */
681250199Sgrehan		uint8_t *vaddr = (uint8_t *)
682250199Sgrehan		    (packet->page_buffers[i].pfn << PAGE_SHIFT);
683250199Sgrehan
684250199Sgrehan		hv_m_append(m_new, packet->page_buffers[i].length,
685250199Sgrehan		    vaddr + packet->page_buffers[i].offset);
686250199Sgrehan	}
687250199Sgrehan
688250199Sgrehan	m_new->m_pkthdr.rcvif = ifp;
689250199Sgrehan
690250199Sgrehan	if ((packet->vlan_tci != 0) &&
691250199Sgrehan			    (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) {
692250199Sgrehan		m_new->m_pkthdr.ether_vtag = packet->vlan_tci;
693250199Sgrehan		m_new->m_flags |= M_VLANTAG;
694250199Sgrehan	}
695250199Sgrehan
696250199Sgrehan	/*
697250199Sgrehan	 * Note:  Moved RX completion back to hv_nv_on_receive() so all
698250199Sgrehan	 * messages (not just data messages) will trigger a response.
699250199Sgrehan	 */
700250199Sgrehan
701250199Sgrehan	ifp->if_ipackets++;
702250199Sgrehan
703250199Sgrehan	/* We're not holding the lock here, so don't release it */
704250199Sgrehan	(*ifp->if_input)(ifp, m_new);
705250199Sgrehan
706250199Sgrehan	return (0);
707250199Sgrehan}
708250199Sgrehan
709250199Sgrehan/*
710256363Sgrehan * Rules for using sc->temp_unusable:
711256363Sgrehan * 1.  sc->temp_unusable can only be read or written while holding NV_LOCK()
712256363Sgrehan * 2.  code reading sc->temp_unusable under NV_LOCK(), and finding
713256363Sgrehan *     sc->temp_unusable set, must release NV_LOCK() and exit
714256363Sgrehan * 3.  to retain exclusive control of the interface,
715256363Sgrehan *     sc->temp_unusable must be set by code before releasing NV_LOCK()
716256363Sgrehan * 4.  only code setting sc->temp_unusable can clear sc->temp_unusable
717256363Sgrehan * 5.  code setting sc->temp_unusable must eventually clear sc->temp_unusable
718256363Sgrehan */
719256363Sgrehan
720256363Sgrehan/*
721250199Sgrehan * Standard ioctl entry point.  Called when the user wants to configure
722250199Sgrehan * the interface.
723250199Sgrehan */
724250199Sgrehanstatic int
725250199Sgrehanhn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
726250199Sgrehan{
727250199Sgrehan	hn_softc_t *sc = ifp->if_softc;
728250199Sgrehan	struct ifreq *ifr = (struct ifreq *)data;
729250199Sgrehan	netvsc_device_info device_info;
730250199Sgrehan	struct hv_device *hn_dev;
731250199Sgrehan	int mask, error = 0;
732256363Sgrehan	int retry_cnt = 500;
733256363Sgrehan
734250199Sgrehan	switch(cmd) {
735250199Sgrehan
736250199Sgrehan	case SIOCSIFADDR:
737250199Sgrehan	case SIOCGIFADDR:
738250199Sgrehan		error = ether_ioctl(ifp, cmd, data);
739250199Sgrehan		break;
740250199Sgrehan	case SIOCSIFMTU:
741250199Sgrehan		hn_dev = vmbus_get_devctx(sc->hn_dev);
742250199Sgrehan
743256363Sgrehan		/* Check MTU value change */
744256363Sgrehan		if (ifp->if_mtu == ifr->ifr_mtu)
745256363Sgrehan			break;
746250199Sgrehan
747250199Sgrehan		if (ifr->ifr_mtu > NETVSC_MAX_CONFIGURABLE_MTU) {
748250199Sgrehan			error = EINVAL;
749250199Sgrehan			break;
750250199Sgrehan		}
751256363Sgrehan
752250199Sgrehan		/* Obtain and record requested MTU */
753250199Sgrehan		ifp->if_mtu = ifr->ifr_mtu;
754256363Sgrehan
755256363Sgrehan		do {
756256363Sgrehan			NV_LOCK(sc);
757256363Sgrehan			if (!sc->temp_unusable) {
758256363Sgrehan				sc->temp_unusable = TRUE;
759256363Sgrehan				retry_cnt = -1;
760256363Sgrehan			}
761256363Sgrehan			NV_UNLOCK(sc);
762256363Sgrehan			if (retry_cnt > 0) {
763256363Sgrehan				retry_cnt--;
764256363Sgrehan				DELAY(5 * 1000);
765256363Sgrehan			}
766256363Sgrehan		} while (retry_cnt > 0);
767250199Sgrehan
768256363Sgrehan		if (retry_cnt == 0) {
769256363Sgrehan			error = EINVAL;
770256363Sgrehan			break;
771256363Sgrehan		}
772256363Sgrehan
773256363Sgrehan		/* We must remove and add back the device to cause the new
774250199Sgrehan		 * MTU to take effect.  This includes tearing down, but not
775250199Sgrehan		 * deleting the channel, then bringing it back up.
776250199Sgrehan		 */
777250199Sgrehan		error = hv_rf_on_device_remove(hn_dev, HV_RF_NV_RETAIN_CHANNEL);
778250199Sgrehan		if (error) {
779256363Sgrehan			NV_LOCK(sc);
780256363Sgrehan			sc->temp_unusable = FALSE;
781250199Sgrehan			NV_UNLOCK(sc);
782250199Sgrehan			break;
783250199Sgrehan		}
784250199Sgrehan		error = hv_rf_on_device_add(hn_dev, &device_info);
785250199Sgrehan		if (error) {
786256363Sgrehan			NV_LOCK(sc);
787256363Sgrehan			sc->temp_unusable = FALSE;
788250199Sgrehan			NV_UNLOCK(sc);
789250199Sgrehan			break;
790250199Sgrehan		}
791250199Sgrehan
792250199Sgrehan		hn_ifinit_locked(sc);
793250199Sgrehan
794256363Sgrehan		NV_LOCK(sc);
795256363Sgrehan		sc->temp_unusable = FALSE;
796250199Sgrehan		NV_UNLOCK(sc);
797250199Sgrehan		break;
798250199Sgrehan	case SIOCSIFFLAGS:
799256363Sgrehan		do {
800256363Sgrehan                       NV_LOCK(sc);
801256363Sgrehan                       if (!sc->temp_unusable) {
802256363Sgrehan                               sc->temp_unusable = TRUE;
803256363Sgrehan                               retry_cnt = -1;
804256363Sgrehan                       }
805256363Sgrehan                       NV_UNLOCK(sc);
806256363Sgrehan                       if (retry_cnt > 0) {
807256363Sgrehan                      	        retry_cnt--;
808256363Sgrehan                        	DELAY(5 * 1000);
809256363Sgrehan                       }
810256363Sgrehan                } while (retry_cnt > 0);
811256363Sgrehan
812256363Sgrehan                if (retry_cnt == 0) {
813256363Sgrehan                       error = EINVAL;
814256363Sgrehan                       break;
815256363Sgrehan                }
816256363Sgrehan
817250199Sgrehan		if (ifp->if_flags & IFF_UP) {
818250199Sgrehan			/*
819250199Sgrehan			 * If only the state of the PROMISC flag changed,
820250199Sgrehan			 * then just use the 'set promisc mode' command
821250199Sgrehan			 * instead of reinitializing the entire NIC. Doing
822250199Sgrehan			 * a full re-init means reloading the firmware and
823250199Sgrehan			 * waiting for it to start up, which may take a
824250199Sgrehan			 * second or two.
825250199Sgrehan			 */
826250199Sgrehan#ifdef notyet
827250199Sgrehan			/* Fixme:  Promiscuous mode? */
828250199Sgrehan			if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
829250199Sgrehan			    ifp->if_flags & IFF_PROMISC &&
830250199Sgrehan			    !(sc->hn_if_flags & IFF_PROMISC)) {
831250199Sgrehan				/* do something here for Hyper-V */
832250199Sgrehan			} else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
833256363Sgrehan			    !(ifp->if_flags & IFF_PROMISC) &&
834256363Sgrehan			    sc->hn_if_flags & IFF_PROMISC) {
835250199Sgrehan				/* do something here for Hyper-V */
836250199Sgrehan			} else
837250199Sgrehan#endif
838250199Sgrehan				hn_ifinit_locked(sc);
839250199Sgrehan		} else {
840250199Sgrehan			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
841250199Sgrehan				hn_stop(sc);
842250199Sgrehan			}
843250199Sgrehan		}
844256363Sgrehan		NV_LOCK(sc);
845256363Sgrehan		sc->temp_unusable = FALSE;
846256363Sgrehan		NV_UNLOCK(sc);
847250199Sgrehan		sc->hn_if_flags = ifp->if_flags;
848250199Sgrehan		error = 0;
849250199Sgrehan		break;
850250199Sgrehan	case SIOCSIFCAP:
851250199Sgrehan		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
852250199Sgrehan		if (mask & IFCAP_HWCSUM) {
853250199Sgrehan			if (IFCAP_HWCSUM & ifp->if_capenable) {
854250199Sgrehan				ifp->if_capenable &= ~IFCAP_HWCSUM;
855250199Sgrehan			} else {
856250199Sgrehan				ifp->if_capenable |= IFCAP_HWCSUM;
857250199Sgrehan			}
858250199Sgrehan		}
859250199Sgrehan		error = 0;
860250199Sgrehan		break;
861250199Sgrehan	case SIOCADDMULTI:
862250199Sgrehan	case SIOCDELMULTI:
863250199Sgrehan#ifdef notyet
864250199Sgrehan		/* Fixme:  Multicast mode? */
865250199Sgrehan		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
866250199Sgrehan			NV_LOCK(sc);
867250199Sgrehan			netvsc_setmulti(sc);
868250199Sgrehan			NV_UNLOCK(sc);
869250199Sgrehan			error = 0;
870250199Sgrehan		}
871250199Sgrehan#endif
872250199Sgrehan		/* FALLTHROUGH */
873250199Sgrehan	case SIOCSIFMEDIA:
874250199Sgrehan	case SIOCGIFMEDIA:
875250199Sgrehan		error = EINVAL;
876250199Sgrehan		break;
877250199Sgrehan	default:
878250199Sgrehan		error = ether_ioctl(ifp, cmd, data);
879250199Sgrehan		break;
880250199Sgrehan	}
881250199Sgrehan
882250199Sgrehan	return (error);
883250199Sgrehan}
884250199Sgrehan
885250199Sgrehan/*
886250199Sgrehan *
887250199Sgrehan */
888250199Sgrehanstatic void
889250199Sgrehanhn_stop(hn_softc_t *sc)
890250199Sgrehan{
891250199Sgrehan	struct ifnet *ifp;
892250199Sgrehan	int ret;
893250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
894250199Sgrehan
895250199Sgrehan	ifp = sc->hn_ifp;
896250199Sgrehan
897250199Sgrehan	printf(" Closing Device ...\n");
898250199Sgrehan
899250199Sgrehan	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
900250199Sgrehan	sc->hn_initdone = 0;
901250199Sgrehan
902250199Sgrehan	ret = hv_rf_on_close(device_ctx);
903250199Sgrehan}
904250199Sgrehan
905250199Sgrehan/*
906250199Sgrehan * FreeBSD transmit entry point
907250199Sgrehan */
908250199Sgrehanstatic void
909250199Sgrehanhn_start(struct ifnet *ifp)
910250199Sgrehan{
911250199Sgrehan	hn_softc_t *sc;
912250199Sgrehan
913250199Sgrehan	sc = ifp->if_softc;
914250199Sgrehan	NV_LOCK(sc);
915256363Sgrehan	if (sc->temp_unusable) {
916256363Sgrehan		NV_UNLOCK(sc);
917256363Sgrehan		return;
918256363Sgrehan	}
919250199Sgrehan	hn_start_locked(ifp);
920250199Sgrehan	NV_UNLOCK(sc);
921250199Sgrehan}
922250199Sgrehan
923250199Sgrehan/*
924250199Sgrehan *
925250199Sgrehan */
926250199Sgrehanstatic void
927250199Sgrehanhn_ifinit_locked(hn_softc_t *sc)
928250199Sgrehan{
929250199Sgrehan	struct ifnet *ifp;
930250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
931250199Sgrehan	int ret;
932250199Sgrehan
933250199Sgrehan	ifp = sc->hn_ifp;
934250199Sgrehan
935250199Sgrehan	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
936250199Sgrehan		return;
937250199Sgrehan	}
938250199Sgrehan
939250199Sgrehan	hv_promisc_mode = 1;
940250199Sgrehan
941250199Sgrehan	ret = hv_rf_on_open(device_ctx);
942250199Sgrehan	if (ret != 0) {
943250199Sgrehan		return;
944250199Sgrehan	} else {
945250199Sgrehan		sc->hn_initdone = 1;
946250199Sgrehan	}
947250199Sgrehan	ifp->if_drv_flags |= IFF_DRV_RUNNING;
948250199Sgrehan	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
949250199Sgrehan}
950250199Sgrehan
951250199Sgrehan/*
952250199Sgrehan *
953250199Sgrehan */
954250199Sgrehanstatic void
955250199Sgrehanhn_ifinit(void *xsc)
956250199Sgrehan{
957250199Sgrehan	hn_softc_t *sc = xsc;
958250199Sgrehan
959250199Sgrehan	NV_LOCK(sc);
960256363Sgrehan	if (sc->temp_unusable) {
961256363Sgrehan		NV_UNLOCK(sc);
962256363Sgrehan		return;
963256363Sgrehan	}
964256363Sgrehan	sc->temp_unusable = TRUE;
965256363Sgrehan	NV_UNLOCK(sc);
966256363Sgrehan
967250199Sgrehan	hn_ifinit_locked(sc);
968256363Sgrehan
969256363Sgrehan	NV_LOCK(sc);
970256363Sgrehan	sc->temp_unusable = FALSE;
971250199Sgrehan	NV_UNLOCK(sc);
972250199Sgrehan}
973250199Sgrehan
974250199Sgrehan#ifdef LATER
975250199Sgrehan/*
976250199Sgrehan *
977250199Sgrehan */
978250199Sgrehanstatic void
979250199Sgrehanhn_watchdog(struct ifnet *ifp)
980250199Sgrehan{
981250199Sgrehan	hn_softc_t *sc;
982250199Sgrehan	sc = ifp->if_softc;
983250199Sgrehan
984250199Sgrehan	printf("hn%d: watchdog timeout -- resetting\n", sc->hn_unit);
985250199Sgrehan	hn_ifinit(sc);    /*???*/
986250199Sgrehan	ifp->if_oerrors++;
987250199Sgrehan}
988250199Sgrehan#endif
989250199Sgrehan
990250199Sgrehanstatic device_method_t netvsc_methods[] = {
991250199Sgrehan        /* Device interface */
992250199Sgrehan        DEVMETHOD(device_probe,         netvsc_probe),
993250199Sgrehan        DEVMETHOD(device_attach,        netvsc_attach),
994250199Sgrehan        DEVMETHOD(device_detach,        netvsc_detach),
995250199Sgrehan        DEVMETHOD(device_shutdown,      netvsc_shutdown),
996250199Sgrehan
997250199Sgrehan        { 0, 0 }
998250199Sgrehan};
999250199Sgrehan
1000250199Sgrehanstatic driver_t netvsc_driver = {
1001250199Sgrehan        NETVSC_DEVNAME,
1002250199Sgrehan        netvsc_methods,
1003250199Sgrehan        sizeof(hn_softc_t)
1004250199Sgrehan};
1005250199Sgrehan
1006250199Sgrehanstatic devclass_t netvsc_devclass;
1007250199Sgrehan
1008250199SgrehanDRIVER_MODULE(hn, vmbus, netvsc_driver, netvsc_devclass, 0, 0);
1009250199SgrehanMODULE_VERSION(hn, 1);
1010250199SgrehanMODULE_DEPEND(hn, vmbus, 1, 1, 1);
1011253869SgrehanSYSINIT(netvsc_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, netvsc_init,
1012250199Sgrehan     NULL);
1013250199Sgrehan
1014