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