162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Cavium, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <linux/netdevice.h> 1062306a36Sopenharmony_ci#include <linux/if_vlan.h> 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <linux/ethtool.h> 1362306a36Sopenharmony_ci#include <linux/log2.h> 1462306a36Sopenharmony_ci#include <linux/prefetch.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/iommu.h> 1762306a36Sopenharmony_ci#include <linux/bpf.h> 1862306a36Sopenharmony_ci#include <linux/bpf_trace.h> 1962306a36Sopenharmony_ci#include <linux/filter.h> 2062306a36Sopenharmony_ci#include <linux/net_tstamp.h> 2162306a36Sopenharmony_ci#include <linux/workqueue.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "nic_reg.h" 2462306a36Sopenharmony_ci#include "nic.h" 2562306a36Sopenharmony_ci#include "nicvf_queues.h" 2662306a36Sopenharmony_ci#include "thunder_bgx.h" 2762306a36Sopenharmony_ci#include "../common/cavium_ptp.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DRV_NAME "nicvf" 3062306a36Sopenharmony_ci#define DRV_VERSION "1.0" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* NOTE: Packets bigger than 1530 are split across multiple pages and XDP needs 3362306a36Sopenharmony_ci * the buffer to be contiguous. Allow XDP to be set up only if we don't exceed 3462306a36Sopenharmony_ci * this value, keeping headroom for the 14 byte Ethernet header and two 3562306a36Sopenharmony_ci * VLAN tags (for QinQ) 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define MAX_XDP_MTU (1530 - ETH_HLEN - VLAN_HLEN * 2) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Supported devices */ 4062306a36Sopenharmony_cistatic const struct pci_device_id nicvf_id_table[] = { 4162306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, 4262306a36Sopenharmony_ci PCI_DEVICE_ID_THUNDER_NIC_VF, 4362306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 4462306a36Sopenharmony_ci PCI_SUBSYS_DEVID_88XX_NIC_VF) }, 4562306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, 4662306a36Sopenharmony_ci PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF, 4762306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 4862306a36Sopenharmony_ci PCI_SUBSYS_DEVID_88XX_PASS1_NIC_VF) }, 4962306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, 5062306a36Sopenharmony_ci PCI_DEVICE_ID_THUNDER_NIC_VF, 5162306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 5262306a36Sopenharmony_ci PCI_SUBSYS_DEVID_81XX_NIC_VF) }, 5362306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, 5462306a36Sopenharmony_ci PCI_DEVICE_ID_THUNDER_NIC_VF, 5562306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 5662306a36Sopenharmony_ci PCI_SUBSYS_DEVID_83XX_NIC_VF) }, 5762306a36Sopenharmony_ci { 0, } /* end of table */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciMODULE_AUTHOR("Sunil Goutham"); 6162306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder NIC Virtual Function Driver"); 6262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6362306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nicvf_id_table); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int debug = 0x00; 6762306a36Sopenharmony_cimodule_param(debug, int, 0644); 6862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug message level bitmap"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int cpi_alg = CPI_ALG_NONE; 7162306a36Sopenharmony_cimodule_param(cpi_alg, int, 0444); 7262306a36Sopenharmony_ciMODULE_PARM_DESC(cpi_alg, 7362306a36Sopenharmony_ci "PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (nic->sqs_mode) 7862306a36Sopenharmony_ci return qidx + ((nic->sqs_id + 1) * MAX_CMP_QUEUES_PER_QS); 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci return qidx; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* The Cavium ThunderX network controller can *only* be found in SoCs 8462306a36Sopenharmony_ci * containing the ThunderX ARM64 CPU implementation. All accesses to the device 8562306a36Sopenharmony_ci * registers on this platform are implicitly strongly ordered with respect 8662306a36Sopenharmony_ci * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use 8762306a36Sopenharmony_ci * with no memory barriers in this driver. The readq()/writeq() functions add 8862306a36Sopenharmony_ci * explicit ordering operation which in this case are redundant, and only 8962306a36Sopenharmony_ci * add overhead. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Register read/write APIs */ 9362306a36Sopenharmony_civoid nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci writeq_relaxed(val, nic->reg_base + offset); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciu64 nicvf_reg_read(struct nicvf *nic, u64 offset) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return readq_relaxed(nic->reg_base + offset); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid nicvf_queue_reg_write(struct nicvf *nic, u64 offset, 10462306a36Sopenharmony_ci u64 qidx, u64 val) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci void __iomem *addr = nic->reg_base + offset; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci writeq_relaxed(val, addr + (qidx << NIC_Q_NUM_SHIFT)); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciu64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci void __iomem *addr = nic->reg_base + offset; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return readq_relaxed(addr + (qidx << NIC_Q_NUM_SHIFT)); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* VF -> PF mailbox communication */ 11962306a36Sopenharmony_cistatic void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u64 *msg = (u64 *)mbx; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]); 12462306a36Sopenharmony_ci nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned long timeout; 13062306a36Sopenharmony_ci int ret = 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mutex_lock(&nic->rx_mode_mtx); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci nic->pf_acked = false; 13562306a36Sopenharmony_ci nic->pf_nacked = false; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci nicvf_write_to_mbx(nic, mbx); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(NIC_MBOX_MSG_TIMEOUT); 14062306a36Sopenharmony_ci /* Wait for previous message to be acked, timeout 2sec */ 14162306a36Sopenharmony_ci while (!nic->pf_acked) { 14262306a36Sopenharmony_ci if (nic->pf_nacked) { 14362306a36Sopenharmony_ci netdev_err(nic->netdev, 14462306a36Sopenharmony_ci "PF NACK to mbox msg 0x%02x from VF%d\n", 14562306a36Sopenharmony_ci (mbx->msg.msg & 0xFF), nic->vf_id); 14662306a36Sopenharmony_ci ret = -EINVAL; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci usleep_range(8000, 10000); 15062306a36Sopenharmony_ci if (nic->pf_acked) 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 15362306a36Sopenharmony_ci netdev_err(nic->netdev, 15462306a36Sopenharmony_ci "PF didn't ACK to mbox msg 0x%02x from VF%d\n", 15562306a36Sopenharmony_ci (mbx->msg.msg & 0xFF), nic->vf_id); 15662306a36Sopenharmony_ci ret = -EBUSY; 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci mutex_unlock(&nic->rx_mode_mtx); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Checks if VF is able to comminicate with PF 16562306a36Sopenharmony_ci* and also gets the VNIC number this VF is associated to. 16662306a36Sopenharmony_ci*/ 16762306a36Sopenharmony_cistatic int nicvf_check_pf_ready(struct nicvf *nic) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci union nic_mbx mbx = {}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci mbx.msg.msg = NIC_MBOX_MSG_READY; 17262306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx)) { 17362306a36Sopenharmony_ci netdev_err(nic->netdev, 17462306a36Sopenharmony_ci "PF didn't respond to READY msg\n"); 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 1; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void nicvf_send_cfg_done(struct nicvf *nic) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci union nic_mbx mbx = {}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE; 18662306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx)) { 18762306a36Sopenharmony_ci netdev_err(nic->netdev, 18862306a36Sopenharmony_ci "PF didn't respond to CFG DONE msg\n"); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void nicvf_read_bgx_stats(struct nicvf *nic, struct bgx_stats_msg *bgx) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci if (bgx->rx) 19562306a36Sopenharmony_ci nic->bgx_stats.rx_stats[bgx->idx] = bgx->stats; 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci nic->bgx_stats.tx_stats[bgx->idx] = bgx->stats; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void nicvf_handle_mbx_intr(struct nicvf *nic) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci union nic_mbx mbx = {}; 20362306a36Sopenharmony_ci u64 *mbx_data; 20462306a36Sopenharmony_ci u64 mbx_addr; 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci mbx_addr = NIC_VF_PF_MAILBOX_0_1; 20862306a36Sopenharmony_ci mbx_data = (u64 *)&mbx; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { 21162306a36Sopenharmony_ci *mbx_data = nicvf_reg_read(nic, mbx_addr); 21262306a36Sopenharmony_ci mbx_data++; 21362306a36Sopenharmony_ci mbx_addr += sizeof(u64); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci netdev_dbg(nic->netdev, "Mbox message: msg: 0x%x\n", mbx.msg.msg); 21762306a36Sopenharmony_ci switch (mbx.msg.msg) { 21862306a36Sopenharmony_ci case NIC_MBOX_MSG_READY: 21962306a36Sopenharmony_ci nic->pf_acked = true; 22062306a36Sopenharmony_ci nic->vf_id = mbx.nic_cfg.vf_id & 0x7F; 22162306a36Sopenharmony_ci nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F; 22262306a36Sopenharmony_ci nic->node = mbx.nic_cfg.node_id; 22362306a36Sopenharmony_ci if (!nic->set_mac_pending) 22462306a36Sopenharmony_ci eth_hw_addr_set(nic->netdev, mbx.nic_cfg.mac_addr); 22562306a36Sopenharmony_ci nic->sqs_mode = mbx.nic_cfg.sqs_mode; 22662306a36Sopenharmony_ci nic->loopback_supported = mbx.nic_cfg.loopback_supported; 22762306a36Sopenharmony_ci nic->link_up = false; 22862306a36Sopenharmony_ci nic->duplex = 0; 22962306a36Sopenharmony_ci nic->speed = 0; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case NIC_MBOX_MSG_ACK: 23262306a36Sopenharmony_ci nic->pf_acked = true; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case NIC_MBOX_MSG_NACK: 23562306a36Sopenharmony_ci nic->pf_nacked = true; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case NIC_MBOX_MSG_RSS_SIZE: 23862306a36Sopenharmony_ci nic->rss_info.rss_size = mbx.rss_size.ind_tbl_size; 23962306a36Sopenharmony_ci nic->pf_acked = true; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case NIC_MBOX_MSG_BGX_STATS: 24262306a36Sopenharmony_ci nicvf_read_bgx_stats(nic, &mbx.bgx_stats); 24362306a36Sopenharmony_ci nic->pf_acked = true; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case NIC_MBOX_MSG_BGX_LINK_CHANGE: 24662306a36Sopenharmony_ci nic->pf_acked = true; 24762306a36Sopenharmony_ci if (nic->link_up != mbx.link_status.link_up) { 24862306a36Sopenharmony_ci nic->link_up = mbx.link_status.link_up; 24962306a36Sopenharmony_ci nic->duplex = mbx.link_status.duplex; 25062306a36Sopenharmony_ci nic->speed = mbx.link_status.speed; 25162306a36Sopenharmony_ci nic->mac_type = mbx.link_status.mac_type; 25262306a36Sopenharmony_ci if (nic->link_up) { 25362306a36Sopenharmony_ci netdev_info(nic->netdev, 25462306a36Sopenharmony_ci "Link is Up %d Mbps %s duplex\n", 25562306a36Sopenharmony_ci nic->speed, 25662306a36Sopenharmony_ci nic->duplex == DUPLEX_FULL ? 25762306a36Sopenharmony_ci "Full" : "Half"); 25862306a36Sopenharmony_ci netif_carrier_on(nic->netdev); 25962306a36Sopenharmony_ci netif_tx_start_all_queues(nic->netdev); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci netdev_info(nic->netdev, "Link is Down\n"); 26262306a36Sopenharmony_ci netif_carrier_off(nic->netdev); 26362306a36Sopenharmony_ci netif_tx_stop_all_queues(nic->netdev); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci case NIC_MBOX_MSG_ALLOC_SQS: 26862306a36Sopenharmony_ci nic->sqs_count = mbx.sqs_alloc.qs_count; 26962306a36Sopenharmony_ci nic->pf_acked = true; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case NIC_MBOX_MSG_SNICVF_PTR: 27262306a36Sopenharmony_ci /* Primary VF: make note of secondary VF's pointer 27362306a36Sopenharmony_ci * to be used while packet transmission. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci nic->snicvf[mbx.nicvf.sqs_id] = 27662306a36Sopenharmony_ci (struct nicvf *)mbx.nicvf.nicvf; 27762306a36Sopenharmony_ci nic->pf_acked = true; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case NIC_MBOX_MSG_PNICVF_PTR: 28062306a36Sopenharmony_ci /* Secondary VF/Qset: make note of primary VF's pointer 28162306a36Sopenharmony_ci * to be used while packet reception, to handover packet 28262306a36Sopenharmony_ci * to primary VF's netdev. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf; 28562306a36Sopenharmony_ci nic->pf_acked = true; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case NIC_MBOX_MSG_PFC: 28862306a36Sopenharmony_ci nic->pfc.autoneg = mbx.pfc.autoneg; 28962306a36Sopenharmony_ci nic->pfc.fc_rx = mbx.pfc.fc_rx; 29062306a36Sopenharmony_ci nic->pfc.fc_tx = mbx.pfc.fc_tx; 29162306a36Sopenharmony_ci nic->pf_acked = true; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci default: 29462306a36Sopenharmony_ci netdev_err(nic->netdev, 29562306a36Sopenharmony_ci "Invalid message from PF, msg 0x%x\n", mbx.msg.msg); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int nicvf_hw_set_mac_addr(struct nicvf *nic, struct net_device *netdev) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci union nic_mbx mbx = {}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci mbx.mac.msg = NIC_MBOX_MSG_SET_MAC; 30662306a36Sopenharmony_ci mbx.mac.vf_id = nic->vf_id; 30762306a36Sopenharmony_ci ether_addr_copy(mbx.mac.mac_addr, netdev->dev_addr); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return nicvf_send_msg_to_pf(nic, &mbx); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void nicvf_config_cpi(struct nicvf *nic) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci union nic_mbx mbx = {}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG; 31762306a36Sopenharmony_ci mbx.cpi_cfg.vf_id = nic->vf_id; 31862306a36Sopenharmony_ci mbx.cpi_cfg.cpi_alg = nic->cpi_alg; 31962306a36Sopenharmony_ci mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void nicvf_get_rss_size(struct nicvf *nic) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci union nic_mbx mbx = {}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci mbx.rss_size.msg = NIC_MBOX_MSG_RSS_SIZE; 32962306a36Sopenharmony_ci mbx.rss_size.vf_id = nic->vf_id; 33062306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_civoid nicvf_config_rss(struct nicvf *nic) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci union nic_mbx mbx = {}; 33662306a36Sopenharmony_ci struct nicvf_rss_info *rss = &nic->rss_info; 33762306a36Sopenharmony_ci int ind_tbl_len = rss->rss_size; 33862306a36Sopenharmony_ci int i, nextq = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci mbx.rss_cfg.vf_id = nic->vf_id; 34162306a36Sopenharmony_ci mbx.rss_cfg.hash_bits = rss->hash_bits; 34262306a36Sopenharmony_ci while (ind_tbl_len) { 34362306a36Sopenharmony_ci mbx.rss_cfg.tbl_offset = nextq; 34462306a36Sopenharmony_ci mbx.rss_cfg.tbl_len = min(ind_tbl_len, 34562306a36Sopenharmony_ci RSS_IND_TBL_LEN_PER_MBX_MSG); 34662306a36Sopenharmony_ci mbx.rss_cfg.msg = mbx.rss_cfg.tbl_offset ? 34762306a36Sopenharmony_ci NIC_MBOX_MSG_RSS_CFG_CONT : NIC_MBOX_MSG_RSS_CFG; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci for (i = 0; i < mbx.rss_cfg.tbl_len; i++) 35062306a36Sopenharmony_ci mbx.rss_cfg.ind_tbl[i] = rss->ind_tbl[nextq++]; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ind_tbl_len -= mbx.rss_cfg.tbl_len; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_civoid nicvf_set_rss_key(struct nicvf *nic) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct nicvf_rss_info *rss = &nic->rss_info; 36162306a36Sopenharmony_ci u64 key_addr = NIC_VNIC_RSS_KEY_0_4; 36262306a36Sopenharmony_ci int idx; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (idx = 0; idx < RSS_HASH_KEY_SIZE; idx++) { 36562306a36Sopenharmony_ci nicvf_reg_write(nic, key_addr, rss->key[idx]); 36662306a36Sopenharmony_ci key_addr += sizeof(u64); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int nicvf_rss_init(struct nicvf *nic) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct nicvf_rss_info *rss = &nic->rss_info; 37362306a36Sopenharmony_ci int idx; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci nicvf_get_rss_size(nic); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (cpi_alg != CPI_ALG_NONE) { 37862306a36Sopenharmony_ci rss->enable = false; 37962306a36Sopenharmony_ci rss->hash_bits = 0; 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci rss->enable = true; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci netdev_rss_key_fill(rss->key, RSS_HASH_KEY_SIZE * sizeof(u64)); 38662306a36Sopenharmony_ci nicvf_set_rss_key(nic); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci rss->cfg = RSS_IP_HASH_ENA | RSS_TCP_HASH_ENA | RSS_UDP_HASH_ENA; 38962306a36Sopenharmony_ci nicvf_reg_write(nic, NIC_VNIC_RSS_CFG, rss->cfg); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci rss->hash_bits = ilog2(rounddown_pow_of_two(rss->rss_size)); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (idx = 0; idx < rss->rss_size; idx++) 39462306a36Sopenharmony_ci rss->ind_tbl[idx] = ethtool_rxfh_indir_default(idx, 39562306a36Sopenharmony_ci nic->rx_queues); 39662306a36Sopenharmony_ci nicvf_config_rss(nic); 39762306a36Sopenharmony_ci return 1; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* Request PF to allocate additional Qsets */ 40162306a36Sopenharmony_cistatic void nicvf_request_sqs(struct nicvf *nic) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci union nic_mbx mbx = {}; 40462306a36Sopenharmony_ci int sqs; 40562306a36Sopenharmony_ci int sqs_count = nic->sqs_count; 40662306a36Sopenharmony_ci int rx_queues = 0, tx_queues = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Only primary VF should request */ 40962306a36Sopenharmony_ci if (nic->sqs_mode || !nic->sqs_count) 41062306a36Sopenharmony_ci return; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci mbx.sqs_alloc.msg = NIC_MBOX_MSG_ALLOC_SQS; 41362306a36Sopenharmony_ci mbx.sqs_alloc.vf_id = nic->vf_id; 41462306a36Sopenharmony_ci mbx.sqs_alloc.qs_count = nic->sqs_count; 41562306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx)) { 41662306a36Sopenharmony_ci /* No response from PF */ 41762306a36Sopenharmony_ci nic->sqs_count = 0; 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Return if no Secondary Qsets available */ 42262306a36Sopenharmony_ci if (!nic->sqs_count) 42362306a36Sopenharmony_ci return; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS) 42662306a36Sopenharmony_ci rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci tx_queues = nic->tx_queues + nic->xdp_tx_queues; 42962306a36Sopenharmony_ci if (tx_queues > MAX_SND_QUEUES_PER_QS) 43062306a36Sopenharmony_ci tx_queues = tx_queues - MAX_SND_QUEUES_PER_QS; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Set no of Rx/Tx queues in each of the SQsets */ 43362306a36Sopenharmony_ci for (sqs = 0; sqs < nic->sqs_count; sqs++) { 43462306a36Sopenharmony_ci mbx.nicvf.msg = NIC_MBOX_MSG_SNICVF_PTR; 43562306a36Sopenharmony_ci mbx.nicvf.vf_id = nic->vf_id; 43662306a36Sopenharmony_ci mbx.nicvf.sqs_id = sqs; 43762306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci nic->snicvf[sqs]->sqs_id = sqs; 44062306a36Sopenharmony_ci if (rx_queues > MAX_RCV_QUEUES_PER_QS) { 44162306a36Sopenharmony_ci nic->snicvf[sqs]->qs->rq_cnt = MAX_RCV_QUEUES_PER_QS; 44262306a36Sopenharmony_ci rx_queues -= MAX_RCV_QUEUES_PER_QS; 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci nic->snicvf[sqs]->qs->rq_cnt = rx_queues; 44562306a36Sopenharmony_ci rx_queues = 0; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (tx_queues > MAX_SND_QUEUES_PER_QS) { 44962306a36Sopenharmony_ci nic->snicvf[sqs]->qs->sq_cnt = MAX_SND_QUEUES_PER_QS; 45062306a36Sopenharmony_ci tx_queues -= MAX_SND_QUEUES_PER_QS; 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci nic->snicvf[sqs]->qs->sq_cnt = tx_queues; 45362306a36Sopenharmony_ci tx_queues = 0; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci nic->snicvf[sqs]->qs->cq_cnt = 45762306a36Sopenharmony_ci max(nic->snicvf[sqs]->qs->rq_cnt, nic->snicvf[sqs]->qs->sq_cnt); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Initialize secondary Qset's queues and its interrupts */ 46062306a36Sopenharmony_ci nicvf_open(nic->snicvf[sqs]->netdev); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Update stack with actual Rx/Tx queue count allocated */ 46462306a36Sopenharmony_ci if (sqs_count != nic->sqs_count) 46562306a36Sopenharmony_ci nicvf_set_real_num_queues(nic->netdev, 46662306a36Sopenharmony_ci nic->tx_queues, nic->rx_queues); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/* Send this Qset's nicvf pointer to PF. 47062306a36Sopenharmony_ci * PF inturn sends primary VF's nicvf struct to secondary Qsets/VFs 47162306a36Sopenharmony_ci * so that packets received by these Qsets can use primary VF's netdev 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_cistatic void nicvf_send_vf_struct(struct nicvf *nic) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci union nic_mbx mbx = {}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci mbx.nicvf.msg = NIC_MBOX_MSG_NICVF_PTR; 47862306a36Sopenharmony_ci mbx.nicvf.sqs_mode = nic->sqs_mode; 47962306a36Sopenharmony_ci mbx.nicvf.nicvf = (u64)nic; 48062306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void nicvf_get_primary_vf_struct(struct nicvf *nic) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci union nic_mbx mbx = {}; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci mbx.nicvf.msg = NIC_MBOX_MSG_PNICVF_PTR; 48862306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ciint nicvf_set_real_num_queues(struct net_device *netdev, 49262306a36Sopenharmony_ci int tx_queues, int rx_queues) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci int err = 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci err = netif_set_real_num_tx_queues(netdev, tx_queues); 49762306a36Sopenharmony_ci if (err) { 49862306a36Sopenharmony_ci netdev_err(netdev, 49962306a36Sopenharmony_ci "Failed to set no of Tx queues: %d\n", tx_queues); 50062306a36Sopenharmony_ci return err; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci err = netif_set_real_num_rx_queues(netdev, rx_queues); 50462306a36Sopenharmony_ci if (err) 50562306a36Sopenharmony_ci netdev_err(netdev, 50662306a36Sopenharmony_ci "Failed to set no of Rx queues: %d\n", rx_queues); 50762306a36Sopenharmony_ci return err; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int nicvf_init_resources(struct nicvf *nic) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci int err; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Enable Qset */ 51562306a36Sopenharmony_ci nicvf_qset_config(nic, true); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Initialize queues and HW for data transfer */ 51862306a36Sopenharmony_ci err = nicvf_config_data_transfer(nic, true); 51962306a36Sopenharmony_ci if (err) { 52062306a36Sopenharmony_ci netdev_err(nic->netdev, 52162306a36Sopenharmony_ci "Failed to alloc/config VF's QSet resources\n"); 52262306a36Sopenharmony_ci return err; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, 52962306a36Sopenharmony_ci struct cqe_rx_t *cqe_rx, struct snd_queue *sq, 53062306a36Sopenharmony_ci struct rcv_queue *rq, struct sk_buff **skb) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci unsigned char *hard_start, *data; 53362306a36Sopenharmony_ci struct xdp_buff xdp; 53462306a36Sopenharmony_ci struct page *page; 53562306a36Sopenharmony_ci u32 action; 53662306a36Sopenharmony_ci u16 len, offset = 0; 53762306a36Sopenharmony_ci u64 dma_addr, cpu_addr; 53862306a36Sopenharmony_ci void *orig_data; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Retrieve packet buffer's DMA address and length */ 54162306a36Sopenharmony_ci len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64)))); 54262306a36Sopenharmony_ci dma_addr = *((u64 *)((void *)cqe_rx + (7 * sizeof(u64)))); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci cpu_addr = nicvf_iova_to_phys(nic, dma_addr); 54562306a36Sopenharmony_ci if (!cpu_addr) 54662306a36Sopenharmony_ci return false; 54762306a36Sopenharmony_ci cpu_addr = (u64)phys_to_virt(cpu_addr); 54862306a36Sopenharmony_ci page = virt_to_page((void *)cpu_addr); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci xdp_init_buff(&xdp, RCV_FRAG_LEN + XDP_PACKET_HEADROOM, 55162306a36Sopenharmony_ci &rq->xdp_rxq); 55262306a36Sopenharmony_ci hard_start = page_address(page); 55362306a36Sopenharmony_ci data = (unsigned char *)cpu_addr; 55462306a36Sopenharmony_ci xdp_prepare_buff(&xdp, hard_start, data - hard_start, len, false); 55562306a36Sopenharmony_ci orig_data = xdp.data; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci action = bpf_prog_run_xdp(prog, &xdp); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci len = xdp.data_end - xdp.data; 56062306a36Sopenharmony_ci /* Check if XDP program has changed headers */ 56162306a36Sopenharmony_ci if (orig_data != xdp.data) { 56262306a36Sopenharmony_ci offset = orig_data - xdp.data; 56362306a36Sopenharmony_ci dma_addr -= offset; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci switch (action) { 56762306a36Sopenharmony_ci case XDP_PASS: 56862306a36Sopenharmony_ci /* Check if it's a recycled page, if not 56962306a36Sopenharmony_ci * unmap the DMA mapping. 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Recycled page holds an extra reference. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci if (page_ref_count(page) == 1) { 57462306a36Sopenharmony_ci dma_addr &= PAGE_MASK; 57562306a36Sopenharmony_ci dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, 57662306a36Sopenharmony_ci RCV_FRAG_LEN + XDP_PACKET_HEADROOM, 57762306a36Sopenharmony_ci DMA_FROM_DEVICE, 57862306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Build SKB and pass on packet to network stack */ 58262306a36Sopenharmony_ci *skb = build_skb(xdp.data, 58362306a36Sopenharmony_ci RCV_FRAG_LEN - cqe_rx->align_pad + offset); 58462306a36Sopenharmony_ci if (!*skb) 58562306a36Sopenharmony_ci put_page(page); 58662306a36Sopenharmony_ci else 58762306a36Sopenharmony_ci skb_put(*skb, len); 58862306a36Sopenharmony_ci return false; 58962306a36Sopenharmony_ci case XDP_TX: 59062306a36Sopenharmony_ci nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len); 59162306a36Sopenharmony_ci return true; 59262306a36Sopenharmony_ci default: 59362306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(nic->netdev, prog, action); 59462306a36Sopenharmony_ci fallthrough; 59562306a36Sopenharmony_ci case XDP_ABORTED: 59662306a36Sopenharmony_ci trace_xdp_exception(nic->netdev, prog, action); 59762306a36Sopenharmony_ci fallthrough; 59862306a36Sopenharmony_ci case XDP_DROP: 59962306a36Sopenharmony_ci /* Check if it's a recycled page, if not 60062306a36Sopenharmony_ci * unmap the DMA mapping. 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * Recycled page holds an extra reference. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (page_ref_count(page) == 1) { 60562306a36Sopenharmony_ci dma_addr &= PAGE_MASK; 60662306a36Sopenharmony_ci dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, 60762306a36Sopenharmony_ci RCV_FRAG_LEN + XDP_PACKET_HEADROOM, 60862306a36Sopenharmony_ci DMA_FROM_DEVICE, 60962306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci put_page(page); 61262306a36Sopenharmony_ci return true; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci return false; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void nicvf_snd_ptp_handler(struct net_device *netdev, 61862306a36Sopenharmony_ci struct cqe_send_t *cqe_tx) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 62162306a36Sopenharmony_ci struct skb_shared_hwtstamps ts; 62262306a36Sopenharmony_ci u64 ns; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci nic = nic->pnicvf; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Sync for 'ptp_skb' */ 62762306a36Sopenharmony_ci smp_rmb(); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* New timestamp request can be queued now */ 63062306a36Sopenharmony_ci atomic_set(&nic->tx_ptp_skbs, 0); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Check for timestamp requested skb */ 63362306a36Sopenharmony_ci if (!nic->ptp_skb) 63462306a36Sopenharmony_ci return; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Check if timestamping is timedout, which is set to 10us */ 63762306a36Sopenharmony_ci if (cqe_tx->send_status == CQ_TX_ERROP_TSTMP_TIMEOUT || 63862306a36Sopenharmony_ci cqe_tx->send_status == CQ_TX_ERROP_TSTMP_CONFLICT) 63962306a36Sopenharmony_ci goto no_tstamp; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Get the timestamp */ 64262306a36Sopenharmony_ci memset(&ts, 0, sizeof(ts)); 64362306a36Sopenharmony_ci ns = cavium_ptp_tstamp2time(nic->ptp_clock, cqe_tx->ptp_timestamp); 64462306a36Sopenharmony_ci ts.hwtstamp = ns_to_ktime(ns); 64562306a36Sopenharmony_ci skb_tstamp_tx(nic->ptp_skb, &ts); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cino_tstamp: 64862306a36Sopenharmony_ci /* Free the original skb */ 64962306a36Sopenharmony_ci dev_kfree_skb_any(nic->ptp_skb); 65062306a36Sopenharmony_ci nic->ptp_skb = NULL; 65162306a36Sopenharmony_ci /* Sync 'ptp_skb' */ 65262306a36Sopenharmony_ci smp_wmb(); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void nicvf_snd_pkt_handler(struct net_device *netdev, 65662306a36Sopenharmony_ci struct cqe_send_t *cqe_tx, 65762306a36Sopenharmony_ci int budget, int *subdesc_cnt, 65862306a36Sopenharmony_ci unsigned int *tx_pkts, unsigned int *tx_bytes) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct sk_buff *skb = NULL; 66162306a36Sopenharmony_ci struct page *page; 66262306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 66362306a36Sopenharmony_ci struct snd_queue *sq; 66462306a36Sopenharmony_ci struct sq_hdr_subdesc *hdr; 66562306a36Sopenharmony_ci struct sq_hdr_subdesc *tso_sqe; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci sq = &nic->qs->sq[cqe_tx->sq_idx]; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr); 67062306a36Sopenharmony_ci if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* Check for errors */ 67462306a36Sopenharmony_ci if (cqe_tx->send_status) 67562306a36Sopenharmony_ci nicvf_check_cqe_tx_errs(nic->pnicvf, cqe_tx); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* Is this a XDP designated Tx queue */ 67862306a36Sopenharmony_ci if (sq->is_xdp) { 67962306a36Sopenharmony_ci page = (struct page *)sq->xdp_page[cqe_tx->sqe_ptr]; 68062306a36Sopenharmony_ci /* Check if it's recycled page or else unmap DMA mapping */ 68162306a36Sopenharmony_ci if (page && (page_ref_count(page) == 1)) 68262306a36Sopenharmony_ci nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr, 68362306a36Sopenharmony_ci hdr->subdesc_cnt); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* Release page reference for recycling */ 68662306a36Sopenharmony_ci if (page) 68762306a36Sopenharmony_ci put_page(page); 68862306a36Sopenharmony_ci sq->xdp_page[cqe_tx->sqe_ptr] = (u64)NULL; 68962306a36Sopenharmony_ci *subdesc_cnt += hdr->subdesc_cnt + 1; 69062306a36Sopenharmony_ci return; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr]; 69462306a36Sopenharmony_ci if (skb) { 69562306a36Sopenharmony_ci /* Check for dummy descriptor used for HW TSO offload on 88xx */ 69662306a36Sopenharmony_ci if (hdr->dont_send) { 69762306a36Sopenharmony_ci /* Get actual TSO descriptors and free them */ 69862306a36Sopenharmony_ci tso_sqe = 69962306a36Sopenharmony_ci (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, hdr->rsvd2); 70062306a36Sopenharmony_ci nicvf_unmap_sndq_buffers(nic, sq, hdr->rsvd2, 70162306a36Sopenharmony_ci tso_sqe->subdesc_cnt); 70262306a36Sopenharmony_ci *subdesc_cnt += tso_sqe->subdesc_cnt + 1; 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr, 70562306a36Sopenharmony_ci hdr->subdesc_cnt); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci *subdesc_cnt += hdr->subdesc_cnt + 1; 70862306a36Sopenharmony_ci prefetch(skb); 70962306a36Sopenharmony_ci (*tx_pkts)++; 71062306a36Sopenharmony_ci *tx_bytes += skb->len; 71162306a36Sopenharmony_ci /* If timestamp is requested for this skb, don't free it */ 71262306a36Sopenharmony_ci if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 71362306a36Sopenharmony_ci !nic->pnicvf->ptp_skb) 71462306a36Sopenharmony_ci nic->pnicvf->ptp_skb = skb; 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci napi_consume_skb(skb, budget); 71762306a36Sopenharmony_ci sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL; 71862306a36Sopenharmony_ci } else { 71962306a36Sopenharmony_ci /* In case of SW TSO on 88xx, only last segment will have 72062306a36Sopenharmony_ci * a SKB attached, so just free SQEs here. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci if (!nic->hw_tso) 72362306a36Sopenharmony_ci *subdesc_cnt += hdr->subdesc_cnt + 1; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic inline void nicvf_set_rxhash(struct net_device *netdev, 72862306a36Sopenharmony_ci struct cqe_rx_t *cqe_rx, 72962306a36Sopenharmony_ci struct sk_buff *skb) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci u8 hash_type; 73262306a36Sopenharmony_ci u32 hash; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!(netdev->features & NETIF_F_RXHASH)) 73562306a36Sopenharmony_ci return; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci switch (cqe_rx->rss_alg) { 73862306a36Sopenharmony_ci case RSS_ALG_TCP_IP: 73962306a36Sopenharmony_ci case RSS_ALG_UDP_IP: 74062306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_L4; 74162306a36Sopenharmony_ci hash = cqe_rx->rss_tag; 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci case RSS_ALG_IP: 74462306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_L3; 74562306a36Sopenharmony_ci hash = cqe_rx->rss_tag; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci default: 74862306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_NONE; 74962306a36Sopenharmony_ci hash = 0; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci skb_set_hash(skb, hash, hash_type); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic inline void nicvf_set_rxtstamp(struct nicvf *nic, struct sk_buff *skb) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci u64 ns; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!nic->ptp_clock || !nic->hw_rx_tstamp) 76062306a36Sopenharmony_ci return; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* The first 8 bytes is the timestamp */ 76362306a36Sopenharmony_ci ns = cavium_ptp_tstamp2time(nic->ptp_clock, 76462306a36Sopenharmony_ci be64_to_cpu(*(__be64 *)skb->data)); 76562306a36Sopenharmony_ci skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci __skb_pull(skb, 8); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic void nicvf_rcv_pkt_handler(struct net_device *netdev, 77162306a36Sopenharmony_ci struct napi_struct *napi, 77262306a36Sopenharmony_ci struct cqe_rx_t *cqe_rx, 77362306a36Sopenharmony_ci struct snd_queue *sq, struct rcv_queue *rq) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct sk_buff *skb = NULL; 77662306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 77762306a36Sopenharmony_ci struct nicvf *snic = nic; 77862306a36Sopenharmony_ci int err = 0; 77962306a36Sopenharmony_ci int rq_idx; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci rq_idx = nicvf_netdev_qidx(nic, cqe_rx->rq_idx); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (nic->sqs_mode) { 78462306a36Sopenharmony_ci /* Use primary VF's 'nicvf' struct */ 78562306a36Sopenharmony_ci nic = nic->pnicvf; 78662306a36Sopenharmony_ci netdev = nic->netdev; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Check for errors */ 79062306a36Sopenharmony_ci if (cqe_rx->err_level || cqe_rx->err_opcode) { 79162306a36Sopenharmony_ci err = nicvf_check_cqe_rx_errs(nic, cqe_rx); 79262306a36Sopenharmony_ci if (err && !cqe_rx->rb_cnt) 79362306a36Sopenharmony_ci return; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* For XDP, ignore pkts spanning multiple pages */ 79762306a36Sopenharmony_ci if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) { 79862306a36Sopenharmony_ci /* Packet consumed by XDP */ 79962306a36Sopenharmony_ci if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq, rq, &skb)) 80062306a36Sopenharmony_ci return; 80162306a36Sopenharmony_ci } else { 80262306a36Sopenharmony_ci skb = nicvf_get_rcv_skb(snic, cqe_rx, 80362306a36Sopenharmony_ci nic->xdp_prog ? true : false); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!skb) 80762306a36Sopenharmony_ci return; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (netif_msg_pktdata(nic)) { 81062306a36Sopenharmony_ci netdev_info(nic->netdev, "skb 0x%p, len=%d\n", skb, skb->len); 81162306a36Sopenharmony_ci print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, 81262306a36Sopenharmony_ci skb->data, skb->len, true); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* If error packet, drop it here */ 81662306a36Sopenharmony_ci if (err) { 81762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 81862306a36Sopenharmony_ci return; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci nicvf_set_rxtstamp(nic, skb); 82262306a36Sopenharmony_ci nicvf_set_rxhash(netdev, cqe_rx, skb); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci skb_record_rx_queue(skb, rq_idx); 82562306a36Sopenharmony_ci if (netdev->hw_features & NETIF_F_RXCSUM) { 82662306a36Sopenharmony_ci /* HW by default verifies TCP/UDP/SCTP checksums */ 82762306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci skb_checksum_none_assert(skb); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* Check for stripped VLAN */ 83562306a36Sopenharmony_ci if (cqe_rx->vlan_found && cqe_rx->vlan_stripped) 83662306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 83762306a36Sopenharmony_ci ntohs((__force __be16)cqe_rx->vlan_tci)); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (napi && (netdev->features & NETIF_F_GRO)) 84062306a36Sopenharmony_ci napi_gro_receive(napi, skb); 84162306a36Sopenharmony_ci else 84262306a36Sopenharmony_ci netif_receive_skb(skb); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx, 84662306a36Sopenharmony_ci struct napi_struct *napi, int budget) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci int processed_cqe, work_done = 0, tx_done = 0; 84962306a36Sopenharmony_ci int cqe_count, cqe_head; 85062306a36Sopenharmony_ci int subdesc_cnt = 0; 85162306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 85262306a36Sopenharmony_ci struct queue_set *qs = nic->qs; 85362306a36Sopenharmony_ci struct cmp_queue *cq = &qs->cq[cq_idx]; 85462306a36Sopenharmony_ci struct cqe_rx_t *cq_desc; 85562306a36Sopenharmony_ci struct netdev_queue *txq; 85662306a36Sopenharmony_ci struct snd_queue *sq = &qs->sq[cq_idx]; 85762306a36Sopenharmony_ci struct rcv_queue *rq = &qs->rq[cq_idx]; 85862306a36Sopenharmony_ci unsigned int tx_pkts = 0, tx_bytes = 0, txq_idx; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci spin_lock_bh(&cq->lock); 86162306a36Sopenharmony_ciloop: 86262306a36Sopenharmony_ci processed_cqe = 0; 86362306a36Sopenharmony_ci /* Get no of valid CQ entries to process */ 86462306a36Sopenharmony_ci cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_idx); 86562306a36Sopenharmony_ci cqe_count &= CQ_CQE_COUNT; 86662306a36Sopenharmony_ci if (!cqe_count) 86762306a36Sopenharmony_ci goto done; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Get head of the valid CQ entries */ 87062306a36Sopenharmony_ci cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_idx) >> 9; 87162306a36Sopenharmony_ci cqe_head &= 0xFFFF; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci while (processed_cqe < cqe_count) { 87462306a36Sopenharmony_ci /* Get the CQ descriptor */ 87562306a36Sopenharmony_ci cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head); 87662306a36Sopenharmony_ci cqe_head++; 87762306a36Sopenharmony_ci cqe_head &= (cq->dmem.q_len - 1); 87862306a36Sopenharmony_ci /* Initiate prefetch for next descriptor */ 87962306a36Sopenharmony_ci prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head)); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if ((work_done >= budget) && napi && 88262306a36Sopenharmony_ci (cq_desc->cqe_type != CQE_TYPE_SEND)) { 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci switch (cq_desc->cqe_type) { 88762306a36Sopenharmony_ci case CQE_TYPE_RX: 88862306a36Sopenharmony_ci nicvf_rcv_pkt_handler(netdev, napi, cq_desc, sq, rq); 88962306a36Sopenharmony_ci work_done++; 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci case CQE_TYPE_SEND: 89262306a36Sopenharmony_ci nicvf_snd_pkt_handler(netdev, (void *)cq_desc, 89362306a36Sopenharmony_ci budget, &subdesc_cnt, 89462306a36Sopenharmony_ci &tx_pkts, &tx_bytes); 89562306a36Sopenharmony_ci tx_done++; 89662306a36Sopenharmony_ci break; 89762306a36Sopenharmony_ci case CQE_TYPE_SEND_PTP: 89862306a36Sopenharmony_ci nicvf_snd_ptp_handler(netdev, (void *)cq_desc); 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci case CQE_TYPE_INVALID: 90162306a36Sopenharmony_ci case CQE_TYPE_RX_SPLIT: 90262306a36Sopenharmony_ci case CQE_TYPE_RX_TCP: 90362306a36Sopenharmony_ci /* Ignore for now */ 90462306a36Sopenharmony_ci break; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci processed_cqe++; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Ring doorbell to inform H/W to reuse processed CQEs */ 91062306a36Sopenharmony_ci nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR, 91162306a36Sopenharmony_ci cq_idx, processed_cqe); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if ((work_done < budget) && napi) 91462306a36Sopenharmony_ci goto loop; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cidone: 91762306a36Sopenharmony_ci /* Update SQ's descriptor free count */ 91862306a36Sopenharmony_ci if (subdesc_cnt) 91962306a36Sopenharmony_ci nicvf_put_sq_desc(sq, subdesc_cnt); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci txq_idx = nicvf_netdev_qidx(nic, cq_idx); 92262306a36Sopenharmony_ci /* Handle XDP TX queues */ 92362306a36Sopenharmony_ci if (nic->pnicvf->xdp_prog) { 92462306a36Sopenharmony_ci if (txq_idx < nic->pnicvf->xdp_tx_queues) { 92562306a36Sopenharmony_ci nicvf_xdp_sq_doorbell(nic, sq, cq_idx); 92662306a36Sopenharmony_ci goto out; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci nic = nic->pnicvf; 92962306a36Sopenharmony_ci txq_idx -= nic->pnicvf->xdp_tx_queues; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Wakeup TXQ if its stopped earlier due to SQ full */ 93362306a36Sopenharmony_ci if (tx_done || 93462306a36Sopenharmony_ci (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) { 93562306a36Sopenharmony_ci netdev = nic->pnicvf->netdev; 93662306a36Sopenharmony_ci txq = netdev_get_tx_queue(netdev, txq_idx); 93762306a36Sopenharmony_ci if (tx_pkts) 93862306a36Sopenharmony_ci netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* To read updated queue and carrier status */ 94162306a36Sopenharmony_ci smp_mb(); 94262306a36Sopenharmony_ci if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) { 94362306a36Sopenharmony_ci netif_tx_wake_queue(txq); 94462306a36Sopenharmony_ci nic = nic->pnicvf; 94562306a36Sopenharmony_ci this_cpu_inc(nic->drv_stats->txq_wake); 94662306a36Sopenharmony_ci netif_warn(nic, tx_err, netdev, 94762306a36Sopenharmony_ci "Transmit queue wakeup SQ%d\n", txq_idx); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciout: 95262306a36Sopenharmony_ci spin_unlock_bh(&cq->lock); 95362306a36Sopenharmony_ci return work_done; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic int nicvf_poll(struct napi_struct *napi, int budget) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci u64 cq_head; 95962306a36Sopenharmony_ci int work_done = 0; 96062306a36Sopenharmony_ci struct net_device *netdev = napi->dev; 96162306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 96262306a36Sopenharmony_ci struct nicvf_cq_poll *cq; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci cq = container_of(napi, struct nicvf_cq_poll, napi); 96562306a36Sopenharmony_ci work_done = nicvf_cq_intr_handler(netdev, cq->cq_idx, napi, budget); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (work_done < budget) { 96862306a36Sopenharmony_ci /* Slow packet rate, exit polling */ 96962306a36Sopenharmony_ci napi_complete_done(napi, work_done); 97062306a36Sopenharmony_ci /* Re-enable interrupts */ 97162306a36Sopenharmony_ci cq_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, 97262306a36Sopenharmony_ci cq->cq_idx); 97362306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_CQ, cq->cq_idx); 97462306a36Sopenharmony_ci nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_HEAD, 97562306a36Sopenharmony_ci cq->cq_idx, cq_head); 97662306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_CQ, cq->cq_idx); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci return work_done; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci/* Qset error interrupt handler 98262306a36Sopenharmony_ci * 98362306a36Sopenharmony_ci * As of now only CQ errors are handled 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_cistatic void nicvf_handle_qs_err(struct tasklet_struct *t) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct nicvf *nic = from_tasklet(nic, t, qs_err_task); 98862306a36Sopenharmony_ci struct queue_set *qs = nic->qs; 98962306a36Sopenharmony_ci int qidx; 99062306a36Sopenharmony_ci u64 status; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci netif_tx_disable(nic->netdev); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Check if it is CQ err */ 99562306a36Sopenharmony_ci for (qidx = 0; qidx < qs->cq_cnt; qidx++) { 99662306a36Sopenharmony_ci status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, 99762306a36Sopenharmony_ci qidx); 99862306a36Sopenharmony_ci if (!(status & CQ_ERR_MASK)) 99962306a36Sopenharmony_ci continue; 100062306a36Sopenharmony_ci /* Process already queued CQEs and reconfig CQ */ 100162306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); 100262306a36Sopenharmony_ci nicvf_sq_disable(nic, qidx); 100362306a36Sopenharmony_ci nicvf_cq_intr_handler(nic->netdev, qidx, NULL, 0); 100462306a36Sopenharmony_ci nicvf_cmp_queue_config(nic, qs, qidx, true); 100562306a36Sopenharmony_ci nicvf_sq_free_used_descs(nic->netdev, &qs->sq[qidx], qidx); 100662306a36Sopenharmony_ci nicvf_sq_enable(nic, &qs->sq[qidx], qidx); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci netif_tx_start_all_queues(nic->netdev); 101262306a36Sopenharmony_ci /* Re-enable Qset error interrupt */ 101362306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic void nicvf_dump_intr_status(struct nicvf *nic) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci netif_info(nic, intr, nic->netdev, "interrupt status 0x%llx\n", 101962306a36Sopenharmony_ci nicvf_reg_read(nic, NIC_VF_INT)); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct nicvf *nic = (struct nicvf *)nicvf_irq; 102562306a36Sopenharmony_ci u64 intr; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci nicvf_dump_intr_status(nic); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci intr = nicvf_reg_read(nic, NIC_VF_INT); 103062306a36Sopenharmony_ci /* Check for spurious interrupt */ 103162306a36Sopenharmony_ci if (!(intr & NICVF_INTR_MBOX_MASK)) 103262306a36Sopenharmony_ci return IRQ_HANDLED; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci nicvf_handle_mbx_intr(nic); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return IRQ_HANDLED; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic irqreturn_t nicvf_intr_handler(int irq, void *cq_irq) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct nicvf_cq_poll *cq_poll = (struct nicvf_cq_poll *)cq_irq; 104262306a36Sopenharmony_ci struct nicvf *nic = cq_poll->nicvf; 104362306a36Sopenharmony_ci int qidx = cq_poll->cq_idx; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci nicvf_dump_intr_status(nic); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* Disable interrupts */ 104862306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* Schedule NAPI */ 105162306a36Sopenharmony_ci napi_schedule_irqoff(&cq_poll->napi); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Clear interrupt */ 105462306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return IRQ_HANDLED; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic irqreturn_t nicvf_rbdr_intr_handler(int irq, void *nicvf_irq) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct nicvf *nic = (struct nicvf *)nicvf_irq; 106262306a36Sopenharmony_ci u8 qidx; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci nicvf_dump_intr_status(nic); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Disable RBDR interrupt and schedule softirq */ 106862306a36Sopenharmony_ci for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) { 106962306a36Sopenharmony_ci if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx)) 107062306a36Sopenharmony_ci continue; 107162306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx); 107262306a36Sopenharmony_ci tasklet_hi_schedule(&nic->rbdr_task); 107362306a36Sopenharmony_ci /* Clear interrupt */ 107462306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci return IRQ_HANDLED; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic irqreturn_t nicvf_qs_err_intr_handler(int irq, void *nicvf_irq) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct nicvf *nic = (struct nicvf *)nicvf_irq; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci nicvf_dump_intr_status(nic); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* Disable Qset err interrupt and schedule softirq */ 108762306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0); 108862306a36Sopenharmony_ci tasklet_hi_schedule(&nic->qs_err_task); 108962306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return IRQ_HANDLED; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic void nicvf_set_irq_affinity(struct nicvf *nic) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci int vec, cpu; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci for (vec = 0; vec < nic->num_vec; vec++) { 109962306a36Sopenharmony_ci if (!nic->irq_allocated[vec]) 110062306a36Sopenharmony_ci continue; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (!zalloc_cpumask_var(&nic->affinity_mask[vec], GFP_KERNEL)) 110362306a36Sopenharmony_ci return; 110462306a36Sopenharmony_ci /* CQ interrupts */ 110562306a36Sopenharmony_ci if (vec < NICVF_INTR_ID_SQ) 110662306a36Sopenharmony_ci /* Leave CPU0 for RBDR and other interrupts */ 110762306a36Sopenharmony_ci cpu = nicvf_netdev_qidx(nic, vec) + 1; 110862306a36Sopenharmony_ci else 110962306a36Sopenharmony_ci cpu = 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci cpumask_set_cpu(cpumask_local_spread(cpu, nic->node), 111262306a36Sopenharmony_ci nic->affinity_mask[vec]); 111362306a36Sopenharmony_ci irq_set_affinity_hint(pci_irq_vector(nic->pdev, vec), 111462306a36Sopenharmony_ci nic->affinity_mask[vec]); 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int nicvf_register_interrupts(struct nicvf *nic) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci int irq, ret = 0; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci for_each_cq_irq(irq) 112362306a36Sopenharmony_ci sprintf(nic->irq_name[irq], "%s-rxtx-%d", 112462306a36Sopenharmony_ci nic->pnicvf->netdev->name, 112562306a36Sopenharmony_ci nicvf_netdev_qidx(nic, irq)); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci for_each_sq_irq(irq) 112862306a36Sopenharmony_ci sprintf(nic->irq_name[irq], "%s-sq-%d", 112962306a36Sopenharmony_ci nic->pnicvf->netdev->name, 113062306a36Sopenharmony_ci nicvf_netdev_qidx(nic, irq - NICVF_INTR_ID_SQ)); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci for_each_rbdr_irq(irq) 113362306a36Sopenharmony_ci sprintf(nic->irq_name[irq], "%s-rbdr-%d", 113462306a36Sopenharmony_ci nic->pnicvf->netdev->name, 113562306a36Sopenharmony_ci nic->sqs_mode ? (nic->sqs_id + 1) : 0); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* Register CQ interrupts */ 113862306a36Sopenharmony_ci for (irq = 0; irq < nic->qs->cq_cnt; irq++) { 113962306a36Sopenharmony_ci ret = request_irq(pci_irq_vector(nic->pdev, irq), 114062306a36Sopenharmony_ci nicvf_intr_handler, 114162306a36Sopenharmony_ci 0, nic->irq_name[irq], nic->napi[irq]); 114262306a36Sopenharmony_ci if (ret) 114362306a36Sopenharmony_ci goto err; 114462306a36Sopenharmony_ci nic->irq_allocated[irq] = true; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* Register RBDR interrupt */ 114862306a36Sopenharmony_ci for (irq = NICVF_INTR_ID_RBDR; 114962306a36Sopenharmony_ci irq < (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt); irq++) { 115062306a36Sopenharmony_ci ret = request_irq(pci_irq_vector(nic->pdev, irq), 115162306a36Sopenharmony_ci nicvf_rbdr_intr_handler, 115262306a36Sopenharmony_ci 0, nic->irq_name[irq], nic); 115362306a36Sopenharmony_ci if (ret) 115462306a36Sopenharmony_ci goto err; 115562306a36Sopenharmony_ci nic->irq_allocated[irq] = true; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Register QS error interrupt */ 115962306a36Sopenharmony_ci sprintf(nic->irq_name[NICVF_INTR_ID_QS_ERR], "%s-qset-err-%d", 116062306a36Sopenharmony_ci nic->pnicvf->netdev->name, 116162306a36Sopenharmony_ci nic->sqs_mode ? (nic->sqs_id + 1) : 0); 116262306a36Sopenharmony_ci irq = NICVF_INTR_ID_QS_ERR; 116362306a36Sopenharmony_ci ret = request_irq(pci_irq_vector(nic->pdev, irq), 116462306a36Sopenharmony_ci nicvf_qs_err_intr_handler, 116562306a36Sopenharmony_ci 0, nic->irq_name[irq], nic); 116662306a36Sopenharmony_ci if (ret) 116762306a36Sopenharmony_ci goto err; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci nic->irq_allocated[irq] = true; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* Set IRQ affinities */ 117262306a36Sopenharmony_ci nicvf_set_irq_affinity(nic); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cierr: 117562306a36Sopenharmony_ci if (ret) 117662306a36Sopenharmony_ci netdev_err(nic->netdev, "request_irq failed, vector %d\n", irq); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci return ret; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic void nicvf_unregister_interrupts(struct nicvf *nic) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct pci_dev *pdev = nic->pdev; 118462306a36Sopenharmony_ci int irq; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* Free registered interrupts */ 118762306a36Sopenharmony_ci for (irq = 0; irq < nic->num_vec; irq++) { 118862306a36Sopenharmony_ci if (!nic->irq_allocated[irq]) 118962306a36Sopenharmony_ci continue; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci irq_set_affinity_hint(pci_irq_vector(pdev, irq), NULL); 119262306a36Sopenharmony_ci free_cpumask_var(nic->affinity_mask[irq]); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (irq < NICVF_INTR_ID_SQ) 119562306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, irq), nic->napi[irq]); 119662306a36Sopenharmony_ci else 119762306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, irq), nic); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci nic->irq_allocated[irq] = false; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* Disable MSI-X */ 120362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 120462306a36Sopenharmony_ci nic->num_vec = 0; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci/* Initialize MSIX vectors and register MISC interrupt. 120862306a36Sopenharmony_ci * Send READY message to PF to check if its alive 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_cistatic int nicvf_register_misc_interrupt(struct nicvf *nic) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci int ret = 0; 121362306a36Sopenharmony_ci int irq = NICVF_INTR_ID_MISC; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* Return if mailbox interrupt is already registered */ 121662306a36Sopenharmony_ci if (nic->pdev->msix_enabled) 121762306a36Sopenharmony_ci return 0; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Enable MSI-X */ 122062306a36Sopenharmony_ci nic->num_vec = pci_msix_vec_count(nic->pdev); 122162306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(nic->pdev, nic->num_vec, nic->num_vec, 122262306a36Sopenharmony_ci PCI_IRQ_MSIX); 122362306a36Sopenharmony_ci if (ret < 0) { 122462306a36Sopenharmony_ci netdev_err(nic->netdev, 122562306a36Sopenharmony_ci "Req for #%d msix vectors failed\n", nic->num_vec); 122662306a36Sopenharmony_ci return ret; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci sprintf(nic->irq_name[irq], "%s Mbox", "NICVF"); 123062306a36Sopenharmony_ci /* Register Misc interrupt */ 123162306a36Sopenharmony_ci ret = request_irq(pci_irq_vector(nic->pdev, irq), 123262306a36Sopenharmony_ci nicvf_misc_intr_handler, 0, nic->irq_name[irq], nic); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (ret) 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci nic->irq_allocated[irq] = true; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* Enable mailbox interrupt */ 123962306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_MBOX, 0); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci /* Check if VF is able to communicate with PF */ 124262306a36Sopenharmony_ci if (!nicvf_check_pf_ready(nic)) { 124362306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); 124462306a36Sopenharmony_ci nicvf_unregister_interrupts(nic); 124562306a36Sopenharmony_ci return -EIO; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 125462306a36Sopenharmony_ci int qid = skb_get_queue_mapping(skb); 125562306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(netdev, qid); 125662306a36Sopenharmony_ci struct nicvf *snic; 125762306a36Sopenharmony_ci struct snd_queue *sq; 125862306a36Sopenharmony_ci int tmp; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* Check for minimum packet length */ 126162306a36Sopenharmony_ci if (skb->len <= ETH_HLEN) { 126262306a36Sopenharmony_ci dev_kfree_skb(skb); 126362306a36Sopenharmony_ci return NETDEV_TX_OK; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* In XDP case, initial HW tx queues are used for XDP, 126762306a36Sopenharmony_ci * but stack's queue mapping starts at '0', so skip the 126862306a36Sopenharmony_ci * Tx queues attached to Rx queues for XDP. 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_ci if (nic->xdp_prog) 127162306a36Sopenharmony_ci qid += nic->xdp_tx_queues; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci snic = nic; 127462306a36Sopenharmony_ci /* Get secondary Qset's SQ structure */ 127562306a36Sopenharmony_ci if (qid >= MAX_SND_QUEUES_PER_QS) { 127662306a36Sopenharmony_ci tmp = qid / MAX_SND_QUEUES_PER_QS; 127762306a36Sopenharmony_ci snic = (struct nicvf *)nic->snicvf[tmp - 1]; 127862306a36Sopenharmony_ci if (!snic) { 127962306a36Sopenharmony_ci netdev_warn(nic->netdev, 128062306a36Sopenharmony_ci "Secondary Qset#%d's ptr not initialized\n", 128162306a36Sopenharmony_ci tmp - 1); 128262306a36Sopenharmony_ci dev_kfree_skb(skb); 128362306a36Sopenharmony_ci return NETDEV_TX_OK; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci qid = qid % MAX_SND_QUEUES_PER_QS; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci sq = &snic->qs->sq[qid]; 128962306a36Sopenharmony_ci if (!netif_tx_queue_stopped(txq) && 129062306a36Sopenharmony_ci !nicvf_sq_append_skb(snic, sq, skb, qid)) { 129162306a36Sopenharmony_ci netif_tx_stop_queue(txq); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* Barrier, so that stop_queue visible to other cpus */ 129462306a36Sopenharmony_ci smp_mb(); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Check again, incase another cpu freed descriptors */ 129762306a36Sopenharmony_ci if (atomic_read(&sq->free_cnt) > MIN_SQ_DESC_PER_PKT_XMIT) { 129862306a36Sopenharmony_ci netif_tx_wake_queue(txq); 129962306a36Sopenharmony_ci } else { 130062306a36Sopenharmony_ci this_cpu_inc(nic->drv_stats->txq_stop); 130162306a36Sopenharmony_ci netif_warn(nic, tx_err, netdev, 130262306a36Sopenharmony_ci "Transmit ring full, stopping SQ%d\n", qid); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return NETDEV_TX_OK; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_cistatic inline void nicvf_free_cq_poll(struct nicvf *nic) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct nicvf_cq_poll *cq_poll; 131362306a36Sopenharmony_ci int qidx; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) { 131662306a36Sopenharmony_ci cq_poll = nic->napi[qidx]; 131762306a36Sopenharmony_ci if (!cq_poll) 131862306a36Sopenharmony_ci continue; 131962306a36Sopenharmony_ci nic->napi[qidx] = NULL; 132062306a36Sopenharmony_ci kfree(cq_poll); 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ciint nicvf_stop(struct net_device *netdev) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci int irq, qidx; 132762306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 132862306a36Sopenharmony_ci struct queue_set *qs = nic->qs; 132962306a36Sopenharmony_ci struct nicvf_cq_poll *cq_poll = NULL; 133062306a36Sopenharmony_ci union nic_mbx mbx = {}; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* wait till all queued set_rx_mode tasks completes */ 133362306a36Sopenharmony_ci if (nic->nicvf_rx_mode_wq) { 133462306a36Sopenharmony_ci cancel_delayed_work_sync(&nic->link_change_work); 133562306a36Sopenharmony_ci drain_workqueue(nic->nicvf_rx_mode_wq); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci mbx.msg.msg = NIC_MBOX_MSG_SHUTDOWN; 133962306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci netif_carrier_off(netdev); 134262306a36Sopenharmony_ci netif_tx_stop_all_queues(nic->netdev); 134362306a36Sopenharmony_ci nic->link_up = false; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* Teardown secondary qsets first */ 134662306a36Sopenharmony_ci if (!nic->sqs_mode) { 134762306a36Sopenharmony_ci for (qidx = 0; qidx < nic->sqs_count; qidx++) { 134862306a36Sopenharmony_ci if (!nic->snicvf[qidx]) 134962306a36Sopenharmony_ci continue; 135062306a36Sopenharmony_ci nicvf_stop(nic->snicvf[qidx]->netdev); 135162306a36Sopenharmony_ci nic->snicvf[qidx] = NULL; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Disable RBDR & QS error interrupts */ 135662306a36Sopenharmony_ci for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { 135762306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx); 135862306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0); 136162306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci /* Wait for pending IRQ handlers to finish */ 136462306a36Sopenharmony_ci for (irq = 0; irq < nic->num_vec; irq++) 136562306a36Sopenharmony_ci synchronize_irq(pci_irq_vector(nic->pdev, irq)); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci tasklet_kill(&nic->rbdr_task); 136862306a36Sopenharmony_ci tasklet_kill(&nic->qs_err_task); 136962306a36Sopenharmony_ci if (nic->rb_work_scheduled) 137062306a36Sopenharmony_ci cancel_delayed_work_sync(&nic->rbdr_work); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) { 137362306a36Sopenharmony_ci cq_poll = nic->napi[qidx]; 137462306a36Sopenharmony_ci if (!cq_poll) 137562306a36Sopenharmony_ci continue; 137662306a36Sopenharmony_ci napi_synchronize(&cq_poll->napi); 137762306a36Sopenharmony_ci /* CQ intr is enabled while napi_complete, 137862306a36Sopenharmony_ci * so disable it now 137962306a36Sopenharmony_ci */ 138062306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); 138162306a36Sopenharmony_ci nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx); 138262306a36Sopenharmony_ci napi_disable(&cq_poll->napi); 138362306a36Sopenharmony_ci netif_napi_del(&cq_poll->napi); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci netif_tx_disable(netdev); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) 138962306a36Sopenharmony_ci netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* Free resources */ 139262306a36Sopenharmony_ci nicvf_config_data_transfer(nic, false); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* Disable HW Qset */ 139562306a36Sopenharmony_ci nicvf_qset_config(nic, false); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* disable mailbox interrupt */ 139862306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci nicvf_unregister_interrupts(nic); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci nicvf_free_cq_poll(nic); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* Free any pending SKB saved to receive timestamp */ 140562306a36Sopenharmony_ci if (nic->ptp_skb) { 140662306a36Sopenharmony_ci dev_kfree_skb_any(nic->ptp_skb); 140762306a36Sopenharmony_ci nic->ptp_skb = NULL; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci /* Clear multiqset info */ 141162306a36Sopenharmony_ci nic->pnicvf = nic; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci return 0; 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic int nicvf_config_hw_rx_tstamp(struct nicvf *nic, bool enable) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci union nic_mbx mbx = {}; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci mbx.ptp.msg = NIC_MBOX_MSG_PTP_CFG; 142162306a36Sopenharmony_ci mbx.ptp.enable = enable; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return nicvf_send_msg_to_pf(nic, &mbx); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci union nic_mbx mbx = {}; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci mbx.frs.msg = NIC_MBOX_MSG_SET_MAX_FRS; 143162306a36Sopenharmony_ci mbx.frs.max_frs = mtu; 143262306a36Sopenharmony_ci mbx.frs.vf_id = nic->vf_id; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci return nicvf_send_msg_to_pf(nic, &mbx); 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic void nicvf_link_status_check_task(struct work_struct *work_arg) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct nicvf *nic = container_of(work_arg, 144062306a36Sopenharmony_ci struct nicvf, 144162306a36Sopenharmony_ci link_change_work.work); 144262306a36Sopenharmony_ci union nic_mbx mbx = {}; 144362306a36Sopenharmony_ci mbx.msg.msg = NIC_MBOX_MSG_BGX_LINK_CHANGE; 144462306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 144562306a36Sopenharmony_ci queue_delayed_work(nic->nicvf_rx_mode_wq, 144662306a36Sopenharmony_ci &nic->link_change_work, 2 * HZ); 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ciint nicvf_open(struct net_device *netdev) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci int cpu, err, qidx; 145262306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 145362306a36Sopenharmony_ci struct queue_set *qs = nic->qs; 145462306a36Sopenharmony_ci struct nicvf_cq_poll *cq_poll = NULL; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* wait till all queued set_rx_mode tasks completes if any */ 145762306a36Sopenharmony_ci if (nic->nicvf_rx_mode_wq) 145862306a36Sopenharmony_ci drain_workqueue(nic->nicvf_rx_mode_wq); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci netif_carrier_off(netdev); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci err = nicvf_register_misc_interrupt(nic); 146362306a36Sopenharmony_ci if (err) 146462306a36Sopenharmony_ci return err; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* Register NAPI handler for processing CQEs */ 146762306a36Sopenharmony_ci for (qidx = 0; qidx < qs->cq_cnt; qidx++) { 146862306a36Sopenharmony_ci cq_poll = kzalloc(sizeof(*cq_poll), GFP_KERNEL); 146962306a36Sopenharmony_ci if (!cq_poll) { 147062306a36Sopenharmony_ci err = -ENOMEM; 147162306a36Sopenharmony_ci goto napi_del; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci cq_poll->cq_idx = qidx; 147462306a36Sopenharmony_ci cq_poll->nicvf = nic; 147562306a36Sopenharmony_ci netif_napi_add(netdev, &cq_poll->napi, nicvf_poll); 147662306a36Sopenharmony_ci napi_enable(&cq_poll->napi); 147762306a36Sopenharmony_ci nic->napi[qidx] = cq_poll; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci /* Check if we got MAC address from PF or else generate a radom MAC */ 148162306a36Sopenharmony_ci if (!nic->sqs_mode && is_zero_ether_addr(netdev->dev_addr)) { 148262306a36Sopenharmony_ci eth_hw_addr_random(netdev); 148362306a36Sopenharmony_ci nicvf_hw_set_mac_addr(nic, netdev); 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (nic->set_mac_pending) { 148762306a36Sopenharmony_ci nic->set_mac_pending = false; 148862306a36Sopenharmony_ci nicvf_hw_set_mac_addr(nic, netdev); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* Init tasklet for handling Qset err interrupt */ 149262306a36Sopenharmony_ci tasklet_setup(&nic->qs_err_task, nicvf_handle_qs_err); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* Init RBDR tasklet which will refill RBDR */ 149562306a36Sopenharmony_ci tasklet_setup(&nic->rbdr_task, nicvf_rbdr_task); 149662306a36Sopenharmony_ci INIT_DELAYED_WORK(&nic->rbdr_work, nicvf_rbdr_work); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* Configure CPI alorithm */ 149962306a36Sopenharmony_ci nic->cpi_alg = cpi_alg; 150062306a36Sopenharmony_ci if (!nic->sqs_mode) 150162306a36Sopenharmony_ci nicvf_config_cpi(nic); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci nicvf_request_sqs(nic); 150462306a36Sopenharmony_ci if (nic->sqs_mode) 150562306a36Sopenharmony_ci nicvf_get_primary_vf_struct(nic); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* Configure PTP timestamp */ 150862306a36Sopenharmony_ci if (nic->ptp_clock) 150962306a36Sopenharmony_ci nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp); 151062306a36Sopenharmony_ci atomic_set(&nic->tx_ptp_skbs, 0); 151162306a36Sopenharmony_ci nic->ptp_skb = NULL; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci /* Configure receive side scaling and MTU */ 151462306a36Sopenharmony_ci if (!nic->sqs_mode) { 151562306a36Sopenharmony_ci nicvf_rss_init(nic); 151662306a36Sopenharmony_ci err = nicvf_update_hw_max_frs(nic, netdev->mtu); 151762306a36Sopenharmony_ci if (err) 151862306a36Sopenharmony_ci goto cleanup; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Clear percpu stats */ 152162306a36Sopenharmony_ci for_each_possible_cpu(cpu) 152262306a36Sopenharmony_ci memset(per_cpu_ptr(nic->drv_stats, cpu), 0, 152362306a36Sopenharmony_ci sizeof(struct nicvf_drv_stats)); 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci err = nicvf_register_interrupts(nic); 152762306a36Sopenharmony_ci if (err) 152862306a36Sopenharmony_ci goto cleanup; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* Initialize the queues */ 153162306a36Sopenharmony_ci err = nicvf_init_resources(nic); 153262306a36Sopenharmony_ci if (err) 153362306a36Sopenharmony_ci goto cleanup; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci /* Make sure queue initialization is written */ 153662306a36Sopenharmony_ci wmb(); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci nicvf_reg_write(nic, NIC_VF_INT, -1); 153962306a36Sopenharmony_ci /* Enable Qset err interrupt */ 154062306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* Enable completion queue interrupt */ 154362306a36Sopenharmony_ci for (qidx = 0; qidx < qs->cq_cnt; qidx++) 154462306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci /* Enable RBDR threshold interrupt */ 154762306a36Sopenharmony_ci for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) 154862306a36Sopenharmony_ci nicvf_enable_intr(nic, NICVF_INTR_RBDR, qidx); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* Send VF config done msg to PF */ 155162306a36Sopenharmony_ci nicvf_send_cfg_done(nic); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (nic->nicvf_rx_mode_wq) { 155462306a36Sopenharmony_ci INIT_DELAYED_WORK(&nic->link_change_work, 155562306a36Sopenharmony_ci nicvf_link_status_check_task); 155662306a36Sopenharmony_ci queue_delayed_work(nic->nicvf_rx_mode_wq, 155762306a36Sopenharmony_ci &nic->link_change_work, 0); 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci return 0; 156162306a36Sopenharmony_cicleanup: 156262306a36Sopenharmony_ci nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); 156362306a36Sopenharmony_ci nicvf_unregister_interrupts(nic); 156462306a36Sopenharmony_ci tasklet_kill(&nic->qs_err_task); 156562306a36Sopenharmony_ci tasklet_kill(&nic->rbdr_task); 156662306a36Sopenharmony_cinapi_del: 156762306a36Sopenharmony_ci for (qidx = 0; qidx < qs->cq_cnt; qidx++) { 156862306a36Sopenharmony_ci cq_poll = nic->napi[qidx]; 156962306a36Sopenharmony_ci if (!cq_poll) 157062306a36Sopenharmony_ci continue; 157162306a36Sopenharmony_ci napi_disable(&cq_poll->napi); 157262306a36Sopenharmony_ci netif_napi_del(&cq_poll->napi); 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci nicvf_free_cq_poll(nic); 157562306a36Sopenharmony_ci return err; 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic int nicvf_change_mtu(struct net_device *netdev, int new_mtu) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 158162306a36Sopenharmony_ci int orig_mtu = netdev->mtu; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* For now just support only the usual MTU sized frames, 158462306a36Sopenharmony_ci * plus some headroom for VLAN, QinQ. 158562306a36Sopenharmony_ci */ 158662306a36Sopenharmony_ci if (nic->xdp_prog && new_mtu > MAX_XDP_MTU) { 158762306a36Sopenharmony_ci netdev_warn(netdev, "Jumbo frames not yet supported with XDP, current MTU %d.\n", 158862306a36Sopenharmony_ci netdev->mtu); 158962306a36Sopenharmony_ci return -EINVAL; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci netdev->mtu = new_mtu; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!netif_running(netdev)) 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (nicvf_update_hw_max_frs(nic, new_mtu)) { 159862306a36Sopenharmony_ci netdev->mtu = orig_mtu; 159962306a36Sopenharmony_ci return -EINVAL; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int nicvf_set_mac_address(struct net_device *netdev, void *p) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci struct sockaddr *addr = p; 160862306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 161162306a36Sopenharmony_ci return -EADDRNOTAVAIL; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci if (nic->pdev->msix_enabled) { 161662306a36Sopenharmony_ci if (nicvf_hw_set_mac_addr(nic, netdev)) 161762306a36Sopenharmony_ci return -EBUSY; 161862306a36Sopenharmony_ci } else { 161962306a36Sopenharmony_ci nic->set_mac_pending = true; 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci return 0; 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_civoid nicvf_update_lmac_stats(struct nicvf *nic) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci int stat = 0; 162862306a36Sopenharmony_ci union nic_mbx mbx = {}; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!netif_running(nic->netdev)) 163162306a36Sopenharmony_ci return; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci mbx.bgx_stats.msg = NIC_MBOX_MSG_BGX_STATS; 163462306a36Sopenharmony_ci mbx.bgx_stats.vf_id = nic->vf_id; 163562306a36Sopenharmony_ci /* Rx stats */ 163662306a36Sopenharmony_ci mbx.bgx_stats.rx = 1; 163762306a36Sopenharmony_ci while (stat < BGX_RX_STATS_COUNT) { 163862306a36Sopenharmony_ci mbx.bgx_stats.idx = stat; 163962306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx)) 164062306a36Sopenharmony_ci return; 164162306a36Sopenharmony_ci stat++; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci stat = 0; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci /* Tx stats */ 164762306a36Sopenharmony_ci mbx.bgx_stats.rx = 0; 164862306a36Sopenharmony_ci while (stat < BGX_TX_STATS_COUNT) { 164962306a36Sopenharmony_ci mbx.bgx_stats.idx = stat; 165062306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx)) 165162306a36Sopenharmony_ci return; 165262306a36Sopenharmony_ci stat++; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_civoid nicvf_update_stats(struct nicvf *nic) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci int qidx, cpu; 165962306a36Sopenharmony_ci u64 tmp_stats = 0; 166062306a36Sopenharmony_ci struct nicvf_hw_stats *stats = &nic->hw_stats; 166162306a36Sopenharmony_ci struct nicvf_drv_stats *drv_stats; 166262306a36Sopenharmony_ci struct queue_set *qs = nic->qs; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci#define GET_RX_STATS(reg) \ 166562306a36Sopenharmony_ci nicvf_reg_read(nic, NIC_VNIC_RX_STAT_0_13 | (reg << 3)) 166662306a36Sopenharmony_ci#define GET_TX_STATS(reg) \ 166762306a36Sopenharmony_ci nicvf_reg_read(nic, NIC_VNIC_TX_STAT_0_4 | (reg << 3)) 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci stats->rx_bytes = GET_RX_STATS(RX_OCTS); 167062306a36Sopenharmony_ci stats->rx_ucast_frames = GET_RX_STATS(RX_UCAST); 167162306a36Sopenharmony_ci stats->rx_bcast_frames = GET_RX_STATS(RX_BCAST); 167262306a36Sopenharmony_ci stats->rx_mcast_frames = GET_RX_STATS(RX_MCAST); 167362306a36Sopenharmony_ci stats->rx_fcs_errors = GET_RX_STATS(RX_FCS); 167462306a36Sopenharmony_ci stats->rx_l2_errors = GET_RX_STATS(RX_L2ERR); 167562306a36Sopenharmony_ci stats->rx_drop_red = GET_RX_STATS(RX_RED); 167662306a36Sopenharmony_ci stats->rx_drop_red_bytes = GET_RX_STATS(RX_RED_OCTS); 167762306a36Sopenharmony_ci stats->rx_drop_overrun = GET_RX_STATS(RX_ORUN); 167862306a36Sopenharmony_ci stats->rx_drop_overrun_bytes = GET_RX_STATS(RX_ORUN_OCTS); 167962306a36Sopenharmony_ci stats->rx_drop_bcast = GET_RX_STATS(RX_DRP_BCAST); 168062306a36Sopenharmony_ci stats->rx_drop_mcast = GET_RX_STATS(RX_DRP_MCAST); 168162306a36Sopenharmony_ci stats->rx_drop_l3_bcast = GET_RX_STATS(RX_DRP_L3BCAST); 168262306a36Sopenharmony_ci stats->rx_drop_l3_mcast = GET_RX_STATS(RX_DRP_L3MCAST); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci stats->tx_bytes = GET_TX_STATS(TX_OCTS); 168562306a36Sopenharmony_ci stats->tx_ucast_frames = GET_TX_STATS(TX_UCAST); 168662306a36Sopenharmony_ci stats->tx_bcast_frames = GET_TX_STATS(TX_BCAST); 168762306a36Sopenharmony_ci stats->tx_mcast_frames = GET_TX_STATS(TX_MCAST); 168862306a36Sopenharmony_ci stats->tx_drops = GET_TX_STATS(TX_DROP); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* On T88 pass 2.0, the dummy SQE added for TSO notification 169162306a36Sopenharmony_ci * via CQE has 'dont_send' set. Hence HW drops the pkt pointed 169262306a36Sopenharmony_ci * pointed by dummy SQE and results in tx_drops counter being 169362306a36Sopenharmony_ci * incremented. Subtracting it from tx_tso counter will give 169462306a36Sopenharmony_ci * exact tx_drops counter. 169562306a36Sopenharmony_ci */ 169662306a36Sopenharmony_ci if (nic->t88 && nic->hw_tso) { 169762306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 169862306a36Sopenharmony_ci drv_stats = per_cpu_ptr(nic->drv_stats, cpu); 169962306a36Sopenharmony_ci tmp_stats += drv_stats->tx_tso; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci stats->tx_drops = tmp_stats - stats->tx_drops; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci stats->tx_frames = stats->tx_ucast_frames + 170462306a36Sopenharmony_ci stats->tx_bcast_frames + 170562306a36Sopenharmony_ci stats->tx_mcast_frames; 170662306a36Sopenharmony_ci stats->rx_frames = stats->rx_ucast_frames + 170762306a36Sopenharmony_ci stats->rx_bcast_frames + 170862306a36Sopenharmony_ci stats->rx_mcast_frames; 170962306a36Sopenharmony_ci stats->rx_drops = stats->rx_drop_red + 171062306a36Sopenharmony_ci stats->rx_drop_overrun; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* Update RQ and SQ stats */ 171362306a36Sopenharmony_ci for (qidx = 0; qidx < qs->rq_cnt; qidx++) 171462306a36Sopenharmony_ci nicvf_update_rq_stats(nic, qidx); 171562306a36Sopenharmony_ci for (qidx = 0; qidx < qs->sq_cnt; qidx++) 171662306a36Sopenharmony_ci nicvf_update_sq_stats(nic, qidx); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic void nicvf_get_stats64(struct net_device *netdev, 172062306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 172362306a36Sopenharmony_ci struct nicvf_hw_stats *hw_stats = &nic->hw_stats; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci nicvf_update_stats(nic); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci stats->rx_bytes = hw_stats->rx_bytes; 172862306a36Sopenharmony_ci stats->rx_packets = hw_stats->rx_frames; 172962306a36Sopenharmony_ci stats->rx_dropped = hw_stats->rx_drops; 173062306a36Sopenharmony_ci stats->multicast = hw_stats->rx_mcast_frames; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci stats->tx_bytes = hw_stats->tx_bytes; 173362306a36Sopenharmony_ci stats->tx_packets = hw_stats->tx_frames; 173462306a36Sopenharmony_ci stats->tx_dropped = hw_stats->tx_drops; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic void nicvf_tx_timeout(struct net_device *dev, unsigned int txqueue) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(dev); 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci netif_warn(nic, tx_err, dev, "Transmit timed out, resetting\n"); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci this_cpu_inc(nic->drv_stats->tx_timeout); 174562306a36Sopenharmony_ci schedule_work(&nic->reset_task); 174662306a36Sopenharmony_ci} 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_cistatic void nicvf_reset_task(struct work_struct *work) 174962306a36Sopenharmony_ci{ 175062306a36Sopenharmony_ci struct nicvf *nic; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci nic = container_of(work, struct nicvf, reset_task); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (!netif_running(nic->netdev)) 175562306a36Sopenharmony_ci return; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci nicvf_stop(nic->netdev); 175862306a36Sopenharmony_ci nicvf_open(nic->netdev); 175962306a36Sopenharmony_ci netif_trans_update(nic->netdev); 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic int nicvf_config_loopback(struct nicvf *nic, 176362306a36Sopenharmony_ci netdev_features_t features) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci union nic_mbx mbx = {}; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci mbx.lbk.msg = NIC_MBOX_MSG_LOOPBACK; 176862306a36Sopenharmony_ci mbx.lbk.vf_id = nic->vf_id; 176962306a36Sopenharmony_ci mbx.lbk.enable = (features & NETIF_F_LOOPBACK) != 0; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return nicvf_send_msg_to_pf(nic, &mbx); 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic netdev_features_t nicvf_fix_features(struct net_device *netdev, 177562306a36Sopenharmony_ci netdev_features_t features) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if ((features & NETIF_F_LOOPBACK) && 178062306a36Sopenharmony_ci netif_running(netdev) && !nic->loopback_supported) 178162306a36Sopenharmony_ci features &= ~NETIF_F_LOOPBACK; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci return features; 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cistatic int nicvf_set_features(struct net_device *netdev, 178762306a36Sopenharmony_ci netdev_features_t features) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 179062306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 179362306a36Sopenharmony_ci nicvf_config_vlan_stripping(nic, features); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev)) 179662306a36Sopenharmony_ci return nicvf_config_loopback(nic, features); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci return 0; 179962306a36Sopenharmony_ci} 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_cistatic void nicvf_set_xdp_queues(struct nicvf *nic, bool bpf_attached) 180262306a36Sopenharmony_ci{ 180362306a36Sopenharmony_ci u8 cq_count, txq_count; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* Set XDP Tx queue count same as Rx queue count */ 180662306a36Sopenharmony_ci if (!bpf_attached) 180762306a36Sopenharmony_ci nic->xdp_tx_queues = 0; 180862306a36Sopenharmony_ci else 180962306a36Sopenharmony_ci nic->xdp_tx_queues = nic->rx_queues; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* If queue count > MAX_CMP_QUEUES_PER_QS, then additional qsets 181262306a36Sopenharmony_ci * needs to be allocated, check how many. 181362306a36Sopenharmony_ci */ 181462306a36Sopenharmony_ci txq_count = nic->xdp_tx_queues + nic->tx_queues; 181562306a36Sopenharmony_ci cq_count = max(nic->rx_queues, txq_count); 181662306a36Sopenharmony_ci if (cq_count > MAX_CMP_QUEUES_PER_QS) { 181762306a36Sopenharmony_ci nic->sqs_count = roundup(cq_count, MAX_CMP_QUEUES_PER_QS); 181862306a36Sopenharmony_ci nic->sqs_count = (nic->sqs_count / MAX_CMP_QUEUES_PER_QS) - 1; 181962306a36Sopenharmony_ci } else { 182062306a36Sopenharmony_ci nic->sqs_count = 0; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci /* Set primary Qset's resources */ 182462306a36Sopenharmony_ci nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS); 182562306a36Sopenharmony_ci nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS); 182662306a36Sopenharmony_ci nic->qs->cq_cnt = max_t(u8, nic->qs->rq_cnt, nic->qs->sq_cnt); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* Update stack */ 182962306a36Sopenharmony_ci nicvf_set_real_num_queues(nic->netdev, nic->tx_queues, nic->rx_queues); 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cistatic int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci struct net_device *dev = nic->netdev; 183562306a36Sopenharmony_ci bool if_up = netif_running(nic->netdev); 183662306a36Sopenharmony_ci struct bpf_prog *old_prog; 183762306a36Sopenharmony_ci bool bpf_attached = false; 183862306a36Sopenharmony_ci int ret = 0; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci /* For now just support only the usual MTU sized frames, 184162306a36Sopenharmony_ci * plus some headroom for VLAN, QinQ. 184262306a36Sopenharmony_ci */ 184362306a36Sopenharmony_ci if (prog && dev->mtu > MAX_XDP_MTU) { 184462306a36Sopenharmony_ci netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n", 184562306a36Sopenharmony_ci dev->mtu); 184662306a36Sopenharmony_ci return -EOPNOTSUPP; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* ALL SQs attached to CQs i.e same as RQs, are treated as 185062306a36Sopenharmony_ci * XDP Tx queues and more Tx queues are allocated for 185162306a36Sopenharmony_ci * network stack to send pkts out. 185262306a36Sopenharmony_ci * 185362306a36Sopenharmony_ci * No of Tx queues are either same as Rx queues or whatever 185462306a36Sopenharmony_ci * is left in max no of queues possible. 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_ci if ((nic->rx_queues + nic->tx_queues) > nic->max_queues) { 185762306a36Sopenharmony_ci netdev_warn(dev, 185862306a36Sopenharmony_ci "Failed to attach BPF prog, RXQs + TXQs > Max %d\n", 185962306a36Sopenharmony_ci nic->max_queues); 186062306a36Sopenharmony_ci return -ENOMEM; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (if_up) 186462306a36Sopenharmony_ci nicvf_stop(nic->netdev); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci old_prog = xchg(&nic->xdp_prog, prog); 186762306a36Sopenharmony_ci /* Detach old prog, if any */ 186862306a36Sopenharmony_ci if (old_prog) 186962306a36Sopenharmony_ci bpf_prog_put(old_prog); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci if (nic->xdp_prog) { 187262306a36Sopenharmony_ci /* Attach BPF program */ 187362306a36Sopenharmony_ci bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1); 187462306a36Sopenharmony_ci bpf_attached = true; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* Calculate Tx queues needed for XDP and network stack */ 187862306a36Sopenharmony_ci nicvf_set_xdp_queues(nic, bpf_attached); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci if (if_up) { 188162306a36Sopenharmony_ci /* Reinitialize interface, clean slate */ 188262306a36Sopenharmony_ci nicvf_open(nic->netdev); 188362306a36Sopenharmony_ci netif_trans_update(nic->netdev); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return ret; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_cistatic int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* To avoid checks while retrieving buffer address from CQE_RX, 189462306a36Sopenharmony_ci * do not support XDP for T88 pass1.x silicons which are anyway 189562306a36Sopenharmony_ci * not in use widely. 189662306a36Sopenharmony_ci */ 189762306a36Sopenharmony_ci if (pass1_silicon(nic->pdev)) 189862306a36Sopenharmony_ci return -EOPNOTSUPP; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci switch (xdp->command) { 190162306a36Sopenharmony_ci case XDP_SETUP_PROG: 190262306a36Sopenharmony_ci return nicvf_xdp_setup(nic, xdp->prog); 190362306a36Sopenharmony_ci default: 190462306a36Sopenharmony_ci return -EINVAL; 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct hwtstamp_config config; 191162306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci if (!nic->ptp_clock) 191462306a36Sopenharmony_ci return -ENODEV; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 191762306a36Sopenharmony_ci return -EFAULT; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci switch (config.tx_type) { 192062306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 192162306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 192262306a36Sopenharmony_ci break; 192362306a36Sopenharmony_ci default: 192462306a36Sopenharmony_ci return -ERANGE; 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci switch (config.rx_filter) { 192862306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 192962306a36Sopenharmony_ci nic->hw_rx_tstamp = false; 193062306a36Sopenharmony_ci break; 193162306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 193262306a36Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 193362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 193462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 193562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 193662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 193762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 193862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 193962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 194062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 194162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 194262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 194362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 194462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 194562306a36Sopenharmony_ci nic->hw_rx_tstamp = true; 194662306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 194762306a36Sopenharmony_ci break; 194862306a36Sopenharmony_ci default: 194962306a36Sopenharmony_ci return -ERANGE; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci if (netif_running(netdev)) 195362306a36Sopenharmony_ci nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) 195662306a36Sopenharmony_ci return -EFAULT; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci switch (cmd) { 196462306a36Sopenharmony_ci case SIOCSHWTSTAMP: 196562306a36Sopenharmony_ci return nicvf_config_hwtstamp(netdev, req); 196662306a36Sopenharmony_ci default: 196762306a36Sopenharmony_ci return -EOPNOTSUPP; 196862306a36Sopenharmony_ci } 196962306a36Sopenharmony_ci} 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_cistatic void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs, 197262306a36Sopenharmony_ci struct nicvf *nic) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci union nic_mbx mbx = {}; 197562306a36Sopenharmony_ci int idx; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* From the inside of VM code flow we have only 128 bits memory 197862306a36Sopenharmony_ci * available to send message to host's PF, so send all mc addrs 197962306a36Sopenharmony_ci * one by one, starting from flush command in case if kernel 198062306a36Sopenharmony_ci * requests to configure specific MAC filtering 198162306a36Sopenharmony_ci */ 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci /* flush DMAC filters and reset RX mode */ 198462306a36Sopenharmony_ci mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST; 198562306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx) < 0) 198662306a36Sopenharmony_ci goto free_mc; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (mode & BGX_XCAST_MCAST_FILTER) { 198962306a36Sopenharmony_ci /* once enabling filtering, we need to signal to PF to add 199062306a36Sopenharmony_ci * its' own LMAC to the filter to accept packets for it. 199162306a36Sopenharmony_ci */ 199262306a36Sopenharmony_ci mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST; 199362306a36Sopenharmony_ci mbx.xcast.mac = 0; 199462306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx) < 0) 199562306a36Sopenharmony_ci goto free_mc; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* check if we have any specific MACs to be added to PF DMAC filter */ 199962306a36Sopenharmony_ci if (mc_addrs) { 200062306a36Sopenharmony_ci /* now go through kernel list of MACs and add them one by one */ 200162306a36Sopenharmony_ci for (idx = 0; idx < mc_addrs->count; idx++) { 200262306a36Sopenharmony_ci mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST; 200362306a36Sopenharmony_ci mbx.xcast.mac = mc_addrs->mc[idx]; 200462306a36Sopenharmony_ci if (nicvf_send_msg_to_pf(nic, &mbx) < 0) 200562306a36Sopenharmony_ci goto free_mc; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci /* and finally set rx mode for PF accordingly */ 201062306a36Sopenharmony_ci mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST; 201162306a36Sopenharmony_ci mbx.xcast.mode = mode; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci nicvf_send_msg_to_pf(nic, &mbx); 201462306a36Sopenharmony_cifree_mc: 201562306a36Sopenharmony_ci kfree(mc_addrs); 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic void nicvf_set_rx_mode_task(struct work_struct *work_arg) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work, 202162306a36Sopenharmony_ci work); 202262306a36Sopenharmony_ci struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work); 202362306a36Sopenharmony_ci u8 mode; 202462306a36Sopenharmony_ci struct xcast_addr_list *mc; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* Save message data locally to prevent them from 202762306a36Sopenharmony_ci * being overwritten by next ndo_set_rx_mode call(). 202862306a36Sopenharmony_ci */ 202962306a36Sopenharmony_ci spin_lock_bh(&nic->rx_mode_wq_lock); 203062306a36Sopenharmony_ci mode = vf_work->mode; 203162306a36Sopenharmony_ci mc = vf_work->mc; 203262306a36Sopenharmony_ci vf_work->mc = NULL; 203362306a36Sopenharmony_ci spin_unlock_bh(&nic->rx_mode_wq_lock); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci __nicvf_set_rx_mode_task(mode, mc, nic); 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic void nicvf_set_rx_mode(struct net_device *netdev) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct nicvf *nic = netdev_priv(netdev); 204162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 204262306a36Sopenharmony_ci struct xcast_addr_list *mc_list = NULL; 204362306a36Sopenharmony_ci u8 mode = 0; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 204662306a36Sopenharmony_ci mode = BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT; 204762306a36Sopenharmony_ci } else { 204862306a36Sopenharmony_ci if (netdev->flags & IFF_BROADCAST) 204962306a36Sopenharmony_ci mode |= BGX_XCAST_BCAST_ACCEPT; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) { 205262306a36Sopenharmony_ci mode |= BGX_XCAST_MCAST_ACCEPT; 205362306a36Sopenharmony_ci } else if (netdev->flags & IFF_MULTICAST) { 205462306a36Sopenharmony_ci mode |= BGX_XCAST_MCAST_FILTER; 205562306a36Sopenharmony_ci /* here we need to copy mc addrs */ 205662306a36Sopenharmony_ci if (netdev_mc_count(netdev)) { 205762306a36Sopenharmony_ci mc_list = kmalloc(struct_size(mc_list, mc, 205862306a36Sopenharmony_ci netdev_mc_count(netdev)), 205962306a36Sopenharmony_ci GFP_ATOMIC); 206062306a36Sopenharmony_ci if (unlikely(!mc_list)) 206162306a36Sopenharmony_ci return; 206262306a36Sopenharmony_ci mc_list->count = 0; 206362306a36Sopenharmony_ci netdev_hw_addr_list_for_each(ha, &netdev->mc) { 206462306a36Sopenharmony_ci mc_list->mc[mc_list->count] = 206562306a36Sopenharmony_ci ether_addr_to_u64(ha->addr); 206662306a36Sopenharmony_ci mc_list->count++; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci spin_lock(&nic->rx_mode_wq_lock); 207262306a36Sopenharmony_ci kfree(nic->rx_mode_work.mc); 207362306a36Sopenharmony_ci nic->rx_mode_work.mc = mc_list; 207462306a36Sopenharmony_ci nic->rx_mode_work.mode = mode; 207562306a36Sopenharmony_ci queue_work(nic->nicvf_rx_mode_wq, &nic->rx_mode_work.work); 207662306a36Sopenharmony_ci spin_unlock(&nic->rx_mode_wq_lock); 207762306a36Sopenharmony_ci} 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_cistatic const struct net_device_ops nicvf_netdev_ops = { 208062306a36Sopenharmony_ci .ndo_open = nicvf_open, 208162306a36Sopenharmony_ci .ndo_stop = nicvf_stop, 208262306a36Sopenharmony_ci .ndo_start_xmit = nicvf_xmit, 208362306a36Sopenharmony_ci .ndo_change_mtu = nicvf_change_mtu, 208462306a36Sopenharmony_ci .ndo_set_mac_address = nicvf_set_mac_address, 208562306a36Sopenharmony_ci .ndo_get_stats64 = nicvf_get_stats64, 208662306a36Sopenharmony_ci .ndo_tx_timeout = nicvf_tx_timeout, 208762306a36Sopenharmony_ci .ndo_fix_features = nicvf_fix_features, 208862306a36Sopenharmony_ci .ndo_set_features = nicvf_set_features, 208962306a36Sopenharmony_ci .ndo_bpf = nicvf_xdp, 209062306a36Sopenharmony_ci .ndo_eth_ioctl = nicvf_ioctl, 209162306a36Sopenharmony_ci .ndo_set_rx_mode = nicvf_set_rx_mode, 209262306a36Sopenharmony_ci}; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_cistatic int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 209562306a36Sopenharmony_ci{ 209662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 209762306a36Sopenharmony_ci struct net_device *netdev; 209862306a36Sopenharmony_ci struct nicvf *nic; 209962306a36Sopenharmony_ci int err, qcount; 210062306a36Sopenharmony_ci u16 sdevid; 210162306a36Sopenharmony_ci struct cavium_ptp *ptp_clock; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci ptp_clock = cavium_ptp_get(); 210462306a36Sopenharmony_ci if (IS_ERR(ptp_clock)) { 210562306a36Sopenharmony_ci if (PTR_ERR(ptp_clock) == -ENODEV) 210662306a36Sopenharmony_ci /* In virtualized environment we proceed without ptp */ 210762306a36Sopenharmony_ci ptp_clock = NULL; 210862306a36Sopenharmony_ci else 210962306a36Sopenharmony_ci return PTR_ERR(ptp_clock); 211062306a36Sopenharmony_ci } 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci err = pci_enable_device(pdev); 211362306a36Sopenharmony_ci if (err) 211462306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to enable PCI device\n"); 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 211762306a36Sopenharmony_ci if (err) { 211862306a36Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 211962306a36Sopenharmony_ci goto err_disable_device; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); 212362306a36Sopenharmony_ci if (err) { 212462306a36Sopenharmony_ci dev_err(dev, "Unable to get usable DMA configuration\n"); 212562306a36Sopenharmony_ci goto err_release_regions; 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci qcount = netif_get_num_default_rss_queues(); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci /* Restrict multiqset support only for host bound VFs */ 213162306a36Sopenharmony_ci if (pdev->is_virtfn) { 213262306a36Sopenharmony_ci /* Set max number of queues per VF */ 213362306a36Sopenharmony_ci qcount = min_t(int, num_online_cpus(), 213462306a36Sopenharmony_ci (MAX_SQS_PER_VF + 1) * MAX_CMP_QUEUES_PER_QS); 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(struct nicvf), qcount, qcount); 213862306a36Sopenharmony_ci if (!netdev) { 213962306a36Sopenharmony_ci err = -ENOMEM; 214062306a36Sopenharmony_ci goto err_release_regions; 214162306a36Sopenharmony_ci } 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci pci_set_drvdata(pdev, netdev); 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci nic = netdev_priv(netdev); 214862306a36Sopenharmony_ci nic->netdev = netdev; 214962306a36Sopenharmony_ci nic->pdev = pdev; 215062306a36Sopenharmony_ci nic->pnicvf = nic; 215162306a36Sopenharmony_ci nic->max_queues = qcount; 215262306a36Sopenharmony_ci /* If no of CPUs are too low, there won't be any queues left 215362306a36Sopenharmony_ci * for XDP_TX, hence double it. 215462306a36Sopenharmony_ci */ 215562306a36Sopenharmony_ci if (!nic->t88) 215662306a36Sopenharmony_ci nic->max_queues *= 2; 215762306a36Sopenharmony_ci nic->ptp_clock = ptp_clock; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci /* Initialize mutex that serializes usage of VF's mailbox */ 216062306a36Sopenharmony_ci mutex_init(&nic->rx_mode_mtx); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci /* MAP VF's configuration registers */ 216362306a36Sopenharmony_ci nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); 216462306a36Sopenharmony_ci if (!nic->reg_base) { 216562306a36Sopenharmony_ci dev_err(dev, "Cannot map config register space, aborting\n"); 216662306a36Sopenharmony_ci err = -ENOMEM; 216762306a36Sopenharmony_ci goto err_free_netdev; 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci nic->drv_stats = netdev_alloc_pcpu_stats(struct nicvf_drv_stats); 217162306a36Sopenharmony_ci if (!nic->drv_stats) { 217262306a36Sopenharmony_ci err = -ENOMEM; 217362306a36Sopenharmony_ci goto err_free_netdev; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci err = nicvf_set_qset_resources(nic); 217762306a36Sopenharmony_ci if (err) 217862306a36Sopenharmony_ci goto err_free_netdev; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* Check if PF is alive and get MAC address for this VF */ 218162306a36Sopenharmony_ci err = nicvf_register_misc_interrupt(nic); 218262306a36Sopenharmony_ci if (err) 218362306a36Sopenharmony_ci goto err_free_netdev; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci nicvf_send_vf_struct(nic); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (!pass1_silicon(nic->pdev)) 218862306a36Sopenharmony_ci nic->hw_tso = true; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci /* Get iommu domain for iova to physical addr conversion */ 219162306a36Sopenharmony_ci nic->iommu_domain = iommu_get_domain_for_dev(dev); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci pci_read_config_word(nic->pdev, PCI_SUBSYSTEM_ID, &sdevid); 219462306a36Sopenharmony_ci if (sdevid == 0xA134) 219562306a36Sopenharmony_ci nic->t88 = true; 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci /* Check if this VF is in QS only mode */ 219862306a36Sopenharmony_ci if (nic->sqs_mode) 219962306a36Sopenharmony_ci return 0; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci err = nicvf_set_real_num_queues(netdev, nic->tx_queues, nic->rx_queues); 220262306a36Sopenharmony_ci if (err) 220362306a36Sopenharmony_ci goto err_unregister_interrupts; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_SG | 220662306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_GRO | NETIF_F_TSO6 | 220762306a36Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 220862306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci netdev->features |= netdev->hw_features; 221362306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_LOOPBACK; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | 221662306a36Sopenharmony_ci NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci netdev->netdev_ops = &nicvf_netdev_ops; 221962306a36Sopenharmony_ci netdev->watchdog_timeo = NICVF_TX_TIMEOUT; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci if (!pass1_silicon(nic->pdev) && 222262306a36Sopenharmony_ci nic->rx_queues + nic->tx_queues <= nic->max_queues) 222362306a36Sopenharmony_ci netdev->xdp_features = NETDEV_XDP_ACT_BASIC; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* MTU range: 64 - 9200 */ 222662306a36Sopenharmony_ci netdev->min_mtu = NIC_HW_MIN_FRS; 222762306a36Sopenharmony_ci netdev->max_mtu = NIC_HW_MAX_FRS; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci INIT_WORK(&nic->reset_task, nicvf_reset_task); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci nic->nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_rx_mode_wq_VF%d", 223262306a36Sopenharmony_ci WQ_MEM_RECLAIM, 223362306a36Sopenharmony_ci nic->vf_id); 223462306a36Sopenharmony_ci if (!nic->nicvf_rx_mode_wq) { 223562306a36Sopenharmony_ci err = -ENOMEM; 223662306a36Sopenharmony_ci dev_err(dev, "Failed to allocate work queue\n"); 223762306a36Sopenharmony_ci goto err_unregister_interrupts; 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci INIT_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task); 224162306a36Sopenharmony_ci spin_lock_init(&nic->rx_mode_wq_lock); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci err = register_netdev(netdev); 224462306a36Sopenharmony_ci if (err) { 224562306a36Sopenharmony_ci dev_err(dev, "Failed to register netdevice\n"); 224662306a36Sopenharmony_ci goto err_destroy_workqueue; 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci nic->msg_enable = debug; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci nicvf_set_ethtool_ops(netdev); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci return 0; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cierr_destroy_workqueue: 225662306a36Sopenharmony_ci destroy_workqueue(nic->nicvf_rx_mode_wq); 225762306a36Sopenharmony_cierr_unregister_interrupts: 225862306a36Sopenharmony_ci nicvf_unregister_interrupts(nic); 225962306a36Sopenharmony_cierr_free_netdev: 226062306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 226162306a36Sopenharmony_ci if (nic->drv_stats) 226262306a36Sopenharmony_ci free_percpu(nic->drv_stats); 226362306a36Sopenharmony_ci free_netdev(netdev); 226462306a36Sopenharmony_cierr_release_regions: 226562306a36Sopenharmony_ci pci_release_regions(pdev); 226662306a36Sopenharmony_cierr_disable_device: 226762306a36Sopenharmony_ci pci_disable_device(pdev); 226862306a36Sopenharmony_ci return err; 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_cistatic void nicvf_remove(struct pci_dev *pdev) 227262306a36Sopenharmony_ci{ 227362306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 227462306a36Sopenharmony_ci struct nicvf *nic; 227562306a36Sopenharmony_ci struct net_device *pnetdev; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (!netdev) 227862306a36Sopenharmony_ci return; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci nic = netdev_priv(netdev); 228162306a36Sopenharmony_ci pnetdev = nic->pnicvf->netdev; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci /* Check if this Qset is assigned to different VF. 228462306a36Sopenharmony_ci * If yes, clean primary and all secondary Qsets. 228562306a36Sopenharmony_ci */ 228662306a36Sopenharmony_ci if (pnetdev && (pnetdev->reg_state == NETREG_REGISTERED)) 228762306a36Sopenharmony_ci unregister_netdev(pnetdev); 228862306a36Sopenharmony_ci if (nic->nicvf_rx_mode_wq) { 228962306a36Sopenharmony_ci destroy_workqueue(nic->nicvf_rx_mode_wq); 229062306a36Sopenharmony_ci nic->nicvf_rx_mode_wq = NULL; 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci nicvf_unregister_interrupts(nic); 229362306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 229462306a36Sopenharmony_ci if (nic->drv_stats) 229562306a36Sopenharmony_ci free_percpu(nic->drv_stats); 229662306a36Sopenharmony_ci cavium_ptp_put(nic->ptp_clock); 229762306a36Sopenharmony_ci free_netdev(netdev); 229862306a36Sopenharmony_ci pci_release_regions(pdev); 229962306a36Sopenharmony_ci pci_disable_device(pdev); 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic void nicvf_shutdown(struct pci_dev *pdev) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci nicvf_remove(pdev); 230562306a36Sopenharmony_ci} 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_cistatic struct pci_driver nicvf_driver = { 230862306a36Sopenharmony_ci .name = DRV_NAME, 230962306a36Sopenharmony_ci .id_table = nicvf_id_table, 231062306a36Sopenharmony_ci .probe = nicvf_probe, 231162306a36Sopenharmony_ci .remove = nicvf_remove, 231262306a36Sopenharmony_ci .shutdown = nicvf_shutdown, 231362306a36Sopenharmony_ci}; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic int __init nicvf_init_module(void) 231662306a36Sopenharmony_ci{ 231762306a36Sopenharmony_ci pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION); 231862306a36Sopenharmony_ci return pci_register_driver(&nicvf_driver); 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic void __exit nicvf_cleanup_module(void) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci pci_unregister_driver(&nicvf_driver); 232462306a36Sopenharmony_ci} 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_cimodule_init(nicvf_init_module); 232762306a36Sopenharmony_cimodule_exit(nicvf_cleanup_module); 2328