162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. 362306a36Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you may redistribute it and/or modify 662306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 762306a36Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1062306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1162306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1262306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 1362306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1462306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 1562306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1662306a36Sopenharmony_ci * SOFTWARE. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/errno.h> 2462306a36Sopenharmony_ci#include <linux/types.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/workqueue.h> 2862306a36Sopenharmony_ci#include <linux/pci.h> 2962306a36Sopenharmony_ci#include <linux/netdevice.h> 3062306a36Sopenharmony_ci#include <linux/etherdevice.h> 3162306a36Sopenharmony_ci#include <linux/if.h> 3262306a36Sopenharmony_ci#include <linux/if_ether.h> 3362306a36Sopenharmony_ci#include <linux/if_vlan.h> 3462306a36Sopenharmony_ci#include <linux/in.h> 3562306a36Sopenharmony_ci#include <linux/ip.h> 3662306a36Sopenharmony_ci#include <linux/ipv6.h> 3762306a36Sopenharmony_ci#include <linux/tcp.h> 3862306a36Sopenharmony_ci#include <linux/rtnetlink.h> 3962306a36Sopenharmony_ci#include <linux/prefetch.h> 4062306a36Sopenharmony_ci#include <net/ip6_checksum.h> 4162306a36Sopenharmony_ci#include <linux/ktime.h> 4262306a36Sopenharmony_ci#include <linux/numa.h> 4362306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 4462306a36Sopenharmony_ci#include <linux/cpu_rmap.h> 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci#include <linux/crash_dump.h> 4762306a36Sopenharmony_ci#include <net/busy_poll.h> 4862306a36Sopenharmony_ci#include <net/vxlan.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "cq_enet_desc.h" 5162306a36Sopenharmony_ci#include "vnic_dev.h" 5262306a36Sopenharmony_ci#include "vnic_intr.h" 5362306a36Sopenharmony_ci#include "vnic_stats.h" 5462306a36Sopenharmony_ci#include "vnic_vic.h" 5562306a36Sopenharmony_ci#include "enic_res.h" 5662306a36Sopenharmony_ci#include "enic.h" 5762306a36Sopenharmony_ci#include "enic_dev.h" 5862306a36Sopenharmony_ci#include "enic_pp.h" 5962306a36Sopenharmony_ci#include "enic_clsf.h" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define ENIC_NOTIFY_TIMER_PERIOD (2 * HZ) 6262306a36Sopenharmony_ci#define WQ_ENET_MAX_DESC_LEN (1 << WQ_ENET_LEN_BITS) 6362306a36Sopenharmony_ci#define MAX_TSO (1 << 16) 6462306a36Sopenharmony_ci#define ENIC_DESC_MAX_SPLITS (MAX_TSO / WQ_ENET_MAX_DESC_LEN + 1) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_VIC_ENET 0x0043 /* ethernet vnic */ 6762306a36Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */ 6862306a36Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define RX_COPYBREAK_DEFAULT 256 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Supported devices */ 7362306a36Sopenharmony_cistatic const struct pci_device_id enic_id_table[] = { 7462306a36Sopenharmony_ci { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET) }, 7562306a36Sopenharmony_ci { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_DYN) }, 7662306a36Sopenharmony_ci { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_VF) }, 7762306a36Sopenharmony_ci { 0, } /* end of table */ 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 8162306a36Sopenharmony_ciMODULE_AUTHOR("Scott Feldman <scofeldm@cisco.com>"); 8262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, enic_id_table); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define ENIC_LARGE_PKT_THRESHOLD 1000 8662306a36Sopenharmony_ci#define ENIC_MAX_COALESCE_TIMERS 10 8762306a36Sopenharmony_ci/* Interrupt moderation table, which will be used to decide the 8862306a36Sopenharmony_ci * coalescing timer values 8962306a36Sopenharmony_ci * {rx_rate in Mbps, mapping percentage of the range} 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { 9262306a36Sopenharmony_ci {4000, 0}, 9362306a36Sopenharmony_ci {4400, 10}, 9462306a36Sopenharmony_ci {5060, 20}, 9562306a36Sopenharmony_ci {5230, 30}, 9662306a36Sopenharmony_ci {5540, 40}, 9762306a36Sopenharmony_ci {5820, 50}, 9862306a36Sopenharmony_ci {6120, 60}, 9962306a36Sopenharmony_ci {6435, 70}, 10062306a36Sopenharmony_ci {6745, 80}, 10162306a36Sopenharmony_ci {7000, 90}, 10262306a36Sopenharmony_ci {0xFFFFFFFF, 100} 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* This table helps the driver to pick different ranges for rx coalescing 10662306a36Sopenharmony_ci * timer depending on the link speed. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = { 10962306a36Sopenharmony_ci {0, 0}, /* 0 - 4 Gbps */ 11062306a36Sopenharmony_ci {0, 3}, /* 4 - 10 Gbps */ 11162306a36Sopenharmony_ci {3, 6}, /* 10 - 40 Gbps */ 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void enic_init_affinity_hint(struct enic *enic) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int numa_node = dev_to_node(&enic->pdev->dev); 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) { 12062306a36Sopenharmony_ci if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i) || 12162306a36Sopenharmony_ci (cpumask_available(enic->msix[i].affinity_mask) && 12262306a36Sopenharmony_ci !cpumask_empty(enic->msix[i].affinity_mask))) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci if (zalloc_cpumask_var(&enic->msix[i].affinity_mask, 12562306a36Sopenharmony_ci GFP_KERNEL)) 12662306a36Sopenharmony_ci cpumask_set_cpu(cpumask_local_spread(i, numa_node), 12762306a36Sopenharmony_ci enic->msix[i].affinity_mask); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void enic_free_affinity_hint(struct enic *enic) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) { 13662306a36Sopenharmony_ci if (enic_is_err_intr(enic, i) || enic_is_notify_intr(enic, i)) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci free_cpumask_var(enic->msix[i].affinity_mask); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void enic_set_affinity_hint(struct enic *enic) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) { 14862306a36Sopenharmony_ci if (enic_is_err_intr(enic, i) || 14962306a36Sopenharmony_ci enic_is_notify_intr(enic, i) || 15062306a36Sopenharmony_ci !cpumask_available(enic->msix[i].affinity_mask) || 15162306a36Sopenharmony_ci cpumask_empty(enic->msix[i].affinity_mask)) 15262306a36Sopenharmony_ci continue; 15362306a36Sopenharmony_ci err = irq_update_affinity_hint(enic->msix_entry[i].vector, 15462306a36Sopenharmony_ci enic->msix[i].affinity_mask); 15562306a36Sopenharmony_ci if (err) 15662306a36Sopenharmony_ci netdev_warn(enic->netdev, "irq_update_affinity_hint failed, err %d\n", 15762306a36Sopenharmony_ci err); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 16162306a36Sopenharmony_ci int wq_intr = enic_msix_wq_intr(enic, i); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (cpumask_available(enic->msix[wq_intr].affinity_mask) && 16462306a36Sopenharmony_ci !cpumask_empty(enic->msix[wq_intr].affinity_mask)) 16562306a36Sopenharmony_ci netif_set_xps_queue(enic->netdev, 16662306a36Sopenharmony_ci enic->msix[wq_intr].affinity_mask, 16762306a36Sopenharmony_ci i); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void enic_unset_affinity_hint(struct enic *enic) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int i; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) 17662306a36Sopenharmony_ci irq_update_affinity_hint(enic->msix_entry[i].vector, NULL); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int enic_udp_tunnel_set_port(struct net_device *netdev, 18062306a36Sopenharmony_ci unsigned int table, unsigned int entry, 18162306a36Sopenharmony_ci struct udp_tunnel_info *ti) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 18462306a36Sopenharmony_ci int err; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci err = vnic_dev_overlay_offload_cfg(enic->vdev, 18962306a36Sopenharmony_ci OVERLAY_CFG_VXLAN_PORT_UPDATE, 19062306a36Sopenharmony_ci ntohs(ti->port)); 19162306a36Sopenharmony_ci if (err) 19262306a36Sopenharmony_ci goto error; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci err = vnic_dev_overlay_offload_ctrl(enic->vdev, OVERLAY_FEATURE_VXLAN, 19562306a36Sopenharmony_ci enic->vxlan.patch_level); 19662306a36Sopenharmony_ci if (err) 19762306a36Sopenharmony_ci goto error; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci enic->vxlan.vxlan_udp_port_number = ntohs(ti->port); 20062306a36Sopenharmony_cierror: 20162306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return err; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int enic_udp_tunnel_unset_port(struct net_device *netdev, 20762306a36Sopenharmony_ci unsigned int table, unsigned int entry, 20862306a36Sopenharmony_ci struct udp_tunnel_info *ti) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 21162306a36Sopenharmony_ci int err; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci err = vnic_dev_overlay_offload_ctrl(enic->vdev, OVERLAY_FEATURE_VXLAN, 21662306a36Sopenharmony_ci OVERLAY_OFFLOAD_DISABLE); 21762306a36Sopenharmony_ci if (err) 21862306a36Sopenharmony_ci goto unlock; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci enic->vxlan.vxlan_udp_port_number = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciunlock: 22362306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return err; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const struct udp_tunnel_nic_info enic_udp_tunnels = { 22962306a36Sopenharmony_ci .set_port = enic_udp_tunnel_set_port, 23062306a36Sopenharmony_ci .unset_port = enic_udp_tunnel_unset_port, 23162306a36Sopenharmony_ci .tables = { 23262306a36Sopenharmony_ci { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci}, enic_udp_tunnels_v4 = { 23562306a36Sopenharmony_ci .set_port = enic_udp_tunnel_set_port, 23662306a36Sopenharmony_ci .unset_port = enic_udp_tunnel_unset_port, 23762306a36Sopenharmony_ci .flags = UDP_TUNNEL_NIC_INFO_IPV4_ONLY, 23862306a36Sopenharmony_ci .tables = { 23962306a36Sopenharmony_ci { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, 24062306a36Sopenharmony_ci }, 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic netdev_features_t enic_features_check(struct sk_buff *skb, 24462306a36Sopenharmony_ci struct net_device *dev, 24562306a36Sopenharmony_ci netdev_features_t features) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci const struct ethhdr *eth = (struct ethhdr *)skb_inner_mac_header(skb); 24862306a36Sopenharmony_ci struct enic *enic = netdev_priv(dev); 24962306a36Sopenharmony_ci struct udphdr *udph; 25062306a36Sopenharmony_ci u16 port = 0; 25162306a36Sopenharmony_ci u8 proto; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!skb->encapsulation) 25462306a36Sopenharmony_ci return features; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci features = vxlan_features_check(skb, features); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (vlan_get_protocol(skb)) { 25962306a36Sopenharmony_ci case htons(ETH_P_IPV6): 26062306a36Sopenharmony_ci if (!(enic->vxlan.flags & ENIC_VXLAN_OUTER_IPV6)) 26162306a36Sopenharmony_ci goto out; 26262306a36Sopenharmony_ci proto = ipv6_hdr(skb)->nexthdr; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case htons(ETH_P_IP): 26562306a36Sopenharmony_ci proto = ip_hdr(skb)->protocol; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci default: 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci switch (eth->h_proto) { 27262306a36Sopenharmony_ci case ntohs(ETH_P_IPV6): 27362306a36Sopenharmony_ci if (!(enic->vxlan.flags & ENIC_VXLAN_INNER_IPV6)) 27462306a36Sopenharmony_ci goto out; 27562306a36Sopenharmony_ci fallthrough; 27662306a36Sopenharmony_ci case ntohs(ETH_P_IP): 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci goto out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (proto == IPPROTO_UDP) { 28462306a36Sopenharmony_ci udph = udp_hdr(skb); 28562306a36Sopenharmony_ci port = be16_to_cpu(udph->dest); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* HW supports offload of only one UDP port. Remove CSUM and GSO MASK 28962306a36Sopenharmony_ci * for other UDP port tunnels 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (port != enic->vxlan.vxlan_udp_port_number) 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return features; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciout: 29762306a36Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint enic_is_dynamic(struct enic *enic) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ciint enic_sriov_enabled(struct enic *enic) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci return (enic->priv_flags & ENIC_SRIOV_ENABLED) ? 1 : 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int enic_is_sriov_vf(struct enic *enic) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_VF; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciint enic_is_valid_vf(struct enic *enic, int vf) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 31862306a36Sopenharmony_ci return vf >= 0 && vf < enic->num_vfs; 31962306a36Sopenharmony_ci#else 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci#endif 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void enic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(wq->vdev); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (buf->sop) 32962306a36Sopenharmony_ci dma_unmap_single(&enic->pdev->dev, buf->dma_addr, buf->len, 33062306a36Sopenharmony_ci DMA_TO_DEVICE); 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci dma_unmap_page(&enic->pdev->dev, buf->dma_addr, buf->len, 33362306a36Sopenharmony_ci DMA_TO_DEVICE); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (buf->os_buf) 33662306a36Sopenharmony_ci dev_kfree_skb_any(buf->os_buf); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void enic_wq_free_buf(struct vnic_wq *wq, 34062306a36Sopenharmony_ci struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci enic_free_wq_buf(wq, buf); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, 34662306a36Sopenharmony_ci u8 type, u16 q_number, u16 completed_index, void *opaque) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(vdev); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci spin_lock(&enic->wq_lock[q_number]); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci vnic_wq_service(&enic->wq[q_number], cq_desc, 35362306a36Sopenharmony_ci completed_index, enic_wq_free_buf, 35462306a36Sopenharmony_ci opaque); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (netif_tx_queue_stopped(netdev_get_tx_queue(enic->netdev, q_number)) && 35762306a36Sopenharmony_ci vnic_wq_desc_avail(&enic->wq[q_number]) >= 35862306a36Sopenharmony_ci (MAX_SKB_FRAGS + ENIC_DESC_MAX_SPLITS)) 35962306a36Sopenharmony_ci netif_wake_subqueue(enic->netdev, q_number); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci spin_unlock(&enic->wq_lock[q_number]); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic bool enic_log_q_error(struct enic *enic) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci unsigned int i; 36962306a36Sopenharmony_ci u32 error_status; 37062306a36Sopenharmony_ci bool err = false; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 37362306a36Sopenharmony_ci error_status = vnic_wq_error_status(&enic->wq[i]); 37462306a36Sopenharmony_ci err |= error_status; 37562306a36Sopenharmony_ci if (error_status) 37662306a36Sopenharmony_ci netdev_err(enic->netdev, "WQ[%d] error_status %d\n", 37762306a36Sopenharmony_ci i, error_status); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 38162306a36Sopenharmony_ci error_status = vnic_rq_error_status(&enic->rq[i]); 38262306a36Sopenharmony_ci err |= error_status; 38362306a36Sopenharmony_ci if (error_status) 38462306a36Sopenharmony_ci netdev_err(enic->netdev, "RQ[%d] error_status %d\n", 38562306a36Sopenharmony_ci i, error_status); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void enic_msglvl_check(struct enic *enic) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci u32 msg_enable = vnic_dev_msg_lvl(enic->vdev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (msg_enable != enic->msg_enable) { 39662306a36Sopenharmony_ci netdev_info(enic->netdev, "msg lvl changed from 0x%x to 0x%x\n", 39762306a36Sopenharmony_ci enic->msg_enable, msg_enable); 39862306a36Sopenharmony_ci enic->msg_enable = msg_enable; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void enic_mtu_check(struct enic *enic) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci u32 mtu = vnic_dev_mtu(enic->vdev); 40562306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (mtu && mtu != enic->port_mtu) { 40862306a36Sopenharmony_ci enic->port_mtu = mtu; 40962306a36Sopenharmony_ci if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) { 41062306a36Sopenharmony_ci mtu = max_t(int, ENIC_MIN_MTU, 41162306a36Sopenharmony_ci min_t(int, ENIC_MAX_MTU, mtu)); 41262306a36Sopenharmony_ci if (mtu != netdev->mtu) 41362306a36Sopenharmony_ci schedule_work(&enic->change_mtu_work); 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci if (mtu < netdev->mtu) 41662306a36Sopenharmony_ci netdev_warn(netdev, 41762306a36Sopenharmony_ci "interface MTU (%d) set higher " 41862306a36Sopenharmony_ci "than switch port MTU (%d)\n", 41962306a36Sopenharmony_ci netdev->mtu, mtu); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void enic_link_check(struct enic *enic) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int link_status = vnic_dev_link_status(enic->vdev); 42762306a36Sopenharmony_ci int carrier_ok = netif_carrier_ok(enic->netdev); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (link_status && !carrier_ok) { 43062306a36Sopenharmony_ci netdev_info(enic->netdev, "Link UP\n"); 43162306a36Sopenharmony_ci netif_carrier_on(enic->netdev); 43262306a36Sopenharmony_ci } else if (!link_status && carrier_ok) { 43362306a36Sopenharmony_ci netdev_info(enic->netdev, "Link DOWN\n"); 43462306a36Sopenharmony_ci netif_carrier_off(enic->netdev); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic void enic_notify_check(struct enic *enic) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci enic_msglvl_check(enic); 44162306a36Sopenharmony_ci enic_mtu_check(enic); 44262306a36Sopenharmony_ci enic_link_check(enic); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci#define ENIC_TEST_INTR(pba, i) (pba & (1 << i)) 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic irqreturn_t enic_isr_legacy(int irq, void *data) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct net_device *netdev = data; 45062306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 45162306a36Sopenharmony_ci unsigned int io_intr = ENIC_LEGACY_IO_INTR; 45262306a36Sopenharmony_ci unsigned int err_intr = ENIC_LEGACY_ERR_INTR; 45362306a36Sopenharmony_ci unsigned int notify_intr = ENIC_LEGACY_NOTIFY_INTR; 45462306a36Sopenharmony_ci u32 pba; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci vnic_intr_mask(&enic->intr[io_intr]); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci pba = vnic_intr_legacy_pba(enic->legacy_pba); 45962306a36Sopenharmony_ci if (!pba) { 46062306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[io_intr]); 46162306a36Sopenharmony_ci return IRQ_NONE; /* not our interrupt */ 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (ENIC_TEST_INTR(pba, notify_intr)) { 46562306a36Sopenharmony_ci enic_notify_check(enic); 46662306a36Sopenharmony_ci vnic_intr_return_all_credits(&enic->intr[notify_intr]); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (ENIC_TEST_INTR(pba, err_intr)) { 47062306a36Sopenharmony_ci vnic_intr_return_all_credits(&enic->intr[err_intr]); 47162306a36Sopenharmony_ci enic_log_q_error(enic); 47262306a36Sopenharmony_ci /* schedule recovery from WQ/RQ error */ 47362306a36Sopenharmony_ci schedule_work(&enic->reset); 47462306a36Sopenharmony_ci return IRQ_HANDLED; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (ENIC_TEST_INTR(pba, io_intr)) 47862306a36Sopenharmony_ci napi_schedule_irqoff(&enic->napi[0]); 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[io_intr]); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return IRQ_HANDLED; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic irqreturn_t enic_isr_msi(int irq, void *data) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct enic *enic = data; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* With MSI, there is no sharing of interrupts, so this is 49062306a36Sopenharmony_ci * our interrupt and there is no need to ack it. The device 49162306a36Sopenharmony_ci * is not providing per-vector masking, so the OS will not 49262306a36Sopenharmony_ci * write to PCI config space to mask/unmask the interrupt. 49362306a36Sopenharmony_ci * We're using mask_on_assertion for MSI, so the device 49462306a36Sopenharmony_ci * automatically masks the interrupt when the interrupt is 49562306a36Sopenharmony_ci * generated. Later, when exiting polling, the interrupt 49662306a36Sopenharmony_ci * will be unmasked (see enic_poll). 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Also, the device uses the same PCIe Traffic Class (TC) 49962306a36Sopenharmony_ci * for Memory Write data and MSI, so there are no ordering 50062306a36Sopenharmony_ci * issues; the MSI will always arrive at the Root Complex 50162306a36Sopenharmony_ci * _after_ corresponding Memory Writes (i.e. descriptor 50262306a36Sopenharmony_ci * writes). 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci napi_schedule_irqoff(&enic->napi[0]); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return IRQ_HANDLED; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic irqreturn_t enic_isr_msix(int irq, void *data) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct napi_struct *napi = data; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci napi_schedule_irqoff(napi); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return IRQ_HANDLED; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic irqreturn_t enic_isr_msix_err(int irq, void *data) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct enic *enic = data; 52262306a36Sopenharmony_ci unsigned int intr = enic_msix_err_intr(enic); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci vnic_intr_return_all_credits(&enic->intr[intr]); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (enic_log_q_error(enic)) 52762306a36Sopenharmony_ci /* schedule recovery from WQ/RQ error */ 52862306a36Sopenharmony_ci schedule_work(&enic->reset); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return IRQ_HANDLED; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic irqreturn_t enic_isr_msix_notify(int irq, void *data) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct enic *enic = data; 53662306a36Sopenharmony_ci unsigned int intr = enic_msix_notify_intr(enic); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci enic_notify_check(enic); 53962306a36Sopenharmony_ci vnic_intr_return_all_credits(&enic->intr[intr]); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return IRQ_HANDLED; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int enic_queue_wq_skb_cont(struct enic *enic, struct vnic_wq *wq, 54562306a36Sopenharmony_ci struct sk_buff *skb, unsigned int len_left, 54662306a36Sopenharmony_ci int loopback) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci const skb_frag_t *frag; 54962306a36Sopenharmony_ci dma_addr_t dma_addr; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Queue additional data fragments */ 55262306a36Sopenharmony_ci for (frag = skb_shinfo(skb)->frags; len_left; frag++) { 55362306a36Sopenharmony_ci len_left -= skb_frag_size(frag); 55462306a36Sopenharmony_ci dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, 0, 55562306a36Sopenharmony_ci skb_frag_size(frag), 55662306a36Sopenharmony_ci DMA_TO_DEVICE); 55762306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci enic_queue_wq_desc_cont(wq, skb, dma_addr, skb_frag_size(frag), 56062306a36Sopenharmony_ci (len_left == 0), /* EOP? */ 56162306a36Sopenharmony_ci loopback); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int enic_queue_wq_skb_vlan(struct enic *enic, struct vnic_wq *wq, 56862306a36Sopenharmony_ci struct sk_buff *skb, int vlan_tag_insert, 56962306a36Sopenharmony_ci unsigned int vlan_tag, int loopback) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci unsigned int head_len = skb_headlen(skb); 57262306a36Sopenharmony_ci unsigned int len_left = skb->len - head_len; 57362306a36Sopenharmony_ci int eop = (len_left == 0); 57462306a36Sopenharmony_ci dma_addr_t dma_addr; 57562306a36Sopenharmony_ci int err = 0; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci dma_addr = dma_map_single(&enic->pdev->dev, skb->data, head_len, 57862306a36Sopenharmony_ci DMA_TO_DEVICE); 57962306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 58062306a36Sopenharmony_ci return -ENOMEM; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Queue the main skb fragment. The fragments are no larger 58362306a36Sopenharmony_ci * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less 58462306a36Sopenharmony_ci * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor 58562306a36Sopenharmony_ci * per fragment is queued. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci enic_queue_wq_desc(wq, skb, dma_addr, head_len, vlan_tag_insert, 58862306a36Sopenharmony_ci vlan_tag, eop, loopback); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!eop) 59162306a36Sopenharmony_ci err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return err; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int enic_queue_wq_skb_csum_l4(struct enic *enic, struct vnic_wq *wq, 59762306a36Sopenharmony_ci struct sk_buff *skb, int vlan_tag_insert, 59862306a36Sopenharmony_ci unsigned int vlan_tag, int loopback) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci unsigned int head_len = skb_headlen(skb); 60162306a36Sopenharmony_ci unsigned int len_left = skb->len - head_len; 60262306a36Sopenharmony_ci unsigned int hdr_len = skb_checksum_start_offset(skb); 60362306a36Sopenharmony_ci unsigned int csum_offset = hdr_len + skb->csum_offset; 60462306a36Sopenharmony_ci int eop = (len_left == 0); 60562306a36Sopenharmony_ci dma_addr_t dma_addr; 60662306a36Sopenharmony_ci int err = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci dma_addr = dma_map_single(&enic->pdev->dev, skb->data, head_len, 60962306a36Sopenharmony_ci DMA_TO_DEVICE); 61062306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 61162306a36Sopenharmony_ci return -ENOMEM; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* Queue the main skb fragment. The fragments are no larger 61462306a36Sopenharmony_ci * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less 61562306a36Sopenharmony_ci * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor 61662306a36Sopenharmony_ci * per fragment is queued. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci enic_queue_wq_desc_csum_l4(wq, skb, dma_addr, head_len, csum_offset, 61962306a36Sopenharmony_ci hdr_len, vlan_tag_insert, vlan_tag, eop, 62062306a36Sopenharmony_ci loopback); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!eop) 62362306a36Sopenharmony_ci err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic void enic_preload_tcp_csum_encap(struct sk_buff *skb) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci const struct ethhdr *eth = (struct ethhdr *)skb_inner_mac_header(skb); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci switch (eth->h_proto) { 63362306a36Sopenharmony_ci case ntohs(ETH_P_IP): 63462306a36Sopenharmony_ci inner_ip_hdr(skb)->check = 0; 63562306a36Sopenharmony_ci inner_tcp_hdr(skb)->check = 63662306a36Sopenharmony_ci ~csum_tcpudp_magic(inner_ip_hdr(skb)->saddr, 63762306a36Sopenharmony_ci inner_ip_hdr(skb)->daddr, 0, 63862306a36Sopenharmony_ci IPPROTO_TCP, 0); 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case ntohs(ETH_P_IPV6): 64162306a36Sopenharmony_ci inner_tcp_hdr(skb)->check = 64262306a36Sopenharmony_ci ~csum_ipv6_magic(&inner_ipv6_hdr(skb)->saddr, 64362306a36Sopenharmony_ci &inner_ipv6_hdr(skb)->daddr, 0, 64462306a36Sopenharmony_ci IPPROTO_TCP, 0); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci default: 64762306a36Sopenharmony_ci WARN_ONCE(1, "Non ipv4/ipv6 inner pkt for encap offload"); 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void enic_preload_tcp_csum(struct sk_buff *skb) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci /* Preload TCP csum field with IP pseudo hdr calculated 65562306a36Sopenharmony_ci * with IP length set to zero. HW will later add in length 65662306a36Sopenharmony_ci * to each TCP segment resulting from the TSO. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_IP)) { 66062306a36Sopenharmony_ci ip_hdr(skb)->check = 0; 66162306a36Sopenharmony_ci tcp_hdr(skb)->check = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, 66262306a36Sopenharmony_ci ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); 66362306a36Sopenharmony_ci } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) { 66462306a36Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int enic_queue_wq_skb_tso(struct enic *enic, struct vnic_wq *wq, 66962306a36Sopenharmony_ci struct sk_buff *skb, unsigned int mss, 67062306a36Sopenharmony_ci int vlan_tag_insert, unsigned int vlan_tag, 67162306a36Sopenharmony_ci int loopback) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci unsigned int frag_len_left = skb_headlen(skb); 67462306a36Sopenharmony_ci unsigned int len_left = skb->len - frag_len_left; 67562306a36Sopenharmony_ci int eop = (len_left == 0); 67662306a36Sopenharmony_ci unsigned int offset = 0; 67762306a36Sopenharmony_ci unsigned int hdr_len; 67862306a36Sopenharmony_ci dma_addr_t dma_addr; 67962306a36Sopenharmony_ci unsigned int len; 68062306a36Sopenharmony_ci skb_frag_t *frag; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (skb->encapsulation) { 68362306a36Sopenharmony_ci hdr_len = skb_inner_tcp_all_headers(skb); 68462306a36Sopenharmony_ci enic_preload_tcp_csum_encap(skb); 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci hdr_len = skb_tcp_all_headers(skb); 68762306a36Sopenharmony_ci enic_preload_tcp_csum(skb); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Queue WQ_ENET_MAX_DESC_LEN length descriptors 69162306a36Sopenharmony_ci * for the main skb fragment 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci while (frag_len_left) { 69462306a36Sopenharmony_ci len = min(frag_len_left, (unsigned int)WQ_ENET_MAX_DESC_LEN); 69562306a36Sopenharmony_ci dma_addr = dma_map_single(&enic->pdev->dev, 69662306a36Sopenharmony_ci skb->data + offset, len, 69762306a36Sopenharmony_ci DMA_TO_DEVICE); 69862306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 69962306a36Sopenharmony_ci return -ENOMEM; 70062306a36Sopenharmony_ci enic_queue_wq_desc_tso(wq, skb, dma_addr, len, mss, hdr_len, 70162306a36Sopenharmony_ci vlan_tag_insert, vlan_tag, 70262306a36Sopenharmony_ci eop && (len == frag_len_left), loopback); 70362306a36Sopenharmony_ci frag_len_left -= len; 70462306a36Sopenharmony_ci offset += len; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (eop) 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Queue WQ_ENET_MAX_DESC_LEN length descriptors 71162306a36Sopenharmony_ci * for additional data fragments 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci for (frag = skb_shinfo(skb)->frags; len_left; frag++) { 71462306a36Sopenharmony_ci len_left -= skb_frag_size(frag); 71562306a36Sopenharmony_ci frag_len_left = skb_frag_size(frag); 71662306a36Sopenharmony_ci offset = 0; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci while (frag_len_left) { 71962306a36Sopenharmony_ci len = min(frag_len_left, 72062306a36Sopenharmony_ci (unsigned int)WQ_ENET_MAX_DESC_LEN); 72162306a36Sopenharmony_ci dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, 72262306a36Sopenharmony_ci offset, len, 72362306a36Sopenharmony_ci DMA_TO_DEVICE); 72462306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 72562306a36Sopenharmony_ci return -ENOMEM; 72662306a36Sopenharmony_ci enic_queue_wq_desc_cont(wq, skb, dma_addr, len, 72762306a36Sopenharmony_ci (len_left == 0) && 72862306a36Sopenharmony_ci (len == frag_len_left),/*EOP*/ 72962306a36Sopenharmony_ci loopback); 73062306a36Sopenharmony_ci frag_len_left -= len; 73162306a36Sopenharmony_ci offset += len; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic inline int enic_queue_wq_skb_encap(struct enic *enic, struct vnic_wq *wq, 73962306a36Sopenharmony_ci struct sk_buff *skb, 74062306a36Sopenharmony_ci int vlan_tag_insert, 74162306a36Sopenharmony_ci unsigned int vlan_tag, int loopback) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci unsigned int head_len = skb_headlen(skb); 74462306a36Sopenharmony_ci unsigned int len_left = skb->len - head_len; 74562306a36Sopenharmony_ci /* Hardware will overwrite the checksum fields, calculating from 74662306a36Sopenharmony_ci * scratch and ignoring the value placed by software. 74762306a36Sopenharmony_ci * Offload mode = 00 74862306a36Sopenharmony_ci * mss[2], mss[1], mss[0] bits are set 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci unsigned int mss_or_csum = 7; 75162306a36Sopenharmony_ci int eop = (len_left == 0); 75262306a36Sopenharmony_ci dma_addr_t dma_addr; 75362306a36Sopenharmony_ci int err = 0; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci dma_addr = dma_map_single(&enic->pdev->dev, skb->data, head_len, 75662306a36Sopenharmony_ci DMA_TO_DEVICE); 75762306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) 75862306a36Sopenharmony_ci return -ENOMEM; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci enic_queue_wq_desc_ex(wq, skb, dma_addr, head_len, mss_or_csum, 0, 76162306a36Sopenharmony_ci vlan_tag_insert, vlan_tag, 76262306a36Sopenharmony_ci WQ_ENET_OFFLOAD_MODE_CSUM, eop, 1 /* SOP */, eop, 76362306a36Sopenharmony_ci loopback); 76462306a36Sopenharmony_ci if (!eop) 76562306a36Sopenharmony_ci err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return err; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic inline int enic_queue_wq_skb(struct enic *enic, 77162306a36Sopenharmony_ci struct vnic_wq *wq, struct sk_buff *skb) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci unsigned int mss = skb_shinfo(skb)->gso_size; 77462306a36Sopenharmony_ci unsigned int vlan_tag = 0; 77562306a36Sopenharmony_ci int vlan_tag_insert = 0; 77662306a36Sopenharmony_ci int loopback = 0; 77762306a36Sopenharmony_ci int err; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 78062306a36Sopenharmony_ci /* VLAN tag from trunking driver */ 78162306a36Sopenharmony_ci vlan_tag_insert = 1; 78262306a36Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 78362306a36Sopenharmony_ci } else if (enic->loop_enable) { 78462306a36Sopenharmony_ci vlan_tag = enic->loop_tag; 78562306a36Sopenharmony_ci loopback = 1; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (mss) 78962306a36Sopenharmony_ci err = enic_queue_wq_skb_tso(enic, wq, skb, mss, 79062306a36Sopenharmony_ci vlan_tag_insert, vlan_tag, 79162306a36Sopenharmony_ci loopback); 79262306a36Sopenharmony_ci else if (skb->encapsulation) 79362306a36Sopenharmony_ci err = enic_queue_wq_skb_encap(enic, wq, skb, vlan_tag_insert, 79462306a36Sopenharmony_ci vlan_tag, loopback); 79562306a36Sopenharmony_ci else if (skb->ip_summed == CHECKSUM_PARTIAL) 79662306a36Sopenharmony_ci err = enic_queue_wq_skb_csum_l4(enic, wq, skb, vlan_tag_insert, 79762306a36Sopenharmony_ci vlan_tag, loopback); 79862306a36Sopenharmony_ci else 79962306a36Sopenharmony_ci err = enic_queue_wq_skb_vlan(enic, wq, skb, vlan_tag_insert, 80062306a36Sopenharmony_ci vlan_tag, loopback); 80162306a36Sopenharmony_ci if (unlikely(err)) { 80262306a36Sopenharmony_ci struct vnic_wq_buf *buf; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci buf = wq->to_use->prev; 80562306a36Sopenharmony_ci /* while not EOP of previous pkt && queue not empty. 80662306a36Sopenharmony_ci * For all non EOP bufs, os_buf is NULL. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci while (!buf->os_buf && (buf->next != wq->to_clean)) { 80962306a36Sopenharmony_ci enic_free_wq_buf(wq, buf); 81062306a36Sopenharmony_ci wq->ring.desc_avail++; 81162306a36Sopenharmony_ci buf = buf->prev; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci wq->to_use = buf->next; 81462306a36Sopenharmony_ci dev_kfree_skb(skb); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci return err; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* netif_tx_lock held, process context with BHs disabled, or BH */ 82062306a36Sopenharmony_cistatic netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb, 82162306a36Sopenharmony_ci struct net_device *netdev) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 82462306a36Sopenharmony_ci struct vnic_wq *wq; 82562306a36Sopenharmony_ci unsigned int txq_map; 82662306a36Sopenharmony_ci struct netdev_queue *txq; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (skb->len <= 0) { 82962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 83062306a36Sopenharmony_ci return NETDEV_TX_OK; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci txq_map = skb_get_queue_mapping(skb) % enic->wq_count; 83462306a36Sopenharmony_ci wq = &enic->wq[txq_map]; 83562306a36Sopenharmony_ci txq = netdev_get_tx_queue(netdev, txq_map); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Non-TSO sends must fit within ENIC_NON_TSO_MAX_DESC descs, 83862306a36Sopenharmony_ci * which is very likely. In the off chance it's going to take 83962306a36Sopenharmony_ci * more than * ENIC_NON_TSO_MAX_DESC, linearize the skb. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_size == 0 && 84362306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags + 1 > ENIC_NON_TSO_MAX_DESC && 84462306a36Sopenharmony_ci skb_linearize(skb)) { 84562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 84662306a36Sopenharmony_ci return NETDEV_TX_OK; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci spin_lock(&enic->wq_lock[txq_map]); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (vnic_wq_desc_avail(wq) < 85262306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags + ENIC_DESC_MAX_SPLITS) { 85362306a36Sopenharmony_ci netif_tx_stop_queue(txq); 85462306a36Sopenharmony_ci /* This is a hard error, log it */ 85562306a36Sopenharmony_ci netdev_err(netdev, "BUG! Tx ring full when queue awake!\n"); 85662306a36Sopenharmony_ci spin_unlock(&enic->wq_lock[txq_map]); 85762306a36Sopenharmony_ci return NETDEV_TX_BUSY; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (enic_queue_wq_skb(enic, wq, skb)) 86162306a36Sopenharmony_ci goto error; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (vnic_wq_desc_avail(wq) < MAX_SKB_FRAGS + ENIC_DESC_MAX_SPLITS) 86462306a36Sopenharmony_ci netif_tx_stop_queue(txq); 86562306a36Sopenharmony_ci skb_tx_timestamp(skb); 86662306a36Sopenharmony_ci if (!netdev_xmit_more() || netif_xmit_stopped(txq)) 86762306a36Sopenharmony_ci vnic_wq_doorbell(wq); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cierror: 87062306a36Sopenharmony_ci spin_unlock(&enic->wq_lock[txq_map]); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return NETDEV_TX_OK; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci/* dev_base_lock rwlock held, nominally process context */ 87662306a36Sopenharmony_cistatic void enic_get_stats(struct net_device *netdev, 87762306a36Sopenharmony_ci struct rtnl_link_stats64 *net_stats) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 88062306a36Sopenharmony_ci struct vnic_stats *stats; 88162306a36Sopenharmony_ci int err; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci err = enic_dev_stats_dump(enic, &stats); 88462306a36Sopenharmony_ci /* return only when dma_alloc_coherent fails in vnic_dev_stats_dump 88562306a36Sopenharmony_ci * For other failures, like devcmd failure, we return previously 88662306a36Sopenharmony_ci * recorded stats. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci if (err == -ENOMEM) 88962306a36Sopenharmony_ci return; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci net_stats->tx_packets = stats->tx.tx_frames_ok; 89262306a36Sopenharmony_ci net_stats->tx_bytes = stats->tx.tx_bytes_ok; 89362306a36Sopenharmony_ci net_stats->tx_errors = stats->tx.tx_errors; 89462306a36Sopenharmony_ci net_stats->tx_dropped = stats->tx.tx_drops; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci net_stats->rx_packets = stats->rx.rx_frames_ok; 89762306a36Sopenharmony_ci net_stats->rx_bytes = stats->rx.rx_bytes_ok; 89862306a36Sopenharmony_ci net_stats->rx_errors = stats->rx.rx_errors; 89962306a36Sopenharmony_ci net_stats->multicast = stats->rx.rx_multicast_frames_ok; 90062306a36Sopenharmony_ci net_stats->rx_over_errors = enic->rq_truncated_pkts; 90162306a36Sopenharmony_ci net_stats->rx_crc_errors = enic->rq_bad_fcs; 90262306a36Sopenharmony_ci net_stats->rx_dropped = stats->rx.rx_no_bufs + stats->rx.rx_drop; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (enic->mc_count == ENIC_MULTICAST_PERFECT_FILTERS) { 91062306a36Sopenharmony_ci unsigned int mc_count = netdev_mc_count(netdev); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci netdev_warn(netdev, "Registering only %d out of %d multicast addresses\n", 91362306a36Sopenharmony_ci ENIC_MULTICAST_PERFECT_FILTERS, mc_count); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return -ENOSPC; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci enic_dev_add_addr(enic, mc_addr); 91962306a36Sopenharmony_ci enic->mc_count++; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int enic_mc_unsync(struct net_device *netdev, const u8 *mc_addr) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci enic_dev_del_addr(enic, mc_addr); 92962306a36Sopenharmony_ci enic->mc_count--; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int enic_uc_sync(struct net_device *netdev, const u8 *uc_addr) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (enic->uc_count == ENIC_UNICAST_PERFECT_FILTERS) { 93962306a36Sopenharmony_ci unsigned int uc_count = netdev_uc_count(netdev); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci netdev_warn(netdev, "Registering only %d out of %d unicast addresses\n", 94262306a36Sopenharmony_ci ENIC_UNICAST_PERFECT_FILTERS, uc_count); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return -ENOSPC; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci enic_dev_add_addr(enic, uc_addr); 94862306a36Sopenharmony_ci enic->uc_count++; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int enic_uc_unsync(struct net_device *netdev, const u8 *uc_addr) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci enic_dev_del_addr(enic, uc_addr); 95862306a36Sopenharmony_ci enic->uc_count--; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_civoid enic_reset_addr_lists(struct enic *enic) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci __dev_uc_unsync(netdev, NULL); 96862306a36Sopenharmony_ci __dev_mc_unsync(netdev, NULL); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci enic->mc_count = 0; 97162306a36Sopenharmony_ci enic->uc_count = 0; 97262306a36Sopenharmony_ci enic->flags = 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int enic_set_mac_addr(struct net_device *netdev, char *addr) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) { 98062306a36Sopenharmony_ci if (!is_valid_ether_addr(addr) && !is_zero_ether_addr(addr)) 98162306a36Sopenharmony_ci return -EADDRNOTAVAIL; 98262306a36Sopenharmony_ci } else { 98362306a36Sopenharmony_ci if (!is_valid_ether_addr(addr)) 98462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci return 0; 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int enic_set_mac_address_dynamic(struct net_device *netdev, void *p) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 99562306a36Sopenharmony_ci struct sockaddr *saddr = p; 99662306a36Sopenharmony_ci char *addr = saddr->sa_data; 99762306a36Sopenharmony_ci int err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (netif_running(enic->netdev)) { 100062306a36Sopenharmony_ci err = enic_dev_del_station_addr(enic); 100162306a36Sopenharmony_ci if (err) 100262306a36Sopenharmony_ci return err; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci err = enic_set_mac_addr(netdev, addr); 100662306a36Sopenharmony_ci if (err) 100762306a36Sopenharmony_ci return err; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (netif_running(enic->netdev)) { 101062306a36Sopenharmony_ci err = enic_dev_add_station_addr(enic); 101162306a36Sopenharmony_ci if (err) 101262306a36Sopenharmony_ci return err; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci return err; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic int enic_set_mac_address(struct net_device *netdev, void *p) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct sockaddr *saddr = p; 102162306a36Sopenharmony_ci char *addr = saddr->sa_data; 102262306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 102362306a36Sopenharmony_ci int err; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci err = enic_dev_del_station_addr(enic); 102662306a36Sopenharmony_ci if (err) 102762306a36Sopenharmony_ci return err; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci err = enic_set_mac_addr(netdev, addr); 103062306a36Sopenharmony_ci if (err) 103162306a36Sopenharmony_ci return err; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return enic_dev_add_station_addr(enic); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/* netif_tx_lock held, BHs disabled */ 103762306a36Sopenharmony_cistatic void enic_set_rx_mode(struct net_device *netdev) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 104062306a36Sopenharmony_ci int directed = 1; 104162306a36Sopenharmony_ci int multicast = (netdev->flags & IFF_MULTICAST) ? 1 : 0; 104262306a36Sopenharmony_ci int broadcast = (netdev->flags & IFF_BROADCAST) ? 1 : 0; 104362306a36Sopenharmony_ci int promisc = (netdev->flags & IFF_PROMISC) || 104462306a36Sopenharmony_ci netdev_uc_count(netdev) > ENIC_UNICAST_PERFECT_FILTERS; 104562306a36Sopenharmony_ci int allmulti = (netdev->flags & IFF_ALLMULTI) || 104662306a36Sopenharmony_ci netdev_mc_count(netdev) > ENIC_MULTICAST_PERFECT_FILTERS; 104762306a36Sopenharmony_ci unsigned int flags = netdev->flags | 104862306a36Sopenharmony_ci (allmulti ? IFF_ALLMULTI : 0) | 104962306a36Sopenharmony_ci (promisc ? IFF_PROMISC : 0); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (enic->flags != flags) { 105262306a36Sopenharmony_ci enic->flags = flags; 105362306a36Sopenharmony_ci enic_dev_packet_filter(enic, directed, 105462306a36Sopenharmony_ci multicast, broadcast, promisc, allmulti); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (!promisc) { 105862306a36Sopenharmony_ci __dev_uc_sync(netdev, enic_uc_sync, enic_uc_unsync); 105962306a36Sopenharmony_ci if (!allmulti) 106062306a36Sopenharmony_ci __dev_mc_sync(netdev, enic_mc_sync, enic_mc_unsync); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci/* netif_tx_lock held, BHs disabled */ 106562306a36Sopenharmony_cistatic void enic_tx_timeout(struct net_device *netdev, unsigned int txqueue) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 106862306a36Sopenharmony_ci schedule_work(&enic->tx_hang_reset); 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 107462306a36Sopenharmony_ci struct enic_port_profile *pp; 107562306a36Sopenharmony_ci int err; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci ENIC_PP_BY_INDEX(enic, vf, pp, &err); 107862306a36Sopenharmony_ci if (err) 107962306a36Sopenharmony_ci return err; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (is_valid_ether_addr(mac) || is_zero_ether_addr(mac)) { 108262306a36Sopenharmony_ci if (vf == PORT_SELF_VF) { 108362306a36Sopenharmony_ci memcpy(pp->vf_mac, mac, ETH_ALEN); 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci } else { 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * For sriov vf's set the mac in hw 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, 109062306a36Sopenharmony_ci vnic_dev_set_mac_addr, mac); 109162306a36Sopenharmony_ci return enic_dev_status_to_errno(err); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci } else 109462306a36Sopenharmony_ci return -EINVAL; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int enic_set_vf_port(struct net_device *netdev, int vf, 109862306a36Sopenharmony_ci struct nlattr *port[]) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci static const u8 zero_addr[ETH_ALEN] = {}; 110162306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 110262306a36Sopenharmony_ci struct enic_port_profile prev_pp; 110362306a36Sopenharmony_ci struct enic_port_profile *pp; 110462306a36Sopenharmony_ci int err = 0, restore_pp = 1; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci ENIC_PP_BY_INDEX(enic, vf, pp, &err); 110762306a36Sopenharmony_ci if (err) 110862306a36Sopenharmony_ci return err; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (!port[IFLA_PORT_REQUEST]) 111162306a36Sopenharmony_ci return -EOPNOTSUPP; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci memcpy(&prev_pp, pp, sizeof(*enic->pp)); 111462306a36Sopenharmony_ci memset(pp, 0, sizeof(*enic->pp)); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci pp->set |= ENIC_SET_REQUEST; 111762306a36Sopenharmony_ci pp->request = nla_get_u8(port[IFLA_PORT_REQUEST]); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (port[IFLA_PORT_PROFILE]) { 112062306a36Sopenharmony_ci pp->set |= ENIC_SET_NAME; 112162306a36Sopenharmony_ci memcpy(pp->name, nla_data(port[IFLA_PORT_PROFILE]), 112262306a36Sopenharmony_ci PORT_PROFILE_MAX); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (port[IFLA_PORT_INSTANCE_UUID]) { 112662306a36Sopenharmony_ci pp->set |= ENIC_SET_INSTANCE; 112762306a36Sopenharmony_ci memcpy(pp->instance_uuid, 112862306a36Sopenharmony_ci nla_data(port[IFLA_PORT_INSTANCE_UUID]), PORT_UUID_MAX); 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (port[IFLA_PORT_HOST_UUID]) { 113262306a36Sopenharmony_ci pp->set |= ENIC_SET_HOST; 113362306a36Sopenharmony_ci memcpy(pp->host_uuid, 113462306a36Sopenharmony_ci nla_data(port[IFLA_PORT_HOST_UUID]), PORT_UUID_MAX); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (vf == PORT_SELF_VF) { 113862306a36Sopenharmony_ci /* Special case handling: mac came from IFLA_VF_MAC */ 113962306a36Sopenharmony_ci if (!is_zero_ether_addr(prev_pp.vf_mac)) 114062306a36Sopenharmony_ci memcpy(pp->mac_addr, prev_pp.vf_mac, ETH_ALEN); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (is_zero_ether_addr(netdev->dev_addr)) 114362306a36Sopenharmony_ci eth_hw_addr_random(netdev); 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci /* SR-IOV VF: get mac from adapter */ 114662306a36Sopenharmony_ci ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, 114762306a36Sopenharmony_ci vnic_dev_get_mac_addr, pp->mac_addr); 114862306a36Sopenharmony_ci if (err) { 114962306a36Sopenharmony_ci netdev_err(netdev, "Error getting mac for vf %d\n", vf); 115062306a36Sopenharmony_ci memcpy(pp, &prev_pp, sizeof(*pp)); 115162306a36Sopenharmony_ci return enic_dev_status_to_errno(err); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci err = enic_process_set_pp_request(enic, vf, &prev_pp, &restore_pp); 115662306a36Sopenharmony_ci if (err) { 115762306a36Sopenharmony_ci if (restore_pp) { 115862306a36Sopenharmony_ci /* Things are still the way they were: Implicit 115962306a36Sopenharmony_ci * DISASSOCIATE failed 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_ci memcpy(pp, &prev_pp, sizeof(*pp)); 116262306a36Sopenharmony_ci } else { 116362306a36Sopenharmony_ci memset(pp, 0, sizeof(*pp)); 116462306a36Sopenharmony_ci if (vf == PORT_SELF_VF) 116562306a36Sopenharmony_ci eth_hw_addr_set(netdev, zero_addr); 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci /* Set flag to indicate that the port assoc/disassoc 116962306a36Sopenharmony_ci * request has been sent out to fw 117062306a36Sopenharmony_ci */ 117162306a36Sopenharmony_ci pp->set |= ENIC_PORT_REQUEST_APPLIED; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* If DISASSOCIATE, clean up all assigned/saved macaddresses */ 117462306a36Sopenharmony_ci if (pp->request == PORT_REQUEST_DISASSOCIATE) { 117562306a36Sopenharmony_ci eth_zero_addr(pp->mac_addr); 117662306a36Sopenharmony_ci if (vf == PORT_SELF_VF) 117762306a36Sopenharmony_ci eth_hw_addr_set(netdev, zero_addr); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (vf == PORT_SELF_VF) 118262306a36Sopenharmony_ci eth_zero_addr(pp->vf_mac); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return err; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic int enic_get_vf_port(struct net_device *netdev, int vf, 118862306a36Sopenharmony_ci struct sk_buff *skb) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 119162306a36Sopenharmony_ci u16 response = PORT_PROFILE_RESPONSE_SUCCESS; 119262306a36Sopenharmony_ci struct enic_port_profile *pp; 119362306a36Sopenharmony_ci int err; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci ENIC_PP_BY_INDEX(enic, vf, pp, &err); 119662306a36Sopenharmony_ci if (err) 119762306a36Sopenharmony_ci return err; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (!(pp->set & ENIC_PORT_REQUEST_APPLIED)) 120062306a36Sopenharmony_ci return -ENODATA; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci err = enic_process_get_pp_request(enic, vf, pp->request, &response); 120362306a36Sopenharmony_ci if (err) 120462306a36Sopenharmony_ci return err; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (nla_put_u16(skb, IFLA_PORT_REQUEST, pp->request) || 120762306a36Sopenharmony_ci nla_put_u16(skb, IFLA_PORT_RESPONSE, response) || 120862306a36Sopenharmony_ci ((pp->set & ENIC_SET_NAME) && 120962306a36Sopenharmony_ci nla_put(skb, IFLA_PORT_PROFILE, PORT_PROFILE_MAX, pp->name)) || 121062306a36Sopenharmony_ci ((pp->set & ENIC_SET_INSTANCE) && 121162306a36Sopenharmony_ci nla_put(skb, IFLA_PORT_INSTANCE_UUID, PORT_UUID_MAX, 121262306a36Sopenharmony_ci pp->instance_uuid)) || 121362306a36Sopenharmony_ci ((pp->set & ENIC_SET_HOST) && 121462306a36Sopenharmony_ci nla_put(skb, IFLA_PORT_HOST_UUID, PORT_UUID_MAX, pp->host_uuid))) 121562306a36Sopenharmony_ci goto nla_put_failure; 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cinla_put_failure: 121962306a36Sopenharmony_ci return -EMSGSIZE; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(rq->vdev); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (!buf->os_buf) 122762306a36Sopenharmony_ci return; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci dma_unmap_single(&enic->pdev->dev, buf->dma_addr, buf->len, 123062306a36Sopenharmony_ci DMA_FROM_DEVICE); 123162306a36Sopenharmony_ci dev_kfree_skb_any(buf->os_buf); 123262306a36Sopenharmony_ci buf->os_buf = NULL; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic int enic_rq_alloc_buf(struct vnic_rq *rq) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(rq->vdev); 123862306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 123962306a36Sopenharmony_ci struct sk_buff *skb; 124062306a36Sopenharmony_ci unsigned int len = netdev->mtu + VLAN_ETH_HLEN; 124162306a36Sopenharmony_ci unsigned int os_buf_index = 0; 124262306a36Sopenharmony_ci dma_addr_t dma_addr; 124362306a36Sopenharmony_ci struct vnic_rq_buf *buf = rq->to_use; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (buf->os_buf) { 124662306a36Sopenharmony_ci enic_queue_rq_desc(rq, buf->os_buf, os_buf_index, buf->dma_addr, 124762306a36Sopenharmony_ci buf->len); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, len); 125262306a36Sopenharmony_ci if (!skb) 125362306a36Sopenharmony_ci return -ENOMEM; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci dma_addr = dma_map_single(&enic->pdev->dev, skb->data, len, 125662306a36Sopenharmony_ci DMA_FROM_DEVICE); 125762306a36Sopenharmony_ci if (unlikely(enic_dma_map_check(enic, dma_addr))) { 125862306a36Sopenharmony_ci dev_kfree_skb(skb); 125962306a36Sopenharmony_ci return -ENOMEM; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci enic_queue_rq_desc(rq, skb, os_buf_index, 126362306a36Sopenharmony_ci dma_addr, len); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size, 126962306a36Sopenharmony_ci u32 pkt_len) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci if (ENIC_LARGE_PKT_THRESHOLD <= pkt_len) 127262306a36Sopenharmony_ci pkt_size->large_pkt_bytes_cnt += pkt_len; 127362306a36Sopenharmony_ci else 127462306a36Sopenharmony_ci pkt_size->small_pkt_bytes_cnt += pkt_len; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic bool enic_rxcopybreak(struct net_device *netdev, struct sk_buff **skb, 127862306a36Sopenharmony_ci struct vnic_rq_buf *buf, u16 len) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 128162306a36Sopenharmony_ci struct sk_buff *new_skb; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (len > enic->rx_copybreak) 128462306a36Sopenharmony_ci return false; 128562306a36Sopenharmony_ci new_skb = netdev_alloc_skb_ip_align(netdev, len); 128662306a36Sopenharmony_ci if (!new_skb) 128762306a36Sopenharmony_ci return false; 128862306a36Sopenharmony_ci dma_sync_single_for_cpu(&enic->pdev->dev, buf->dma_addr, len, 128962306a36Sopenharmony_ci DMA_FROM_DEVICE); 129062306a36Sopenharmony_ci memcpy(new_skb->data, (*skb)->data, len); 129162306a36Sopenharmony_ci *skb = new_skb; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci return true; 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic void enic_rq_indicate_buf(struct vnic_rq *rq, 129762306a36Sopenharmony_ci struct cq_desc *cq_desc, struct vnic_rq_buf *buf, 129862306a36Sopenharmony_ci int skipped, void *opaque) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(rq->vdev); 130162306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 130262306a36Sopenharmony_ci struct sk_buff *skb; 130362306a36Sopenharmony_ci struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci u8 type, color, eop, sop, ingress_port, vlan_stripped; 130662306a36Sopenharmony_ci u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; 130762306a36Sopenharmony_ci u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; 130862306a36Sopenharmony_ci u8 ipv6, ipv4, ipv4_fragment, fcs_ok, rss_type, csum_not_calc; 130962306a36Sopenharmony_ci u8 packet_error; 131062306a36Sopenharmony_ci u16 q_number, completed_index, bytes_written, vlan_tci, checksum; 131162306a36Sopenharmony_ci u32 rss_hash; 131262306a36Sopenharmony_ci bool outer_csum_ok = true, encap = false; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (skipped) 131562306a36Sopenharmony_ci return; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci skb = buf->os_buf; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc, 132062306a36Sopenharmony_ci &type, &color, &q_number, &completed_index, 132162306a36Sopenharmony_ci &ingress_port, &fcoe, &eop, &sop, &rss_type, 132262306a36Sopenharmony_ci &csum_not_calc, &rss_hash, &bytes_written, 132362306a36Sopenharmony_ci &packet_error, &vlan_stripped, &vlan_tci, &checksum, 132462306a36Sopenharmony_ci &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, 132562306a36Sopenharmony_ci &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, 132662306a36Sopenharmony_ci &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, 132762306a36Sopenharmony_ci &fcs_ok); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (packet_error) { 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (!fcs_ok) { 133262306a36Sopenharmony_ci if (bytes_written > 0) 133362306a36Sopenharmony_ci enic->rq_bad_fcs++; 133462306a36Sopenharmony_ci else if (bytes_written == 0) 133562306a36Sopenharmony_ci enic->rq_truncated_pkts++; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci dma_unmap_single(&enic->pdev->dev, buf->dma_addr, buf->len, 133962306a36Sopenharmony_ci DMA_FROM_DEVICE); 134062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 134162306a36Sopenharmony_ci buf->os_buf = NULL; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (eop && bytes_written > 0) { 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Good receive 134962306a36Sopenharmony_ci */ 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (!enic_rxcopybreak(netdev, &skb, buf, bytes_written)) { 135262306a36Sopenharmony_ci buf->os_buf = NULL; 135362306a36Sopenharmony_ci dma_unmap_single(&enic->pdev->dev, buf->dma_addr, 135462306a36Sopenharmony_ci buf->len, DMA_FROM_DEVICE); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci prefetch(skb->data - NET_IP_ALIGN); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci skb_put(skb, bytes_written); 135962306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 136062306a36Sopenharmony_ci skb_record_rx_queue(skb, q_number); 136162306a36Sopenharmony_ci if ((netdev->features & NETIF_F_RXHASH) && rss_hash && 136262306a36Sopenharmony_ci (type == 3)) { 136362306a36Sopenharmony_ci switch (rss_type) { 136462306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv4: 136562306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6: 136662306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6_EX: 136762306a36Sopenharmony_ci skb_set_hash(skb, rss_hash, PKT_HASH_TYPE_L4); 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_IPv4: 137062306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_IPv6: 137162306a36Sopenharmony_ci case CQ_ENET_RQ_DESC_RSS_TYPE_IPv6_EX: 137262306a36Sopenharmony_ci skb_set_hash(skb, rss_hash, PKT_HASH_TYPE_L3); 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci if (enic->vxlan.vxlan_udp_port_number) { 137762306a36Sopenharmony_ci switch (enic->vxlan.patch_level) { 137862306a36Sopenharmony_ci case 0: 137962306a36Sopenharmony_ci if (fcoe) { 138062306a36Sopenharmony_ci encap = true; 138162306a36Sopenharmony_ci outer_csum_ok = fcoe_fc_crc_ok; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci break; 138462306a36Sopenharmony_ci case 2: 138562306a36Sopenharmony_ci if ((type == 7) && 138662306a36Sopenharmony_ci (rss_hash & BIT(0))) { 138762306a36Sopenharmony_ci encap = true; 138862306a36Sopenharmony_ci outer_csum_ok = (rss_hash & BIT(1)) && 138962306a36Sopenharmony_ci (rss_hash & BIT(2)); 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* Hardware does not provide whole packet checksum. It only 139662306a36Sopenharmony_ci * provides pseudo checksum. Since hw validates the packet 139762306a36Sopenharmony_ci * checksum but not provide us the checksum value. use 139862306a36Sopenharmony_ci * CHECSUM_UNNECESSARY. 139962306a36Sopenharmony_ci * 140062306a36Sopenharmony_ci * In case of encap pkt tcp_udp_csum_ok/tcp_udp_csum_ok is 140162306a36Sopenharmony_ci * inner csum_ok. outer_csum_ok is set by hw when outer udp 140262306a36Sopenharmony_ci * csum is correct or is zero. 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_ci if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc && 140562306a36Sopenharmony_ci tcp_udp_csum_ok && outer_csum_ok && 140662306a36Sopenharmony_ci (ipv4_csum_ok || ipv6)) { 140762306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 140862306a36Sopenharmony_ci skb->csum_level = encap; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (vlan_stripped) 141262306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci skb_mark_napi_id(skb, &enic->napi[rq->index]); 141562306a36Sopenharmony_ci if (!(netdev->features & NETIF_F_GRO)) 141662306a36Sopenharmony_ci netif_receive_skb(skb); 141762306a36Sopenharmony_ci else 141862306a36Sopenharmony_ci napi_gro_receive(&enic->napi[q_number], skb); 141962306a36Sopenharmony_ci if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) 142062306a36Sopenharmony_ci enic_intr_update_pkt_size(&cq->pkt_size_counter, 142162306a36Sopenharmony_ci bytes_written); 142262306a36Sopenharmony_ci } else { 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* Buffer overflow 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci dma_unmap_single(&enic->pdev->dev, buf->dma_addr, buf->len, 142862306a36Sopenharmony_ci DMA_FROM_DEVICE); 142962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 143062306a36Sopenharmony_ci buf->os_buf = NULL; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic int enic_rq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, 143562306a36Sopenharmony_ci u8 type, u16 q_number, u16 completed_index, void *opaque) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct enic *enic = vnic_dev_priv(vdev); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci vnic_rq_service(&enic->rq[q_number], cq_desc, 144062306a36Sopenharmony_ci completed_index, VNIC_RQ_RETURN_DESC, 144162306a36Sopenharmony_ci enic_rq_indicate_buf, opaque); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci return 0; 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic void enic_set_int_moderation(struct enic *enic, struct vnic_rq *rq) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci unsigned int intr = enic_msix_rq_intr(enic, rq->index); 144962306a36Sopenharmony_ci struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; 145062306a36Sopenharmony_ci u32 timer = cq->tobe_rx_coal_timeval; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (cq->tobe_rx_coal_timeval != cq->cur_rx_coal_timeval) { 145362306a36Sopenharmony_ci vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); 145462306a36Sopenharmony_ci cq->cur_rx_coal_timeval = cq->tobe_rx_coal_timeval; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci} 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_cistatic void enic_calc_int_moderation(struct enic *enic, struct vnic_rq *rq) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; 146162306a36Sopenharmony_ci struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; 146262306a36Sopenharmony_ci struct vnic_rx_bytes_counter *pkt_size_counter = &cq->pkt_size_counter; 146362306a36Sopenharmony_ci int index; 146462306a36Sopenharmony_ci u32 timer; 146562306a36Sopenharmony_ci u32 range_start; 146662306a36Sopenharmony_ci u32 traffic; 146762306a36Sopenharmony_ci u64 delta; 146862306a36Sopenharmony_ci ktime_t now = ktime_get(); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci delta = ktime_us_delta(now, cq->prev_ts); 147162306a36Sopenharmony_ci if (delta < ENIC_AIC_TS_BREAK) 147262306a36Sopenharmony_ci return; 147362306a36Sopenharmony_ci cq->prev_ts = now; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci traffic = pkt_size_counter->large_pkt_bytes_cnt + 147662306a36Sopenharmony_ci pkt_size_counter->small_pkt_bytes_cnt; 147762306a36Sopenharmony_ci /* The table takes Mbps 147862306a36Sopenharmony_ci * traffic *= 8 => bits 147962306a36Sopenharmony_ci * traffic *= (10^6 / delta) => bps 148062306a36Sopenharmony_ci * traffic /= 10^6 => Mbps 148162306a36Sopenharmony_ci * 148262306a36Sopenharmony_ci * Combining, traffic *= (8 / delta) 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci traffic <<= 3; 148662306a36Sopenharmony_ci traffic = delta > UINT_MAX ? 0 : traffic / (u32)delta; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci for (index = 0; index < ENIC_MAX_COALESCE_TIMERS; index++) 148962306a36Sopenharmony_ci if (traffic < mod_table[index].rx_rate) 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci range_start = (pkt_size_counter->small_pkt_bytes_cnt > 149262306a36Sopenharmony_ci pkt_size_counter->large_pkt_bytes_cnt << 1) ? 149362306a36Sopenharmony_ci rx_coal->small_pkt_range_start : 149462306a36Sopenharmony_ci rx_coal->large_pkt_range_start; 149562306a36Sopenharmony_ci timer = range_start + ((rx_coal->range_end - range_start) * 149662306a36Sopenharmony_ci mod_table[index].range_percent / 100); 149762306a36Sopenharmony_ci /* Damping */ 149862306a36Sopenharmony_ci cq->tobe_rx_coal_timeval = (timer + cq->tobe_rx_coal_timeval) >> 1; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci pkt_size_counter->large_pkt_bytes_cnt = 0; 150162306a36Sopenharmony_ci pkt_size_counter->small_pkt_bytes_cnt = 0; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cistatic int enic_poll(struct napi_struct *napi, int budget) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci struct net_device *netdev = napi->dev; 150762306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 150862306a36Sopenharmony_ci unsigned int cq_rq = enic_cq_rq(enic, 0); 150962306a36Sopenharmony_ci unsigned int cq_wq = enic_cq_wq(enic, 0); 151062306a36Sopenharmony_ci unsigned int intr = ENIC_LEGACY_IO_INTR; 151162306a36Sopenharmony_ci unsigned int rq_work_to_do = budget; 151262306a36Sopenharmony_ci unsigned int wq_work_to_do = ENIC_WQ_NAPI_BUDGET; 151362306a36Sopenharmony_ci unsigned int work_done, rq_work_done = 0, wq_work_done; 151462306a36Sopenharmony_ci int err; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci wq_work_done = vnic_cq_service(&enic->cq[cq_wq], wq_work_to_do, 151762306a36Sopenharmony_ci enic_wq_service, NULL); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (budget > 0) 152062306a36Sopenharmony_ci rq_work_done = vnic_cq_service(&enic->cq[cq_rq], 152162306a36Sopenharmony_ci rq_work_to_do, enic_rq_service, NULL); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* Accumulate intr event credits for this polling 152462306a36Sopenharmony_ci * cycle. An intr event is the completion of a 152562306a36Sopenharmony_ci * a WQ or RQ packet. 152662306a36Sopenharmony_ci */ 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci work_done = rq_work_done + wq_work_done; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (work_done > 0) 153162306a36Sopenharmony_ci vnic_intr_return_credits(&enic->intr[intr], 153262306a36Sopenharmony_ci work_done, 153362306a36Sopenharmony_ci 0 /* don't unmask intr */, 153462306a36Sopenharmony_ci 0 /* don't reset intr timer */); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci err = vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci /* Buffer allocation failed. Stay in polling 153962306a36Sopenharmony_ci * mode so we can try to fill the ring again. 154062306a36Sopenharmony_ci */ 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci if (err) 154362306a36Sopenharmony_ci rq_work_done = rq_work_to_do; 154462306a36Sopenharmony_ci if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) 154562306a36Sopenharmony_ci /* Call the function which refreshes the intr coalescing timer 154662306a36Sopenharmony_ci * value based on the traffic. 154762306a36Sopenharmony_ci */ 154862306a36Sopenharmony_ci enic_calc_int_moderation(enic, &enic->rq[0]); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if ((rq_work_done < budget) && napi_complete_done(napi, rq_work_done)) { 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci /* Some work done, but not enough to stay in polling, 155362306a36Sopenharmony_ci * exit polling 155462306a36Sopenharmony_ci */ 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) 155762306a36Sopenharmony_ci enic_set_int_moderation(enic, &enic->rq[0]); 155862306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[intr]); 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci return rq_work_done; 156262306a36Sopenharmony_ci} 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 156562306a36Sopenharmony_cistatic void enic_free_rx_cpu_rmap(struct enic *enic) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci free_irq_cpu_rmap(enic->netdev->rx_cpu_rmap); 156862306a36Sopenharmony_ci enic->netdev->rx_cpu_rmap = NULL; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic void enic_set_rx_cpu_rmap(struct enic *enic) 157262306a36Sopenharmony_ci{ 157362306a36Sopenharmony_ci int i, res; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) { 157662306a36Sopenharmony_ci enic->netdev->rx_cpu_rmap = alloc_irq_cpu_rmap(enic->rq_count); 157762306a36Sopenharmony_ci if (unlikely(!enic->netdev->rx_cpu_rmap)) 157862306a36Sopenharmony_ci return; 157962306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 158062306a36Sopenharmony_ci res = irq_cpu_rmap_add(enic->netdev->rx_cpu_rmap, 158162306a36Sopenharmony_ci enic->msix_entry[i].vector); 158262306a36Sopenharmony_ci if (unlikely(res)) { 158362306a36Sopenharmony_ci enic_free_rx_cpu_rmap(enic); 158462306a36Sopenharmony_ci return; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci#else 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic void enic_free_rx_cpu_rmap(struct enic *enic) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic void enic_set_rx_cpu_rmap(struct enic *enic) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci#endif /* CONFIG_RFS_ACCEL */ 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic int enic_poll_msix_wq(struct napi_struct *napi, int budget) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci struct net_device *netdev = napi->dev; 160562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 160662306a36Sopenharmony_ci unsigned int wq_index = (napi - &enic->napi[0]) - enic->rq_count; 160762306a36Sopenharmony_ci struct vnic_wq *wq = &enic->wq[wq_index]; 160862306a36Sopenharmony_ci unsigned int cq; 160962306a36Sopenharmony_ci unsigned int intr; 161062306a36Sopenharmony_ci unsigned int wq_work_to_do = ENIC_WQ_NAPI_BUDGET; 161162306a36Sopenharmony_ci unsigned int wq_work_done; 161262306a36Sopenharmony_ci unsigned int wq_irq; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci wq_irq = wq->index; 161562306a36Sopenharmony_ci cq = enic_cq_wq(enic, wq_irq); 161662306a36Sopenharmony_ci intr = enic_msix_wq_intr(enic, wq_irq); 161762306a36Sopenharmony_ci wq_work_done = vnic_cq_service(&enic->cq[cq], wq_work_to_do, 161862306a36Sopenharmony_ci enic_wq_service, NULL); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci vnic_intr_return_credits(&enic->intr[intr], wq_work_done, 162162306a36Sopenharmony_ci 0 /* don't unmask intr */, 162262306a36Sopenharmony_ci 1 /* reset intr timer */); 162362306a36Sopenharmony_ci if (!wq_work_done) { 162462306a36Sopenharmony_ci napi_complete(napi); 162562306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[intr]); 162662306a36Sopenharmony_ci return 0; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci return budget; 163062306a36Sopenharmony_ci} 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_cistatic int enic_poll_msix_rq(struct napi_struct *napi, int budget) 163362306a36Sopenharmony_ci{ 163462306a36Sopenharmony_ci struct net_device *netdev = napi->dev; 163562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 163662306a36Sopenharmony_ci unsigned int rq = (napi - &enic->napi[0]); 163762306a36Sopenharmony_ci unsigned int cq = enic_cq_rq(enic, rq); 163862306a36Sopenharmony_ci unsigned int intr = enic_msix_rq_intr(enic, rq); 163962306a36Sopenharmony_ci unsigned int work_to_do = budget; 164062306a36Sopenharmony_ci unsigned int work_done = 0; 164162306a36Sopenharmony_ci int err; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci /* Service RQ 164462306a36Sopenharmony_ci */ 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (budget > 0) 164762306a36Sopenharmony_ci work_done = vnic_cq_service(&enic->cq[cq], 164862306a36Sopenharmony_ci work_to_do, enic_rq_service, NULL); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* Return intr event credits for this polling 165162306a36Sopenharmony_ci * cycle. An intr event is the completion of a 165262306a36Sopenharmony_ci * RQ packet. 165362306a36Sopenharmony_ci */ 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (work_done > 0) 165662306a36Sopenharmony_ci vnic_intr_return_credits(&enic->intr[intr], 165762306a36Sopenharmony_ci work_done, 165862306a36Sopenharmony_ci 0 /* don't unmask intr */, 165962306a36Sopenharmony_ci 0 /* don't reset intr timer */); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci err = vnic_rq_fill(&enic->rq[rq], enic_rq_alloc_buf); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci /* Buffer allocation failed. Stay in polling mode 166462306a36Sopenharmony_ci * so we can try to fill the ring again. 166562306a36Sopenharmony_ci */ 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (err) 166862306a36Sopenharmony_ci work_done = work_to_do; 166962306a36Sopenharmony_ci if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) 167062306a36Sopenharmony_ci /* Call the function which refreshes the intr coalescing timer 167162306a36Sopenharmony_ci * value based on the traffic. 167262306a36Sopenharmony_ci */ 167362306a36Sopenharmony_ci enic_calc_int_moderation(enic, &enic->rq[rq]); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if ((work_done < budget) && napi_complete_done(napi, work_done)) { 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci /* Some work done, but not enough to stay in polling, 167862306a36Sopenharmony_ci * exit polling 167962306a36Sopenharmony_ci */ 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) 168262306a36Sopenharmony_ci enic_set_int_moderation(enic, &enic->rq[rq]); 168362306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[intr]); 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci return work_done; 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic void enic_notify_timer(struct timer_list *t) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct enic *enic = from_timer(enic, t, notify_timer); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci enic_notify_check(enic); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci mod_timer(&enic->notify_timer, 169662306a36Sopenharmony_ci round_jiffies(jiffies + ENIC_NOTIFY_TIMER_PERIOD)); 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_cistatic void enic_free_intr(struct enic *enic) 170062306a36Sopenharmony_ci{ 170162306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 170262306a36Sopenharmony_ci unsigned int i; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci enic_free_rx_cpu_rmap(enic); 170562306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 170662306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 170762306a36Sopenharmony_ci free_irq(enic->pdev->irq, netdev); 170862306a36Sopenharmony_ci break; 170962306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 171062306a36Sopenharmony_ci free_irq(enic->pdev->irq, enic); 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 171362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(enic->msix); i++) 171462306a36Sopenharmony_ci if (enic->msix[i].requested) 171562306a36Sopenharmony_ci free_irq(enic->msix_entry[i].vector, 171662306a36Sopenharmony_ci enic->msix[i].devid); 171762306a36Sopenharmony_ci break; 171862306a36Sopenharmony_ci default: 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci} 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_cistatic int enic_request_intr(struct enic *enic) 172462306a36Sopenharmony_ci{ 172562306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 172662306a36Sopenharmony_ci unsigned int i, intr; 172762306a36Sopenharmony_ci int err = 0; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci enic_set_rx_cpu_rmap(enic); 173062306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci err = request_irq(enic->pdev->irq, enic_isr_legacy, 173562306a36Sopenharmony_ci IRQF_SHARED, netdev->name, netdev); 173662306a36Sopenharmony_ci break; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci err = request_irq(enic->pdev->irq, enic_isr_msi, 174162306a36Sopenharmony_ci 0, netdev->name, enic); 174262306a36Sopenharmony_ci break; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 174762306a36Sopenharmony_ci intr = enic_msix_rq_intr(enic, i); 174862306a36Sopenharmony_ci snprintf(enic->msix[intr].devname, 174962306a36Sopenharmony_ci sizeof(enic->msix[intr].devname), 175062306a36Sopenharmony_ci "%s-rx-%u", netdev->name, i); 175162306a36Sopenharmony_ci enic->msix[intr].isr = enic_isr_msix; 175262306a36Sopenharmony_ci enic->msix[intr].devid = &enic->napi[i]; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 175662306a36Sopenharmony_ci int wq = enic_cq_wq(enic, i); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci intr = enic_msix_wq_intr(enic, i); 175962306a36Sopenharmony_ci snprintf(enic->msix[intr].devname, 176062306a36Sopenharmony_ci sizeof(enic->msix[intr].devname), 176162306a36Sopenharmony_ci "%s-tx-%u", netdev->name, i); 176262306a36Sopenharmony_ci enic->msix[intr].isr = enic_isr_msix; 176362306a36Sopenharmony_ci enic->msix[intr].devid = &enic->napi[wq]; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci intr = enic_msix_err_intr(enic); 176762306a36Sopenharmony_ci snprintf(enic->msix[intr].devname, 176862306a36Sopenharmony_ci sizeof(enic->msix[intr].devname), 176962306a36Sopenharmony_ci "%s-err", netdev->name); 177062306a36Sopenharmony_ci enic->msix[intr].isr = enic_isr_msix_err; 177162306a36Sopenharmony_ci enic->msix[intr].devid = enic; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci intr = enic_msix_notify_intr(enic); 177462306a36Sopenharmony_ci snprintf(enic->msix[intr].devname, 177562306a36Sopenharmony_ci sizeof(enic->msix[intr].devname), 177662306a36Sopenharmony_ci "%s-notify", netdev->name); 177762306a36Sopenharmony_ci enic->msix[intr].isr = enic_isr_msix_notify; 177862306a36Sopenharmony_ci enic->msix[intr].devid = enic; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(enic->msix); i++) 178162306a36Sopenharmony_ci enic->msix[i].requested = 0; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) { 178462306a36Sopenharmony_ci err = request_irq(enic->msix_entry[i].vector, 178562306a36Sopenharmony_ci enic->msix[i].isr, 0, 178662306a36Sopenharmony_ci enic->msix[i].devname, 178762306a36Sopenharmony_ci enic->msix[i].devid); 178862306a36Sopenharmony_ci if (err) { 178962306a36Sopenharmony_ci enic_free_intr(enic); 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci enic->msix[i].requested = 1; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci break; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci default: 179862306a36Sopenharmony_ci break; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci return err; 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic void enic_synchronize_irqs(struct enic *enic) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci unsigned int i; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 180962306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 181062306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 181162306a36Sopenharmony_ci synchronize_irq(enic->pdev->irq); 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 181462306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) 181562306a36Sopenharmony_ci synchronize_irq(enic->msix_entry[i].vector); 181662306a36Sopenharmony_ci break; 181762306a36Sopenharmony_ci default: 181862306a36Sopenharmony_ci break; 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic void enic_set_rx_coal_setting(struct enic *enic) 182362306a36Sopenharmony_ci{ 182462306a36Sopenharmony_ci unsigned int speed; 182562306a36Sopenharmony_ci int index = -1; 182662306a36Sopenharmony_ci struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* 1. Read the link speed from fw 182962306a36Sopenharmony_ci * 2. Pick the default range for the speed 183062306a36Sopenharmony_ci * 3. Update it in enic->rx_coalesce_setting 183162306a36Sopenharmony_ci */ 183262306a36Sopenharmony_ci speed = vnic_dev_port_speed(enic->vdev); 183362306a36Sopenharmony_ci if (ENIC_LINK_SPEED_10G < speed) 183462306a36Sopenharmony_ci index = ENIC_LINK_40G_INDEX; 183562306a36Sopenharmony_ci else if (ENIC_LINK_SPEED_4G < speed) 183662306a36Sopenharmony_ci index = ENIC_LINK_10G_INDEX; 183762306a36Sopenharmony_ci else 183862306a36Sopenharmony_ci index = ENIC_LINK_4G_INDEX; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start; 184162306a36Sopenharmony_ci rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start; 184262306a36Sopenharmony_ci rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci /* Start with the value provided by UCSM */ 184562306a36Sopenharmony_ci for (index = 0; index < enic->rq_count; index++) 184662306a36Sopenharmony_ci enic->cq[index].cur_rx_coal_timeval = 184762306a36Sopenharmony_ci enic->config.intr_timer_usec; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci rx_coal->use_adaptive_rx_coalesce = 1; 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic int enic_dev_notify_set(struct enic *enic) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci int err; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 185762306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 185862306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 185962306a36Sopenharmony_ci err = vnic_dev_notify_set(enic->vdev, ENIC_LEGACY_NOTIFY_INTR); 186062306a36Sopenharmony_ci break; 186162306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 186262306a36Sopenharmony_ci err = vnic_dev_notify_set(enic->vdev, 186362306a36Sopenharmony_ci enic_msix_notify_intr(enic)); 186462306a36Sopenharmony_ci break; 186562306a36Sopenharmony_ci default: 186662306a36Sopenharmony_ci err = vnic_dev_notify_set(enic->vdev, -1 /* no intr */); 186762306a36Sopenharmony_ci break; 186862306a36Sopenharmony_ci } 186962306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci return err; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_cistatic void enic_notify_timer_start(struct enic *enic) 187562306a36Sopenharmony_ci{ 187662306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 187762306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 187862306a36Sopenharmony_ci mod_timer(&enic->notify_timer, jiffies); 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci default: 188162306a36Sopenharmony_ci /* Using intr for notification for INTx/MSI-X */ 188262306a36Sopenharmony_ci break; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci} 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci/* rtnl lock is held, process context */ 188762306a36Sopenharmony_cistatic int enic_open(struct net_device *netdev) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 189062306a36Sopenharmony_ci unsigned int i; 189162306a36Sopenharmony_ci int err, ret; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci err = enic_request_intr(enic); 189462306a36Sopenharmony_ci if (err) { 189562306a36Sopenharmony_ci netdev_err(netdev, "Unable to request irq.\n"); 189662306a36Sopenharmony_ci return err; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci enic_init_affinity_hint(enic); 189962306a36Sopenharmony_ci enic_set_affinity_hint(enic); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci err = enic_dev_notify_set(enic); 190262306a36Sopenharmony_ci if (err) { 190362306a36Sopenharmony_ci netdev_err(netdev, 190462306a36Sopenharmony_ci "Failed to alloc notify buffer, aborting.\n"); 190562306a36Sopenharmony_ci goto err_out_free_intr; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 190962306a36Sopenharmony_ci /* enable rq before updating rq desc */ 191062306a36Sopenharmony_ci vnic_rq_enable(&enic->rq[i]); 191162306a36Sopenharmony_ci vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf); 191262306a36Sopenharmony_ci /* Need at least one buffer on ring to get going */ 191362306a36Sopenharmony_ci if (vnic_rq_desc_used(&enic->rq[i]) == 0) { 191462306a36Sopenharmony_ci netdev_err(netdev, "Unable to alloc receive buffers\n"); 191562306a36Sopenharmony_ci err = -ENOMEM; 191662306a36Sopenharmony_ci goto err_out_free_rq; 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 192162306a36Sopenharmony_ci vnic_wq_enable(&enic->wq[i]); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) 192462306a36Sopenharmony_ci enic_dev_add_station_addr(enic); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci enic_set_rx_mode(netdev); 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci netif_tx_wake_all_queues(netdev); 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) 193162306a36Sopenharmony_ci napi_enable(&enic->napi[i]); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) 193462306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 193562306a36Sopenharmony_ci napi_enable(&enic->napi[enic_cq_wq(enic, i)]); 193662306a36Sopenharmony_ci enic_dev_enable(enic); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) 193962306a36Sopenharmony_ci vnic_intr_unmask(&enic->intr[i]); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci enic_notify_timer_start(enic); 194262306a36Sopenharmony_ci enic_rfs_timer_start(enic); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci return 0; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_cierr_out_free_rq: 194762306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 194862306a36Sopenharmony_ci ret = vnic_rq_disable(&enic->rq[i]); 194962306a36Sopenharmony_ci if (!ret) 195062306a36Sopenharmony_ci vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); 195162306a36Sopenharmony_ci } 195262306a36Sopenharmony_ci enic_dev_notify_unset(enic); 195362306a36Sopenharmony_cierr_out_free_intr: 195462306a36Sopenharmony_ci enic_unset_affinity_hint(enic); 195562306a36Sopenharmony_ci enic_free_intr(enic); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci return err; 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci/* rtnl lock is held, process context */ 196162306a36Sopenharmony_cistatic int enic_stop(struct net_device *netdev) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 196462306a36Sopenharmony_ci unsigned int i; 196562306a36Sopenharmony_ci int err; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) { 196862306a36Sopenharmony_ci vnic_intr_mask(&enic->intr[i]); 196962306a36Sopenharmony_ci (void)vnic_intr_masked(&enic->intr[i]); /* flush write */ 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci enic_synchronize_irqs(enic); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci del_timer_sync(&enic->notify_timer); 197562306a36Sopenharmony_ci enic_rfs_flw_tbl_free(enic); 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci enic_dev_disable(enic); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) 198062306a36Sopenharmony_ci napi_disable(&enic->napi[i]); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci netif_carrier_off(netdev); 198362306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) 198462306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 198562306a36Sopenharmony_ci napi_disable(&enic->napi[enic_cq_wq(enic, i)]); 198662306a36Sopenharmony_ci netif_tx_disable(netdev); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) 198962306a36Sopenharmony_ci enic_dev_del_station_addr(enic); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 199262306a36Sopenharmony_ci err = vnic_wq_disable(&enic->wq[i]); 199362306a36Sopenharmony_ci if (err) 199462306a36Sopenharmony_ci return err; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 199762306a36Sopenharmony_ci err = vnic_rq_disable(&enic->rq[i]); 199862306a36Sopenharmony_ci if (err) 199962306a36Sopenharmony_ci return err; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci enic_dev_notify_unset(enic); 200362306a36Sopenharmony_ci enic_unset_affinity_hint(enic); 200462306a36Sopenharmony_ci enic_free_intr(enic); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 200762306a36Sopenharmony_ci vnic_wq_clean(&enic->wq[i], enic_free_wq_buf); 200862306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) 200962306a36Sopenharmony_ci vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); 201062306a36Sopenharmony_ci for (i = 0; i < enic->cq_count; i++) 201162306a36Sopenharmony_ci vnic_cq_clean(&enic->cq[i]); 201262306a36Sopenharmony_ci for (i = 0; i < enic->intr_count; i++) 201362306a36Sopenharmony_ci vnic_intr_clean(&enic->intr[i]); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return 0; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic int _enic_change_mtu(struct net_device *netdev, int new_mtu) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci bool running = netif_running(netdev); 202162306a36Sopenharmony_ci int err = 0; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci ASSERT_RTNL(); 202462306a36Sopenharmony_ci if (running) { 202562306a36Sopenharmony_ci err = enic_stop(netdev); 202662306a36Sopenharmony_ci if (err) 202762306a36Sopenharmony_ci return err; 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci netdev->mtu = new_mtu; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci if (running) { 203362306a36Sopenharmony_ci err = enic_open(netdev); 203462306a36Sopenharmony_ci if (err) 203562306a36Sopenharmony_ci return err; 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci return 0; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_cistatic int enic_change_mtu(struct net_device *netdev, int new_mtu) 204262306a36Sopenharmony_ci{ 204362306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) 204662306a36Sopenharmony_ci return -EOPNOTSUPP; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (netdev->mtu > enic->port_mtu) 204962306a36Sopenharmony_ci netdev_warn(netdev, 205062306a36Sopenharmony_ci "interface MTU (%d) set higher than port MTU (%d)\n", 205162306a36Sopenharmony_ci netdev->mtu, enic->port_mtu); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci return _enic_change_mtu(netdev, new_mtu); 205462306a36Sopenharmony_ci} 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_cistatic void enic_change_mtu_work(struct work_struct *work) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct enic *enic = container_of(work, struct enic, change_mtu_work); 205962306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 206062306a36Sopenharmony_ci int new_mtu = vnic_dev_mtu(enic->vdev); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci rtnl_lock(); 206362306a36Sopenharmony_ci (void)_enic_change_mtu(netdev, new_mtu); 206462306a36Sopenharmony_ci rtnl_unlock(); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci netdev_info(netdev, "interface MTU set as %d\n", netdev->mtu); 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 207062306a36Sopenharmony_cistatic void enic_poll_controller(struct net_device *netdev) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 207362306a36Sopenharmony_ci struct vnic_dev *vdev = enic->vdev; 207462306a36Sopenharmony_ci unsigned int i, intr; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(vdev)) { 207762306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 207862306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 207962306a36Sopenharmony_ci intr = enic_msix_rq_intr(enic, i); 208062306a36Sopenharmony_ci enic_isr_msix(enic->msix_entry[intr].vector, 208162306a36Sopenharmony_ci &enic->napi[i]); 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) { 208562306a36Sopenharmony_ci intr = enic_msix_wq_intr(enic, i); 208662306a36Sopenharmony_ci enic_isr_msix(enic->msix_entry[intr].vector, 208762306a36Sopenharmony_ci &enic->napi[enic_cq_wq(enic, i)]); 208862306a36Sopenharmony_ci } 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci break; 209162306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 209262306a36Sopenharmony_ci enic_isr_msi(enic->pdev->irq, enic); 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_INTX: 209562306a36Sopenharmony_ci enic_isr_legacy(enic->pdev->irq, netdev); 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci default: 209862306a36Sopenharmony_ci break; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci#endif 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_cistatic int enic_dev_wait(struct vnic_dev *vdev, 210462306a36Sopenharmony_ci int (*start)(struct vnic_dev *, int), 210562306a36Sopenharmony_ci int (*finished)(struct vnic_dev *, int *), 210662306a36Sopenharmony_ci int arg) 210762306a36Sopenharmony_ci{ 210862306a36Sopenharmony_ci unsigned long time; 210962306a36Sopenharmony_ci int done; 211062306a36Sopenharmony_ci int err; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci err = start(vdev, arg); 211362306a36Sopenharmony_ci if (err) 211462306a36Sopenharmony_ci return err; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci /* Wait for func to complete...2 seconds max 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci time = jiffies + (HZ * 2); 212062306a36Sopenharmony_ci do { 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci err = finished(vdev, &done); 212362306a36Sopenharmony_ci if (err) 212462306a36Sopenharmony_ci return err; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (done) 212762306a36Sopenharmony_ci return 0; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci schedule_timeout_uninterruptible(HZ / 10); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci } while (time_after(time, jiffies)); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci return -ETIMEDOUT; 213462306a36Sopenharmony_ci} 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_cistatic int enic_dev_open(struct enic *enic) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci int err; 213962306a36Sopenharmony_ci u32 flags = CMD_OPENF_IG_DESCCACHE; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci err = enic_dev_wait(enic->vdev, vnic_dev_open, 214262306a36Sopenharmony_ci vnic_dev_open_done, flags); 214362306a36Sopenharmony_ci if (err) 214462306a36Sopenharmony_ci dev_err(enic_get_dev(enic), "vNIC device open failed, err %d\n", 214562306a36Sopenharmony_ci err); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci return err; 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cistatic int enic_dev_soft_reset(struct enic *enic) 215162306a36Sopenharmony_ci{ 215262306a36Sopenharmony_ci int err; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, 215562306a36Sopenharmony_ci vnic_dev_soft_reset_done, 0); 215662306a36Sopenharmony_ci if (err) 215762306a36Sopenharmony_ci netdev_err(enic->netdev, "vNIC soft reset failed, err %d\n", 215862306a36Sopenharmony_ci err); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci return err; 216162306a36Sopenharmony_ci} 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic int enic_dev_hang_reset(struct enic *enic) 216462306a36Sopenharmony_ci{ 216562306a36Sopenharmony_ci int err; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci err = enic_dev_wait(enic->vdev, vnic_dev_hang_reset, 216862306a36Sopenharmony_ci vnic_dev_hang_reset_done, 0); 216962306a36Sopenharmony_ci if (err) 217062306a36Sopenharmony_ci netdev_err(enic->netdev, "vNIC hang reset failed, err %d\n", 217162306a36Sopenharmony_ci err); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci return err; 217462306a36Sopenharmony_ci} 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ciint __enic_set_rsskey(struct enic *enic) 217762306a36Sopenharmony_ci{ 217862306a36Sopenharmony_ci union vnic_rss_key *rss_key_buf_va; 217962306a36Sopenharmony_ci dma_addr_t rss_key_buf_pa; 218062306a36Sopenharmony_ci int i, kidx, bidx, err; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci rss_key_buf_va = dma_alloc_coherent(&enic->pdev->dev, 218362306a36Sopenharmony_ci sizeof(union vnic_rss_key), 218462306a36Sopenharmony_ci &rss_key_buf_pa, GFP_ATOMIC); 218562306a36Sopenharmony_ci if (!rss_key_buf_va) 218662306a36Sopenharmony_ci return -ENOMEM; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci for (i = 0; i < ENIC_RSS_LEN; i++) { 218962306a36Sopenharmony_ci kidx = i / ENIC_RSS_BYTES_PER_KEY; 219062306a36Sopenharmony_ci bidx = i % ENIC_RSS_BYTES_PER_KEY; 219162306a36Sopenharmony_ci rss_key_buf_va->key[kidx].b[bidx] = enic->rss_key[i]; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 219462306a36Sopenharmony_ci err = enic_set_rss_key(enic, 219562306a36Sopenharmony_ci rss_key_buf_pa, 219662306a36Sopenharmony_ci sizeof(union vnic_rss_key)); 219762306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci dma_free_coherent(&enic->pdev->dev, sizeof(union vnic_rss_key), 220062306a36Sopenharmony_ci rss_key_buf_va, rss_key_buf_pa); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci return err; 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_cistatic int enic_set_rsskey(struct enic *enic) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci netdev_rss_key_fill(enic->rss_key, ENIC_RSS_LEN); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci return __enic_set_rsskey(enic); 221062306a36Sopenharmony_ci} 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_cistatic int enic_set_rsscpu(struct enic *enic, u8 rss_hash_bits) 221362306a36Sopenharmony_ci{ 221462306a36Sopenharmony_ci dma_addr_t rss_cpu_buf_pa; 221562306a36Sopenharmony_ci union vnic_rss_cpu *rss_cpu_buf_va = NULL; 221662306a36Sopenharmony_ci unsigned int i; 221762306a36Sopenharmony_ci int err; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci rss_cpu_buf_va = dma_alloc_coherent(&enic->pdev->dev, 222062306a36Sopenharmony_ci sizeof(union vnic_rss_cpu), 222162306a36Sopenharmony_ci &rss_cpu_buf_pa, GFP_ATOMIC); 222262306a36Sopenharmony_ci if (!rss_cpu_buf_va) 222362306a36Sopenharmony_ci return -ENOMEM; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci for (i = 0; i < (1 << rss_hash_bits); i++) 222662306a36Sopenharmony_ci (*rss_cpu_buf_va).cpu[i/4].b[i%4] = i % enic->rq_count; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 222962306a36Sopenharmony_ci err = enic_set_rss_cpu(enic, 223062306a36Sopenharmony_ci rss_cpu_buf_pa, 223162306a36Sopenharmony_ci sizeof(union vnic_rss_cpu)); 223262306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci dma_free_coherent(&enic->pdev->dev, sizeof(union vnic_rss_cpu), 223562306a36Sopenharmony_ci rss_cpu_buf_va, rss_cpu_buf_pa); 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci return err; 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_cistatic int enic_set_niccfg(struct enic *enic, u8 rss_default_cpu, 224162306a36Sopenharmony_ci u8 rss_hash_type, u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci const u8 tso_ipid_split_en = 0; 224462306a36Sopenharmony_ci const u8 ig_vlan_strip_en = 1; 224562306a36Sopenharmony_ci int err; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci /* Enable VLAN tag stripping. 224862306a36Sopenharmony_ci */ 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 225162306a36Sopenharmony_ci err = enic_set_nic_cfg(enic, 225262306a36Sopenharmony_ci rss_default_cpu, rss_hash_type, 225362306a36Sopenharmony_ci rss_hash_bits, rss_base_cpu, 225462306a36Sopenharmony_ci rss_enable, tso_ipid_split_en, 225562306a36Sopenharmony_ci ig_vlan_strip_en); 225662306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci return err; 225962306a36Sopenharmony_ci} 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_cistatic int enic_set_rss_nic_cfg(struct enic *enic) 226262306a36Sopenharmony_ci{ 226362306a36Sopenharmony_ci struct device *dev = enic_get_dev(enic); 226462306a36Sopenharmony_ci const u8 rss_default_cpu = 0; 226562306a36Sopenharmony_ci const u8 rss_hash_bits = 7; 226662306a36Sopenharmony_ci const u8 rss_base_cpu = 0; 226762306a36Sopenharmony_ci u8 rss_hash_type; 226862306a36Sopenharmony_ci int res; 226962306a36Sopenharmony_ci u8 rss_enable = ENIC_SETTING(enic, RSS) && (enic->rq_count > 1); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 227262306a36Sopenharmony_ci res = vnic_dev_capable_rss_hash_type(enic->vdev, &rss_hash_type); 227362306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 227462306a36Sopenharmony_ci if (res) { 227562306a36Sopenharmony_ci /* defaults for old adapters 227662306a36Sopenharmony_ci */ 227762306a36Sopenharmony_ci rss_hash_type = NIC_CFG_RSS_HASH_TYPE_IPV4 | 227862306a36Sopenharmony_ci NIC_CFG_RSS_HASH_TYPE_TCP_IPV4 | 227962306a36Sopenharmony_ci NIC_CFG_RSS_HASH_TYPE_IPV6 | 228062306a36Sopenharmony_ci NIC_CFG_RSS_HASH_TYPE_TCP_IPV6; 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci if (rss_enable) { 228462306a36Sopenharmony_ci if (!enic_set_rsskey(enic)) { 228562306a36Sopenharmony_ci if (enic_set_rsscpu(enic, rss_hash_bits)) { 228662306a36Sopenharmony_ci rss_enable = 0; 228762306a36Sopenharmony_ci dev_warn(dev, "RSS disabled, " 228862306a36Sopenharmony_ci "Failed to set RSS cpu indirection table."); 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci } else { 229162306a36Sopenharmony_ci rss_enable = 0; 229262306a36Sopenharmony_ci dev_warn(dev, "RSS disabled, Failed to set RSS key.\n"); 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci return enic_set_niccfg(enic, rss_default_cpu, rss_hash_type, 229762306a36Sopenharmony_ci rss_hash_bits, rss_base_cpu, rss_enable); 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_cistatic void enic_set_api_busy(struct enic *enic, bool busy) 230162306a36Sopenharmony_ci{ 230262306a36Sopenharmony_ci spin_lock(&enic->enic_api_lock); 230362306a36Sopenharmony_ci enic->enic_api_busy = busy; 230462306a36Sopenharmony_ci spin_unlock(&enic->enic_api_lock); 230562306a36Sopenharmony_ci} 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_cistatic void enic_reset(struct work_struct *work) 230862306a36Sopenharmony_ci{ 230962306a36Sopenharmony_ci struct enic *enic = container_of(work, struct enic, reset); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci if (!netif_running(enic->netdev)) 231262306a36Sopenharmony_ci return; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci rtnl_lock(); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci /* Stop any activity from infiniband */ 231762306a36Sopenharmony_ci enic_set_api_busy(enic, true); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci enic_stop(enic->netdev); 232062306a36Sopenharmony_ci enic_dev_soft_reset(enic); 232162306a36Sopenharmony_ci enic_reset_addr_lists(enic); 232262306a36Sopenharmony_ci enic_init_vnic_resources(enic); 232362306a36Sopenharmony_ci enic_set_rss_nic_cfg(enic); 232462306a36Sopenharmony_ci enic_dev_set_ig_vlan_rewrite_mode(enic); 232562306a36Sopenharmony_ci enic_open(enic->netdev); 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci /* Allow infiniband to fiddle with the device again */ 232862306a36Sopenharmony_ci enic_set_api_busy(enic, false); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev); 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci rtnl_unlock(); 233362306a36Sopenharmony_ci} 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cistatic void enic_tx_hang_reset(struct work_struct *work) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct enic *enic = container_of(work, struct enic, tx_hang_reset); 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci rtnl_lock(); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci /* Stop any activity from infiniband */ 234262306a36Sopenharmony_ci enic_set_api_busy(enic, true); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci enic_dev_hang_notify(enic); 234562306a36Sopenharmony_ci enic_stop(enic->netdev); 234662306a36Sopenharmony_ci enic_dev_hang_reset(enic); 234762306a36Sopenharmony_ci enic_reset_addr_lists(enic); 234862306a36Sopenharmony_ci enic_init_vnic_resources(enic); 234962306a36Sopenharmony_ci enic_set_rss_nic_cfg(enic); 235062306a36Sopenharmony_ci enic_dev_set_ig_vlan_rewrite_mode(enic); 235162306a36Sopenharmony_ci enic_open(enic->netdev); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci /* Allow infiniband to fiddle with the device again */ 235462306a36Sopenharmony_ci enic_set_api_busy(enic, false); 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci rtnl_unlock(); 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_cistatic int enic_set_intr_mode(struct enic *enic) 236262306a36Sopenharmony_ci{ 236362306a36Sopenharmony_ci unsigned int n = min_t(unsigned int, enic->rq_count, ENIC_RQ_MAX); 236462306a36Sopenharmony_ci unsigned int m = min_t(unsigned int, enic->wq_count, ENIC_WQ_MAX); 236562306a36Sopenharmony_ci unsigned int i; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci /* Set interrupt mode (INTx, MSI, MSI-X) depending 236862306a36Sopenharmony_ci * on system capabilities. 236962306a36Sopenharmony_ci * 237062306a36Sopenharmony_ci * Try MSI-X first 237162306a36Sopenharmony_ci * 237262306a36Sopenharmony_ci * We need n RQs, m WQs, n+m CQs, and n+m+2 INTRs 237362306a36Sopenharmony_ci * (the second to last INTR is used for WQ/RQ errors) 237462306a36Sopenharmony_ci * (the last INTR is used for notifications) 237562306a36Sopenharmony_ci */ 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci BUG_ON(ARRAY_SIZE(enic->msix_entry) < n + m + 2); 237862306a36Sopenharmony_ci for (i = 0; i < n + m + 2; i++) 237962306a36Sopenharmony_ci enic->msix_entry[i].entry = i; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci /* Use multiple RQs if RSS is enabled 238262306a36Sopenharmony_ci */ 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci if (ENIC_SETTING(enic, RSS) && 238562306a36Sopenharmony_ci enic->config.intr_mode < 1 && 238662306a36Sopenharmony_ci enic->rq_count >= n && 238762306a36Sopenharmony_ci enic->wq_count >= m && 238862306a36Sopenharmony_ci enic->cq_count >= n + m && 238962306a36Sopenharmony_ci enic->intr_count >= n + m + 2) { 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci if (pci_enable_msix_range(enic->pdev, enic->msix_entry, 239262306a36Sopenharmony_ci n + m + 2, n + m + 2) > 0) { 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci enic->rq_count = n; 239562306a36Sopenharmony_ci enic->wq_count = m; 239662306a36Sopenharmony_ci enic->cq_count = n + m; 239762306a36Sopenharmony_ci enic->intr_count = n + m + 2; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, 240062306a36Sopenharmony_ci VNIC_DEV_INTR_MODE_MSIX); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci return 0; 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci if (enic->config.intr_mode < 1 && 240762306a36Sopenharmony_ci enic->rq_count >= 1 && 240862306a36Sopenharmony_ci enic->wq_count >= m && 240962306a36Sopenharmony_ci enic->cq_count >= 1 + m && 241062306a36Sopenharmony_ci enic->intr_count >= 1 + m + 2) { 241162306a36Sopenharmony_ci if (pci_enable_msix_range(enic->pdev, enic->msix_entry, 241262306a36Sopenharmony_ci 1 + m + 2, 1 + m + 2) > 0) { 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci enic->rq_count = 1; 241562306a36Sopenharmony_ci enic->wq_count = m; 241662306a36Sopenharmony_ci enic->cq_count = 1 + m; 241762306a36Sopenharmony_ci enic->intr_count = 1 + m + 2; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, 242062306a36Sopenharmony_ci VNIC_DEV_INTR_MODE_MSIX); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci return 0; 242362306a36Sopenharmony_ci } 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci /* Next try MSI 242762306a36Sopenharmony_ci * 242862306a36Sopenharmony_ci * We need 1 RQ, 1 WQ, 2 CQs, and 1 INTR 242962306a36Sopenharmony_ci */ 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci if (enic->config.intr_mode < 2 && 243262306a36Sopenharmony_ci enic->rq_count >= 1 && 243362306a36Sopenharmony_ci enic->wq_count >= 1 && 243462306a36Sopenharmony_ci enic->cq_count >= 2 && 243562306a36Sopenharmony_ci enic->intr_count >= 1 && 243662306a36Sopenharmony_ci !pci_enable_msi(enic->pdev)) { 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci enic->rq_count = 1; 243962306a36Sopenharmony_ci enic->wq_count = 1; 244062306a36Sopenharmony_ci enic->cq_count = 2; 244162306a36Sopenharmony_ci enic->intr_count = 1; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_MSI); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci return 0; 244662306a36Sopenharmony_ci } 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci /* Next try INTx 244962306a36Sopenharmony_ci * 245062306a36Sopenharmony_ci * We need 1 RQ, 1 WQ, 2 CQs, and 3 INTRs 245162306a36Sopenharmony_ci * (the first INTR is used for WQ/RQ) 245262306a36Sopenharmony_ci * (the second INTR is used for WQ/RQ errors) 245362306a36Sopenharmony_ci * (the last INTR is used for notifications) 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci if (enic->config.intr_mode < 3 && 245762306a36Sopenharmony_ci enic->rq_count >= 1 && 245862306a36Sopenharmony_ci enic->wq_count >= 1 && 245962306a36Sopenharmony_ci enic->cq_count >= 2 && 246062306a36Sopenharmony_ci enic->intr_count >= 3) { 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci enic->rq_count = 1; 246362306a36Sopenharmony_ci enic->wq_count = 1; 246462306a36Sopenharmony_ci enic->cq_count = 2; 246562306a36Sopenharmony_ci enic->intr_count = 3; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_INTX); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci return 0; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci return -EINVAL; 247562306a36Sopenharmony_ci} 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_cistatic void enic_clear_intr_mode(struct enic *enic) 247862306a36Sopenharmony_ci{ 247962306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 248062306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 248162306a36Sopenharmony_ci pci_disable_msix(enic->pdev); 248262306a36Sopenharmony_ci break; 248362306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSI: 248462306a36Sopenharmony_ci pci_disable_msi(enic->pdev); 248562306a36Sopenharmony_ci break; 248662306a36Sopenharmony_ci default: 248762306a36Sopenharmony_ci break; 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 249162306a36Sopenharmony_ci} 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_cistatic const struct net_device_ops enic_netdev_dynamic_ops = { 249462306a36Sopenharmony_ci .ndo_open = enic_open, 249562306a36Sopenharmony_ci .ndo_stop = enic_stop, 249662306a36Sopenharmony_ci .ndo_start_xmit = enic_hard_start_xmit, 249762306a36Sopenharmony_ci .ndo_get_stats64 = enic_get_stats, 249862306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 249962306a36Sopenharmony_ci .ndo_set_rx_mode = enic_set_rx_mode, 250062306a36Sopenharmony_ci .ndo_set_mac_address = enic_set_mac_address_dynamic, 250162306a36Sopenharmony_ci .ndo_change_mtu = enic_change_mtu, 250262306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid, 250362306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid, 250462306a36Sopenharmony_ci .ndo_tx_timeout = enic_tx_timeout, 250562306a36Sopenharmony_ci .ndo_set_vf_port = enic_set_vf_port, 250662306a36Sopenharmony_ci .ndo_get_vf_port = enic_get_vf_port, 250762306a36Sopenharmony_ci .ndo_set_vf_mac = enic_set_vf_mac, 250862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 250962306a36Sopenharmony_ci .ndo_poll_controller = enic_poll_controller, 251062306a36Sopenharmony_ci#endif 251162306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 251262306a36Sopenharmony_ci .ndo_rx_flow_steer = enic_rx_flow_steer, 251362306a36Sopenharmony_ci#endif 251462306a36Sopenharmony_ci .ndo_features_check = enic_features_check, 251562306a36Sopenharmony_ci}; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_cistatic const struct net_device_ops enic_netdev_ops = { 251862306a36Sopenharmony_ci .ndo_open = enic_open, 251962306a36Sopenharmony_ci .ndo_stop = enic_stop, 252062306a36Sopenharmony_ci .ndo_start_xmit = enic_hard_start_xmit, 252162306a36Sopenharmony_ci .ndo_get_stats64 = enic_get_stats, 252262306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 252362306a36Sopenharmony_ci .ndo_set_mac_address = enic_set_mac_address, 252462306a36Sopenharmony_ci .ndo_set_rx_mode = enic_set_rx_mode, 252562306a36Sopenharmony_ci .ndo_change_mtu = enic_change_mtu, 252662306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid, 252762306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid, 252862306a36Sopenharmony_ci .ndo_tx_timeout = enic_tx_timeout, 252962306a36Sopenharmony_ci .ndo_set_vf_port = enic_set_vf_port, 253062306a36Sopenharmony_ci .ndo_get_vf_port = enic_get_vf_port, 253162306a36Sopenharmony_ci .ndo_set_vf_mac = enic_set_vf_mac, 253262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 253362306a36Sopenharmony_ci .ndo_poll_controller = enic_poll_controller, 253462306a36Sopenharmony_ci#endif 253562306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 253662306a36Sopenharmony_ci .ndo_rx_flow_steer = enic_rx_flow_steer, 253762306a36Sopenharmony_ci#endif 253862306a36Sopenharmony_ci .ndo_features_check = enic_features_check, 253962306a36Sopenharmony_ci}; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cistatic void enic_dev_deinit(struct enic *enic) 254262306a36Sopenharmony_ci{ 254362306a36Sopenharmony_ci unsigned int i; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) 254662306a36Sopenharmony_ci __netif_napi_del(&enic->napi[i]); 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) 254962306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 255062306a36Sopenharmony_ci __netif_napi_del(&enic->napi[enic_cq_wq(enic, i)]); 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci /* observe RCU grace period after __netif_napi_del() calls */ 255362306a36Sopenharmony_ci synchronize_net(); 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci enic_free_vnic_resources(enic); 255662306a36Sopenharmony_ci enic_clear_intr_mode(enic); 255762306a36Sopenharmony_ci enic_free_affinity_hint(enic); 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_cistatic void enic_kdump_kernel_config(struct enic *enic) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci if (is_kdump_kernel()) { 256362306a36Sopenharmony_ci dev_info(enic_get_dev(enic), "Running from within kdump kernel. Using minimal resources\n"); 256462306a36Sopenharmony_ci enic->rq_count = 1; 256562306a36Sopenharmony_ci enic->wq_count = 1; 256662306a36Sopenharmony_ci enic->config.rq_desc_count = ENIC_MIN_RQ_DESCS; 256762306a36Sopenharmony_ci enic->config.wq_desc_count = ENIC_MIN_WQ_DESCS; 256862306a36Sopenharmony_ci enic->config.mtu = min_t(u16, 1500, enic->config.mtu); 256962306a36Sopenharmony_ci } 257062306a36Sopenharmony_ci} 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_cistatic int enic_dev_init(struct enic *enic) 257362306a36Sopenharmony_ci{ 257462306a36Sopenharmony_ci struct device *dev = enic_get_dev(enic); 257562306a36Sopenharmony_ci struct net_device *netdev = enic->netdev; 257662306a36Sopenharmony_ci unsigned int i; 257762306a36Sopenharmony_ci int err; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci /* Get interrupt coalesce timer info */ 258062306a36Sopenharmony_ci err = enic_dev_intr_coal_timer_info(enic); 258162306a36Sopenharmony_ci if (err) { 258262306a36Sopenharmony_ci dev_warn(dev, "Using default conversion factor for " 258362306a36Sopenharmony_ci "interrupt coalesce timer\n"); 258462306a36Sopenharmony_ci vnic_dev_intr_coal_timer_info_default(enic->vdev); 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci /* Get vNIC configuration 258862306a36Sopenharmony_ci */ 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci err = enic_get_vnic_config(enic); 259162306a36Sopenharmony_ci if (err) { 259262306a36Sopenharmony_ci dev_err(dev, "Get vNIC configuration failed, aborting\n"); 259362306a36Sopenharmony_ci return err; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci /* Get available resource counts 259762306a36Sopenharmony_ci */ 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci enic_get_res_counts(enic); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci /* modify resource count if we are in kdump_kernel 260262306a36Sopenharmony_ci */ 260362306a36Sopenharmony_ci enic_kdump_kernel_config(enic); 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci /* Set interrupt mode based on resource counts and system 260662306a36Sopenharmony_ci * capabilities 260762306a36Sopenharmony_ci */ 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci err = enic_set_intr_mode(enic); 261062306a36Sopenharmony_ci if (err) { 261162306a36Sopenharmony_ci dev_err(dev, "Failed to set intr mode based on resource " 261262306a36Sopenharmony_ci "counts and system capabilities, aborting\n"); 261362306a36Sopenharmony_ci return err; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci /* Allocate and configure vNIC resources 261762306a36Sopenharmony_ci */ 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci err = enic_alloc_vnic_resources(enic); 262062306a36Sopenharmony_ci if (err) { 262162306a36Sopenharmony_ci dev_err(dev, "Failed to alloc vNIC resources, aborting\n"); 262262306a36Sopenharmony_ci goto err_out_free_vnic_resources; 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci enic_init_vnic_resources(enic); 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci err = enic_set_rss_nic_cfg(enic); 262862306a36Sopenharmony_ci if (err) { 262962306a36Sopenharmony_ci dev_err(dev, "Failed to config nic, aborting\n"); 263062306a36Sopenharmony_ci goto err_out_free_vnic_resources; 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci switch (vnic_dev_get_intr_mode(enic->vdev)) { 263462306a36Sopenharmony_ci default: 263562306a36Sopenharmony_ci netif_napi_add(netdev, &enic->napi[0], enic_poll); 263662306a36Sopenharmony_ci break; 263762306a36Sopenharmony_ci case VNIC_DEV_INTR_MODE_MSIX: 263862306a36Sopenharmony_ci for (i = 0; i < enic->rq_count; i++) { 263962306a36Sopenharmony_ci netif_napi_add(netdev, &enic->napi[i], 264062306a36Sopenharmony_ci enic_poll_msix_rq); 264162306a36Sopenharmony_ci } 264262306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 264362306a36Sopenharmony_ci netif_napi_add(netdev, 264462306a36Sopenharmony_ci &enic->napi[enic_cq_wq(enic, i)], 264562306a36Sopenharmony_ci enic_poll_msix_wq); 264662306a36Sopenharmony_ci break; 264762306a36Sopenharmony_ci } 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci return 0; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_cierr_out_free_vnic_resources: 265262306a36Sopenharmony_ci enic_free_affinity_hint(enic); 265362306a36Sopenharmony_ci enic_clear_intr_mode(enic); 265462306a36Sopenharmony_ci enic_free_vnic_resources(enic); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci return err; 265762306a36Sopenharmony_ci} 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cistatic void enic_iounmap(struct enic *enic) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci unsigned int i; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(enic->bar); i++) 266462306a36Sopenharmony_ci if (enic->bar[i].vaddr) 266562306a36Sopenharmony_ci iounmap(enic->bar[i].vaddr); 266662306a36Sopenharmony_ci} 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_cistatic int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 267162306a36Sopenharmony_ci struct net_device *netdev; 267262306a36Sopenharmony_ci struct enic *enic; 267362306a36Sopenharmony_ci int using_dac = 0; 267462306a36Sopenharmony_ci unsigned int i; 267562306a36Sopenharmony_ci int err; 267662306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 267762306a36Sopenharmony_ci int pos = 0; 267862306a36Sopenharmony_ci#endif 267962306a36Sopenharmony_ci int num_pps = 1; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci /* Allocate net device structure and initialize. Private 268262306a36Sopenharmony_ci * instance data is initialized to zero. 268362306a36Sopenharmony_ci */ 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(struct enic), 268662306a36Sopenharmony_ci ENIC_RQ_MAX, ENIC_WQ_MAX); 268762306a36Sopenharmony_ci if (!netdev) 268862306a36Sopenharmony_ci return -ENOMEM; 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci pci_set_drvdata(pdev, netdev); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci enic = netdev_priv(netdev); 269562306a36Sopenharmony_ci enic->netdev = netdev; 269662306a36Sopenharmony_ci enic->pdev = pdev; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci /* Setup PCI resources 269962306a36Sopenharmony_ci */ 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci err = pci_enable_device_mem(pdev); 270262306a36Sopenharmony_ci if (err) { 270362306a36Sopenharmony_ci dev_err(dev, "Cannot enable PCI device, aborting\n"); 270462306a36Sopenharmony_ci goto err_out_free_netdev; 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 270862306a36Sopenharmony_ci if (err) { 270962306a36Sopenharmony_ci dev_err(dev, "Cannot request PCI regions, aborting\n"); 271062306a36Sopenharmony_ci goto err_out_disable_device; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci pci_set_master(pdev); 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci /* Query PCI controller on system for DMA addressing 271662306a36Sopenharmony_ci * limitation for the device. Try 47-bit first, and 271762306a36Sopenharmony_ci * fail to 32-bit. 271862306a36Sopenharmony_ci */ 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(47)); 272162306a36Sopenharmony_ci if (err) { 272262306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 272362306a36Sopenharmony_ci if (err) { 272462306a36Sopenharmony_ci dev_err(dev, "No usable DMA configuration, aborting\n"); 272562306a36Sopenharmony_ci goto err_out_release_regions; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci } else { 272862306a36Sopenharmony_ci using_dac = 1; 272962306a36Sopenharmony_ci } 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci /* Map vNIC resources from BAR0-5 273262306a36Sopenharmony_ci */ 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(enic->bar); i++) { 273562306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM)) 273662306a36Sopenharmony_ci continue; 273762306a36Sopenharmony_ci enic->bar[i].len = pci_resource_len(pdev, i); 273862306a36Sopenharmony_ci enic->bar[i].vaddr = pci_iomap(pdev, i, enic->bar[i].len); 273962306a36Sopenharmony_ci if (!enic->bar[i].vaddr) { 274062306a36Sopenharmony_ci dev_err(dev, "Cannot memory-map BAR %d, aborting\n", i); 274162306a36Sopenharmony_ci err = -ENODEV; 274262306a36Sopenharmony_ci goto err_out_iounmap; 274362306a36Sopenharmony_ci } 274462306a36Sopenharmony_ci enic->bar[i].bus_addr = pci_resource_start(pdev, i); 274562306a36Sopenharmony_ci } 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci /* Register vNIC device 274862306a36Sopenharmony_ci */ 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci enic->vdev = vnic_dev_register(NULL, enic, pdev, enic->bar, 275162306a36Sopenharmony_ci ARRAY_SIZE(enic->bar)); 275262306a36Sopenharmony_ci if (!enic->vdev) { 275362306a36Sopenharmony_ci dev_err(dev, "vNIC registration failed, aborting\n"); 275462306a36Sopenharmony_ci err = -ENODEV; 275562306a36Sopenharmony_ci goto err_out_iounmap; 275662306a36Sopenharmony_ci } 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci err = vnic_devcmd_init(enic->vdev); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci if (err) 276162306a36Sopenharmony_ci goto err_out_vnic_unregister; 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 276462306a36Sopenharmony_ci /* Get number of subvnics */ 276562306a36Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); 276662306a36Sopenharmony_ci if (pos) { 276762306a36Sopenharmony_ci pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF, 276862306a36Sopenharmony_ci &enic->num_vfs); 276962306a36Sopenharmony_ci if (enic->num_vfs) { 277062306a36Sopenharmony_ci err = pci_enable_sriov(pdev, enic->num_vfs); 277162306a36Sopenharmony_ci if (err) { 277262306a36Sopenharmony_ci dev_err(dev, "SRIOV enable failed, aborting." 277362306a36Sopenharmony_ci " pci_enable_sriov() returned %d\n", 277462306a36Sopenharmony_ci err); 277562306a36Sopenharmony_ci goto err_out_vnic_unregister; 277662306a36Sopenharmony_ci } 277762306a36Sopenharmony_ci enic->priv_flags |= ENIC_SRIOV_ENABLED; 277862306a36Sopenharmony_ci num_pps = enic->num_vfs; 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci } 278162306a36Sopenharmony_ci#endif 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci /* Allocate structure for port profiles */ 278462306a36Sopenharmony_ci enic->pp = kcalloc(num_pps, sizeof(*enic->pp), GFP_KERNEL); 278562306a36Sopenharmony_ci if (!enic->pp) { 278662306a36Sopenharmony_ci err = -ENOMEM; 278762306a36Sopenharmony_ci goto err_out_disable_sriov_pp; 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci /* Issue device open to get device in known state 279162306a36Sopenharmony_ci */ 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_ci err = enic_dev_open(enic); 279462306a36Sopenharmony_ci if (err) { 279562306a36Sopenharmony_ci dev_err(dev, "vNIC dev open failed, aborting\n"); 279662306a36Sopenharmony_ci goto err_out_disable_sriov; 279762306a36Sopenharmony_ci } 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci /* Setup devcmd lock 280062306a36Sopenharmony_ci */ 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci spin_lock_init(&enic->devcmd_lock); 280362306a36Sopenharmony_ci spin_lock_init(&enic->enic_api_lock); 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci /* 280662306a36Sopenharmony_ci * Set ingress vlan rewrite mode before vnic initialization 280762306a36Sopenharmony_ci */ 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci err = enic_dev_set_ig_vlan_rewrite_mode(enic); 281062306a36Sopenharmony_ci if (err) { 281162306a36Sopenharmony_ci dev_err(dev, 281262306a36Sopenharmony_ci "Failed to set ingress vlan rewrite mode, aborting.\n"); 281362306a36Sopenharmony_ci goto err_out_dev_close; 281462306a36Sopenharmony_ci } 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci /* Issue device init to initialize the vnic-to-switch link. 281762306a36Sopenharmony_ci * We'll start with carrier off and wait for link UP 281862306a36Sopenharmony_ci * notification later to turn on carrier. We don't need 281962306a36Sopenharmony_ci * to wait here for the vnic-to-switch link initialization 282062306a36Sopenharmony_ci * to complete; link UP notification is the indication that 282162306a36Sopenharmony_ci * the process is complete. 282262306a36Sopenharmony_ci */ 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci netif_carrier_off(netdev); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci /* Do not call dev_init for a dynamic vnic. 282762306a36Sopenharmony_ci * For a dynamic vnic, init_prov_info will be 282862306a36Sopenharmony_ci * called later by an upper layer. 282962306a36Sopenharmony_ci */ 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci if (!enic_is_dynamic(enic)) { 283262306a36Sopenharmony_ci err = vnic_dev_init(enic->vdev, 0); 283362306a36Sopenharmony_ci if (err) { 283462306a36Sopenharmony_ci dev_err(dev, "vNIC dev init failed, aborting\n"); 283562306a36Sopenharmony_ci goto err_out_dev_close; 283662306a36Sopenharmony_ci } 283762306a36Sopenharmony_ci } 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci err = enic_dev_init(enic); 284062306a36Sopenharmony_ci if (err) { 284162306a36Sopenharmony_ci dev_err(dev, "Device initialization failed, aborting\n"); 284262306a36Sopenharmony_ci goto err_out_dev_close; 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci netif_set_real_num_tx_queues(netdev, enic->wq_count); 284662306a36Sopenharmony_ci netif_set_real_num_rx_queues(netdev, enic->rq_count); 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci /* Setup notification timer, HW reset task, and wq locks 284962306a36Sopenharmony_ci */ 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci timer_setup(&enic->notify_timer, enic_notify_timer, 0); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci enic_rfs_flw_tbl_init(enic); 285462306a36Sopenharmony_ci enic_set_rx_coal_setting(enic); 285562306a36Sopenharmony_ci INIT_WORK(&enic->reset, enic_reset); 285662306a36Sopenharmony_ci INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset); 285762306a36Sopenharmony_ci INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci for (i = 0; i < enic->wq_count; i++) 286062306a36Sopenharmony_ci spin_lock_init(&enic->wq_lock[i]); 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci /* Register net device 286362306a36Sopenharmony_ci */ 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci enic->port_mtu = enic->config.mtu; 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci err = enic_set_mac_addr(netdev, enic->mac_addr); 286862306a36Sopenharmony_ci if (err) { 286962306a36Sopenharmony_ci dev_err(dev, "Invalid MAC address, aborting\n"); 287062306a36Sopenharmony_ci goto err_out_dev_deinit; 287162306a36Sopenharmony_ci } 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci enic->tx_coalesce_usecs = enic->config.intr_timer_usec; 287462306a36Sopenharmony_ci /* rx coalesce time already got initialized. This gets used 287562306a36Sopenharmony_ci * if adaptive coal is turned off 287662306a36Sopenharmony_ci */ 287762306a36Sopenharmony_ci enic->rx_coalesce_usecs = enic->tx_coalesce_usecs; 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) 288062306a36Sopenharmony_ci netdev->netdev_ops = &enic_netdev_dynamic_ops; 288162306a36Sopenharmony_ci else 288262306a36Sopenharmony_ci netdev->netdev_ops = &enic_netdev_ops; 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci netdev->watchdog_timeo = 2 * HZ; 288562306a36Sopenharmony_ci enic_set_ethtool_ops(netdev); 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 288862306a36Sopenharmony_ci if (ENIC_SETTING(enic, LOOP)) { 288962306a36Sopenharmony_ci netdev->features &= ~NETIF_F_HW_VLAN_CTAG_TX; 289062306a36Sopenharmony_ci enic->loop_enable = 1; 289162306a36Sopenharmony_ci enic->loop_tag = enic->config.loop_tag; 289262306a36Sopenharmony_ci dev_info(dev, "loopback tag=0x%04x\n", enic->loop_tag); 289362306a36Sopenharmony_ci } 289462306a36Sopenharmony_ci if (ENIC_SETTING(enic, TXCSUM)) 289562306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM; 289662306a36Sopenharmony_ci if (ENIC_SETTING(enic, TSO)) 289762306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_TSO | 289862306a36Sopenharmony_ci NETIF_F_TSO6 | NETIF_F_TSO_ECN; 289962306a36Sopenharmony_ci if (ENIC_SETTING(enic, RSS)) 290062306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 290162306a36Sopenharmony_ci if (ENIC_SETTING(enic, RXCSUM)) 290262306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXCSUM; 290362306a36Sopenharmony_ci if (ENIC_SETTING(enic, VXLAN)) { 290462306a36Sopenharmony_ci u64 patch_level; 290562306a36Sopenharmony_ci u64 a1 = 0; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_RXCSUM | 290862306a36Sopenharmony_ci NETIF_F_TSO | 290962306a36Sopenharmony_ci NETIF_F_TSO6 | 291062306a36Sopenharmony_ci NETIF_F_TSO_ECN | 291162306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL | 291262306a36Sopenharmony_ci NETIF_F_HW_CSUM | 291362306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM; 291462306a36Sopenharmony_ci netdev->hw_features |= netdev->hw_enc_features; 291562306a36Sopenharmony_ci /* get bit mask from hw about supported offload bit level 291662306a36Sopenharmony_ci * BIT(0) = fw supports patch_level 0 291762306a36Sopenharmony_ci * fcoe bit = encap 291862306a36Sopenharmony_ci * fcoe_fc_crc_ok = outer csum ok 291962306a36Sopenharmony_ci * BIT(1) = always set by fw 292062306a36Sopenharmony_ci * BIT(2) = fw supports patch_level 2 292162306a36Sopenharmony_ci * BIT(0) in rss_hash = encap 292262306a36Sopenharmony_ci * BIT(1,2) in rss_hash = outer_ip_csum_ok/ 292362306a36Sopenharmony_ci * outer_tcp_csum_ok 292462306a36Sopenharmony_ci * used in enic_rq_indicate_buf 292562306a36Sopenharmony_ci */ 292662306a36Sopenharmony_ci err = vnic_dev_get_supported_feature_ver(enic->vdev, 292762306a36Sopenharmony_ci VIC_FEATURE_VXLAN, 292862306a36Sopenharmony_ci &patch_level, &a1); 292962306a36Sopenharmony_ci if (err) 293062306a36Sopenharmony_ci patch_level = 0; 293162306a36Sopenharmony_ci enic->vxlan.flags = (u8)a1; 293262306a36Sopenharmony_ci /* mask bits that are supported by driver 293362306a36Sopenharmony_ci */ 293462306a36Sopenharmony_ci patch_level &= BIT_ULL(0) | BIT_ULL(2); 293562306a36Sopenharmony_ci patch_level = fls(patch_level); 293662306a36Sopenharmony_ci patch_level = patch_level ? patch_level - 1 : 0; 293762306a36Sopenharmony_ci enic->vxlan.patch_level = patch_level; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci if (vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ) == 1 || 294062306a36Sopenharmony_ci enic->vxlan.flags & ENIC_VXLAN_MULTI_WQ) { 294162306a36Sopenharmony_ci netdev->udp_tunnel_nic_info = &enic_udp_tunnels_v4; 294262306a36Sopenharmony_ci if (enic->vxlan.flags & ENIC_VXLAN_OUTER_IPV6) 294362306a36Sopenharmony_ci netdev->udp_tunnel_nic_info = &enic_udp_tunnels; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci } 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci netdev->features |= netdev->hw_features; 294862306a36Sopenharmony_ci netdev->vlan_features |= netdev->features; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 295162306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_NTUPLE; 295262306a36Sopenharmony_ci#endif 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci if (using_dac) 295562306a36Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci netdev->priv_flags |= IFF_UNICAST_FLT; 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci /* MTU range: 68 - 9000 */ 296062306a36Sopenharmony_ci netdev->min_mtu = ENIC_MIN_MTU; 296162306a36Sopenharmony_ci netdev->max_mtu = ENIC_MAX_MTU; 296262306a36Sopenharmony_ci netdev->mtu = enic->port_mtu; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci err = register_netdev(netdev); 296562306a36Sopenharmony_ci if (err) { 296662306a36Sopenharmony_ci dev_err(dev, "Cannot register net device, aborting\n"); 296762306a36Sopenharmony_ci goto err_out_dev_deinit; 296862306a36Sopenharmony_ci } 296962306a36Sopenharmony_ci enic->rx_copybreak = RX_COPYBREAK_DEFAULT; 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci return 0; 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_cierr_out_dev_deinit: 297462306a36Sopenharmony_ci enic_dev_deinit(enic); 297562306a36Sopenharmony_cierr_out_dev_close: 297662306a36Sopenharmony_ci vnic_dev_close(enic->vdev); 297762306a36Sopenharmony_cierr_out_disable_sriov: 297862306a36Sopenharmony_ci kfree(enic->pp); 297962306a36Sopenharmony_cierr_out_disable_sriov_pp: 298062306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 298162306a36Sopenharmony_ci if (enic_sriov_enabled(enic)) { 298262306a36Sopenharmony_ci pci_disable_sriov(pdev); 298362306a36Sopenharmony_ci enic->priv_flags &= ~ENIC_SRIOV_ENABLED; 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci#endif 298662306a36Sopenharmony_cierr_out_vnic_unregister: 298762306a36Sopenharmony_ci vnic_dev_unregister(enic->vdev); 298862306a36Sopenharmony_cierr_out_iounmap: 298962306a36Sopenharmony_ci enic_iounmap(enic); 299062306a36Sopenharmony_cierr_out_release_regions: 299162306a36Sopenharmony_ci pci_release_regions(pdev); 299262306a36Sopenharmony_cierr_out_disable_device: 299362306a36Sopenharmony_ci pci_disable_device(pdev); 299462306a36Sopenharmony_cierr_out_free_netdev: 299562306a36Sopenharmony_ci free_netdev(netdev); 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci return err; 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_cistatic void enic_remove(struct pci_dev *pdev) 300162306a36Sopenharmony_ci{ 300262306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci if (netdev) { 300562306a36Sopenharmony_ci struct enic *enic = netdev_priv(netdev); 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci cancel_work_sync(&enic->reset); 300862306a36Sopenharmony_ci cancel_work_sync(&enic->change_mtu_work); 300962306a36Sopenharmony_ci unregister_netdev(netdev); 301062306a36Sopenharmony_ci enic_dev_deinit(enic); 301162306a36Sopenharmony_ci vnic_dev_close(enic->vdev); 301262306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 301362306a36Sopenharmony_ci if (enic_sriov_enabled(enic)) { 301462306a36Sopenharmony_ci pci_disable_sriov(pdev); 301562306a36Sopenharmony_ci enic->priv_flags &= ~ENIC_SRIOV_ENABLED; 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci#endif 301862306a36Sopenharmony_ci kfree(enic->pp); 301962306a36Sopenharmony_ci vnic_dev_unregister(enic->vdev); 302062306a36Sopenharmony_ci enic_iounmap(enic); 302162306a36Sopenharmony_ci pci_release_regions(pdev); 302262306a36Sopenharmony_ci pci_disable_device(pdev); 302362306a36Sopenharmony_ci free_netdev(netdev); 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci} 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_cistatic struct pci_driver enic_driver = { 302862306a36Sopenharmony_ci .name = DRV_NAME, 302962306a36Sopenharmony_ci .id_table = enic_id_table, 303062306a36Sopenharmony_ci .probe = enic_probe, 303162306a36Sopenharmony_ci .remove = enic_remove, 303262306a36Sopenharmony_ci}; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_cimodule_pci_driver(enic_driver); 3035