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