xref: /freebsd-src/sys/dev/vnic/nicvf_main.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
13c0086b8SZbigniew Bodek /*
23c0086b8SZbigniew Bodek  * Copyright (C) 2015 Cavium Inc.
33c0086b8SZbigniew Bodek  * All rights reserved.
43c0086b8SZbigniew Bodek  *
53c0086b8SZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
63c0086b8SZbigniew Bodek  * modification, are permitted provided that the following conditions
73c0086b8SZbigniew Bodek  * are met:
83c0086b8SZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
93c0086b8SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
103c0086b8SZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
113c0086b8SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
123c0086b8SZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
133c0086b8SZbigniew Bodek  *
143c0086b8SZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
153c0086b8SZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
163c0086b8SZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173c0086b8SZbigniew Bodek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
183c0086b8SZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
193c0086b8SZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
203c0086b8SZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
213c0086b8SZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223c0086b8SZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233c0086b8SZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
243c0086b8SZbigniew Bodek  * SUCH DAMAGE.
253c0086b8SZbigniew Bodek  *
263c0086b8SZbigniew Bodek  */
272306b72aSZbigniew Bodek #include <sys/cdefs.h>
282306b72aSZbigniew Bodek #include "opt_inet.h"
292306b72aSZbigniew Bodek #include "opt_inet6.h"
303c0086b8SZbigniew Bodek 
312306b72aSZbigniew Bodek #include <sys/param.h>
322306b72aSZbigniew Bodek #include <sys/systm.h>
332306b72aSZbigniew Bodek #include <sys/bitset.h>
342306b72aSZbigniew Bodek #include <sys/bitstring.h>
352306b72aSZbigniew Bodek #include <sys/bus.h>
362306b72aSZbigniew Bodek #include <sys/endian.h>
372306b72aSZbigniew Bodek #include <sys/kernel.h>
382306b72aSZbigniew Bodek #include <sys/malloc.h>
392306b72aSZbigniew Bodek #include <sys/mbuf.h>
402306b72aSZbigniew Bodek #include <sys/module.h>
412306b72aSZbigniew Bodek #include <sys/rman.h>
422306b72aSZbigniew Bodek #include <sys/pciio.h>
432306b72aSZbigniew Bodek #include <sys/pcpu.h>
442306b72aSZbigniew Bodek #include <sys/proc.h>
452306b72aSZbigniew Bodek #include <sys/socket.h>
462306b72aSZbigniew Bodek #include <sys/sockio.h>
472306b72aSZbigniew Bodek #include <sys/stdatomic.h>
482306b72aSZbigniew Bodek #include <sys/cpuset.h>
492306b72aSZbigniew Bodek #include <sys/lock.h>
502306b72aSZbigniew Bodek #include <sys/mutex.h>
512306b72aSZbigniew Bodek #include <sys/smp.h>
522306b72aSZbigniew Bodek #include <sys/taskqueue.h>
532306b72aSZbigniew Bodek 
542306b72aSZbigniew Bodek #include <net/bpf.h>
552306b72aSZbigniew Bodek #include <net/ethernet.h>
562306b72aSZbigniew Bodek #include <net/if.h>
572306b72aSZbigniew Bodek #include <net/if_var.h>
582306b72aSZbigniew Bodek #include <net/if_arp.h>
592306b72aSZbigniew Bodek #include <net/if_dl.h>
602306b72aSZbigniew Bodek #include <net/if_media.h>
612306b72aSZbigniew Bodek #include <net/if_types.h>
622306b72aSZbigniew Bodek #include <net/if_vlan_var.h>
632306b72aSZbigniew Bodek 
642306b72aSZbigniew Bodek #include <netinet/in.h>
65af8fe8f1SZbigniew Bodek #include <netinet/ip.h>
662306b72aSZbigniew Bodek #include <netinet/if_ether.h>
67053f3d0eSZbigniew Bodek #include <netinet/tcp_lro.h>
682306b72aSZbigniew Bodek 
692306b72aSZbigniew Bodek #include <dev/pci/pcireg.h>
702306b72aSZbigniew Bodek #include <dev/pci/pcivar.h>
712306b72aSZbigniew Bodek 
722306b72aSZbigniew Bodek #include <sys/dnv.h>
732306b72aSZbigniew Bodek #include <sys/nv.h>
742306b72aSZbigniew Bodek #include <sys/iov_schema.h>
752306b72aSZbigniew Bodek 
762306b72aSZbigniew Bodek #include <machine/bus.h>
772306b72aSZbigniew Bodek 
782306b72aSZbigniew Bodek #include "thunder_bgx.h"
793c0086b8SZbigniew Bodek #include "nic_reg.h"
803c0086b8SZbigniew Bodek #include "nic.h"
813c0086b8SZbigniew Bodek #include "nicvf_queues.h"
823c0086b8SZbigniew Bodek 
832306b72aSZbigniew Bodek #define	VNIC_VF_DEVSTR		"Cavium Thunder NIC Virtual Function Driver"
843c0086b8SZbigniew Bodek 
852306b72aSZbigniew Bodek #define	VNIC_VF_REG_RID		PCIR_BAR(PCI_CFG_REG_BAR_NUM)
862306b72aSZbigniew Bodek 
872306b72aSZbigniew Bodek /* Lock for core interface settings */
882306b72aSZbigniew Bodek #define	NICVF_CORE_LOCK_INIT(nic)				\
892306b72aSZbigniew Bodek     sx_init(&(nic)->core_sx, device_get_nameunit((nic)->dev))
902306b72aSZbigniew Bodek 
912306b72aSZbigniew Bodek #define	NICVF_CORE_LOCK_DESTROY(nic)				\
922306b72aSZbigniew Bodek     sx_destroy(&(nic)->core_sx)
932306b72aSZbigniew Bodek 
942306b72aSZbigniew Bodek #define	NICVF_CORE_LOCK(nic)		sx_xlock(&(nic)->core_sx)
952306b72aSZbigniew Bodek #define	NICVF_CORE_UNLOCK(nic)		sx_xunlock(&(nic)->core_sx)
962306b72aSZbigniew Bodek 
972306b72aSZbigniew Bodek #define	NICVF_CORE_LOCK_ASSERT(nic)	sx_assert(&(nic)->core_sx, SA_XLOCKED)
982306b72aSZbigniew Bodek 
992306b72aSZbigniew Bodek #define	SPEED_10	10
1002306b72aSZbigniew Bodek #define	SPEED_100	100
1012306b72aSZbigniew Bodek #define	SPEED_1000	1000
1022306b72aSZbigniew Bodek #define	SPEED_10000	10000
1032306b72aSZbigniew Bodek #define	SPEED_40000	40000
1042306b72aSZbigniew Bodek 
1052306b72aSZbigniew Bodek MALLOC_DEFINE(M_NICVF, "nicvf", "ThunderX VNIC VF dynamic memory");
1062306b72aSZbigniew Bodek 
1072306b72aSZbigniew Bodek static int nicvf_probe(device_t);
1082306b72aSZbigniew Bodek static int nicvf_attach(device_t);
1092306b72aSZbigniew Bodek static int nicvf_detach(device_t);
1102306b72aSZbigniew Bodek 
1112306b72aSZbigniew Bodek static device_method_t nicvf_methods[] = {
1122306b72aSZbigniew Bodek 	/* Device interface */
1132306b72aSZbigniew Bodek 	DEVMETHOD(device_probe,		nicvf_probe),
1142306b72aSZbigniew Bodek 	DEVMETHOD(device_attach,	nicvf_attach),
1152306b72aSZbigniew Bodek 	DEVMETHOD(device_detach,	nicvf_detach),
1162306b72aSZbigniew Bodek 
1172306b72aSZbigniew Bodek 	DEVMETHOD_END,
1183c0086b8SZbigniew Bodek };
1193c0086b8SZbigniew Bodek 
1202306b72aSZbigniew Bodek static driver_t nicvf_driver = {
1212306b72aSZbigniew Bodek 	"vnic",
1222306b72aSZbigniew Bodek 	nicvf_methods,
1232306b72aSZbigniew Bodek 	sizeof(struct nicvf),
1242306b72aSZbigniew Bodek };
1253c0086b8SZbigniew Bodek 
12694412ad7SJohn Baldwin DRIVER_MODULE(vnicvf, pci, nicvf_driver, 0, 0);
127f4aafb9eSWojciech Macek MODULE_VERSION(vnicvf, 1);
128f4aafb9eSWojciech Macek MODULE_DEPEND(vnicvf, pci, 1, 1, 1);
129f4aafb9eSWojciech Macek MODULE_DEPEND(vnicvf, ether, 1, 1, 1);
130f4aafb9eSWojciech Macek MODULE_DEPEND(vnicvf, vnicpf, 1, 1, 1);
1313c0086b8SZbigniew Bodek 
1322306b72aSZbigniew Bodek static int nicvf_allocate_misc_interrupt(struct nicvf *);
1332306b72aSZbigniew Bodek static int nicvf_enable_misc_interrupt(struct nicvf *);
1342306b72aSZbigniew Bodek static int nicvf_allocate_net_interrupts(struct nicvf *);
1352306b72aSZbigniew Bodek static void nicvf_release_all_interrupts(struct nicvf *);
1360ef477eaSZbigniew Bodek static int nicvf_update_hw_max_frs(struct nicvf *, int);
1372306b72aSZbigniew Bodek static int nicvf_hw_set_mac_addr(struct nicvf *, uint8_t *);
1382306b72aSZbigniew Bodek static void nicvf_config_cpi(struct nicvf *);
1398191a879SZbigniew Bodek static int nicvf_rss_init(struct nicvf *);
1402306b72aSZbigniew Bodek static int nicvf_init_resources(struct nicvf *);
1412306b72aSZbigniew Bodek 
142*aa386085SZhenlei Huang static void nicvf_setup_ifnet(struct nicvf *);
1432306b72aSZbigniew Bodek static int nicvf_setup_ifmedia(struct nicvf *);
1442306b72aSZbigniew Bodek static void nicvf_hw_addr_random(uint8_t *);
1452306b72aSZbigniew Bodek 
146b9545c57SJustin Hibbits static int nicvf_if_ioctl(if_t, u_long, caddr_t);
1472306b72aSZbigniew Bodek static void nicvf_if_init(void *);
1482306b72aSZbigniew Bodek static void nicvf_if_init_locked(struct nicvf *);
149b9545c57SJustin Hibbits static int nicvf_if_transmit(if_t, struct mbuf *);
150b9545c57SJustin Hibbits static void nicvf_if_qflush(if_t);
151b9545c57SJustin Hibbits static uint64_t nicvf_if_getcounter(if_t, ift_counter);
1522306b72aSZbigniew Bodek 
1532306b72aSZbigniew Bodek static int nicvf_stop_locked(struct nicvf *);
1542306b72aSZbigniew Bodek 
155b9545c57SJustin Hibbits static void nicvf_media_status(if_t, struct ifmediareq *);
156b9545c57SJustin Hibbits static int nicvf_media_change(if_t);
1572306b72aSZbigniew Bodek 
1582306b72aSZbigniew Bodek static void nicvf_tick_stats(void *);
1592306b72aSZbigniew Bodek 
1602306b72aSZbigniew Bodek static int
nicvf_probe(device_t dev)1612306b72aSZbigniew Bodek nicvf_probe(device_t dev)
1623c0086b8SZbigniew Bodek {
1632306b72aSZbigniew Bodek 	uint16_t vendor_id;
1642306b72aSZbigniew Bodek 	uint16_t device_id;
1652306b72aSZbigniew Bodek 
1662306b72aSZbigniew Bodek 	vendor_id = pci_get_vendor(dev);
1672306b72aSZbigniew Bodek 	device_id = pci_get_device(dev);
1682306b72aSZbigniew Bodek 
1692306b72aSZbigniew Bodek 	if (vendor_id != PCI_VENDOR_ID_CAVIUM)
1702306b72aSZbigniew Bodek 		return (ENXIO);
1712306b72aSZbigniew Bodek 
1722306b72aSZbigniew Bodek 	if (device_id == PCI_DEVICE_ID_THUNDER_NIC_VF ||
1732306b72aSZbigniew Bodek 	    device_id == PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF) {
1742306b72aSZbigniew Bodek 		device_set_desc(dev, VNIC_VF_DEVSTR);
1752306b72aSZbigniew Bodek 		return (BUS_PROBE_DEFAULT);
1763c0086b8SZbigniew Bodek 	}
1773c0086b8SZbigniew Bodek 
1782306b72aSZbigniew Bodek 	return (ENXIO);
1792306b72aSZbigniew Bodek }
1802306b72aSZbigniew Bodek 
1812306b72aSZbigniew Bodek static int
nicvf_attach(device_t dev)1822306b72aSZbigniew Bodek nicvf_attach(device_t dev)
1833c0086b8SZbigniew Bodek {
1842306b72aSZbigniew Bodek 	struct nicvf *nic;
1852306b72aSZbigniew Bodek 	int rid, qcount;
1863c0086b8SZbigniew Bodek 	int err = 0;
1872306b72aSZbigniew Bodek 	uint8_t hwaddr[ETHER_ADDR_LEN];
1882306b72aSZbigniew Bodek 	uint8_t zeromac[] = {[0 ... (ETHER_ADDR_LEN - 1)] = 0};
1893c0086b8SZbigniew Bodek 
1902306b72aSZbigniew Bodek 	nic = device_get_softc(dev);
1912306b72aSZbigniew Bodek 	nic->dev = dev;
1923c0086b8SZbigniew Bodek 	nic->pnicvf = nic;
1933c0086b8SZbigniew Bodek 
1942306b72aSZbigniew Bodek 	NICVF_CORE_LOCK_INIT(nic);
195af8fe8f1SZbigniew Bodek 	/* Enable HW TSO on Pass2 */
196af8fe8f1SZbigniew Bodek 	if (!pass1_silicon(dev))
197af8fe8f1SZbigniew Bodek 		nic->hw_tso = TRUE;
1982306b72aSZbigniew Bodek 
1992306b72aSZbigniew Bodek 	rid = VNIC_VF_REG_RID;
2002306b72aSZbigniew Bodek 	nic->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
2012306b72aSZbigniew Bodek 	    RF_ACTIVE);
2022306b72aSZbigniew Bodek 	if (nic->reg_base == NULL) {
2032306b72aSZbigniew Bodek 		device_printf(dev, "Could not allocate registers memory\n");
2042306b72aSZbigniew Bodek 		return (ENXIO);
2053c0086b8SZbigniew Bodek 	}
2063c0086b8SZbigniew Bodek 
2072306b72aSZbigniew Bodek 	qcount = MAX_CMP_QUEUES_PER_QS;
2082306b72aSZbigniew Bodek 	nic->max_queues = qcount;
2093c0086b8SZbigniew Bodek 
2102306b72aSZbigniew Bodek 	err = nicvf_set_qset_resources(nic);
2112306b72aSZbigniew Bodek 	if (err != 0)
2122306b72aSZbigniew Bodek 		goto err_free_res;
2133c0086b8SZbigniew Bodek 
2142306b72aSZbigniew Bodek 	/* Check if PF is alive and get MAC address for this VF */
2152306b72aSZbigniew Bodek 	err = nicvf_allocate_misc_interrupt(nic);
2162306b72aSZbigniew Bodek 	if (err != 0)
2172306b72aSZbigniew Bodek 		goto err_free_res;
2183c0086b8SZbigniew Bodek 
2192306b72aSZbigniew Bodek 	NICVF_CORE_LOCK(nic);
2202306b72aSZbigniew Bodek 	err = nicvf_enable_misc_interrupt(nic);
2212306b72aSZbigniew Bodek 	NICVF_CORE_UNLOCK(nic);
2222306b72aSZbigniew Bodek 	if (err != 0)
2232306b72aSZbigniew Bodek 		goto err_release_intr;
2243c0086b8SZbigniew Bodek 
2252306b72aSZbigniew Bodek 	err = nicvf_allocate_net_interrupts(nic);
2262306b72aSZbigniew Bodek 	if (err != 0) {
2272306b72aSZbigniew Bodek 		device_printf(dev,
2282306b72aSZbigniew Bodek 		    "Could not allocate network interface interrupts\n");
2292306b72aSZbigniew Bodek 		goto err_free_ifnet;
2303c0086b8SZbigniew Bodek 	}
2313c0086b8SZbigniew Bodek 
2322306b72aSZbigniew Bodek 	/* If no MAC address was obtained we generate random one */
2332306b72aSZbigniew Bodek 	if (memcmp(nic->hwaddr, zeromac, ETHER_ADDR_LEN) == 0) {
2342306b72aSZbigniew Bodek 		nicvf_hw_addr_random(hwaddr);
2352306b72aSZbigniew Bodek 		memcpy(nic->hwaddr, hwaddr, ETHER_ADDR_LEN);
2362306b72aSZbigniew Bodek 		NICVF_CORE_LOCK(nic);
2372306b72aSZbigniew Bodek 		nicvf_hw_set_mac_addr(nic, hwaddr);
2382306b72aSZbigniew Bodek 		NICVF_CORE_UNLOCK(nic);
2393c0086b8SZbigniew Bodek 	}
2403c0086b8SZbigniew Bodek 
2413c0086b8SZbigniew Bodek 	/* Configure CPI alorithm */
2422306b72aSZbigniew Bodek 	nic->cpi_alg = CPI_ALG_NONE;
2432306b72aSZbigniew Bodek 	NICVF_CORE_LOCK(nic);
2443c0086b8SZbigniew Bodek 	nicvf_config_cpi(nic);
2458191a879SZbigniew Bodek 	/* Configure receive side scaling */
2468191a879SZbigniew Bodek 	if (nic->qs->rq_cnt > 1)
2478191a879SZbigniew Bodek 		nicvf_rss_init(nic);
2482306b72aSZbigniew Bodek 	NICVF_CORE_UNLOCK(nic);
2493c0086b8SZbigniew Bodek 
250*aa386085SZhenlei Huang 	nicvf_setup_ifnet(nic);
2512306b72aSZbigniew Bodek 
2522306b72aSZbigniew Bodek 	err = nicvf_setup_ifmedia(nic);
2532306b72aSZbigniew Bodek 	if (err != 0) {
2542306b72aSZbigniew Bodek 		device_printf(dev, "Could not set-up ifmedia\n");
2552306b72aSZbigniew Bodek 		goto err_free_ifnet;
2562306b72aSZbigniew Bodek 	}
2572306b72aSZbigniew Bodek 
2582306b72aSZbigniew Bodek 	mtx_init(&nic->stats_mtx, "VNIC stats", NULL, MTX_DEF);
2592306b72aSZbigniew Bodek 	callout_init_mtx(&nic->stats_callout, &nic->stats_mtx, 0);
2602306b72aSZbigniew Bodek 
2612306b72aSZbigniew Bodek 	ether_ifattach(nic->ifp, nic->hwaddr);
2622306b72aSZbigniew Bodek 
2632306b72aSZbigniew Bodek 	return (0);
2642306b72aSZbigniew Bodek 
2652306b72aSZbigniew Bodek err_free_ifnet:
2662306b72aSZbigniew Bodek 	if_free(nic->ifp);
2672306b72aSZbigniew Bodek err_release_intr:
2682306b72aSZbigniew Bodek 	nicvf_release_all_interrupts(nic);
2692306b72aSZbigniew Bodek err_free_res:
2702306b72aSZbigniew Bodek 	bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(nic->reg_base),
2712306b72aSZbigniew Bodek 	    nic->reg_base);
2722306b72aSZbigniew Bodek 
2732306b72aSZbigniew Bodek 	return (err);
2742306b72aSZbigniew Bodek }
2752306b72aSZbigniew Bodek 
2762306b72aSZbigniew Bodek static int
nicvf_detach(device_t dev)2772306b72aSZbigniew Bodek nicvf_detach(device_t dev)
2782306b72aSZbigniew Bodek {
2792306b72aSZbigniew Bodek 	struct nicvf *nic;
2802306b72aSZbigniew Bodek 
2812306b72aSZbigniew Bodek 	nic = device_get_softc(dev);
2822306b72aSZbigniew Bodek 
2832306b72aSZbigniew Bodek 	NICVF_CORE_LOCK(nic);
2842306b72aSZbigniew Bodek 	/* Shut down the port and release ring resources */
2852306b72aSZbigniew Bodek 	nicvf_stop_locked(nic);
2862306b72aSZbigniew Bodek 	/* Release stats lock */
2872306b72aSZbigniew Bodek 	mtx_destroy(&nic->stats_mtx);
2882306b72aSZbigniew Bodek 	/* Release interrupts */
2892306b72aSZbigniew Bodek 	nicvf_release_all_interrupts(nic);
2902306b72aSZbigniew Bodek 	/* Release memory resource */
2912306b72aSZbigniew Bodek 	if (nic->reg_base != NULL) {
2922306b72aSZbigniew Bodek 		bus_release_resource(dev, SYS_RES_MEMORY,
2932306b72aSZbigniew Bodek 		    rman_get_rid(nic->reg_base), nic->reg_base);
2942306b72aSZbigniew Bodek 	}
2952306b72aSZbigniew Bodek 
2962306b72aSZbigniew Bodek 	/* Remove all ifmedia configurations */
2972306b72aSZbigniew Bodek 	ifmedia_removeall(&nic->if_media);
2982306b72aSZbigniew Bodek 	/* Free this ifnet */
2992306b72aSZbigniew Bodek 	if_free(nic->ifp);
3002306b72aSZbigniew Bodek 	NICVF_CORE_UNLOCK(nic);
3012306b72aSZbigniew Bodek 	/* Finally destroy the lock */
3022306b72aSZbigniew Bodek 	NICVF_CORE_LOCK_DESTROY(nic);
3032306b72aSZbigniew Bodek 
3042306b72aSZbigniew Bodek 	return (0);
3052306b72aSZbigniew Bodek }
3062306b72aSZbigniew Bodek 
3072306b72aSZbigniew Bodek static void
nicvf_hw_addr_random(uint8_t * hwaddr)3082306b72aSZbigniew Bodek nicvf_hw_addr_random(uint8_t *hwaddr)
3092306b72aSZbigniew Bodek {
3102306b72aSZbigniew Bodek 	uint32_t rnd;
3112306b72aSZbigniew Bodek 	uint8_t addr[ETHER_ADDR_LEN];
3122306b72aSZbigniew Bodek 
3132306b72aSZbigniew Bodek 	/*
3142306b72aSZbigniew Bodek 	 * Create randomized MAC address.
3152306b72aSZbigniew Bodek 	 * Set 'bsd' + random 24 low-order bits.
3162306b72aSZbigniew Bodek 	 */
3172306b72aSZbigniew Bodek 	rnd = arc4random() & 0x00ffffff;
3182306b72aSZbigniew Bodek 	addr[0] = 'b';
3192306b72aSZbigniew Bodek 	addr[1] = 's';
3202306b72aSZbigniew Bodek 	addr[2] = 'd';
3212306b72aSZbigniew Bodek 	addr[3] = rnd >> 16;
3222306b72aSZbigniew Bodek 	addr[4] = rnd >> 8;
3232306b72aSZbigniew Bodek 	addr[5] = rnd >> 0;
3242306b72aSZbigniew Bodek 
3252306b72aSZbigniew Bodek 	memcpy(hwaddr, addr, ETHER_ADDR_LEN);
3262306b72aSZbigniew Bodek }
3272306b72aSZbigniew Bodek 
328*aa386085SZhenlei Huang static void
nicvf_setup_ifnet(struct nicvf * nic)3292306b72aSZbigniew Bodek nicvf_setup_ifnet(struct nicvf *nic)
3302306b72aSZbigniew Bodek {
331b9545c57SJustin Hibbits 	if_t ifp;
3322306b72aSZbigniew Bodek 
3332306b72aSZbigniew Bodek 	ifp = if_alloc(IFT_ETHER);
3342306b72aSZbigniew Bodek 	nic->ifp = ifp;
3352306b72aSZbigniew Bodek 
3362306b72aSZbigniew Bodek 	if_setsoftc(ifp, nic);
3372306b72aSZbigniew Bodek 	if_initname(ifp, device_get_name(nic->dev), device_get_unit(nic->dev));
3383bfef74aSEd Maste 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
3392306b72aSZbigniew Bodek 
3402306b72aSZbigniew Bodek 	if_settransmitfn(ifp, nicvf_if_transmit);
3412306b72aSZbigniew Bodek 	if_setqflushfn(ifp, nicvf_if_qflush);
3422306b72aSZbigniew Bodek 	if_setioctlfn(ifp, nicvf_if_ioctl);
3432306b72aSZbigniew Bodek 	if_setinitfn(ifp, nicvf_if_init);
3442306b72aSZbigniew Bodek 	if_setgetcounterfn(ifp, nicvf_if_getcounter);
3452306b72aSZbigniew Bodek 
3462306b72aSZbigniew Bodek 	if_setmtu(ifp, ETHERMTU);
3472306b72aSZbigniew Bodek 
3486dc23459SZbigniew Bodek 	/* Reset caps */
3496dc23459SZbigniew Bodek 	if_setcapabilities(ifp, 0);
3506dc23459SZbigniew Bodek 
3516dc23459SZbigniew Bodek 	/* Set the default values */
3520ef477eaSZbigniew Bodek 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU, 0);
353053f3d0eSZbigniew Bodek 	if_setcapabilitiesbit(ifp, IFCAP_LRO, 0);
354af8fe8f1SZbigniew Bodek 	if (nic->hw_tso) {
355af8fe8f1SZbigniew Bodek 		/* TSO */
356af8fe8f1SZbigniew Bodek 		if_setcapabilitiesbit(ifp, IFCAP_TSO4, 0);
357af8fe8f1SZbigniew Bodek 		/* TSO parameters */
358d0b2cad1SStephen J. Kiernan 		if_sethwtsomax(ifp, NICVF_TSO_MAXSIZE);
359d0b2cad1SStephen J. Kiernan 		if_sethwtsomaxsegcount(ifp, NICVF_TSO_NSEGS);
360d0b2cad1SStephen J. Kiernan 		if_sethwtsomaxsegsize(ifp, MCLBYTES);
361af8fe8f1SZbigniew Bodek 	}
362856dce91SZbigniew Bodek 	/* IP/TCP/UDP HW checksums */
363856dce91SZbigniew Bodek 	if_setcapabilitiesbit(ifp, IFCAP_HWCSUM, 0);
36444f2eb96SZbigniew Bodek 	if_setcapabilitiesbit(ifp, IFCAP_HWSTATS, 0);
3656dc23459SZbigniew Bodek 	/*
3666dc23459SZbigniew Bodek 	 * HW offload enable
3676dc23459SZbigniew Bodek 	 */
3686dc23459SZbigniew Bodek 	if_clearhwassist(ifp);
3696dc23459SZbigniew Bodek 	if_sethwassistbits(ifp, (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP), 0);
370af8fe8f1SZbigniew Bodek 	if (nic->hw_tso)
371af8fe8f1SZbigniew Bodek 		if_sethwassistbits(ifp, (CSUM_TSO), 0);
3722306b72aSZbigniew Bodek 	if_setcapenable(ifp, if_getcapabilities(ifp));
3732306b72aSZbigniew Bodek }
3742306b72aSZbigniew Bodek 
3752306b72aSZbigniew Bodek static int
nicvf_setup_ifmedia(struct nicvf * nic)3762306b72aSZbigniew Bodek nicvf_setup_ifmedia(struct nicvf *nic)
3772306b72aSZbigniew Bodek {
3782306b72aSZbigniew Bodek 
3792306b72aSZbigniew Bodek 	ifmedia_init(&nic->if_media, IFM_IMASK, nicvf_media_change,
3802306b72aSZbigniew Bodek 	    nicvf_media_status);
3812306b72aSZbigniew Bodek 
3822306b72aSZbigniew Bodek 	/*
3832306b72aSZbigniew Bodek 	 * Advertise availability of all possible connection types,
3842306b72aSZbigniew Bodek 	 * even though not all are possible at the same time.
3852306b72aSZbigniew Bodek 	 */
3862306b72aSZbigniew Bodek 
3872306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_10_T | IFM_FDX),
3882306b72aSZbigniew Bodek 	    0, NULL);
3892306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_100_TX | IFM_FDX),
3902306b72aSZbigniew Bodek 	    0, NULL);
3912306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_1000_T | IFM_FDX),
3922306b72aSZbigniew Bodek 	    0, NULL);
3932306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_10G_SR | IFM_FDX),
3942306b72aSZbigniew Bodek 	    0, NULL);
3952306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_40G_CR4 | IFM_FDX),
3962306b72aSZbigniew Bodek 	    0, NULL);
3972306b72aSZbigniew Bodek 	ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_AUTO | IFM_FDX),
3982306b72aSZbigniew Bodek 	    0, NULL);
3992306b72aSZbigniew Bodek 
4002306b72aSZbigniew Bodek 	ifmedia_set(&nic->if_media, (IFM_ETHER | IFM_AUTO | IFM_FDX));
4012306b72aSZbigniew Bodek 
4022306b72aSZbigniew Bodek 	return (0);
4032306b72aSZbigniew Bodek }
4042306b72aSZbigniew Bodek 
4052306b72aSZbigniew Bodek static int
nicvf_if_ioctl(if_t ifp,u_long cmd,caddr_t data)406b9545c57SJustin Hibbits nicvf_if_ioctl(if_t ifp, u_long cmd, caddr_t data)
4072306b72aSZbigniew Bodek {
4082306b72aSZbigniew Bodek 	struct nicvf *nic;
409053f3d0eSZbigniew Bodek 	struct rcv_queue *rq;
4102306b72aSZbigniew Bodek 	struct ifreq *ifr;
41173f4b4ebSEd Maste 	uint32_t flags;
4122306b72aSZbigniew Bodek 	int mask, err;
413053f3d0eSZbigniew Bodek 	int rq_idx;
4142306b72aSZbigniew Bodek #if defined(INET) || defined(INET6)
4152306b72aSZbigniew Bodek 	struct ifaddr *ifa;
4162306b72aSZbigniew Bodek 	boolean_t avoid_reset = FALSE;
4173c0086b8SZbigniew Bodek #endif
4183c0086b8SZbigniew Bodek 
4192306b72aSZbigniew Bodek 	nic = if_getsoftc(ifp);
4202306b72aSZbigniew Bodek 	ifr = (struct ifreq *)data;
4212306b72aSZbigniew Bodek #if defined(INET) || defined(INET6)
4222306b72aSZbigniew Bodek 	ifa = (struct ifaddr *)data;
4232306b72aSZbigniew Bodek #endif
4242306b72aSZbigniew Bodek 	err = 0;
4252306b72aSZbigniew Bodek 	switch (cmd) {
4262306b72aSZbigniew Bodek 	case SIOCSIFADDR:
4272306b72aSZbigniew Bodek #ifdef INET
4282306b72aSZbigniew Bodek 		if (ifa->ifa_addr->sa_family == AF_INET)
4292306b72aSZbigniew Bodek 			avoid_reset = TRUE;
4302306b72aSZbigniew Bodek #endif
4312306b72aSZbigniew Bodek #ifdef INET6
4322306b72aSZbigniew Bodek 		if (ifa->ifa_addr->sa_family == AF_INET6)
4332306b72aSZbigniew Bodek 			avoid_reset = TRUE;
4343c0086b8SZbigniew Bodek #endif
4353c0086b8SZbigniew Bodek 
4362306b72aSZbigniew Bodek #if defined(INET) || defined(INET6)
4372306b72aSZbigniew Bodek 		/* Avoid reinitialization unless it's necessary */
4382306b72aSZbigniew Bodek 		if (avoid_reset) {
439d0b2cad1SStephen J. Kiernan 			if_setflagbits(ifp, IFF_UP, 0);
4402306b72aSZbigniew Bodek 			if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
4412306b72aSZbigniew Bodek 				nicvf_if_init(nic);
4422306b72aSZbigniew Bodek #ifdef INET
4432306b72aSZbigniew Bodek 			if (!(if_getflags(ifp) & IFF_NOARP))
4442306b72aSZbigniew Bodek 				arp_ifinit(ifp, ifa);
4452306b72aSZbigniew Bodek #endif
4462306b72aSZbigniew Bodek 
4472306b72aSZbigniew Bodek 			return (0);
4482306b72aSZbigniew Bodek 		}
4492306b72aSZbigniew Bodek #endif
4502306b72aSZbigniew Bodek 		err = ether_ioctl(ifp, cmd, data);
4512306b72aSZbigniew Bodek 		break;
4522306b72aSZbigniew Bodek 	case SIOCSIFMTU:
4530ef477eaSZbigniew Bodek 		if (ifr->ifr_mtu < NIC_HW_MIN_FRS ||
4540ef477eaSZbigniew Bodek 		    ifr->ifr_mtu > NIC_HW_MAX_FRS) {
4550ef477eaSZbigniew Bodek 			err = EINVAL;
4560ef477eaSZbigniew Bodek 		} else {
4570ef477eaSZbigniew Bodek 			NICVF_CORE_LOCK(nic);
4580ef477eaSZbigniew Bodek 			err = nicvf_update_hw_max_frs(nic, ifr->ifr_mtu);
4590ef477eaSZbigniew Bodek 			if (err == 0)
4600ef477eaSZbigniew Bodek 				if_setmtu(ifp, ifr->ifr_mtu);
4610ef477eaSZbigniew Bodek 			NICVF_CORE_UNLOCK(nic);
4620ef477eaSZbigniew Bodek 		}
4632306b72aSZbigniew Bodek 		break;
4642306b72aSZbigniew Bodek 	case SIOCSIFFLAGS:
4652306b72aSZbigniew Bodek 		NICVF_CORE_LOCK(nic);
46673f4b4ebSEd Maste 		flags = if_getflags(ifp);
46773f4b4ebSEd Maste 		if (flags & IFF_UP) {
4682306b72aSZbigniew Bodek 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
46973f4b4ebSEd Maste 				if ((flags ^ nic->if_flags) & IFF_PROMISC) {
4702306b72aSZbigniew Bodek 					/* Change promiscous mode */
471941650aaSEd Maste #if 0 /* XXX */
4722306b72aSZbigniew Bodek 					nicvf_set_promiscous(nic);
4732306b72aSZbigniew Bodek #endif
4742306b72aSZbigniew Bodek 				}
4752306b72aSZbigniew Bodek 
47673f4b4ebSEd Maste 				if ((flags ^ nic->if_flags) & IFF_ALLMULTI) {
4772306b72aSZbigniew Bodek 					/* Change multicasting settings */
478941650aaSEd Maste #if 0 /* XXX */
4792306b72aSZbigniew Bodek 					nicvf_set_multicast(nic);
4802306b72aSZbigniew Bodek #endif
4812306b72aSZbigniew Bodek 				}
4822306b72aSZbigniew Bodek 			} else {
4832306b72aSZbigniew Bodek 				nicvf_if_init_locked(nic);
4842306b72aSZbigniew Bodek 			}
4852306b72aSZbigniew Bodek 		} else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
4862306b72aSZbigniew Bodek 			nicvf_stop_locked(nic);
4872306b72aSZbigniew Bodek 
48873f4b4ebSEd Maste 		nic->if_flags = flags;
4892306b72aSZbigniew Bodek 		NICVF_CORE_UNLOCK(nic);
4902306b72aSZbigniew Bodek 		break;
4912306b72aSZbigniew Bodek 
4922306b72aSZbigniew Bodek 	case SIOCADDMULTI:
4932306b72aSZbigniew Bodek 	case SIOCDELMULTI:
4942306b72aSZbigniew Bodek 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
4952306b72aSZbigniew Bodek #if 0
4962306b72aSZbigniew Bodek 			NICVF_CORE_LOCK(nic);
4972306b72aSZbigniew Bodek 			/* ARM64TODO */
4982306b72aSZbigniew Bodek 			nicvf_set_multicast(nic);
4992306b72aSZbigniew Bodek 			NICVF_CORE_UNLOCK(nic);
5002306b72aSZbigniew Bodek #endif
5012306b72aSZbigniew Bodek 		}
5022306b72aSZbigniew Bodek 		break;
5032306b72aSZbigniew Bodek 
5042306b72aSZbigniew Bodek 	case SIOCSIFMEDIA:
5052306b72aSZbigniew Bodek 	case SIOCGIFMEDIA:
5062306b72aSZbigniew Bodek 		err = ifmedia_ioctl(ifp, ifr, &nic->if_media, cmd);
5072306b72aSZbigniew Bodek 		break;
5082306b72aSZbigniew Bodek 
5092306b72aSZbigniew Bodek 	case SIOCSIFCAP:
510d0b2cad1SStephen J. Kiernan 		mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap;
5112306b72aSZbigniew Bodek 		if (mask & IFCAP_VLAN_MTU) {
5122306b72aSZbigniew Bodek 			/* No work to do except acknowledge the change took. */
513d0b2cad1SStephen J. Kiernan 			if_togglecapenable(ifp, IFCAP_VLAN_MTU);
5142306b72aSZbigniew Bodek 		}
515856dce91SZbigniew Bodek 		if (mask & IFCAP_TXCSUM)
516d0b2cad1SStephen J. Kiernan 			if_togglecapenable(ifp, IFCAP_TXCSUM);
517856dce91SZbigniew Bodek 		if (mask & IFCAP_RXCSUM)
518d0b2cad1SStephen J. Kiernan 			if_togglecapenable(ifp, IFCAP_RXCSUM);
519af8fe8f1SZbigniew Bodek 		if ((mask & IFCAP_TSO4) && nic->hw_tso)
520d0b2cad1SStephen J. Kiernan 			if_togglecapenable(ifp, IFCAP_TSO4);
521053f3d0eSZbigniew Bodek 		if (mask & IFCAP_LRO) {
522053f3d0eSZbigniew Bodek 			/*
523053f3d0eSZbigniew Bodek 			 * Lock the driver for a moment to avoid
524053f3d0eSZbigniew Bodek 			 * mismatch in per-queue settings.
525053f3d0eSZbigniew Bodek 			 */
526053f3d0eSZbigniew Bodek 			NICVF_CORE_LOCK(nic);
527d0b2cad1SStephen J. Kiernan 			if_togglecapenable(ifp, IFCAP_LRO);
528053f3d0eSZbigniew Bodek 			if ((if_getdrvflags(nic->ifp) & IFF_DRV_RUNNING) != 0) {
529053f3d0eSZbigniew Bodek 				/*
530053f3d0eSZbigniew Bodek 				 * Now disable LRO for subsequent packets.
531053f3d0eSZbigniew Bodek 				 * Atomicity of this change is not necessary
532053f3d0eSZbigniew Bodek 				 * as we don't need precise toggle of this
533053f3d0eSZbigniew Bodek 				 * feature for all threads processing the
534053f3d0eSZbigniew Bodek 				 * completion queue.
535053f3d0eSZbigniew Bodek 				 */
536053f3d0eSZbigniew Bodek 				for (rq_idx = 0;
537053f3d0eSZbigniew Bodek 				    rq_idx < nic->qs->rq_cnt; rq_idx++) {
538053f3d0eSZbigniew Bodek 					rq = &nic->qs->rq[rq_idx];
539053f3d0eSZbigniew Bodek 					rq->lro_enabled = !rq->lro_enabled;
540053f3d0eSZbigniew Bodek 				}
541053f3d0eSZbigniew Bodek 			}
542053f3d0eSZbigniew Bodek 			NICVF_CORE_UNLOCK(nic);
543053f3d0eSZbigniew Bodek 		}
544053f3d0eSZbigniew Bodek 
5452306b72aSZbigniew Bodek 		break;
5462306b72aSZbigniew Bodek 
5472306b72aSZbigniew Bodek 	default:
5482306b72aSZbigniew Bodek 		err = ether_ioctl(ifp, cmd, data);
5492306b72aSZbigniew Bodek 		break;
5502306b72aSZbigniew Bodek 	}
5512306b72aSZbigniew Bodek 
5522306b72aSZbigniew Bodek 	return (err);
5532306b72aSZbigniew Bodek }
5542306b72aSZbigniew Bodek 
5552306b72aSZbigniew Bodek static void
nicvf_if_init_locked(struct nicvf * nic)5562306b72aSZbigniew Bodek nicvf_if_init_locked(struct nicvf *nic)
5572306b72aSZbigniew Bodek {
5582306b72aSZbigniew Bodek 	struct queue_set *qs = nic->qs;
559b9545c57SJustin Hibbits 	if_t ifp;
5602306b72aSZbigniew Bodek 	int qidx;
5612306b72aSZbigniew Bodek 	int err;
5622306b72aSZbigniew Bodek 	caddr_t if_addr;
5632306b72aSZbigniew Bodek 
5642306b72aSZbigniew Bodek 	NICVF_CORE_LOCK_ASSERT(nic);
5652306b72aSZbigniew Bodek 	ifp = nic->ifp;
5662306b72aSZbigniew Bodek 
5672306b72aSZbigniew Bodek 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
5682306b72aSZbigniew Bodek 		nicvf_stop_locked(nic);
5692306b72aSZbigniew Bodek 
5702306b72aSZbigniew Bodek 	err = nicvf_enable_misc_interrupt(nic);
5712306b72aSZbigniew Bodek 	if (err != 0) {
5722306b72aSZbigniew Bodek 		if_printf(ifp, "Could not reenable Mbox interrupt\n");
5732306b72aSZbigniew Bodek 		return;
5742306b72aSZbigniew Bodek 	}
5752306b72aSZbigniew Bodek 
5762306b72aSZbigniew Bodek 	/* Get the latest MAC address */
5772306b72aSZbigniew Bodek 	if_addr = if_getlladdr(ifp);
5782306b72aSZbigniew Bodek 	/* Update MAC address if changed */
5792306b72aSZbigniew Bodek 	if (memcmp(nic->hwaddr, if_addr, ETHER_ADDR_LEN) != 0) {
5802306b72aSZbigniew Bodek 		memcpy(nic->hwaddr, if_addr, ETHER_ADDR_LEN);
5812306b72aSZbigniew Bodek 		nicvf_hw_set_mac_addr(nic, if_addr);
5822306b72aSZbigniew Bodek 	}
5833c0086b8SZbigniew Bodek 
5843c0086b8SZbigniew Bodek 	/* Initialize the queues */
5853c0086b8SZbigniew Bodek 	err = nicvf_init_resources(nic);
5862306b72aSZbigniew Bodek 	if (err != 0)
5872306b72aSZbigniew Bodek 		goto error;
5883c0086b8SZbigniew Bodek 
5893c0086b8SZbigniew Bodek 	/* Make sure queue initialization is written */
5903c0086b8SZbigniew Bodek 	wmb();
5913c0086b8SZbigniew Bodek 
5922306b72aSZbigniew Bodek 	nicvf_reg_write(nic, NIC_VF_INT, ~0UL);
5933c0086b8SZbigniew Bodek 	/* Enable Qset err interrupt */
5943c0086b8SZbigniew Bodek 	nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
5953c0086b8SZbigniew Bodek 
5963c0086b8SZbigniew Bodek 	/* Enable completion queue interrupt */
5973c0086b8SZbigniew Bodek 	for (qidx = 0; qidx < qs->cq_cnt; qidx++)
5983c0086b8SZbigniew Bodek 		nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx);
5993c0086b8SZbigniew Bodek 
6003c0086b8SZbigniew Bodek 	/* Enable RBDR threshold interrupt */
6013c0086b8SZbigniew Bodek 	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
6023c0086b8SZbigniew Bodek 		nicvf_enable_intr(nic, NICVF_INTR_RBDR, qidx);
6033c0086b8SZbigniew Bodek 
6043c0086b8SZbigniew Bodek 	nic->drv_stats.txq_stop = 0;
6053c0086b8SZbigniew Bodek 	nic->drv_stats.txq_wake = 0;
6063c0086b8SZbigniew Bodek 
6072306b72aSZbigniew Bodek 	/* Activate network interface */
6082306b72aSZbigniew Bodek 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
6093c0086b8SZbigniew Bodek 
6102306b72aSZbigniew Bodek 	/* Schedule callout to update stats */
6112306b72aSZbigniew Bodek 	callout_reset(&nic->stats_callout, hz, nicvf_tick_stats, nic);
6122306b72aSZbigniew Bodek 
6132306b72aSZbigniew Bodek 	return;
6142306b72aSZbigniew Bodek 
6152306b72aSZbigniew Bodek error:
6162306b72aSZbigniew Bodek 	/* Something went very wrong. Disable this ifnet for good */
6172306b72aSZbigniew Bodek 	if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
6182306b72aSZbigniew Bodek }
6192306b72aSZbigniew Bodek 
6202306b72aSZbigniew Bodek static void
nicvf_if_init(void * if_softc)6212306b72aSZbigniew Bodek nicvf_if_init(void *if_softc)
6222306b72aSZbigniew Bodek {
6232306b72aSZbigniew Bodek 	struct nicvf *nic = if_softc;
6242306b72aSZbigniew Bodek 
6252306b72aSZbigniew Bodek 	NICVF_CORE_LOCK(nic);
6262306b72aSZbigniew Bodek 	nicvf_if_init_locked(nic);
6272306b72aSZbigniew Bodek 	NICVF_CORE_UNLOCK(nic);
6282306b72aSZbigniew Bodek }
6292306b72aSZbigniew Bodek 
6302306b72aSZbigniew Bodek static int
nicvf_if_transmit(if_t ifp,struct mbuf * mbuf)631b9545c57SJustin Hibbits nicvf_if_transmit(if_t ifp, struct mbuf *mbuf)
6322306b72aSZbigniew Bodek {
6332306b72aSZbigniew Bodek 	struct nicvf *nic = if_getsoftc(ifp);
6342306b72aSZbigniew Bodek 	struct queue_set *qs = nic->qs;
6352306b72aSZbigniew Bodek 	struct snd_queue *sq;
636856dce91SZbigniew Bodek 	struct mbuf *mtmp;
6372306b72aSZbigniew Bodek 	int qidx;
6382306b72aSZbigniew Bodek 	int err = 0;
6392306b72aSZbigniew Bodek 
6402306b72aSZbigniew Bodek 	if (__predict_false(qs == NULL)) {
6412306b72aSZbigniew Bodek 		panic("%s: missing queue set for %s", __func__,
6422306b72aSZbigniew Bodek 		    device_get_nameunit(nic->dev));
6432306b72aSZbigniew Bodek 	}
6442306b72aSZbigniew Bodek 
6452306b72aSZbigniew Bodek 	/* Select queue */
6462306b72aSZbigniew Bodek 	if (M_HASHTYPE_GET(mbuf) != M_HASHTYPE_NONE)
6472306b72aSZbigniew Bodek 		qidx = mbuf->m_pkthdr.flowid % qs->sq_cnt;
6482306b72aSZbigniew Bodek 	else
6492306b72aSZbigniew Bodek 		qidx = curcpu % qs->sq_cnt;
6502306b72aSZbigniew Bodek 
6512306b72aSZbigniew Bodek 	sq = &qs->sq[qidx];
6522306b72aSZbigniew Bodek 
653856dce91SZbigniew Bodek 	if (mbuf->m_next != NULL &&
654856dce91SZbigniew Bodek 	    (mbuf->m_pkthdr.csum_flags &
655856dce91SZbigniew Bodek 	    (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP)) != 0) {
656856dce91SZbigniew Bodek 		if (M_WRITABLE(mbuf) == 0) {
657856dce91SZbigniew Bodek 			mtmp = m_dup(mbuf, M_NOWAIT);
658856dce91SZbigniew Bodek 			m_freem(mbuf);
659856dce91SZbigniew Bodek 			if (mtmp == NULL)
660856dce91SZbigniew Bodek 				return (ENOBUFS);
661856dce91SZbigniew Bodek 			mbuf = mtmp;
662856dce91SZbigniew Bodek 		}
663856dce91SZbigniew Bodek 	}
664332c8697SZbigniew Bodek 
6652306b72aSZbigniew Bodek 	err = drbr_enqueue(ifp, sq->br, mbuf);
66636a947e9SZbigniew Bodek 	if (((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
66736a947e9SZbigniew Bodek 	    IFF_DRV_RUNNING) || !nic->link_up || (err != 0)) {
66836a947e9SZbigniew Bodek 		/*
66936a947e9SZbigniew Bodek 		 * Try to enqueue packet to the ring buffer.
67036a947e9SZbigniew Bodek 		 * If the driver is not active, link down or enqueue operation
67136a947e9SZbigniew Bodek 		 * failed, return with the appropriate error code.
67236a947e9SZbigniew Bodek 		 */
6732306b72aSZbigniew Bodek 		return (err);
67436a947e9SZbigniew Bodek 	}
6752306b72aSZbigniew Bodek 
6767c617aceSZbigniew Bodek 	if (NICVF_TX_TRYLOCK(sq) != 0) {
6777c617aceSZbigniew Bodek 		err = nicvf_xmit_locked(sq);
6787c617aceSZbigniew Bodek 		NICVF_TX_UNLOCK(sq);
6797c617aceSZbigniew Bodek 		return (err);
6807c617aceSZbigniew Bodek 	} else
6812306b72aSZbigniew Bodek 		taskqueue_enqueue(sq->snd_taskq, &sq->snd_task);
6822306b72aSZbigniew Bodek 
6832306b72aSZbigniew Bodek 	return (0);
6842306b72aSZbigniew Bodek }
6852306b72aSZbigniew Bodek 
6862306b72aSZbigniew Bodek static void
nicvf_if_qflush(if_t ifp)687b9545c57SJustin Hibbits nicvf_if_qflush(if_t ifp)
6882306b72aSZbigniew Bodek {
6892306b72aSZbigniew Bodek 	struct nicvf *nic;
6902306b72aSZbigniew Bodek 	struct queue_set *qs;
6912306b72aSZbigniew Bodek 	struct snd_queue *sq;
6922306b72aSZbigniew Bodek 	struct mbuf *mbuf;
6932306b72aSZbigniew Bodek 	size_t idx;
6942306b72aSZbigniew Bodek 
6952306b72aSZbigniew Bodek 	nic = if_getsoftc(ifp);
6962306b72aSZbigniew Bodek 	qs = nic->qs;
6972306b72aSZbigniew Bodek 
6982306b72aSZbigniew Bodek 	for (idx = 0; idx < qs->sq_cnt; idx++) {
6992306b72aSZbigniew Bodek 		sq = &qs->sq[idx];
7002306b72aSZbigniew Bodek 		NICVF_TX_LOCK(sq);
7012306b72aSZbigniew Bodek 		while ((mbuf = buf_ring_dequeue_sc(sq->br)) != NULL)
7022306b72aSZbigniew Bodek 			m_freem(mbuf);
7032306b72aSZbigniew Bodek 		NICVF_TX_UNLOCK(sq);
7042306b72aSZbigniew Bodek 	}
7052306b72aSZbigniew Bodek 	if_qflush(ifp);
7062306b72aSZbigniew Bodek }
7072306b72aSZbigniew Bodek 
7082306b72aSZbigniew Bodek static uint64_t
nicvf_if_getcounter(if_t ifp,ift_counter cnt)709b9545c57SJustin Hibbits nicvf_if_getcounter(if_t ifp, ift_counter cnt)
7102306b72aSZbigniew Bodek {
7112306b72aSZbigniew Bodek 	struct nicvf *nic;
7122306b72aSZbigniew Bodek 	struct nicvf_hw_stats *hw_stats;
7132306b72aSZbigniew Bodek 	struct nicvf_drv_stats *drv_stats;
7142306b72aSZbigniew Bodek 
7152306b72aSZbigniew Bodek 	nic = if_getsoftc(ifp);
7162306b72aSZbigniew Bodek 	hw_stats = &nic->hw_stats;
7172306b72aSZbigniew Bodek 	drv_stats = &nic->drv_stats;
7182306b72aSZbigniew Bodek 
7192306b72aSZbigniew Bodek 	switch (cnt) {
7202306b72aSZbigniew Bodek 	case IFCOUNTER_IPACKETS:
7212306b72aSZbigniew Bodek 		return (drv_stats->rx_frames_ok);
7222306b72aSZbigniew Bodek 	case IFCOUNTER_OPACKETS:
7232306b72aSZbigniew Bodek 		return (drv_stats->tx_frames_ok);
7242306b72aSZbigniew Bodek 	case IFCOUNTER_IBYTES:
7252306b72aSZbigniew Bodek 		return (hw_stats->rx_bytes);
7262306b72aSZbigniew Bodek 	case IFCOUNTER_OBYTES:
7272306b72aSZbigniew Bodek 		return (hw_stats->tx_bytes_ok);
7282306b72aSZbigniew Bodek 	case IFCOUNTER_IMCASTS:
7292306b72aSZbigniew Bodek 		return (hw_stats->rx_mcast_frames);
7302306b72aSZbigniew Bodek 	case IFCOUNTER_COLLISIONS:
7312306b72aSZbigniew Bodek 		return (0);
7322306b72aSZbigniew Bodek 	case IFCOUNTER_IQDROPS:
7332306b72aSZbigniew Bodek 		return (drv_stats->rx_drops);
7342306b72aSZbigniew Bodek 	case IFCOUNTER_OQDROPS:
7352306b72aSZbigniew Bodek 		return (drv_stats->tx_drops);
7362306b72aSZbigniew Bodek 	default:
7372306b72aSZbigniew Bodek 		return (if_get_counter_default(ifp, cnt));
7382306b72aSZbigniew Bodek 	}
7392306b72aSZbigniew Bodek 
7402306b72aSZbigniew Bodek }
7412306b72aSZbigniew Bodek 
7422306b72aSZbigniew Bodek static void
nicvf_media_status(if_t ifp,struct ifmediareq * ifmr)743b9545c57SJustin Hibbits nicvf_media_status(if_t ifp, struct ifmediareq *ifmr)
7442306b72aSZbigniew Bodek {
7452306b72aSZbigniew Bodek 	struct nicvf *nic = if_getsoftc(ifp);
7462306b72aSZbigniew Bodek 
7472306b72aSZbigniew Bodek 	NICVF_CORE_LOCK(nic);
7482306b72aSZbigniew Bodek 
7492306b72aSZbigniew Bodek 	ifmr->ifm_status = IFM_AVALID;
7502306b72aSZbigniew Bodek 	ifmr->ifm_active = IFM_ETHER;
7512306b72aSZbigniew Bodek 
7522306b72aSZbigniew Bodek 	if (nic->link_up) {
7532306b72aSZbigniew Bodek 		/* Device attached to working network */
7542306b72aSZbigniew Bodek 		ifmr->ifm_status |= IFM_ACTIVE;
7552306b72aSZbigniew Bodek 	}
7562306b72aSZbigniew Bodek 
7572306b72aSZbigniew Bodek 	switch (nic->speed) {
7582306b72aSZbigniew Bodek 	case SPEED_10:
7592306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_10_T;
7602306b72aSZbigniew Bodek 		break;
7612306b72aSZbigniew Bodek 	case SPEED_100:
7622306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_100_TX;
7632306b72aSZbigniew Bodek 		break;
7642306b72aSZbigniew Bodek 	case SPEED_1000:
7652306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_1000_T;
7662306b72aSZbigniew Bodek 		break;
7672306b72aSZbigniew Bodek 	case SPEED_10000:
7682306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_10G_SR;
7692306b72aSZbigniew Bodek 		break;
7702306b72aSZbigniew Bodek 	case SPEED_40000:
7712306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_40G_CR4;
7722306b72aSZbigniew Bodek 		break;
7732306b72aSZbigniew Bodek 	default:
7742306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_AUTO;
7752306b72aSZbigniew Bodek 		break;
7762306b72aSZbigniew Bodek 	}
7772306b72aSZbigniew Bodek 
7782306b72aSZbigniew Bodek 	if (nic->duplex)
7792306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_FDX;
7802306b72aSZbigniew Bodek 	else
7812306b72aSZbigniew Bodek 		ifmr->ifm_active |= IFM_HDX;
7822306b72aSZbigniew Bodek 
7832306b72aSZbigniew Bodek 	NICVF_CORE_UNLOCK(nic);
7842306b72aSZbigniew Bodek }
7852306b72aSZbigniew Bodek 
7862306b72aSZbigniew Bodek static int
nicvf_media_change(if_t ifp __unused)787b9545c57SJustin Hibbits nicvf_media_change(if_t ifp __unused)
7882306b72aSZbigniew Bodek {
7892306b72aSZbigniew Bodek 
7902306b72aSZbigniew Bodek 	return (0);
7912306b72aSZbigniew Bodek }
7922306b72aSZbigniew Bodek 
7932306b72aSZbigniew Bodek /* Register read/write APIs */
7942306b72aSZbigniew Bodek void
nicvf_reg_write(struct nicvf * nic,bus_space_handle_t offset,uint64_t val)7952306b72aSZbigniew Bodek nicvf_reg_write(struct nicvf *nic, bus_space_handle_t offset, uint64_t val)
7962306b72aSZbigniew Bodek {
7972306b72aSZbigniew Bodek 
7982306b72aSZbigniew Bodek 	bus_write_8(nic->reg_base, offset, val);
7992306b72aSZbigniew Bodek }
8002306b72aSZbigniew Bodek 
8012306b72aSZbigniew Bodek uint64_t
nicvf_reg_read(struct nicvf * nic,uint64_t offset)8022306b72aSZbigniew Bodek nicvf_reg_read(struct nicvf *nic, uint64_t offset)
8032306b72aSZbigniew Bodek {
8042306b72aSZbigniew Bodek 
8052306b72aSZbigniew Bodek 	return (bus_read_8(nic->reg_base, offset));
8062306b72aSZbigniew Bodek }
8072306b72aSZbigniew Bodek 
8082306b72aSZbigniew Bodek void
nicvf_queue_reg_write(struct nicvf * nic,bus_space_handle_t offset,uint64_t qidx,uint64_t val)8092306b72aSZbigniew Bodek nicvf_queue_reg_write(struct nicvf *nic, bus_space_handle_t offset,
8102306b72aSZbigniew Bodek     uint64_t qidx, uint64_t val)
8112306b72aSZbigniew Bodek {
8122306b72aSZbigniew Bodek 
8132306b72aSZbigniew Bodek 	bus_write_8(nic->reg_base, offset + (qidx << NIC_Q_NUM_SHIFT), val);
8142306b72aSZbigniew Bodek }
8152306b72aSZbigniew Bodek 
8162306b72aSZbigniew Bodek uint64_t
nicvf_queue_reg_read(struct nicvf * nic,bus_space_handle_t offset,uint64_t qidx)8172306b72aSZbigniew Bodek nicvf_queue_reg_read(struct nicvf *nic, bus_space_handle_t offset,
8182306b72aSZbigniew Bodek     uint64_t qidx)
8192306b72aSZbigniew Bodek {
8202306b72aSZbigniew Bodek 
8212306b72aSZbigniew Bodek 	return (bus_read_8(nic->reg_base, offset + (qidx << NIC_Q_NUM_SHIFT)));
8222306b72aSZbigniew Bodek }
8232306b72aSZbigniew Bodek 
8242306b72aSZbigniew Bodek /* VF -> PF mailbox communication */
8252306b72aSZbigniew Bodek static void
nicvf_write_to_mbx(struct nicvf * nic,union nic_mbx * mbx)8262306b72aSZbigniew Bodek nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx)
8272306b72aSZbigniew Bodek {
8282306b72aSZbigniew Bodek 	uint64_t *msg = (uint64_t *)mbx;
8292306b72aSZbigniew Bodek 
8302306b72aSZbigniew Bodek 	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]);
8312306b72aSZbigniew Bodek 	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]);
8322306b72aSZbigniew Bodek }
8332306b72aSZbigniew Bodek 
8342306b72aSZbigniew Bodek int
nicvf_send_msg_to_pf(struct nicvf * nic,union nic_mbx * mbx)8352306b72aSZbigniew Bodek nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
8362306b72aSZbigniew Bodek {
8372306b72aSZbigniew Bodek 	int timeout = NIC_MBOX_MSG_TIMEOUT * 10;
8382306b72aSZbigniew Bodek 	int sleep = 2;
8392306b72aSZbigniew Bodek 
8402306b72aSZbigniew Bodek 	NICVF_CORE_LOCK_ASSERT(nic);
8412306b72aSZbigniew Bodek 
8422306b72aSZbigniew Bodek 	nic->pf_acked = FALSE;
8432306b72aSZbigniew Bodek 	nic->pf_nacked = FALSE;
8442306b72aSZbigniew Bodek 
8452306b72aSZbigniew Bodek 	nicvf_write_to_mbx(nic, mbx);
8462306b72aSZbigniew Bodek 
8472306b72aSZbigniew Bodek 	/* Wait for previous message to be acked, timeout 2sec */
8482306b72aSZbigniew Bodek 	while (!nic->pf_acked) {
8492306b72aSZbigniew Bodek 		if (nic->pf_nacked)
8502306b72aSZbigniew Bodek 			return (EINVAL);
8512306b72aSZbigniew Bodek 
8522306b72aSZbigniew Bodek 		DELAY(sleep * 1000);
8532306b72aSZbigniew Bodek 
8542306b72aSZbigniew Bodek 		if (nic->pf_acked)
8552306b72aSZbigniew Bodek 			break;
8562306b72aSZbigniew Bodek 		timeout -= sleep;
8572306b72aSZbigniew Bodek 		if (!timeout) {
8582306b72aSZbigniew Bodek 			device_printf(nic->dev,
8592306b72aSZbigniew Bodek 				   "PF didn't ack to mbox msg %d from VF%d\n",
8602306b72aSZbigniew Bodek 				   (mbx->msg.msg & 0xFF), nic->vf_id);
8612306b72aSZbigniew Bodek 
8622306b72aSZbigniew Bodek 			return (EBUSY);
8632306b72aSZbigniew Bodek 		}
8642306b72aSZbigniew Bodek 	}
8652306b72aSZbigniew Bodek 	return (0);
8662306b72aSZbigniew Bodek }
8672306b72aSZbigniew Bodek 
8682306b72aSZbigniew Bodek /*
8692306b72aSZbigniew Bodek  * Checks if VF is able to comminicate with PF
8702306b72aSZbigniew Bodek  * and also gets the VNIC number this VF is associated to.
8712306b72aSZbigniew Bodek  */
8722306b72aSZbigniew Bodek static int
nicvf_check_pf_ready(struct nicvf * nic)8732306b72aSZbigniew Bodek nicvf_check_pf_ready(struct nicvf *nic)
8742306b72aSZbigniew Bodek {
8752306b72aSZbigniew Bodek 	union nic_mbx mbx = {};
8762306b72aSZbigniew Bodek 
8772306b72aSZbigniew Bodek 	mbx.msg.msg = NIC_MBOX_MSG_READY;
8782306b72aSZbigniew Bodek 	if (nicvf_send_msg_to_pf(nic, &mbx)) {
8792306b72aSZbigniew Bodek 		device_printf(nic->dev,
8802306b72aSZbigniew Bodek 			   "PF didn't respond to READY msg\n");
8813c0086b8SZbigniew Bodek 		return 0;
8822306b72aSZbigniew Bodek 	}
8832306b72aSZbigniew Bodek 
8842306b72aSZbigniew Bodek 	return 1;
8852306b72aSZbigniew Bodek }
8862306b72aSZbigniew Bodek 
8872306b72aSZbigniew Bodek static void
nicvf_read_bgx_stats(struct nicvf * nic,struct bgx_stats_msg * bgx)8882306b72aSZbigniew Bodek nicvf_read_bgx_stats(struct nicvf *nic, struct bgx_stats_msg *bgx)
8892306b72aSZbigniew Bodek {
8902306b72aSZbigniew Bodek 
8912306b72aSZbigniew Bodek 	if (bgx->rx)
8922306b72aSZbigniew Bodek 		nic->bgx_stats.rx_stats[bgx->idx] = bgx->stats;
8932306b72aSZbigniew Bodek 	else
8942306b72aSZbigniew Bodek 		nic->bgx_stats.tx_stats[bgx->idx] = bgx->stats;
8952306b72aSZbigniew Bodek }
8962306b72aSZbigniew Bodek 
8972306b72aSZbigniew Bodek static void
nicvf_handle_mbx_intr(struct nicvf * nic)8982306b72aSZbigniew Bodek nicvf_handle_mbx_intr(struct nicvf *nic)
8992306b72aSZbigniew Bodek {
9002306b72aSZbigniew Bodek 	union nic_mbx mbx = {};
9012306b72aSZbigniew Bodek 	uint64_t *mbx_data;
9022306b72aSZbigniew Bodek 	uint64_t mbx_addr;
9032306b72aSZbigniew Bodek 	int i;
9042306b72aSZbigniew Bodek 
9052306b72aSZbigniew Bodek 	mbx_addr = NIC_VF_PF_MAILBOX_0_1;
9062306b72aSZbigniew Bodek 	mbx_data = (uint64_t *)&mbx;
9072306b72aSZbigniew Bodek 
9082306b72aSZbigniew Bodek 	for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) {
9092306b72aSZbigniew Bodek 		*mbx_data = nicvf_reg_read(nic, mbx_addr);
9102306b72aSZbigniew Bodek 		mbx_data++;
9112306b72aSZbigniew Bodek 		mbx_addr += sizeof(uint64_t);
9122306b72aSZbigniew Bodek 	}
9132306b72aSZbigniew Bodek 
9142306b72aSZbigniew Bodek 	switch (mbx.msg.msg) {
9152306b72aSZbigniew Bodek 	case NIC_MBOX_MSG_READY:
9162306b72aSZbigniew Bodek 		nic->pf_acked = TRUE;
9172306b72aSZbigniew Bodek 		nic->vf_id = mbx.nic_cfg.vf_id & 0x7F;
9182306b72aSZbigniew Bodek 		nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F;
9192306b72aSZbigniew Bodek 		nic->node = mbx.nic_cfg.node_id;
9202306b72aSZbigniew Bodek 		memcpy(nic->hwaddr, mbx.nic_cfg.mac_addr, ETHER_ADDR_LEN);
9212306b72aSZbigniew Bodek 		nic->loopback_supported = mbx.nic_cfg.loopback_supported;
9222306b72aSZbigniew Bodek 		nic->link_up = FALSE;
9232306b72aSZbigniew Bodek 		nic->duplex = 0;
9242306b72aSZbigniew Bodek 		nic->speed = 0;
9252306b72aSZbigniew Bodek 		break;
9262306b72aSZbigniew Bodek 	case NIC_MBOX_MSG_ACK:
9272306b72aSZbigniew Bodek 		nic->pf_acked = TRUE;
9282306b72aSZbigniew Bodek 		break;
9292306b72aSZbigniew Bodek 	case NIC_MBOX_MSG_NACK:
9302306b72aSZbigniew Bodek 		nic->pf_nacked = TRUE;
9312306b72aSZbigniew Bodek 		break;
9328191a879SZbigniew Bodek 	case NIC_MBOX_MSG_RSS_SIZE:
9338191a879SZbigniew Bodek 		nic->rss_info.rss_size = mbx.rss_size.ind_tbl_size;
9348191a879SZbigniew Bodek 		nic->pf_acked = TRUE;
9358191a879SZbigniew Bodek 		break;
9362306b72aSZbigniew Bodek 	case NIC_MBOX_MSG_BGX_STATS:
9372306b72aSZbigniew Bodek 		nicvf_read_bgx_stats(nic, &mbx.bgx_stats);
9382306b72aSZbigniew Bodek 		nic->pf_acked = TRUE;
9392306b72aSZbigniew Bodek 		break;
9402306b72aSZbigniew Bodek 	case NIC_MBOX_MSG_BGX_LINK_CHANGE:
9412306b72aSZbigniew Bodek 		nic->pf_acked = TRUE;
9422306b72aSZbigniew Bodek 		nic->link_up = mbx.link_status.link_up;
9432306b72aSZbigniew Bodek 		nic->duplex = mbx.link_status.duplex;
9442306b72aSZbigniew Bodek 		nic->speed = mbx.link_status.speed;
9452306b72aSZbigniew Bodek 		if (nic->link_up) {
9462306b72aSZbigniew Bodek 			if_setbaudrate(nic->ifp, nic->speed * 1000000);
9472306b72aSZbigniew Bodek 			if_link_state_change(nic->ifp, LINK_STATE_UP);
9482306b72aSZbigniew Bodek 		} else {
9492306b72aSZbigniew Bodek 			if_setbaudrate(nic->ifp, 0);
9502306b72aSZbigniew Bodek 			if_link_state_change(nic->ifp, LINK_STATE_DOWN);
9512306b72aSZbigniew Bodek 		}
9522306b72aSZbigniew Bodek 		break;
9532306b72aSZbigniew Bodek 	default:
9542306b72aSZbigniew Bodek 		device_printf(nic->dev,
9552306b72aSZbigniew Bodek 			   "Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
9562306b72aSZbigniew Bodek 		break;
9572306b72aSZbigniew Bodek 	}
9582306b72aSZbigniew Bodek 	nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0);
9592306b72aSZbigniew Bodek }
9602306b72aSZbigniew Bodek 
9612306b72aSZbigniew Bodek static int
nicvf_update_hw_max_frs(struct nicvf * nic,int mtu)9620ef477eaSZbigniew Bodek nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
9630ef477eaSZbigniew Bodek {
9640ef477eaSZbigniew Bodek 	union nic_mbx mbx = {};
9650ef477eaSZbigniew Bodek 
9660ef477eaSZbigniew Bodek 	mbx.frs.msg = NIC_MBOX_MSG_SET_MAX_FRS;
9670ef477eaSZbigniew Bodek 	mbx.frs.max_frs = mtu;
9680ef477eaSZbigniew Bodek 	mbx.frs.vf_id = nic->vf_id;
9690ef477eaSZbigniew Bodek 
9700ef477eaSZbigniew Bodek 	return nicvf_send_msg_to_pf(nic, &mbx);
9710ef477eaSZbigniew Bodek }
9720ef477eaSZbigniew Bodek 
9730ef477eaSZbigniew Bodek static int
nicvf_hw_set_mac_addr(struct nicvf * nic,uint8_t * hwaddr)9742306b72aSZbigniew Bodek nicvf_hw_set_mac_addr(struct nicvf *nic, uint8_t *hwaddr)
9752306b72aSZbigniew Bodek {
9762306b72aSZbigniew Bodek 	union nic_mbx mbx = {};
9772306b72aSZbigniew Bodek 
9782306b72aSZbigniew Bodek 	mbx.mac.msg = NIC_MBOX_MSG_SET_MAC;
9792306b72aSZbigniew Bodek 	mbx.mac.vf_id = nic->vf_id;
9802306b72aSZbigniew Bodek 	memcpy(mbx.mac.mac_addr, hwaddr, ETHER_ADDR_LEN);
9812306b72aSZbigniew Bodek 
9822306b72aSZbigniew Bodek 	return (nicvf_send_msg_to_pf(nic, &mbx));
9832306b72aSZbigniew Bodek }
9842306b72aSZbigniew Bodek 
9852306b72aSZbigniew Bodek static void
nicvf_config_cpi(struct nicvf * nic)9862306b72aSZbigniew Bodek nicvf_config_cpi(struct nicvf *nic)
9872306b72aSZbigniew Bodek {
9882306b72aSZbigniew Bodek 	union nic_mbx mbx = {};
9892306b72aSZbigniew Bodek 
9902306b72aSZbigniew Bodek 	mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG;
9912306b72aSZbigniew Bodek 	mbx.cpi_cfg.vf_id = nic->vf_id;
9922306b72aSZbigniew Bodek 	mbx.cpi_cfg.cpi_alg = nic->cpi_alg;
9932306b72aSZbigniew Bodek 	mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt;
9942306b72aSZbigniew Bodek 
9952306b72aSZbigniew Bodek 	nicvf_send_msg_to_pf(nic, &mbx);
9962306b72aSZbigniew Bodek }
9972306b72aSZbigniew Bodek 
9988191a879SZbigniew Bodek static void
nicvf_get_rss_size(struct nicvf * nic)9998191a879SZbigniew Bodek nicvf_get_rss_size(struct nicvf *nic)
10008191a879SZbigniew Bodek {
10018191a879SZbigniew Bodek 	union nic_mbx mbx = {};
10028191a879SZbigniew Bodek 
10038191a879SZbigniew Bodek 	mbx.rss_size.msg = NIC_MBOX_MSG_RSS_SIZE;
10048191a879SZbigniew Bodek 	mbx.rss_size.vf_id = nic->vf_id;
10058191a879SZbigniew Bodek 	nicvf_send_msg_to_pf(nic, &mbx);
10068191a879SZbigniew Bodek }
10078191a879SZbigniew Bodek 
10088191a879SZbigniew Bodek static void
nicvf_config_rss(struct nicvf * nic)10098191a879SZbigniew Bodek nicvf_config_rss(struct nicvf *nic)
10108191a879SZbigniew Bodek {
10118191a879SZbigniew Bodek 	union nic_mbx mbx = {};
10128191a879SZbigniew Bodek 	struct nicvf_rss_info *rss;
10138191a879SZbigniew Bodek 	int ind_tbl_len;
10148191a879SZbigniew Bodek 	int i, nextq;
10158191a879SZbigniew Bodek 
10168191a879SZbigniew Bodek 	rss = &nic->rss_info;
10178191a879SZbigniew Bodek 	ind_tbl_len = rss->rss_size;
10188191a879SZbigniew Bodek 	nextq = 0;
10198191a879SZbigniew Bodek 
10208191a879SZbigniew Bodek 	mbx.rss_cfg.vf_id = nic->vf_id;
10218191a879SZbigniew Bodek 	mbx.rss_cfg.hash_bits = rss->hash_bits;
10228191a879SZbigniew Bodek 	while (ind_tbl_len != 0) {
10238191a879SZbigniew Bodek 		mbx.rss_cfg.tbl_offset = nextq;
10248191a879SZbigniew Bodek 		mbx.rss_cfg.tbl_len = MIN(ind_tbl_len,
10258191a879SZbigniew Bodek 		    RSS_IND_TBL_LEN_PER_MBX_MSG);
10268191a879SZbigniew Bodek 		mbx.rss_cfg.msg = mbx.rss_cfg.tbl_offset ?
10278191a879SZbigniew Bodek 		    NIC_MBOX_MSG_RSS_CFG_CONT : NIC_MBOX_MSG_RSS_CFG;
10288191a879SZbigniew Bodek 
10298191a879SZbigniew Bodek 		for (i = 0; i < mbx.rss_cfg.tbl_len; i++)
10308191a879SZbigniew Bodek 			mbx.rss_cfg.ind_tbl[i] = rss->ind_tbl[nextq++];
10318191a879SZbigniew Bodek 
10328191a879SZbigniew Bodek 		nicvf_send_msg_to_pf(nic, &mbx);
10338191a879SZbigniew Bodek 
10348191a879SZbigniew Bodek 		ind_tbl_len -= mbx.rss_cfg.tbl_len;
10358191a879SZbigniew Bodek 	}
10368191a879SZbigniew Bodek }
10378191a879SZbigniew Bodek 
10388191a879SZbigniew Bodek static void
nicvf_set_rss_key(struct nicvf * nic)10398191a879SZbigniew Bodek nicvf_set_rss_key(struct nicvf *nic)
10408191a879SZbigniew Bodek {
10418191a879SZbigniew Bodek 	struct nicvf_rss_info *rss;
10428191a879SZbigniew Bodek 	uint64_t key_addr;
10438191a879SZbigniew Bodek 	int idx;
10448191a879SZbigniew Bodek 
10458191a879SZbigniew Bodek 	rss = &nic->rss_info;
10468191a879SZbigniew Bodek 	key_addr = NIC_VNIC_RSS_KEY_0_4;
10478191a879SZbigniew Bodek 
10488191a879SZbigniew Bodek 	for (idx = 0; idx < RSS_HASH_KEY_SIZE; idx++) {
10498191a879SZbigniew Bodek 		nicvf_reg_write(nic, key_addr, rss->key[idx]);
10508191a879SZbigniew Bodek 		key_addr += sizeof(uint64_t);
10518191a879SZbigniew Bodek 	}
10528191a879SZbigniew Bodek }
10538191a879SZbigniew Bodek 
10548191a879SZbigniew Bodek static int
nicvf_rss_init(struct nicvf * nic)10558191a879SZbigniew Bodek nicvf_rss_init(struct nicvf *nic)
10568191a879SZbigniew Bodek {
10578191a879SZbigniew Bodek 	struct nicvf_rss_info *rss;
10588191a879SZbigniew Bodek 	int idx;
10598191a879SZbigniew Bodek 
10608191a879SZbigniew Bodek 	nicvf_get_rss_size(nic);
10618191a879SZbigniew Bodek 
10628191a879SZbigniew Bodek 	rss = &nic->rss_info;
10638191a879SZbigniew Bodek 	if (nic->cpi_alg != CPI_ALG_NONE) {
10648191a879SZbigniew Bodek 		rss->enable = FALSE;
10658191a879SZbigniew Bodek 		rss->hash_bits = 0;
10668191a879SZbigniew Bodek 		return (ENXIO);
10678191a879SZbigniew Bodek 	}
10688191a879SZbigniew Bodek 
10698191a879SZbigniew Bodek 	rss->enable = TRUE;
10708191a879SZbigniew Bodek 
10718191a879SZbigniew Bodek 	/* Using the HW reset value for now */
10728191a879SZbigniew Bodek 	rss->key[0] = 0xFEED0BADFEED0BADUL;
10738191a879SZbigniew Bodek 	rss->key[1] = 0xFEED0BADFEED0BADUL;
10748191a879SZbigniew Bodek 	rss->key[2] = 0xFEED0BADFEED0BADUL;
10758191a879SZbigniew Bodek 	rss->key[3] = 0xFEED0BADFEED0BADUL;
10768191a879SZbigniew Bodek 	rss->key[4] = 0xFEED0BADFEED0BADUL;
10778191a879SZbigniew Bodek 
10788191a879SZbigniew Bodek 	nicvf_set_rss_key(nic);
10798191a879SZbigniew Bodek 
10808191a879SZbigniew Bodek 	rss->cfg = RSS_IP_HASH_ENA | RSS_TCP_HASH_ENA | RSS_UDP_HASH_ENA;
10818191a879SZbigniew Bodek 	nicvf_reg_write(nic, NIC_VNIC_RSS_CFG, rss->cfg);
10828191a879SZbigniew Bodek 
10838191a879SZbigniew Bodek 	rss->hash_bits = fls(rss->rss_size) - 1;
10848191a879SZbigniew Bodek 	for (idx = 0; idx < rss->rss_size; idx++)
10858191a879SZbigniew Bodek 		rss->ind_tbl[idx] = idx % nic->rx_queues;
10868191a879SZbigniew Bodek 
10878191a879SZbigniew Bodek 	nicvf_config_rss(nic);
10888191a879SZbigniew Bodek 
10898191a879SZbigniew Bodek 	return (0);
10908191a879SZbigniew Bodek }
10918191a879SZbigniew Bodek 
10922306b72aSZbigniew Bodek static int
nicvf_init_resources(struct nicvf * nic)10932306b72aSZbigniew Bodek nicvf_init_resources(struct nicvf *nic)
10942306b72aSZbigniew Bodek {
10952306b72aSZbigniew Bodek 	int err;
10962306b72aSZbigniew Bodek 	union nic_mbx mbx = {};
10972306b72aSZbigniew Bodek 
10982306b72aSZbigniew Bodek 	mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE;
10992306b72aSZbigniew Bodek 
11002306b72aSZbigniew Bodek 	/* Enable Qset */
11012306b72aSZbigniew Bodek 	nicvf_qset_config(nic, TRUE);
11022306b72aSZbigniew Bodek 
11032306b72aSZbigniew Bodek 	/* Initialize queues and HW for data transfer */
11042306b72aSZbigniew Bodek 	err = nicvf_config_data_transfer(nic, TRUE);
11052306b72aSZbigniew Bodek 	if (err) {
11062306b72aSZbigniew Bodek 		device_printf(nic->dev,
11072306b72aSZbigniew Bodek 		    "Failed to alloc/config VF's QSet resources\n");
11082306b72aSZbigniew Bodek 		return (err);
11092306b72aSZbigniew Bodek 	}
11102306b72aSZbigniew Bodek 
11112306b72aSZbigniew Bodek 	/* Send VF config done msg to PF */
11122306b72aSZbigniew Bodek 	nicvf_write_to_mbx(nic, &mbx);
11132306b72aSZbigniew Bodek 
11142306b72aSZbigniew Bodek 	return (0);
11152306b72aSZbigniew Bodek }
11162306b72aSZbigniew Bodek 
11172306b72aSZbigniew Bodek static void
nicvf_misc_intr_handler(void * arg)11182306b72aSZbigniew Bodek nicvf_misc_intr_handler(void *arg)
11192306b72aSZbigniew Bodek {
11202306b72aSZbigniew Bodek 	struct nicvf *nic = (struct nicvf *)arg;
11212306b72aSZbigniew Bodek 	uint64_t intr;
11222306b72aSZbigniew Bodek 
11232306b72aSZbigniew Bodek 	intr = nicvf_reg_read(nic, NIC_VF_INT);
11242306b72aSZbigniew Bodek 	/* Check for spurious interrupt */
11252306b72aSZbigniew Bodek 	if (!(intr & NICVF_INTR_MBOX_MASK))
11262306b72aSZbigniew Bodek 		return;
11272306b72aSZbigniew Bodek 
11282306b72aSZbigniew Bodek 	nicvf_handle_mbx_intr(nic);
11292306b72aSZbigniew Bodek }
11302306b72aSZbigniew Bodek 
11312306b72aSZbigniew Bodek static int
nicvf_intr_handler(void * arg)11322306b72aSZbigniew Bodek nicvf_intr_handler(void *arg)
11332306b72aSZbigniew Bodek {
11342306b72aSZbigniew Bodek 	struct nicvf *nic;
11352306b72aSZbigniew Bodek 	struct cmp_queue *cq;
11362306b72aSZbigniew Bodek 	int qidx;
11372306b72aSZbigniew Bodek 
11382306b72aSZbigniew Bodek 	cq = (struct cmp_queue *)arg;
11392306b72aSZbigniew Bodek 	nic = cq->nic;
11402306b72aSZbigniew Bodek 	qidx = cq->idx;
11412306b72aSZbigniew Bodek 
11422306b72aSZbigniew Bodek 	/* Disable interrupts */
11432306b72aSZbigniew Bodek 	nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
11442306b72aSZbigniew Bodek 
11452306b72aSZbigniew Bodek 	taskqueue_enqueue(cq->cmp_taskq, &cq->cmp_task);
11462306b72aSZbigniew Bodek 
11472306b72aSZbigniew Bodek 	/* Clear interrupt */
11482306b72aSZbigniew Bodek 	nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
11492306b72aSZbigniew Bodek 
11502306b72aSZbigniew Bodek 	return (FILTER_HANDLED);
11512306b72aSZbigniew Bodek }
11522306b72aSZbigniew Bodek 
11532306b72aSZbigniew Bodek static void
nicvf_rbdr_intr_handler(void * arg)11542306b72aSZbigniew Bodek nicvf_rbdr_intr_handler(void *arg)
11552306b72aSZbigniew Bodek {
11562306b72aSZbigniew Bodek 	struct nicvf *nic;
11572306b72aSZbigniew Bodek 	struct queue_set *qs;
11582306b72aSZbigniew Bodek 	struct rbdr *rbdr;
11592306b72aSZbigniew Bodek 	int qidx;
11602306b72aSZbigniew Bodek 
11612306b72aSZbigniew Bodek 	nic = (struct nicvf *)arg;
11622306b72aSZbigniew Bodek 
11632306b72aSZbigniew Bodek 	/* Disable RBDR interrupt and schedule softirq */
11642306b72aSZbigniew Bodek 	for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) {
11652306b72aSZbigniew Bodek 		if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx))
11663c0086b8SZbigniew Bodek 			continue;
11672306b72aSZbigniew Bodek 		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
11682306b72aSZbigniew Bodek 
11692306b72aSZbigniew Bodek 		qs = nic->qs;
11702306b72aSZbigniew Bodek 		rbdr = &qs->rbdr[qidx];
11712306b72aSZbigniew Bodek 		taskqueue_enqueue(rbdr->rbdr_taskq, &rbdr->rbdr_task_nowait);
11722306b72aSZbigniew Bodek 		/* Clear interrupt */
11732306b72aSZbigniew Bodek 		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
11743c0086b8SZbigniew Bodek 	}
11753c0086b8SZbigniew Bodek }
11763c0086b8SZbigniew Bodek 
11772306b72aSZbigniew Bodek static void
nicvf_qs_err_intr_handler(void * arg)11782306b72aSZbigniew Bodek nicvf_qs_err_intr_handler(void *arg)
11793c0086b8SZbigniew Bodek {
11802306b72aSZbigniew Bodek 	struct nicvf *nic = (struct nicvf *)arg;
11812306b72aSZbigniew Bodek 	struct queue_set *qs = nic->qs;
11823c0086b8SZbigniew Bodek 
11832306b72aSZbigniew Bodek 	/* Disable Qset err interrupt and schedule softirq */
11842306b72aSZbigniew Bodek 	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
11852306b72aSZbigniew Bodek 	taskqueue_enqueue(qs->qs_err_taskq, &qs->qs_err_task);
11862306b72aSZbigniew Bodek 	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
11873c0086b8SZbigniew Bodek 
11883c0086b8SZbigniew Bodek }
11893c0086b8SZbigniew Bodek 
11902306b72aSZbigniew Bodek static int
nicvf_enable_msix(struct nicvf * nic)11912306b72aSZbigniew Bodek nicvf_enable_msix(struct nicvf *nic)
11923c0086b8SZbigniew Bodek {
11932306b72aSZbigniew Bodek 	struct pci_devinfo *dinfo;
11942306b72aSZbigniew Bodek 	int rid, count;
11952306b72aSZbigniew Bodek 	int ret;
11963c0086b8SZbigniew Bodek 
11972306b72aSZbigniew Bodek 	dinfo = device_get_ivars(nic->dev);
11982306b72aSZbigniew Bodek 	rid = dinfo->cfg.msix.msix_table_bar;
11992306b72aSZbigniew Bodek 	nic->msix_table_res =
12002306b72aSZbigniew Bodek 	    bus_alloc_resource_any(nic->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
12012306b72aSZbigniew Bodek 	if (nic->msix_table_res == NULL) {
12022306b72aSZbigniew Bodek 		device_printf(nic->dev,
12032306b72aSZbigniew Bodek 		    "Could not allocate memory for MSI-X table\n");
12042306b72aSZbigniew Bodek 		return (ENXIO);
12053c0086b8SZbigniew Bodek 	}
12063c0086b8SZbigniew Bodek 
12072306b72aSZbigniew Bodek 	count = nic->num_vec = NIC_VF_MSIX_VECTORS;
12082306b72aSZbigniew Bodek 
12092306b72aSZbigniew Bodek 	ret = pci_alloc_msix(nic->dev, &count);
12102306b72aSZbigniew Bodek 	if ((ret != 0) || (count != nic->num_vec)) {
12112306b72aSZbigniew Bodek 		device_printf(nic->dev,
12122306b72aSZbigniew Bodek 		    "Request for #%d msix vectors failed, error: %d\n",
12132306b72aSZbigniew Bodek 		    nic->num_vec, ret);
12142306b72aSZbigniew Bodek 		return (ret);
12152306b72aSZbigniew Bodek 	}
12162306b72aSZbigniew Bodek 
12172306b72aSZbigniew Bodek 	nic->msix_enabled = 1;
12182306b72aSZbigniew Bodek 	return (0);
12192306b72aSZbigniew Bodek }
12202306b72aSZbigniew Bodek 
12212306b72aSZbigniew Bodek static void
nicvf_disable_msix(struct nicvf * nic)12222306b72aSZbigniew Bodek nicvf_disable_msix(struct nicvf *nic)
12233c0086b8SZbigniew Bodek {
12243c0086b8SZbigniew Bodek 
12252306b72aSZbigniew Bodek 	if (nic->msix_enabled) {
12262306b72aSZbigniew Bodek 		pci_release_msi(nic->dev);
12272306b72aSZbigniew Bodek 		nic->msix_enabled = 0;
12282306b72aSZbigniew Bodek 		nic->num_vec = 0;
12292306b72aSZbigniew Bodek 	}
12302306b72aSZbigniew Bodek }
12313c0086b8SZbigniew Bodek 
12322306b72aSZbigniew Bodek static void
nicvf_release_all_interrupts(struct nicvf * nic)12332306b72aSZbigniew Bodek nicvf_release_all_interrupts(struct nicvf *nic)
12342306b72aSZbigniew Bodek {
12352306b72aSZbigniew Bodek 	struct resource *res;
12362306b72aSZbigniew Bodek 	int irq;
123721653635SJohn Baldwin 	int err __diagused;
12383c0086b8SZbigniew Bodek 
12392306b72aSZbigniew Bodek 	/* Free registered interrupts */
12402306b72aSZbigniew Bodek 	for (irq = 0; irq < nic->num_vec; irq++) {
12412306b72aSZbigniew Bodek 		res = nic->msix_entries[irq].irq_res;
12422306b72aSZbigniew Bodek 		if (res == NULL)
12432306b72aSZbigniew Bodek 			continue;
12442306b72aSZbigniew Bodek 		/* Teardown interrupt first */
12452306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].handle != NULL) {
12462306b72aSZbigniew Bodek 			err = bus_teardown_intr(nic->dev,
12472306b72aSZbigniew Bodek 			    nic->msix_entries[irq].irq_res,
12482306b72aSZbigniew Bodek 			    nic->msix_entries[irq].handle);
12492306b72aSZbigniew Bodek 			KASSERT(err == 0,
12502306b72aSZbigniew Bodek 			    ("ERROR: Unable to teardown interrupt %d", irq));
12512306b72aSZbigniew Bodek 			nic->msix_entries[irq].handle = NULL;
12522306b72aSZbigniew Bodek 		}
12532306b72aSZbigniew Bodek 
12542306b72aSZbigniew Bodek 		bus_release_resource(nic->dev, SYS_RES_IRQ,
12552306b72aSZbigniew Bodek 			    rman_get_rid(res), nic->msix_entries[irq].irq_res);
12562306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = NULL;
12572306b72aSZbigniew Bodek 	}
12582306b72aSZbigniew Bodek 	/* Disable MSI-X */
12592306b72aSZbigniew Bodek 	nicvf_disable_msix(nic);
12602306b72aSZbigniew Bodek }
12612306b72aSZbigniew Bodek 
12622306b72aSZbigniew Bodek /*
12632306b72aSZbigniew Bodek  * Initialize MSIX vectors and register MISC interrupt.
12642306b72aSZbigniew Bodek  * Send READY message to PF to check if its alive
12652306b72aSZbigniew Bodek  */
12662306b72aSZbigniew Bodek static int
nicvf_allocate_misc_interrupt(struct nicvf * nic)12672306b72aSZbigniew Bodek nicvf_allocate_misc_interrupt(struct nicvf *nic)
12682306b72aSZbigniew Bodek {
12692306b72aSZbigniew Bodek 	struct resource *res;
12702306b72aSZbigniew Bodek 	int irq, rid;
12712306b72aSZbigniew Bodek 	int ret = 0;
12722306b72aSZbigniew Bodek 
12732306b72aSZbigniew Bodek 	/* Return if mailbox interrupt is already registered */
12743c0086b8SZbigniew Bodek 	if (nic->msix_enabled)
12752306b72aSZbigniew Bodek 		return (0);
12763c0086b8SZbigniew Bodek 
12772306b72aSZbigniew Bodek 	/* Enable MSI-X */
12782306b72aSZbigniew Bodek 	if (nicvf_enable_msix(nic) != 0)
12792306b72aSZbigniew Bodek 		return (ENXIO);
12802306b72aSZbigniew Bodek 
12812306b72aSZbigniew Bodek 	irq = NICVF_INTR_ID_MISC;
12822306b72aSZbigniew Bodek 	rid = irq + 1;
12832306b72aSZbigniew Bodek 	nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev,
12842306b72aSZbigniew Bodek 	    SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE));
12852306b72aSZbigniew Bodek 	if (nic->msix_entries[irq].irq_res == NULL) {
12862306b72aSZbigniew Bodek 		device_printf(nic->dev,
12872306b72aSZbigniew Bodek 		    "Could not allocate Mbox interrupt for VF%d\n",
12882306b72aSZbigniew Bodek 		    device_get_unit(nic->dev));
12892306b72aSZbigniew Bodek 		return (ENXIO);
12903c0086b8SZbigniew Bodek 	}
12913c0086b8SZbigniew Bodek 
12922306b72aSZbigniew Bodek 	ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res,
12932306b72aSZbigniew Bodek 	    (INTR_MPSAFE | INTR_TYPE_MISC), NULL, nicvf_misc_intr_handler, nic,
12942306b72aSZbigniew Bodek 	    &nic->msix_entries[irq].handle);
12952306b72aSZbigniew Bodek 	if (ret != 0) {
12962306b72aSZbigniew Bodek 		res = nic->msix_entries[irq].irq_res;
12972306b72aSZbigniew Bodek 		bus_release_resource(nic->dev, SYS_RES_IRQ,
12982306b72aSZbigniew Bodek 			    rman_get_rid(res), res);
12992306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = NULL;
13002306b72aSZbigniew Bodek 		return (ret);
13012306b72aSZbigniew Bodek 	}
13022306b72aSZbigniew Bodek 
13032306b72aSZbigniew Bodek 	return (0);
13042306b72aSZbigniew Bodek }
13052306b72aSZbigniew Bodek 
13062306b72aSZbigniew Bodek static int
nicvf_enable_misc_interrupt(struct nicvf * nic)13072306b72aSZbigniew Bodek nicvf_enable_misc_interrupt(struct nicvf *nic)
13083c0086b8SZbigniew Bodek {
13092306b72aSZbigniew Bodek 
13102306b72aSZbigniew Bodek 	/* Enable mailbox interrupt */
13112306b72aSZbigniew Bodek 	nicvf_enable_intr(nic, NICVF_INTR_MBOX, 0);
13122306b72aSZbigniew Bodek 
13132306b72aSZbigniew Bodek 	/* Check if VF is able to communicate with PF */
13142306b72aSZbigniew Bodek 	if (!nicvf_check_pf_ready(nic)) {
13152306b72aSZbigniew Bodek 		nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
13162306b72aSZbigniew Bodek 		return (ENXIO);
13172306b72aSZbigniew Bodek 	}
13182306b72aSZbigniew Bodek 
13192306b72aSZbigniew Bodek 	return (0);
13202306b72aSZbigniew Bodek }
13212306b72aSZbigniew Bodek 
13222306b72aSZbigniew Bodek static void
nicvf_release_net_interrupts(struct nicvf * nic)13232306b72aSZbigniew Bodek nicvf_release_net_interrupts(struct nicvf *nic)
13242306b72aSZbigniew Bodek {
13252306b72aSZbigniew Bodek 	struct resource *res;
13262306b72aSZbigniew Bodek 	int irq;
13272306b72aSZbigniew Bodek 	int err;
13282306b72aSZbigniew Bodek 
13292306b72aSZbigniew Bodek 	for_each_cq_irq(irq) {
13302306b72aSZbigniew Bodek 		res = nic->msix_entries[irq].irq_res;
13312306b72aSZbigniew Bodek 		if (res == NULL)
13322306b72aSZbigniew Bodek 			continue;
13332306b72aSZbigniew Bodek 		/* Teardown active interrupts first */
13342306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].handle != NULL) {
13352306b72aSZbigniew Bodek 			err = bus_teardown_intr(nic->dev,
13362306b72aSZbigniew Bodek 			    nic->msix_entries[irq].irq_res,
13372306b72aSZbigniew Bodek 			    nic->msix_entries[irq].handle);
13382306b72aSZbigniew Bodek 			KASSERT(err == 0,
13392306b72aSZbigniew Bodek 			    ("ERROR: Unable to teardown CQ interrupt %d",
13402306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_CQ)));
13412306b72aSZbigniew Bodek 			if (err != 0)
13422306b72aSZbigniew Bodek 				continue;
13432306b72aSZbigniew Bodek 		}
13442306b72aSZbigniew Bodek 
13452306b72aSZbigniew Bodek 		/* Release resource */
13462306b72aSZbigniew Bodek 		bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res),
13472306b72aSZbigniew Bodek 		    res);
13482306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = NULL;
13492306b72aSZbigniew Bodek 	}
13502306b72aSZbigniew Bodek 
13512306b72aSZbigniew Bodek 	for_each_rbdr_irq(irq) {
13522306b72aSZbigniew Bodek 		res = nic->msix_entries[irq].irq_res;
13532306b72aSZbigniew Bodek 		if (res == NULL)
13542306b72aSZbigniew Bodek 			continue;
13552306b72aSZbigniew Bodek 		/* Teardown active interrupts first */
13562306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].handle != NULL) {
13572306b72aSZbigniew Bodek 			err = bus_teardown_intr(nic->dev,
13582306b72aSZbigniew Bodek 			    nic->msix_entries[irq].irq_res,
13592306b72aSZbigniew Bodek 			    nic->msix_entries[irq].handle);
13602306b72aSZbigniew Bodek 			KASSERT(err == 0,
13612306b72aSZbigniew Bodek 			    ("ERROR: Unable to teardown RDBR interrupt %d",
13622306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_RBDR)));
13632306b72aSZbigniew Bodek 			if (err != 0)
13642306b72aSZbigniew Bodek 				continue;
13652306b72aSZbigniew Bodek 		}
13662306b72aSZbigniew Bodek 
13672306b72aSZbigniew Bodek 		/* Release resource */
13682306b72aSZbigniew Bodek 		bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res),
13692306b72aSZbigniew Bodek 		    res);
13702306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = NULL;
13712306b72aSZbigniew Bodek 	}
13722306b72aSZbigniew Bodek 
13732306b72aSZbigniew Bodek 	irq = NICVF_INTR_ID_QS_ERR;
13742306b72aSZbigniew Bodek 	res = nic->msix_entries[irq].irq_res;
13752306b72aSZbigniew Bodek 	if (res != NULL) {
13762306b72aSZbigniew Bodek 		/* Teardown active interrupts first */
13772306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].handle != NULL) {
13782306b72aSZbigniew Bodek 			err = bus_teardown_intr(nic->dev,
13792306b72aSZbigniew Bodek 			    nic->msix_entries[irq].irq_res,
13802306b72aSZbigniew Bodek 			    nic->msix_entries[irq].handle);
13812306b72aSZbigniew Bodek 			KASSERT(err == 0,
13822306b72aSZbigniew Bodek 			    ("ERROR: Unable to teardown QS Error interrupt %d",
13832306b72aSZbigniew Bodek 			    irq));
13842306b72aSZbigniew Bodek 			if (err != 0)
13852306b72aSZbigniew Bodek 				return;
13862306b72aSZbigniew Bodek 		}
13872306b72aSZbigniew Bodek 
13882306b72aSZbigniew Bodek 		/* Release resource */
13892306b72aSZbigniew Bodek 		bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res),
13902306b72aSZbigniew Bodek 		    res);
13912306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = NULL;
13922306b72aSZbigniew Bodek 	}
13932306b72aSZbigniew Bodek }
13942306b72aSZbigniew Bodek 
13952306b72aSZbigniew Bodek static int
nicvf_allocate_net_interrupts(struct nicvf * nic)13962306b72aSZbigniew Bodek nicvf_allocate_net_interrupts(struct nicvf *nic)
13972306b72aSZbigniew Bodek {
1398ec6f8f42SZbigniew Bodek 	u_int cpuid;
13992306b72aSZbigniew Bodek 	int irq, rid;
14002306b72aSZbigniew Bodek 	int qidx;
14012306b72aSZbigniew Bodek 	int ret = 0;
14022306b72aSZbigniew Bodek 
14032306b72aSZbigniew Bodek 	/* MSI-X must be configured by now */
14042306b72aSZbigniew Bodek 	if (!nic->msix_enabled) {
14052306b72aSZbigniew Bodek 		device_printf(nic->dev, "Cannot alloacte queue interrups. "
14062306b72aSZbigniew Bodek 		    "MSI-X interrupts disabled.\n");
14072306b72aSZbigniew Bodek 		return (ENXIO);
14082306b72aSZbigniew Bodek 	}
14092306b72aSZbigniew Bodek 
14102306b72aSZbigniew Bodek 	/* Register CQ interrupts */
14112306b72aSZbigniew Bodek 	for_each_cq_irq(irq) {
14122306b72aSZbigniew Bodek 		if (irq >= (NICVF_INTR_ID_CQ + nic->qs->cq_cnt))
14132306b72aSZbigniew Bodek 			break;
14142306b72aSZbigniew Bodek 
14152306b72aSZbigniew Bodek 		qidx = irq - NICVF_INTR_ID_CQ;
14162306b72aSZbigniew Bodek 		rid = irq + 1;
14172306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev,
14182306b72aSZbigniew Bodek 		    SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE));
14192306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].irq_res == NULL) {
14202306b72aSZbigniew Bodek 			device_printf(nic->dev,
14212306b72aSZbigniew Bodek 			    "Could not allocate CQ interrupt %d for VF%d\n",
14222306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_CQ), device_get_unit(nic->dev));
14232306b72aSZbigniew Bodek 			ret = ENXIO;
14242306b72aSZbigniew Bodek 			goto error;
14252306b72aSZbigniew Bodek 		}
14262306b72aSZbigniew Bodek 		ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res,
14272306b72aSZbigniew Bodek 		    (INTR_MPSAFE | INTR_TYPE_NET), nicvf_intr_handler,
14282306b72aSZbigniew Bodek 		    NULL, &nic->qs->cq[qidx], &nic->msix_entries[irq].handle);
14292306b72aSZbigniew Bodek 		if (ret != 0) {
14302306b72aSZbigniew Bodek 			device_printf(nic->dev,
14312306b72aSZbigniew Bodek 			    "Could not setup CQ interrupt %d for VF%d\n",
14322306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_CQ), device_get_unit(nic->dev));
14332306b72aSZbigniew Bodek 			goto error;
14342306b72aSZbigniew Bodek 		}
1435ec6f8f42SZbigniew Bodek 		cpuid = (device_get_unit(nic->dev) * CMP_QUEUE_CNT) + qidx;
1436ec6f8f42SZbigniew Bodek 		cpuid %= mp_ncpus;
1437ec6f8f42SZbigniew Bodek 		/*
1438ec6f8f42SZbigniew Bodek 		 * Save CPU ID for later use when system-wide RSS is enabled.
1439ec6f8f42SZbigniew Bodek 		 * It will be used to pit the CQ task to the same CPU that got
1440ec6f8f42SZbigniew Bodek 		 * interrupted.
1441ec6f8f42SZbigniew Bodek 		 */
1442ec6f8f42SZbigniew Bodek 		nic->qs->cq[qidx].cmp_cpuid = cpuid;
1443ec6f8f42SZbigniew Bodek 		if (bootverbose) {
1444ec6f8f42SZbigniew Bodek 			device_printf(nic->dev, "bind CQ%d IRQ to CPU%d\n",
1445ec6f8f42SZbigniew Bodek 			    qidx, cpuid);
1446ec6f8f42SZbigniew Bodek 		}
1447ec6f8f42SZbigniew Bodek 		/* Bind interrupts to the given CPU */
1448ec6f8f42SZbigniew Bodek 		bus_bind_intr(nic->dev, nic->msix_entries[irq].irq_res, cpuid);
14492306b72aSZbigniew Bodek 	}
14502306b72aSZbigniew Bodek 
14512306b72aSZbigniew Bodek 	/* Register RBDR interrupt */
14522306b72aSZbigniew Bodek 	for_each_rbdr_irq(irq) {
14532306b72aSZbigniew Bodek 		if (irq >= (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt))
14542306b72aSZbigniew Bodek 			break;
14552306b72aSZbigniew Bodek 
14562306b72aSZbigniew Bodek 		rid = irq + 1;
14572306b72aSZbigniew Bodek 		nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev,
14582306b72aSZbigniew Bodek 		    SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE));
14592306b72aSZbigniew Bodek 		if (nic->msix_entries[irq].irq_res == NULL) {
14602306b72aSZbigniew Bodek 			device_printf(nic->dev,
14612306b72aSZbigniew Bodek 			    "Could not allocate RBDR interrupt %d for VF%d\n",
14622306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_RBDR),
14632306b72aSZbigniew Bodek 			    device_get_unit(nic->dev));
14642306b72aSZbigniew Bodek 			ret = ENXIO;
14652306b72aSZbigniew Bodek 			goto error;
14662306b72aSZbigniew Bodek 		}
14672306b72aSZbigniew Bodek 		ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res,
14682306b72aSZbigniew Bodek 		    (INTR_MPSAFE | INTR_TYPE_NET), NULL,
14692306b72aSZbigniew Bodek 		    nicvf_rbdr_intr_handler, nic,
14702306b72aSZbigniew Bodek 		    &nic->msix_entries[irq].handle);
14712306b72aSZbigniew Bodek 		if (ret != 0) {
14722306b72aSZbigniew Bodek 			device_printf(nic->dev,
14732306b72aSZbigniew Bodek 			    "Could not setup RBDR interrupt %d for VF%d\n",
14742306b72aSZbigniew Bodek 			    (irq - NICVF_INTR_ID_RBDR),
14752306b72aSZbigniew Bodek 			    device_get_unit(nic->dev));
14762306b72aSZbigniew Bodek 			goto error;
14772306b72aSZbigniew Bodek 		}
14782306b72aSZbigniew Bodek 	}
14792306b72aSZbigniew Bodek 
14802306b72aSZbigniew Bodek 	/* Register QS error interrupt */
14812306b72aSZbigniew Bodek 	irq = NICVF_INTR_ID_QS_ERR;
14822306b72aSZbigniew Bodek 	rid = irq + 1;
14832306b72aSZbigniew Bodek 	nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev,
14842306b72aSZbigniew Bodek 	    SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE));
14852306b72aSZbigniew Bodek 	if (nic->msix_entries[irq].irq_res == NULL) {
14862306b72aSZbigniew Bodek 		device_printf(nic->dev,
14872306b72aSZbigniew Bodek 		    "Could not allocate QS Error interrupt for VF%d\n",
14882306b72aSZbigniew Bodek 		    device_get_unit(nic->dev));
14892306b72aSZbigniew Bodek 		ret = ENXIO;
14902306b72aSZbigniew Bodek 		goto error;
14912306b72aSZbigniew Bodek 	}
14922306b72aSZbigniew Bodek 	ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res,
14932306b72aSZbigniew Bodek 	    (INTR_MPSAFE | INTR_TYPE_NET), NULL, nicvf_qs_err_intr_handler,
14942306b72aSZbigniew Bodek 	    nic, &nic->msix_entries[irq].handle);
14952306b72aSZbigniew Bodek 	if (ret != 0) {
14962306b72aSZbigniew Bodek 		device_printf(nic->dev,
14972306b72aSZbigniew Bodek 		    "Could not setup QS Error interrupt for VF%d\n",
14982306b72aSZbigniew Bodek 		    device_get_unit(nic->dev));
14992306b72aSZbigniew Bodek 		goto error;
15002306b72aSZbigniew Bodek 	}
15012306b72aSZbigniew Bodek 
15022306b72aSZbigniew Bodek 	return (0);
15032306b72aSZbigniew Bodek error:
15042306b72aSZbigniew Bodek 	nicvf_release_net_interrupts(nic);
15052306b72aSZbigniew Bodek 	return (ret);
15062306b72aSZbigniew Bodek }
15072306b72aSZbigniew Bodek 
15082306b72aSZbigniew Bodek static int
nicvf_stop_locked(struct nicvf * nic)15092306b72aSZbigniew Bodek nicvf_stop_locked(struct nicvf *nic)
15102306b72aSZbigniew Bodek {
1511b9545c57SJustin Hibbits 	if_t ifp;
15122306b72aSZbigniew Bodek 	int qidx;
15132306b72aSZbigniew Bodek 	struct queue_set *qs = nic->qs;
15143c0086b8SZbigniew Bodek 	union nic_mbx mbx = {};
15153c0086b8SZbigniew Bodek 
15162306b72aSZbigniew Bodek 	NICVF_CORE_LOCK_ASSERT(nic);
15172306b72aSZbigniew Bodek 	/* Stop callout. Can block here since holding SX lock */
15182306b72aSZbigniew Bodek 	callout_drain(&nic->stats_callout);
15193c0086b8SZbigniew Bodek 
15202306b72aSZbigniew Bodek 	ifp = nic->ifp;
15212306b72aSZbigniew Bodek 
15222306b72aSZbigniew Bodek 	mbx.msg.msg = NIC_MBOX_MSG_SHUTDOWN;
15232306b72aSZbigniew Bodek 	nicvf_send_msg_to_pf(nic, &mbx);
15242306b72aSZbigniew Bodek 
15252306b72aSZbigniew Bodek 	/* Disable RBDR & QS error interrupts */
15262306b72aSZbigniew Bodek 	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) {
15272306b72aSZbigniew Bodek 		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
15282306b72aSZbigniew Bodek 		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
15292306b72aSZbigniew Bodek 	}
15302306b72aSZbigniew Bodek 	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
15312306b72aSZbigniew Bodek 	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
15322306b72aSZbigniew Bodek 
15332306b72aSZbigniew Bodek 	/* Deactivate network interface */
15342306b72aSZbigniew Bodek 	if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
15352306b72aSZbigniew Bodek 
15362306b72aSZbigniew Bodek 	/* Free resources */
15372306b72aSZbigniew Bodek 	nicvf_config_data_transfer(nic, FALSE);
15382306b72aSZbigniew Bodek 
15392306b72aSZbigniew Bodek 	/* Disable HW Qset */
15402306b72aSZbigniew Bodek 	nicvf_qset_config(nic, FALSE);
15412306b72aSZbigniew Bodek 
15422306b72aSZbigniew Bodek 	/* disable mailbox interrupt */
15432306b72aSZbigniew Bodek 	nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
15442306b72aSZbigniew Bodek 
15452306b72aSZbigniew Bodek 	return (0);
15463c0086b8SZbigniew Bodek }
15473c0086b8SZbigniew Bodek 
15482306b72aSZbigniew Bodek static void
nicvf_update_stats(struct nicvf * nic)15492306b72aSZbigniew Bodek nicvf_update_stats(struct nicvf *nic)
15503c0086b8SZbigniew Bodek {
15513c0086b8SZbigniew Bodek 	int qidx;
15523c0086b8SZbigniew Bodek 	struct nicvf_hw_stats *stats = &nic->hw_stats;
15533c0086b8SZbigniew Bodek 	struct nicvf_drv_stats *drv_stats = &nic->drv_stats;
15543c0086b8SZbigniew Bodek 	struct queue_set *qs = nic->qs;
15553c0086b8SZbigniew Bodek 
15563c0086b8SZbigniew Bodek #define	GET_RX_STATS(reg) \
15572306b72aSZbigniew Bodek     nicvf_reg_read(nic, NIC_VNIC_RX_STAT_0_13 | ((reg) << 3))
15583c0086b8SZbigniew Bodek #define GET_TX_STATS(reg) \
15592306b72aSZbigniew Bodek     nicvf_reg_read(nic, NIC_VNIC_TX_STAT_0_4 | ((reg) << 3))
15603c0086b8SZbigniew Bodek 
15613c0086b8SZbigniew Bodek 	stats->rx_bytes = GET_RX_STATS(RX_OCTS);
15623c0086b8SZbigniew Bodek 	stats->rx_ucast_frames = GET_RX_STATS(RX_UCAST);
15633c0086b8SZbigniew Bodek 	stats->rx_bcast_frames = GET_RX_STATS(RX_BCAST);
15643c0086b8SZbigniew Bodek 	stats->rx_mcast_frames = GET_RX_STATS(RX_MCAST);
15653c0086b8SZbigniew Bodek 	stats->rx_fcs_errors = GET_RX_STATS(RX_FCS);
15663c0086b8SZbigniew Bodek 	stats->rx_l2_errors = GET_RX_STATS(RX_L2ERR);
15673c0086b8SZbigniew Bodek 	stats->rx_drop_red = GET_RX_STATS(RX_RED);
15683c0086b8SZbigniew Bodek 	stats->rx_drop_red_bytes = GET_RX_STATS(RX_RED_OCTS);
15693c0086b8SZbigniew Bodek 	stats->rx_drop_overrun = GET_RX_STATS(RX_ORUN);
15703c0086b8SZbigniew Bodek 	stats->rx_drop_overrun_bytes = GET_RX_STATS(RX_ORUN_OCTS);
15713c0086b8SZbigniew Bodek 	stats->rx_drop_bcast = GET_RX_STATS(RX_DRP_BCAST);
15723c0086b8SZbigniew Bodek 	stats->rx_drop_mcast = GET_RX_STATS(RX_DRP_MCAST);
15733c0086b8SZbigniew Bodek 	stats->rx_drop_l3_bcast = GET_RX_STATS(RX_DRP_L3BCAST);
15743c0086b8SZbigniew Bodek 	stats->rx_drop_l3_mcast = GET_RX_STATS(RX_DRP_L3MCAST);
15753c0086b8SZbigniew Bodek 
15763c0086b8SZbigniew Bodek 	stats->tx_bytes_ok = GET_TX_STATS(TX_OCTS);
15773c0086b8SZbigniew Bodek 	stats->tx_ucast_frames_ok = GET_TX_STATS(TX_UCAST);
15783c0086b8SZbigniew Bodek 	stats->tx_bcast_frames_ok = GET_TX_STATS(TX_BCAST);
15793c0086b8SZbigniew Bodek 	stats->tx_mcast_frames_ok = GET_TX_STATS(TX_MCAST);
15803c0086b8SZbigniew Bodek 	stats->tx_drops = GET_TX_STATS(TX_DROP);
15813c0086b8SZbigniew Bodek 
15823c0086b8SZbigniew Bodek 	drv_stats->tx_frames_ok = stats->tx_ucast_frames_ok +
15832306b72aSZbigniew Bodek 	    stats->tx_bcast_frames_ok + stats->tx_mcast_frames_ok;
15842306b72aSZbigniew Bodek 	drv_stats->rx_drops = stats->rx_drop_red + stats->rx_drop_overrun;
15853c0086b8SZbigniew Bodek 	drv_stats->tx_drops = stats->tx_drops;
15863c0086b8SZbigniew Bodek 
15873c0086b8SZbigniew Bodek 	/* Update RQ and SQ stats */
15883c0086b8SZbigniew Bodek 	for (qidx = 0; qidx < qs->rq_cnt; qidx++)
15893c0086b8SZbigniew Bodek 		nicvf_update_rq_stats(nic, qidx);
15903c0086b8SZbigniew Bodek 	for (qidx = 0; qidx < qs->sq_cnt; qidx++)
15913c0086b8SZbigniew Bodek 		nicvf_update_sq_stats(nic, qidx);
15923c0086b8SZbigniew Bodek }
15933c0086b8SZbigniew Bodek 
15942306b72aSZbigniew Bodek static void
nicvf_tick_stats(void * arg)15952306b72aSZbigniew Bodek nicvf_tick_stats(void *arg)
15963c0086b8SZbigniew Bodek {
15972306b72aSZbigniew Bodek 	struct nicvf *nic;
15983c0086b8SZbigniew Bodek 
15992306b72aSZbigniew Bodek 	nic = (struct nicvf *)arg;
16002306b72aSZbigniew Bodek 
16012306b72aSZbigniew Bodek 	/* Read the statistics */
16023c0086b8SZbigniew Bodek 	nicvf_update_stats(nic);
16033c0086b8SZbigniew Bodek 
16042306b72aSZbigniew Bodek 	callout_reset(&nic->stats_callout, hz, nicvf_tick_stats, nic);
16053c0086b8SZbigniew Bodek }
1606