18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * QLogic qlcnic NIC Driver 48c2ecf20Sopenharmony_ci * Copyright (c) 2009-2013 QLogic Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 98c2ecf20Sopenharmony_ci#include <net/ip.h> 108c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 118c2ecf20Sopenharmony_ci#include <net/checksum.h> 128c2ecf20Sopenharmony_ci#include <linux/printk.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "qlcnic.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define QLCNIC_TX_ETHER_PKT 0x01 178c2ecf20Sopenharmony_ci#define QLCNIC_TX_TCP_PKT 0x02 188c2ecf20Sopenharmony_ci#define QLCNIC_TX_UDP_PKT 0x03 198c2ecf20Sopenharmony_ci#define QLCNIC_TX_IP_PKT 0x04 208c2ecf20Sopenharmony_ci#define QLCNIC_TX_TCP_LSO 0x05 218c2ecf20Sopenharmony_ci#define QLCNIC_TX_TCP_LSO6 0x06 228c2ecf20Sopenharmony_ci#define QLCNIC_TX_ENCAP_PKT 0x07 238c2ecf20Sopenharmony_ci#define QLCNIC_TX_ENCAP_LSO 0x08 248c2ecf20Sopenharmony_ci#define QLCNIC_TX_TCPV6_PKT 0x0b 258c2ecf20Sopenharmony_ci#define QLCNIC_TX_UDPV6_PKT 0x0c 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define QLCNIC_FLAGS_VLAN_TAGGED 0x10 288c2ecf20Sopenharmony_ci#define QLCNIC_FLAGS_VLAN_OOB 0x40 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \ 318c2ecf20Sopenharmony_ci (cmd_desc)->vlan_TCI = cpu_to_le16(v); 328c2ecf20Sopenharmony_ci#define qlcnic_set_cmd_desc_port(cmd_desc, var) \ 338c2ecf20Sopenharmony_ci ((cmd_desc)->port_ctxid |= ((var) & 0x0F)) 348c2ecf20Sopenharmony_ci#define qlcnic_set_cmd_desc_ctxid(cmd_desc, var) \ 358c2ecf20Sopenharmony_ci ((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0)) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define qlcnic_set_tx_port(_desc, _port) \ 388c2ecf20Sopenharmony_ci ((_desc)->port_ctxid = ((_port) & 0xf) | (((_port) << 4) & 0xf0)) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define qlcnic_set_tx_flags_opcode(_desc, _flags, _opcode) \ 418c2ecf20Sopenharmony_ci ((_desc)->flags_opcode |= \ 428c2ecf20Sopenharmony_ci cpu_to_le16(((_flags) & 0x7f) | (((_opcode) & 0x3f) << 7))) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define qlcnic_set_tx_frags_len(_desc, _frags, _len) \ 458c2ecf20Sopenharmony_ci ((_desc)->nfrags__length = \ 468c2ecf20Sopenharmony_ci cpu_to_le32(((_frags) & 0xff) | (((_len) & 0xffffff) << 8))) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* owner bits of status_desc */ 498c2ecf20Sopenharmony_ci#define STATUS_OWNER_HOST (0x1ULL << 56) 508c2ecf20Sopenharmony_ci#define STATUS_OWNER_PHANTOM (0x2ULL << 56) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Status descriptor: 538c2ecf20Sopenharmony_ci 0-3 port, 4-7 status, 8-11 type, 12-27 total_length 548c2ecf20Sopenharmony_ci 28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset 558c2ecf20Sopenharmony_ci 53-55 desc_cnt, 56-57 owner, 58-63 opcode 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#define qlcnic_get_sts_port(sts_data) \ 588c2ecf20Sopenharmony_ci ((sts_data) & 0x0F) 598c2ecf20Sopenharmony_ci#define qlcnic_get_sts_status(sts_data) \ 608c2ecf20Sopenharmony_ci (((sts_data) >> 4) & 0x0F) 618c2ecf20Sopenharmony_ci#define qlcnic_get_sts_type(sts_data) \ 628c2ecf20Sopenharmony_ci (((sts_data) >> 8) & 0x0F) 638c2ecf20Sopenharmony_ci#define qlcnic_get_sts_totallength(sts_data) \ 648c2ecf20Sopenharmony_ci (((sts_data) >> 12) & 0xFFFF) 658c2ecf20Sopenharmony_ci#define qlcnic_get_sts_refhandle(sts_data) \ 668c2ecf20Sopenharmony_ci (((sts_data) >> 28) & 0xFFFF) 678c2ecf20Sopenharmony_ci#define qlcnic_get_sts_prot(sts_data) \ 688c2ecf20Sopenharmony_ci (((sts_data) >> 44) & 0x0F) 698c2ecf20Sopenharmony_ci#define qlcnic_get_sts_pkt_offset(sts_data) \ 708c2ecf20Sopenharmony_ci (((sts_data) >> 48) & 0x1F) 718c2ecf20Sopenharmony_ci#define qlcnic_get_sts_desc_cnt(sts_data) \ 728c2ecf20Sopenharmony_ci (((sts_data) >> 53) & 0x7) 738c2ecf20Sopenharmony_ci#define qlcnic_get_sts_opcode(sts_data) \ 748c2ecf20Sopenharmony_ci (((sts_data) >> 58) & 0x03F) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_refhandle(sts_data) \ 778c2ecf20Sopenharmony_ci ((sts_data) & 0x07FFF) 788c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_length(sts_data) \ 798c2ecf20Sopenharmony_ci (((sts_data) >> 16) & 0x0FFFF) 808c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_l2_hdr_offset(sts_data) \ 818c2ecf20Sopenharmony_ci (((sts_data) >> 32) & 0x0FF) 828c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_l4_hdr_offset(sts_data) \ 838c2ecf20Sopenharmony_ci (((sts_data) >> 40) & 0x0FF) 848c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_timestamp(sts_data) \ 858c2ecf20Sopenharmony_ci (((sts_data) >> 48) & 0x1) 868c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_type(sts_data) \ 878c2ecf20Sopenharmony_ci (((sts_data) >> 49) & 0x7) 888c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_push_flag(sts_data) \ 898c2ecf20Sopenharmony_ci (((sts_data) >> 52) & 0x1) 908c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_seq_number(sts_data) \ 918c2ecf20Sopenharmony_ci ((sts_data) & 0x0FFFFFFFF) 928c2ecf20Sopenharmony_ci#define qlcnic_get_lro_sts_mss(sts_data1) \ 938c2ecf20Sopenharmony_ci ((sts_data1 >> 32) & 0x0FFFF) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define qlcnic_83xx_get_lro_sts_mss(sts) ((sts) & 0xffff) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* opcode field in status_desc */ 988c2ecf20Sopenharmony_ci#define QLCNIC_SYN_OFFLOAD 0x03 998c2ecf20Sopenharmony_ci#define QLCNIC_RXPKT_DESC 0x04 1008c2ecf20Sopenharmony_ci#define QLCNIC_OLD_RXPKT_DESC 0x3f 1018c2ecf20Sopenharmony_ci#define QLCNIC_RESPONSE_DESC 0x05 1028c2ecf20Sopenharmony_ci#define QLCNIC_LRO_DESC 0x12 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define QLCNIC_TCP_HDR_SIZE 20 1058c2ecf20Sopenharmony_ci#define QLCNIC_TCP_TS_OPTION_SIZE 12 1068c2ecf20Sopenharmony_ci#define QLCNIC_FETCH_RING_ID(handle) ((handle) >> 63) 1078c2ecf20Sopenharmony_ci#define QLCNIC_DESC_OWNER_FW cpu_to_le64(STATUS_OWNER_PHANTOM) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define QLCNIC_TCP_TS_HDR_SIZE (QLCNIC_TCP_HDR_SIZE + QLCNIC_TCP_TS_OPTION_SIZE) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* for status field in status_desc */ 1128c2ecf20Sopenharmony_ci#define STATUS_CKSUM_LOOP 0 1138c2ecf20Sopenharmony_ci#define STATUS_CKSUM_OK 2 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define qlcnic_83xx_pktln(sts) ((sts >> 32) & 0x3FFF) 1168c2ecf20Sopenharmony_ci#define qlcnic_83xx_hndl(sts) ((sts >> 48) & 0x7FFF) 1178c2ecf20Sopenharmony_ci#define qlcnic_83xx_csum_status(sts) ((sts >> 39) & 7) 1188c2ecf20Sopenharmony_ci#define qlcnic_83xx_opcode(sts) ((sts >> 42) & 0xF) 1198c2ecf20Sopenharmony_ci#define qlcnic_83xx_vlan_tag(sts) (((sts) >> 48) & 0xFFFF) 1208c2ecf20Sopenharmony_ci#define qlcnic_83xx_lro_pktln(sts) (((sts) >> 32) & 0x3FFF) 1218c2ecf20Sopenharmony_ci#define qlcnic_83xx_l2_hdr_off(sts) (((sts) >> 16) & 0xFF) 1228c2ecf20Sopenharmony_ci#define qlcnic_83xx_l4_hdr_off(sts) (((sts) >> 24) & 0xFF) 1238c2ecf20Sopenharmony_ci#define qlcnic_83xx_pkt_cnt(sts) (((sts) >> 16) & 0x7) 1248c2ecf20Sopenharmony_ci#define qlcnic_83xx_is_tstamp(sts) (((sts) >> 40) & 1) 1258c2ecf20Sopenharmony_ci#define qlcnic_83xx_is_psh_bit(sts) (((sts) >> 41) & 1) 1268c2ecf20Sopenharmony_ci#define qlcnic_83xx_is_ip_align(sts) (((sts) >> 46) & 1) 1278c2ecf20Sopenharmony_ci#define qlcnic_83xx_has_vlan_tag(sts) (((sts) >> 47) & 1) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, 1308c2ecf20Sopenharmony_ci int max); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *, 1338c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *, 1348c2ecf20Sopenharmony_ci u16, u16); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline u8 qlcnic_mac_hash(u64 mac, u16 vlan) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff) ^ (vlan & 0xff)); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter, 1428c2ecf20Sopenharmony_ci u16 handle, u8 ring_id) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 1458c2ecf20Sopenharmony_ci return handle | (ring_id << 15); 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci return handle; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic inline int qlcnic_82xx_is_lb_pkt(u64 sts_data) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return (qlcnic_get_sts_status(sts_data) == STATUS_CKSUM_LOOP) ? 1 : 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void qlcnic_delete_rx_list_mac(struct qlcnic_adapter *adapter, 1568c2ecf20Sopenharmony_ci struct qlcnic_filter *fil, 1578c2ecf20Sopenharmony_ci void *addr, u16 vlan_id) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int ret; 1608c2ecf20Sopenharmony_ci u8 op; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD; 1638c2ecf20Sopenharmony_ci ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op); 1648c2ecf20Sopenharmony_ci if (ret) 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; 1688c2ecf20Sopenharmony_ci ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op); 1698c2ecf20Sopenharmony_ci if (!ret) { 1708c2ecf20Sopenharmony_ci hlist_del(&fil->fnode); 1718c2ecf20Sopenharmony_ci adapter->rx_fhash.fnum--; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic struct qlcnic_filter *qlcnic_find_mac_filter(struct hlist_head *head, 1768c2ecf20Sopenharmony_ci void *addr, u16 vlan_id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct qlcnic_filter *tmp_fil = NULL; 1798c2ecf20Sopenharmony_ci struct hlist_node *n; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { 1828c2ecf20Sopenharmony_ci if (ether_addr_equal(tmp_fil->faddr, addr) && 1838c2ecf20Sopenharmony_ci tmp_fil->vlan_id == vlan_id) 1848c2ecf20Sopenharmony_ci return tmp_fil; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return NULL; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, 1918c2ecf20Sopenharmony_ci struct sk_buff *skb, int loopback_pkt, u16 vlan_id) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct ethhdr *phdr = (struct ethhdr *)(skb->data); 1948c2ecf20Sopenharmony_ci struct qlcnic_filter *fil, *tmp_fil; 1958c2ecf20Sopenharmony_ci struct hlist_head *head; 1968c2ecf20Sopenharmony_ci unsigned long time; 1978c2ecf20Sopenharmony_ci u64 src_addr = 0; 1988c2ecf20Sopenharmony_ci u8 hindex, op; 1998c2ecf20Sopenharmony_ci int ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!qlcnic_sriov_pf_check(adapter) || (vlan_id == 0xffff)) 2028c2ecf20Sopenharmony_ci vlan_id = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci memcpy(&src_addr, phdr->h_source, ETH_ALEN); 2058c2ecf20Sopenharmony_ci hindex = qlcnic_mac_hash(src_addr, vlan_id) & 2068c2ecf20Sopenharmony_ci (adapter->fhash.fbucket_size - 1); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (loopback_pkt) { 2098c2ecf20Sopenharmony_ci if (adapter->rx_fhash.fnum >= adapter->rx_fhash.fmax) 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci head = &(adapter->rx_fhash.fhead[hindex]); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id); 2158c2ecf20Sopenharmony_ci if (tmp_fil) { 2168c2ecf20Sopenharmony_ci time = tmp_fil->ftime; 2178c2ecf20Sopenharmony_ci if (time_after(jiffies, QLCNIC_READD_AGE * HZ + time)) 2188c2ecf20Sopenharmony_ci tmp_fil->ftime = jiffies; 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC); 2238c2ecf20Sopenharmony_ci if (!fil) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci fil->ftime = jiffies; 2278c2ecf20Sopenharmony_ci memcpy(fil->faddr, &src_addr, ETH_ALEN); 2288c2ecf20Sopenharmony_ci fil->vlan_id = vlan_id; 2298c2ecf20Sopenharmony_ci spin_lock(&adapter->rx_mac_learn_lock); 2308c2ecf20Sopenharmony_ci hlist_add_head(&(fil->fnode), head); 2318c2ecf20Sopenharmony_ci adapter->rx_fhash.fnum++; 2328c2ecf20Sopenharmony_ci spin_unlock(&adapter->rx_mac_learn_lock); 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci head = &adapter->fhash.fhead[hindex]; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci spin_lock(&adapter->mac_learn_lock); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id); 2398c2ecf20Sopenharmony_ci if (tmp_fil) { 2408c2ecf20Sopenharmony_ci op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; 2418c2ecf20Sopenharmony_ci ret = qlcnic_sre_macaddr_change(adapter, 2428c2ecf20Sopenharmony_ci (u8 *)&src_addr, 2438c2ecf20Sopenharmony_ci vlan_id, op); 2448c2ecf20Sopenharmony_ci if (!ret) { 2458c2ecf20Sopenharmony_ci hlist_del(&tmp_fil->fnode); 2468c2ecf20Sopenharmony_ci adapter->fhash.fnum--; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spin_unlock(&adapter->mac_learn_lock); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spin_unlock(&adapter->mac_learn_lock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci head = &adapter->rx_fhash.fhead[hindex]; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_lock(&adapter->rx_mac_learn_lock); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id); 2618c2ecf20Sopenharmony_ci if (tmp_fil) 2628c2ecf20Sopenharmony_ci qlcnic_delete_rx_list_mac(adapter, tmp_fil, &src_addr, 2638c2ecf20Sopenharmony_ci vlan_id); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_unlock(&adapter->rx_mac_learn_lock); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr, 2708c2ecf20Sopenharmony_ci u16 vlan_id, struct qlcnic_host_tx_ring *tx_ring) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct cmd_desc_type0 *hwdesc; 2738c2ecf20Sopenharmony_ci struct qlcnic_nic_req *req; 2748c2ecf20Sopenharmony_ci struct qlcnic_mac_req *mac_req; 2758c2ecf20Sopenharmony_ci struct qlcnic_vlan_req *vlan_req; 2768c2ecf20Sopenharmony_ci u32 producer; 2778c2ecf20Sopenharmony_ci u64 word; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci producer = tx_ring->producer; 2808c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[tx_ring->producer]; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci req = (struct qlcnic_nic_req *)hwdesc; 2838c2ecf20Sopenharmony_ci memset(req, 0, sizeof(struct qlcnic_nic_req)); 2848c2ecf20Sopenharmony_ci req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16); 2878c2ecf20Sopenharmony_ci req->req_hdr = cpu_to_le64(word); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mac_req = (struct qlcnic_mac_req *)&(req->words[0]); 2908c2ecf20Sopenharmony_ci mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD; 2918c2ecf20Sopenharmony_ci memcpy(mac_req->mac_addr, uaddr, ETH_ALEN); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci vlan_req = (struct qlcnic_vlan_req *)&req->words[1]; 2948c2ecf20Sopenharmony_ci vlan_req->vlan_id = cpu_to_le16(vlan_id); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci tx_ring->producer = get_next_index(producer, tx_ring->num_desc); 2978c2ecf20Sopenharmony_ci smp_mb(); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void qlcnic_send_filter(struct qlcnic_adapter *adapter, 3018c2ecf20Sopenharmony_ci struct cmd_desc_type0 *first_desc, 3028c2ecf20Sopenharmony_ci struct sk_buff *skb, 3038c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data); 3068c2ecf20Sopenharmony_ci struct ethhdr *phdr = (struct ethhdr *)(skb->data); 3078c2ecf20Sopenharmony_ci u16 protocol = ntohs(skb->protocol); 3088c2ecf20Sopenharmony_ci struct qlcnic_filter *fil, *tmp_fil; 3098c2ecf20Sopenharmony_ci struct hlist_head *head; 3108c2ecf20Sopenharmony_ci struct hlist_node *n; 3118c2ecf20Sopenharmony_ci u64 src_addr = 0; 3128c2ecf20Sopenharmony_ci u16 vlan_id = 0; 3138c2ecf20Sopenharmony_ci u8 hindex, hval; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (ether_addr_equal(phdr->h_source, adapter->mac_addr)) 3168c2ecf20Sopenharmony_ci return; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_VLAN_FILTERING) { 3198c2ecf20Sopenharmony_ci if (protocol == ETH_P_8021Q) { 3208c2ecf20Sopenharmony_ci vh = skb_vlan_eth_hdr(skb); 3218c2ecf20Sopenharmony_ci vlan_id = ntohs(vh->h_vlan_TCI); 3228c2ecf20Sopenharmony_ci } else if (skb_vlan_tag_present(skb)) { 3238c2ecf20Sopenharmony_ci vlan_id = skb_vlan_tag_get(skb); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci memcpy(&src_addr, phdr->h_source, ETH_ALEN); 3288c2ecf20Sopenharmony_ci hval = qlcnic_mac_hash(src_addr, vlan_id); 3298c2ecf20Sopenharmony_ci hindex = hval & (adapter->fhash.fbucket_size - 1); 3308c2ecf20Sopenharmony_ci head = &(adapter->fhash.fhead[hindex]); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { 3338c2ecf20Sopenharmony_ci if (ether_addr_equal(tmp_fil->faddr, (u8 *)&src_addr) && 3348c2ecf20Sopenharmony_ci tmp_fil->vlan_id == vlan_id) { 3358c2ecf20Sopenharmony_ci if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) 3368c2ecf20Sopenharmony_ci qlcnic_change_filter(adapter, &src_addr, 3378c2ecf20Sopenharmony_ci vlan_id, tx_ring); 3388c2ecf20Sopenharmony_ci tmp_fil->ftime = jiffies; 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (unlikely(adapter->fhash.fnum >= adapter->fhash.fmax)) { 3448c2ecf20Sopenharmony_ci adapter->stats.mac_filter_limit_overrun++; 3458c2ecf20Sopenharmony_ci return; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC); 3498c2ecf20Sopenharmony_ci if (!fil) 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring); 3538c2ecf20Sopenharmony_ci fil->ftime = jiffies; 3548c2ecf20Sopenharmony_ci fil->vlan_id = vlan_id; 3558c2ecf20Sopenharmony_ci memcpy(fil->faddr, &src_addr, ETH_ALEN); 3568c2ecf20Sopenharmony_ci spin_lock(&adapter->mac_learn_lock); 3578c2ecf20Sopenharmony_ci hlist_add_head(&(fil->fnode), head); 3588c2ecf20Sopenharmony_ci adapter->fhash.fnum++; 3598c2ecf20Sopenharmony_ci spin_unlock(&adapter->mac_learn_lock); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_VXLAN_PKT BIT_0 3638c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_OUTER_L3_IP6 BIT_1 3648c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_INNER_L3_IP6 BIT_2 3658c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_INNER_L4_UDP BIT_3 3668c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_DO_L3_CSUM BIT_4 3678c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_DO_L4_CSUM BIT_5 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int qlcnic_tx_encap_pkt(struct qlcnic_adapter *adapter, 3708c2ecf20Sopenharmony_ci struct cmd_desc_type0 *first_desc, 3718c2ecf20Sopenharmony_ci struct sk_buff *skb, 3728c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci u8 opcode = 0, inner_hdr_len = 0, outer_hdr_len = 0, total_hdr_len = 0; 3758c2ecf20Sopenharmony_ci int copied, copy_len, descr_size; 3768c2ecf20Sopenharmony_ci u32 producer = tx_ring->producer; 3778c2ecf20Sopenharmony_ci struct cmd_desc_type0 *hwdesc; 3788c2ecf20Sopenharmony_ci u16 flags = 0, encap_descr = 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_ETHER_PKT; 3818c2ecf20Sopenharmony_ci encap_descr = QLCNIC_ENCAP_VXLAN_PKT; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 3848c2ecf20Sopenharmony_ci inner_hdr_len = skb_inner_transport_header(skb) + 3858c2ecf20Sopenharmony_ci inner_tcp_hdrlen(skb) - 3868c2ecf20Sopenharmony_ci skb_inner_mac_header(skb); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* VXLAN header size = 8 */ 3898c2ecf20Sopenharmony_ci outer_hdr_len = skb_transport_offset(skb) + 8 + 3908c2ecf20Sopenharmony_ci sizeof(struct udphdr); 3918c2ecf20Sopenharmony_ci first_desc->outer_hdr_length = outer_hdr_len; 3928c2ecf20Sopenharmony_ci total_hdr_len = inner_hdr_len + outer_hdr_len; 3938c2ecf20Sopenharmony_ci encap_descr |= QLCNIC_ENCAP_DO_L3_CSUM | 3948c2ecf20Sopenharmony_ci QLCNIC_ENCAP_DO_L4_CSUM; 3958c2ecf20Sopenharmony_ci first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); 3968c2ecf20Sopenharmony_ci first_desc->hdr_length = inner_hdr_len; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Copy inner and outer headers in Tx descriptor(s) 3998c2ecf20Sopenharmony_ci * If total_hdr_len > cmd_desc_type0, use multiple 4008c2ecf20Sopenharmony_ci * descriptors 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci copied = 0; 4038c2ecf20Sopenharmony_ci descr_size = (int)sizeof(struct cmd_desc_type0); 4048c2ecf20Sopenharmony_ci while (copied < total_hdr_len) { 4058c2ecf20Sopenharmony_ci copy_len = min(descr_size, (total_hdr_len - copied)); 4068c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[producer]; 4078c2ecf20Sopenharmony_ci tx_ring->cmd_buf_arr[producer].skb = NULL; 4088c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, copied, 4098c2ecf20Sopenharmony_ci (char *)hwdesc, 4108c2ecf20Sopenharmony_ci copy_len); 4118c2ecf20Sopenharmony_ci copied += copy_len; 4128c2ecf20Sopenharmony_ci producer = get_next_index(producer, tx_ring->num_desc); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci tx_ring->producer = producer; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Make sure updated tx_ring->producer is visible 4188c2ecf20Sopenharmony_ci * for qlcnic_tx_avail() 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci smp_mb(); 4218c2ecf20Sopenharmony_ci adapter->stats.encap_lso_frames++; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_ENCAP_LSO; 4248c2ecf20Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 4258c2ecf20Sopenharmony_ci if (inner_ip_hdr(skb)->version == 6) { 4268c2ecf20Sopenharmony_ci if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) 4278c2ecf20Sopenharmony_ci encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP; 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci if (inner_ip_hdr(skb)->protocol == IPPROTO_UDP) 4308c2ecf20Sopenharmony_ci encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci adapter->stats.encap_tx_csummed++; 4348c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_ENCAP_PKT; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Prepare first 16 bits of byte offset 16 of Tx descriptor */ 4388c2ecf20Sopenharmony_ci if (ip_hdr(skb)->version == 6) 4398c2ecf20Sopenharmony_ci encap_descr |= QLCNIC_ENCAP_OUTER_L3_IP6; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* outer IP header's size in 32bit words size*/ 4428c2ecf20Sopenharmony_ci encap_descr |= (skb_network_header_len(skb) >> 2) << 6; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* outer IP header offset */ 4458c2ecf20Sopenharmony_ci encap_descr |= skb_network_offset(skb) << 10; 4468c2ecf20Sopenharmony_ci first_desc->encap_descr = cpu_to_le16(encap_descr); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci first_desc->tcp_hdr_offset = skb_inner_transport_header(skb) - 4498c2ecf20Sopenharmony_ci skb->data; 4508c2ecf20Sopenharmony_ci first_desc->ip_hdr_offset = skb_inner_network_offset(skb); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int qlcnic_tx_pkt(struct qlcnic_adapter *adapter, 4588c2ecf20Sopenharmony_ci struct cmd_desc_type0 *first_desc, struct sk_buff *skb, 4598c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci u8 l4proto, opcode = 0, hdr_len = 0, tag_vlan = 0; 4628c2ecf20Sopenharmony_ci u16 flags = 0, vlan_tci = 0; 4638c2ecf20Sopenharmony_ci int copied, offset, copy_len, size; 4648c2ecf20Sopenharmony_ci struct cmd_desc_type0 *hwdesc; 4658c2ecf20Sopenharmony_ci struct vlan_ethhdr *vh; 4668c2ecf20Sopenharmony_ci u16 protocol = ntohs(skb->protocol); 4678c2ecf20Sopenharmony_ci u32 producer = tx_ring->producer; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (protocol == ETH_P_8021Q) { 4708c2ecf20Sopenharmony_ci vh = skb_vlan_eth_hdr(skb); 4718c2ecf20Sopenharmony_ci flags = QLCNIC_FLAGS_VLAN_TAGGED; 4728c2ecf20Sopenharmony_ci vlan_tci = ntohs(vh->h_vlan_TCI); 4738c2ecf20Sopenharmony_ci protocol = ntohs(vh->h_vlan_encapsulated_proto); 4748c2ecf20Sopenharmony_ci tag_vlan = 1; 4758c2ecf20Sopenharmony_ci } else if (skb_vlan_tag_present(skb)) { 4768c2ecf20Sopenharmony_ci flags = QLCNIC_FLAGS_VLAN_OOB; 4778c2ecf20Sopenharmony_ci vlan_tci = skb_vlan_tag_get(skb); 4788c2ecf20Sopenharmony_ci tag_vlan = 1; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci if (unlikely(adapter->tx_pvid)) { 4818c2ecf20Sopenharmony_ci if (tag_vlan && !(adapter->flags & QLCNIC_TAGGING_ENABLED)) 4828c2ecf20Sopenharmony_ci return -EIO; 4838c2ecf20Sopenharmony_ci if (tag_vlan && (adapter->flags & QLCNIC_TAGGING_ENABLED)) 4848c2ecf20Sopenharmony_ci goto set_flags; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci flags = QLCNIC_FLAGS_VLAN_OOB; 4878c2ecf20Sopenharmony_ci vlan_tci = adapter->tx_pvid; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ciset_flags: 4908c2ecf20Sopenharmony_ci qlcnic_set_tx_vlan_tci(first_desc, vlan_tci); 4918c2ecf20Sopenharmony_ci qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (*(skb->data) & BIT_0) { 4948c2ecf20Sopenharmony_ci flags |= BIT_0; 4958c2ecf20Sopenharmony_ci memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_ETHER_PKT; 4988c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 4998c2ecf20Sopenharmony_ci hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 5008c2ecf20Sopenharmony_ci first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); 5018c2ecf20Sopenharmony_ci first_desc->hdr_length = hdr_len; 5028c2ecf20Sopenharmony_ci opcode = (protocol == ETH_P_IPV6) ? QLCNIC_TX_TCP_LSO6 : 5038c2ecf20Sopenharmony_ci QLCNIC_TX_TCP_LSO; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* For LSO, we need to copy the MAC/IP/TCP headers into 5068c2ecf20Sopenharmony_ci * the descriptor ring */ 5078c2ecf20Sopenharmony_ci copied = 0; 5088c2ecf20Sopenharmony_ci offset = 2; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (flags & QLCNIC_FLAGS_VLAN_OOB) { 5118c2ecf20Sopenharmony_ci first_desc->hdr_length += VLAN_HLEN; 5128c2ecf20Sopenharmony_ci first_desc->tcp_hdr_offset = VLAN_HLEN; 5138c2ecf20Sopenharmony_ci first_desc->ip_hdr_offset = VLAN_HLEN; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Only in case of TSO on vlan device */ 5168c2ecf20Sopenharmony_ci flags |= QLCNIC_FLAGS_VLAN_TAGGED; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Create a TSO vlan header template for firmware */ 5198c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[producer]; 5208c2ecf20Sopenharmony_ci tx_ring->cmd_buf_arr[producer].skb = NULL; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci copy_len = min((int)sizeof(struct cmd_desc_type0) - 5238c2ecf20Sopenharmony_ci offset, hdr_len + VLAN_HLEN); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci vh = (struct vlan_ethhdr *)((char *) hwdesc + 2); 5268c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, vh, 12); 5278c2ecf20Sopenharmony_ci vh->h_vlan_proto = htons(ETH_P_8021Q); 5288c2ecf20Sopenharmony_ci vh->h_vlan_TCI = htons(vlan_tci); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, 12, 5318c2ecf20Sopenharmony_ci (char *)vh + 16, 5328c2ecf20Sopenharmony_ci copy_len - 16); 5338c2ecf20Sopenharmony_ci copied = copy_len - VLAN_HLEN; 5348c2ecf20Sopenharmony_ci offset = 0; 5358c2ecf20Sopenharmony_ci producer = get_next_index(producer, tx_ring->num_desc); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci while (copied < hdr_len) { 5398c2ecf20Sopenharmony_ci size = (int)sizeof(struct cmd_desc_type0) - offset; 5408c2ecf20Sopenharmony_ci copy_len = min(size, (hdr_len - copied)); 5418c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[producer]; 5428c2ecf20Sopenharmony_ci tx_ring->cmd_buf_arr[producer].skb = NULL; 5438c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, copied, 5448c2ecf20Sopenharmony_ci (char *)hwdesc + 5458c2ecf20Sopenharmony_ci offset, copy_len); 5468c2ecf20Sopenharmony_ci copied += copy_len; 5478c2ecf20Sopenharmony_ci offset = 0; 5488c2ecf20Sopenharmony_ci producer = get_next_index(producer, tx_ring->num_desc); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci tx_ring->producer = producer; 5528c2ecf20Sopenharmony_ci smp_mb(); 5538c2ecf20Sopenharmony_ci adapter->stats.lso_frames++; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 5568c2ecf20Sopenharmony_ci if (protocol == ETH_P_IP) { 5578c2ecf20Sopenharmony_ci l4proto = ip_hdr(skb)->protocol; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (l4proto == IPPROTO_TCP) 5608c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_TCP_PKT; 5618c2ecf20Sopenharmony_ci else if (l4proto == IPPROTO_UDP) 5628c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_UDP_PKT; 5638c2ecf20Sopenharmony_ci } else if (protocol == ETH_P_IPV6) { 5648c2ecf20Sopenharmony_ci l4proto = ipv6_hdr(skb)->nexthdr; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (l4proto == IPPROTO_TCP) 5678c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_TCPV6_PKT; 5688c2ecf20Sopenharmony_ci else if (l4proto == IPPROTO_UDP) 5698c2ecf20Sopenharmony_ci opcode = QLCNIC_TX_UDPV6_PKT; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci first_desc->tcp_hdr_offset += skb_transport_offset(skb); 5738c2ecf20Sopenharmony_ci first_desc->ip_hdr_offset += skb_network_offset(skb); 5748c2ecf20Sopenharmony_ci qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int qlcnic_map_tx_skb(struct pci_dev *pdev, struct sk_buff *skb, 5808c2ecf20Sopenharmony_ci struct qlcnic_cmd_buffer *pbuf) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct qlcnic_skb_frag *nf; 5838c2ecf20Sopenharmony_ci skb_frag_t *frag; 5848c2ecf20Sopenharmony_ci int i, nr_frags; 5858c2ecf20Sopenharmony_ci dma_addr_t map; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 5888c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[0]; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci map = pci_map_single(pdev, skb->data, skb_headlen(skb), 5918c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 5928c2ecf20Sopenharmony_ci if (pci_dma_mapping_error(pdev, map)) 5938c2ecf20Sopenharmony_ci goto out_err; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci nf->dma = map; 5968c2ecf20Sopenharmony_ci nf->length = skb_headlen(skb); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 5998c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 6008c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[i+1]; 6018c2ecf20Sopenharmony_ci map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), 6028c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6038c2ecf20Sopenharmony_ci if (dma_mapping_error(&pdev->dev, map)) 6048c2ecf20Sopenharmony_ci goto unwind; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci nf->dma = map; 6078c2ecf20Sopenharmony_ci nf->length = skb_frag_size(frag); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciunwind: 6138c2ecf20Sopenharmony_ci while (--i >= 0) { 6148c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[i+1]; 6158c2ecf20Sopenharmony_ci pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[0]; 6198c2ecf20Sopenharmony_ci pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ciout_err: 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic void qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb, 6268c2ecf20Sopenharmony_ci struct qlcnic_cmd_buffer *pbuf) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct qlcnic_skb_frag *nf = &pbuf->frag_array[0]; 6298c2ecf20Sopenharmony_ci int i, nr_frags = skb_shinfo(skb)->nr_frags; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 6328c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[i+1]; 6338c2ecf20Sopenharmony_ci pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci nf = &pbuf->frag_array[0]; 6378c2ecf20Sopenharmony_ci pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); 6388c2ecf20Sopenharmony_ci pbuf->skb = NULL; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic inline void qlcnic_clear_cmddesc(u64 *desc) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci desc[0] = 0ULL; 6448c2ecf20Sopenharmony_ci desc[2] = 0ULL; 6458c2ecf20Sopenharmony_ci desc[7] = 0ULL; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cinetdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 6518c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 6528c2ecf20Sopenharmony_ci struct qlcnic_cmd_buffer *pbuf; 6538c2ecf20Sopenharmony_ci struct qlcnic_skb_frag *buffrag; 6548c2ecf20Sopenharmony_ci struct cmd_desc_type0 *hwdesc, *first_desc; 6558c2ecf20Sopenharmony_ci struct pci_dev *pdev; 6568c2ecf20Sopenharmony_ci struct ethhdr *phdr; 6578c2ecf20Sopenharmony_ci int i, k, frag_count, delta = 0; 6588c2ecf20Sopenharmony_ci u32 producer, num_txd; 6598c2ecf20Sopenharmony_ci u16 protocol; 6608c2ecf20Sopenharmony_ci bool l4_is_udp = false; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { 6638c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(netdev); 6648c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_MACSPOOF) { 6688c2ecf20Sopenharmony_ci phdr = (struct ethhdr *)skb->data; 6698c2ecf20Sopenharmony_ci if (!ether_addr_equal(phdr->h_source, adapter->mac_addr)) 6708c2ecf20Sopenharmony_ci goto drop_packet; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)]; 6748c2ecf20Sopenharmony_ci num_txd = tx_ring->num_desc; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci frag_count = skb_shinfo(skb)->nr_frags + 1; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* 14 frags supported for normal packet and 6798c2ecf20Sopenharmony_ci * 32 frags supported for TSO packet 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) { 6828c2ecf20Sopenharmony_ci for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++) 6838c2ecf20Sopenharmony_ci delta += skb_frag_size(&skb_shinfo(skb)->frags[i]); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (!__pskb_pull_tail(skb, delta)) 6868c2ecf20Sopenharmony_ci goto drop_packet; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci frag_count = 1 + skb_shinfo(skb)->nr_frags; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) { 6928c2ecf20Sopenharmony_ci netif_tx_stop_queue(tx_ring->txq); 6938c2ecf20Sopenharmony_ci if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { 6948c2ecf20Sopenharmony_ci netif_tx_start_queue(tx_ring->txq); 6958c2ecf20Sopenharmony_ci } else { 6968c2ecf20Sopenharmony_ci tx_ring->tx_stats.xmit_off++; 6978c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci producer = tx_ring->producer; 7028c2ecf20Sopenharmony_ci pbuf = &tx_ring->cmd_buf_arr[producer]; 7038c2ecf20Sopenharmony_ci pdev = adapter->pdev; 7048c2ecf20Sopenharmony_ci first_desc = &tx_ring->desc_head[producer]; 7058c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[producer]; 7068c2ecf20Sopenharmony_ci qlcnic_clear_cmddesc((u64 *)hwdesc); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (qlcnic_map_tx_skb(pdev, skb, pbuf)) { 7098c2ecf20Sopenharmony_ci adapter->stats.tx_dma_map_error++; 7108c2ecf20Sopenharmony_ci goto drop_packet; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci pbuf->skb = skb; 7148c2ecf20Sopenharmony_ci pbuf->frag_count = frag_count; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len); 7178c2ecf20Sopenharmony_ci qlcnic_set_tx_port(first_desc, adapter->portnum); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci for (i = 0; i < frag_count; i++) { 7208c2ecf20Sopenharmony_ci k = i % 4; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if ((k == 0) && (i > 0)) { 7238c2ecf20Sopenharmony_ci /* move to next desc.*/ 7248c2ecf20Sopenharmony_ci producer = get_next_index(producer, num_txd); 7258c2ecf20Sopenharmony_ci hwdesc = &tx_ring->desc_head[producer]; 7268c2ecf20Sopenharmony_ci qlcnic_clear_cmddesc((u64 *)hwdesc); 7278c2ecf20Sopenharmony_ci tx_ring->cmd_buf_arr[producer].skb = NULL; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci buffrag = &pbuf->frag_array[i]; 7318c2ecf20Sopenharmony_ci hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length); 7328c2ecf20Sopenharmony_ci switch (k) { 7338c2ecf20Sopenharmony_ci case 0: 7348c2ecf20Sopenharmony_ci hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma); 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci case 1: 7378c2ecf20Sopenharmony_ci hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case 2: 7408c2ecf20Sopenharmony_ci hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma); 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci case 3: 7438c2ecf20Sopenharmony_ci hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma); 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci tx_ring->producer = get_next_index(producer, num_txd); 7498c2ecf20Sopenharmony_ci smp_mb(); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci protocol = ntohs(skb->protocol); 7528c2ecf20Sopenharmony_ci if (protocol == ETH_P_IP) 7538c2ecf20Sopenharmony_ci l4_is_udp = ip_hdr(skb)->protocol == IPPROTO_UDP; 7548c2ecf20Sopenharmony_ci else if (protocol == ETH_P_IPV6) 7558c2ecf20Sopenharmony_ci l4_is_udp = ipv6_hdr(skb)->nexthdr == IPPROTO_UDP; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* Check if it is a VXLAN packet */ 7588c2ecf20Sopenharmony_ci if (!skb->encapsulation || !l4_is_udp || 7598c2ecf20Sopenharmony_ci !qlcnic_encap_tx_offload(adapter)) { 7608c2ecf20Sopenharmony_ci if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb, 7618c2ecf20Sopenharmony_ci tx_ring))) 7628c2ecf20Sopenharmony_ci goto unwind_buff; 7638c2ecf20Sopenharmony_ci } else { 7648c2ecf20Sopenharmony_ci if (unlikely(qlcnic_tx_encap_pkt(adapter, first_desc, 7658c2ecf20Sopenharmony_ci skb, tx_ring))) 7668c2ecf20Sopenharmony_ci goto unwind_buff; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (adapter->drv_mac_learn) 7708c2ecf20Sopenharmony_ci qlcnic_send_filter(adapter, first_desc, skb, tx_ring); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci tx_ring->tx_stats.tx_bytes += skb->len; 7738c2ecf20Sopenharmony_ci tx_ring->tx_stats.xmit_called++; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* Ensure writes are complete before HW fetches Tx descriptors */ 7768c2ecf20Sopenharmony_ci wmb(); 7778c2ecf20Sopenharmony_ci qlcnic_update_cmd_producer(tx_ring); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ciunwind_buff: 7828c2ecf20Sopenharmony_ci qlcnic_unmap_buffers(pdev, skb, pbuf); 7838c2ecf20Sopenharmony_cidrop_packet: 7848c2ecf20Sopenharmony_ci adapter->stats.txdropped++; 7858c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7868c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_civoid qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (adapter->ahw->linkup && !linkup) { 7948c2ecf20Sopenharmony_ci netdev_info(netdev, "NIC Link is down\n"); 7958c2ecf20Sopenharmony_ci adapter->ahw->linkup = 0; 7968c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7978c2ecf20Sopenharmony_ci } else if (!adapter->ahw->linkup && linkup) { 7988c2ecf20Sopenharmony_ci adapter->ahw->linkup = 1; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* Do not advertise Link up to the stack if device 8018c2ecf20Sopenharmony_ci * is in loopback mode 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci if (qlcnic_83xx_check(adapter) && adapter->ahw->lb_mode) { 8048c2ecf20Sopenharmony_ci netdev_info(netdev, "NIC Link is up for loopback test\n"); 8058c2ecf20Sopenharmony_ci return; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci netdev_info(netdev, "NIC Link is up\n"); 8098c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter, 8148c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring, 8158c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct sk_buff *skb; 8188c2ecf20Sopenharmony_ci dma_addr_t dma; 8198c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size); 8228c2ecf20Sopenharmony_ci if (!skb) { 8238c2ecf20Sopenharmony_ci adapter->stats.skb_alloc_failure++; 8248c2ecf20Sopenharmony_ci return -ENOMEM; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 8288c2ecf20Sopenharmony_ci dma = pci_map_single(pdev, skb->data, 8298c2ecf20Sopenharmony_ci rds_ring->dma_size, PCI_DMA_FROMDEVICE); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (pci_dma_mapping_error(pdev, dma)) { 8328c2ecf20Sopenharmony_ci adapter->stats.rx_dma_map_error++; 8338c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8348c2ecf20Sopenharmony_ci return -ENOMEM; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci buffer->skb = skb; 8388c2ecf20Sopenharmony_ci buffer->dma = dma; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic void qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, 8448c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring, 8458c2ecf20Sopenharmony_ci u8 ring_id) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct rcv_desc *pdesc; 8488c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 8498c2ecf20Sopenharmony_ci int count = 0; 8508c2ecf20Sopenharmony_ci uint32_t producer, handle; 8518c2ecf20Sopenharmony_ci struct list_head *head; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!spin_trylock(&rds_ring->lock)) 8548c2ecf20Sopenharmony_ci return; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci producer = rds_ring->producer; 8578c2ecf20Sopenharmony_ci head = &rds_ring->free_list; 8588c2ecf20Sopenharmony_ci while (!list_empty(head)) { 8598c2ecf20Sopenharmony_ci buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (!buffer->skb) { 8628c2ecf20Sopenharmony_ci if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci count++; 8668c2ecf20Sopenharmony_ci list_del(&buffer->list); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* make a rcv descriptor */ 8698c2ecf20Sopenharmony_ci pdesc = &rds_ring->desc_head[producer]; 8708c2ecf20Sopenharmony_ci handle = qlcnic_get_ref_handle(adapter, 8718c2ecf20Sopenharmony_ci buffer->ref_handle, ring_id); 8728c2ecf20Sopenharmony_ci pdesc->reference_handle = cpu_to_le16(handle); 8738c2ecf20Sopenharmony_ci pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); 8748c2ecf20Sopenharmony_ci pdesc->addr_buffer = cpu_to_le64(buffer->dma); 8758c2ecf20Sopenharmony_ci producer = get_next_index(producer, rds_ring->num_desc); 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci if (count) { 8788c2ecf20Sopenharmony_ci rds_ring->producer = producer; 8798c2ecf20Sopenharmony_ci writel((producer - 1) & (rds_ring->num_desc - 1), 8808c2ecf20Sopenharmony_ci rds_ring->crb_rcv_producer); 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci spin_unlock(&rds_ring->lock); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter, 8868c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring, 8878c2ecf20Sopenharmony_ci int budget) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci u32 sw_consumer, hw_consumer; 8908c2ecf20Sopenharmony_ci int i, done, count = 0; 8918c2ecf20Sopenharmony_ci struct qlcnic_cmd_buffer *buffer; 8928c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 8938c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 8948c2ecf20Sopenharmony_ci struct qlcnic_skb_frag *frag; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (!spin_trylock(&tx_ring->tx_clean_lock)) 8978c2ecf20Sopenharmony_ci return 1; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci sw_consumer = tx_ring->sw_consumer; 9008c2ecf20Sopenharmony_ci hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci while (sw_consumer != hw_consumer) { 9038c2ecf20Sopenharmony_ci buffer = &tx_ring->cmd_buf_arr[sw_consumer]; 9048c2ecf20Sopenharmony_ci if (buffer->skb) { 9058c2ecf20Sopenharmony_ci frag = &buffer->frag_array[0]; 9068c2ecf20Sopenharmony_ci pci_unmap_single(pdev, frag->dma, frag->length, 9078c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 9088c2ecf20Sopenharmony_ci frag->dma = 0ULL; 9098c2ecf20Sopenharmony_ci for (i = 1; i < buffer->frag_count; i++) { 9108c2ecf20Sopenharmony_ci frag++; 9118c2ecf20Sopenharmony_ci pci_unmap_page(pdev, frag->dma, frag->length, 9128c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 9138c2ecf20Sopenharmony_ci frag->dma = 0ULL; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci tx_ring->tx_stats.xmit_finished++; 9168c2ecf20Sopenharmony_ci dev_kfree_skb_any(buffer->skb); 9178c2ecf20Sopenharmony_ci buffer->skb = NULL; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc); 9218c2ecf20Sopenharmony_ci if (++count >= budget) 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci tx_ring->sw_consumer = sw_consumer; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (count && netif_running(netdev)) { 9288c2ecf20Sopenharmony_ci smp_mb(); 9298c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(tx_ring->txq) && 9308c2ecf20Sopenharmony_ci netif_carrier_ok(netdev)) { 9318c2ecf20Sopenharmony_ci if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { 9328c2ecf20Sopenharmony_ci netif_tx_wake_queue(tx_ring->txq); 9338c2ecf20Sopenharmony_ci tx_ring->tx_stats.xmit_on++; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci adapter->tx_timeo_cnt = 0; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci /* 9398c2ecf20Sopenharmony_ci * If everything is freed up to consumer then check if the ring is full 9408c2ecf20Sopenharmony_ci * If the ring is full then check if more needs to be freed and 9418c2ecf20Sopenharmony_ci * schedule the call back again. 9428c2ecf20Sopenharmony_ci * 9438c2ecf20Sopenharmony_ci * This happens when there are 2 CPUs. One could be freeing and the 9448c2ecf20Sopenharmony_ci * other filling it. If the ring is full when we get out of here and 9458c2ecf20Sopenharmony_ci * the card has already interrupted the host then the host can miss the 9468c2ecf20Sopenharmony_ci * interrupt. 9478c2ecf20Sopenharmony_ci * 9488c2ecf20Sopenharmony_ci * There is still a possible race condition and the host could miss an 9498c2ecf20Sopenharmony_ci * interrupt. The card has to take care of this. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); 9528c2ecf20Sopenharmony_ci done = (sw_consumer == hw_consumer); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci spin_unlock(&tx_ring->tx_clean_lock); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return done; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic int qlcnic_poll(struct napi_struct *napi, int budget) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci int tx_complete, work_done; 9628c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 9638c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 9648c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); 9678c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 9688c2ecf20Sopenharmony_ci tx_ring = sds_ring->tx_ring; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, 9718c2ecf20Sopenharmony_ci budget); 9728c2ecf20Sopenharmony_ci work_done = qlcnic_process_rcv_ring(sds_ring, budget); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Check if we need a repoll */ 9758c2ecf20Sopenharmony_ci if (!tx_complete) 9768c2ecf20Sopenharmony_ci work_done = budget; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (work_done < budget) { 9798c2ecf20Sopenharmony_ci napi_complete_done(&sds_ring->napi, work_done); 9808c2ecf20Sopenharmony_ci if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { 9818c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 9828c2ecf20Sopenharmony_ci qlcnic_enable_tx_intr(adapter, tx_ring); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return work_done; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int qlcnic_tx_poll(struct napi_struct *napi, int budget) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 9928c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 9938c2ecf20Sopenharmony_ci int work_done; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi); 9968c2ecf20Sopenharmony_ci adapter = tx_ring->adapter; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget); 9998c2ecf20Sopenharmony_ci if (work_done) { 10008c2ecf20Sopenharmony_ci napi_complete(&tx_ring->napi); 10018c2ecf20Sopenharmony_ci if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) 10028c2ecf20Sopenharmony_ci qlcnic_enable_tx_intr(adapter, tx_ring); 10038c2ecf20Sopenharmony_ci } else { 10048c2ecf20Sopenharmony_ci /* As qlcnic_process_cmd_ring() returned 0, we need a repoll*/ 10058c2ecf20Sopenharmony_ci work_done = budget; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return work_done; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic int qlcnic_rx_poll(struct napi_struct *napi, int budget) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 10148c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 10158c2ecf20Sopenharmony_ci int work_done; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); 10188c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci work_done = qlcnic_process_rcv_ring(sds_ring, budget); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (work_done < budget) { 10238c2ecf20Sopenharmony_ci napi_complete_done(&sds_ring->napi, work_done); 10248c2ecf20Sopenharmony_ci if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) 10258c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci return work_done; 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic void qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, 10328c2ecf20Sopenharmony_ci struct qlcnic_fw_msg *msg) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci u32 cable_OUI; 10358c2ecf20Sopenharmony_ci u16 cable_len, link_speed; 10368c2ecf20Sopenharmony_ci u8 link_status, module, duplex, autoneg, lb_status = 0; 10378c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci adapter->ahw->has_link_events = 1; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci cable_OUI = msg->body[1] & 0xffffffff; 10428c2ecf20Sopenharmony_ci cable_len = (msg->body[1] >> 32) & 0xffff; 10438c2ecf20Sopenharmony_ci link_speed = (msg->body[1] >> 48) & 0xffff; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci link_status = msg->body[2] & 0xff; 10468c2ecf20Sopenharmony_ci duplex = (msg->body[2] >> 16) & 0xff; 10478c2ecf20Sopenharmony_ci autoneg = (msg->body[2] >> 24) & 0xff; 10488c2ecf20Sopenharmony_ci lb_status = (msg->body[2] >> 32) & 0x3; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci module = (msg->body[2] >> 8) & 0xff; 10518c2ecf20Sopenharmony_ci if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) 10528c2ecf20Sopenharmony_ci dev_info(&netdev->dev, 10538c2ecf20Sopenharmony_ci "unsupported cable: OUI 0x%x, length %d\n", 10548c2ecf20Sopenharmony_ci cable_OUI, cable_len); 10558c2ecf20Sopenharmony_ci else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN) 10568c2ecf20Sopenharmony_ci dev_info(&netdev->dev, "unsupported cable length %d\n", 10578c2ecf20Sopenharmony_ci cable_len); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (!link_status && (lb_status == QLCNIC_ILB_MODE || 10608c2ecf20Sopenharmony_ci lb_status == QLCNIC_ELB_MODE)) 10618c2ecf20Sopenharmony_ci adapter->ahw->loopback_state |= QLCNIC_LINKEVENT; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci qlcnic_advert_link_change(adapter, link_status); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (duplex == LINKEVENT_FULL_DUPLEX) 10668c2ecf20Sopenharmony_ci adapter->ahw->link_duplex = DUPLEX_FULL; 10678c2ecf20Sopenharmony_ci else 10688c2ecf20Sopenharmony_ci adapter->ahw->link_duplex = DUPLEX_HALF; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci adapter->ahw->module_type = module; 10718c2ecf20Sopenharmony_ci adapter->ahw->link_autoneg = autoneg; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (link_status) { 10748c2ecf20Sopenharmony_ci adapter->ahw->link_speed = link_speed; 10758c2ecf20Sopenharmony_ci } else { 10768c2ecf20Sopenharmony_ci adapter->ahw->link_speed = SPEED_UNKNOWN; 10778c2ecf20Sopenharmony_ci adapter->ahw->link_duplex = DUPLEX_UNKNOWN; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic void qlcnic_handle_fw_message(int desc_cnt, int index, 10828c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct qlcnic_fw_msg msg; 10858c2ecf20Sopenharmony_ci struct status_desc *desc; 10868c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 10878c2ecf20Sopenharmony_ci struct device *dev; 10888c2ecf20Sopenharmony_ci int i = 0, opcode, ret; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci while (desc_cnt > 0 && i < 8) { 10918c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[index]; 10928c2ecf20Sopenharmony_ci msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]); 10938c2ecf20Sopenharmony_ci msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci index = get_next_index(index, sds_ring->num_desc); 10968c2ecf20Sopenharmony_ci desc_cnt--; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 11008c2ecf20Sopenharmony_ci dev = &adapter->pdev->dev; 11018c2ecf20Sopenharmony_ci opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci switch (opcode) { 11048c2ecf20Sopenharmony_ci case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: 11058c2ecf20Sopenharmony_ci qlcnic_handle_linkevent(adapter, &msg); 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK: 11088c2ecf20Sopenharmony_ci ret = (u32)(msg.body[1]); 11098c2ecf20Sopenharmony_ci switch (ret) { 11108c2ecf20Sopenharmony_ci case 0: 11118c2ecf20Sopenharmony_ci adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE; 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci case 1: 11148c2ecf20Sopenharmony_ci dev_info(dev, "loopback already in progress\n"); 11158c2ecf20Sopenharmony_ci adapter->ahw->diag_cnt = -EINPROGRESS; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci case 2: 11188c2ecf20Sopenharmony_ci dev_info(dev, "loopback cable is not connected\n"); 11198c2ecf20Sopenharmony_ci adapter->ahw->diag_cnt = -ENODEV; 11208c2ecf20Sopenharmony_ci break; 11218c2ecf20Sopenharmony_ci default: 11228c2ecf20Sopenharmony_ci dev_info(dev, 11238c2ecf20Sopenharmony_ci "loopback configure request failed, err %x\n", 11248c2ecf20Sopenharmony_ci ret); 11258c2ecf20Sopenharmony_ci adapter->ahw->diag_cnt = -EIO; 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci break; 11298c2ecf20Sopenharmony_ci case QLCNIC_C2H_OPCODE_GET_DCB_AEN: 11308c2ecf20Sopenharmony_ci qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg); 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci default: 11338c2ecf20Sopenharmony_ci break; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter, 11388c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *ring, 11398c2ecf20Sopenharmony_ci u16 index, u16 cksum) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 11428c2ecf20Sopenharmony_ci struct sk_buff *skb; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci buffer = &ring->rx_buf_arr[index]; 11458c2ecf20Sopenharmony_ci if (unlikely(buffer->skb == NULL)) { 11468c2ecf20Sopenharmony_ci WARN_ON(1); 11478c2ecf20Sopenharmony_ci return NULL; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci pci_unmap_single(adapter->pdev, buffer->dma, ring->dma_size, 11518c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci skb = buffer->skb; 11548c2ecf20Sopenharmony_ci if (likely((adapter->netdev->features & NETIF_F_RXCSUM) && 11558c2ecf20Sopenharmony_ci (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) { 11568c2ecf20Sopenharmony_ci adapter->stats.csummed++; 11578c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci buffer->skb = NULL; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci return skb; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter, 11698c2ecf20Sopenharmony_ci struct sk_buff *skb, u16 *vlan_tag) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci struct ethhdr *eth_hdr; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (!__vlan_get_tag(skb, vlan_tag)) { 11748c2ecf20Sopenharmony_ci eth_hdr = (struct ethhdr *)skb->data; 11758c2ecf20Sopenharmony_ci memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2); 11768c2ecf20Sopenharmony_ci skb_pull(skb, VLAN_HLEN); 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci if (!adapter->rx_pvid) 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (*vlan_tag == adapter->rx_pvid) { 11828c2ecf20Sopenharmony_ci /* Outer vlan tag. Packet should follow non-vlan path */ 11838c2ecf20Sopenharmony_ci *vlan_tag = 0xffff; 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_TAGGING_ENABLED) 11878c2ecf20Sopenharmony_ci return 0; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci return -EINVAL; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic struct qlcnic_rx_buffer * 11938c2ecf20Sopenharmony_ciqlcnic_process_rcv(struct qlcnic_adapter *adapter, 11948c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring, int ring, 11958c2ecf20Sopenharmony_ci u64 sts_data0) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 11988c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 11998c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 12008c2ecf20Sopenharmony_ci struct sk_buff *skb; 12018c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 12028c2ecf20Sopenharmony_ci int index, length, cksum, pkt_offset, is_lb_pkt; 12038c2ecf20Sopenharmony_ci u16 vid = 0xffff, t_vid; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 12068c2ecf20Sopenharmony_ci return NULL; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci index = qlcnic_get_sts_refhandle(sts_data0); 12118c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 12128c2ecf20Sopenharmony_ci return NULL; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci buffer = &rds_ring->rx_buf_arr[index]; 12158c2ecf20Sopenharmony_ci length = qlcnic_get_sts_totallength(sts_data0); 12168c2ecf20Sopenharmony_ci cksum = qlcnic_get_sts_status(sts_data0); 12178c2ecf20Sopenharmony_ci pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); 12208c2ecf20Sopenharmony_ci if (!skb) 12218c2ecf20Sopenharmony_ci return buffer; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (adapter->rx_mac_learn) { 12248c2ecf20Sopenharmony_ci t_vid = 0; 12258c2ecf20Sopenharmony_ci is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); 12268c2ecf20Sopenharmony_ci qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid); 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (length > rds_ring->skb_size) 12308c2ecf20Sopenharmony_ci skb_put(skb, rds_ring->skb_size); 12318c2ecf20Sopenharmony_ci else 12328c2ecf20Sopenharmony_ci skb_put(skb, length); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (pkt_offset) 12358c2ecf20Sopenharmony_ci skb_pull(skb, pkt_offset); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { 12388c2ecf20Sopenharmony_ci adapter->stats.rxdropped++; 12398c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 12408c2ecf20Sopenharmony_ci return buffer; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (vid != 0xffff) 12468c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci napi_gro_receive(&sds_ring->napi, skb); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci adapter->stats.rx_pkts++; 12518c2ecf20Sopenharmony_ci adapter->stats.rxbytes += length; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci return buffer; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci#define QLC_TCP_HDR_SIZE 20 12578c2ecf20Sopenharmony_ci#define QLC_TCP_TS_OPTION_SIZE 12 12588c2ecf20Sopenharmony_ci#define QLC_TCP_TS_HDR_SIZE (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE) 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic struct qlcnic_rx_buffer * 12618c2ecf20Sopenharmony_ciqlcnic_process_lro(struct qlcnic_adapter *adapter, 12628c2ecf20Sopenharmony_ci int ring, u64 sts_data0, u64 sts_data1) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 12658c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 12668c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 12678c2ecf20Sopenharmony_ci struct sk_buff *skb; 12688c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 12698c2ecf20Sopenharmony_ci struct iphdr *iph; 12708c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h; 12718c2ecf20Sopenharmony_ci struct tcphdr *th; 12728c2ecf20Sopenharmony_ci bool push, timestamp; 12738c2ecf20Sopenharmony_ci int index, l2_hdr_offset, l4_hdr_offset, is_lb_pkt; 12748c2ecf20Sopenharmony_ci u16 lro_length, length, data_offset, t_vid, vid = 0xffff; 12758c2ecf20Sopenharmony_ci u32 seq_number; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 12788c2ecf20Sopenharmony_ci return NULL; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci index = qlcnic_get_lro_sts_refhandle(sts_data0); 12838c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 12848c2ecf20Sopenharmony_ci return NULL; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci buffer = &rds_ring->rx_buf_arr[index]; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci timestamp = qlcnic_get_lro_sts_timestamp(sts_data0); 12898c2ecf20Sopenharmony_ci lro_length = qlcnic_get_lro_sts_length(sts_data0); 12908c2ecf20Sopenharmony_ci l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0); 12918c2ecf20Sopenharmony_ci l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0); 12928c2ecf20Sopenharmony_ci push = qlcnic_get_lro_sts_push_flag(sts_data0); 12938c2ecf20Sopenharmony_ci seq_number = qlcnic_get_lro_sts_seq_number(sts_data1); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); 12968c2ecf20Sopenharmony_ci if (!skb) 12978c2ecf20Sopenharmony_ci return buffer; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (adapter->rx_mac_learn) { 13008c2ecf20Sopenharmony_ci t_vid = 0; 13018c2ecf20Sopenharmony_ci is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); 13028c2ecf20Sopenharmony_ci qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (timestamp) 13068c2ecf20Sopenharmony_ci data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE; 13078c2ecf20Sopenharmony_ci else 13088c2ecf20Sopenharmony_ci data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci skb_put(skb, lro_length + data_offset); 13118c2ecf20Sopenharmony_ci skb_pull(skb, l2_hdr_offset); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { 13148c2ecf20Sopenharmony_ci adapter->stats.rxdropped++; 13158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 13168c2ecf20Sopenharmony_ci return buffer; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (ntohs(skb->protocol) == ETH_P_IPV6) { 13228c2ecf20Sopenharmony_ci ipv6h = (struct ipv6hdr *)skb->data; 13238c2ecf20Sopenharmony_ci th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr)); 13248c2ecf20Sopenharmony_ci length = (th->doff << 2) + lro_length; 13258c2ecf20Sopenharmony_ci ipv6h->payload_len = htons(length); 13268c2ecf20Sopenharmony_ci } else { 13278c2ecf20Sopenharmony_ci iph = (struct iphdr *)skb->data; 13288c2ecf20Sopenharmony_ci th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); 13298c2ecf20Sopenharmony_ci length = (iph->ihl << 2) + (th->doff << 2) + lro_length; 13308c2ecf20Sopenharmony_ci csum_replace2(&iph->check, iph->tot_len, htons(length)); 13318c2ecf20Sopenharmony_ci iph->tot_len = htons(length); 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci th->psh = push; 13358c2ecf20Sopenharmony_ci th->seq = htonl(seq_number); 13368c2ecf20Sopenharmony_ci length = skb->len; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) { 13398c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1); 13408c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) 13418c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; 13428c2ecf20Sopenharmony_ci else 13438c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci if (vid != 0xffff) 13478c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 13488c2ecf20Sopenharmony_ci netif_receive_skb(skb); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci adapter->stats.lro_pkts++; 13518c2ecf20Sopenharmony_ci adapter->stats.lrobytes += length; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return buffer; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 13598c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter = sds_ring->adapter; 13608c2ecf20Sopenharmony_ci struct list_head *cur; 13618c2ecf20Sopenharmony_ci struct status_desc *desc; 13628c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *rxbuf; 13638c2ecf20Sopenharmony_ci int opcode, desc_cnt, count = 0; 13648c2ecf20Sopenharmony_ci u64 sts_data0, sts_data1; 13658c2ecf20Sopenharmony_ci u8 ring; 13668c2ecf20Sopenharmony_ci u32 consumer = sds_ring->consumer; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci while (count < max) { 13698c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 13708c2ecf20Sopenharmony_ci sts_data0 = le64_to_cpu(desc->status_desc_data[0]); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci if (!(sts_data0 & STATUS_OWNER_HOST)) 13738c2ecf20Sopenharmony_ci break; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); 13768c2ecf20Sopenharmony_ci opcode = qlcnic_get_sts_opcode(sts_data0); 13778c2ecf20Sopenharmony_ci switch (opcode) { 13788c2ecf20Sopenharmony_ci case QLCNIC_RXPKT_DESC: 13798c2ecf20Sopenharmony_ci case QLCNIC_OLD_RXPKT_DESC: 13808c2ecf20Sopenharmony_ci case QLCNIC_SYN_OFFLOAD: 13818c2ecf20Sopenharmony_ci ring = qlcnic_get_sts_type(sts_data0); 13828c2ecf20Sopenharmony_ci rxbuf = qlcnic_process_rcv(adapter, sds_ring, ring, 13838c2ecf20Sopenharmony_ci sts_data0); 13848c2ecf20Sopenharmony_ci break; 13858c2ecf20Sopenharmony_ci case QLCNIC_LRO_DESC: 13868c2ecf20Sopenharmony_ci ring = qlcnic_get_lro_sts_type(sts_data0); 13878c2ecf20Sopenharmony_ci sts_data1 = le64_to_cpu(desc->status_desc_data[1]); 13888c2ecf20Sopenharmony_ci rxbuf = qlcnic_process_lro(adapter, ring, sts_data0, 13898c2ecf20Sopenharmony_ci sts_data1); 13908c2ecf20Sopenharmony_ci break; 13918c2ecf20Sopenharmony_ci case QLCNIC_RESPONSE_DESC: 13928c2ecf20Sopenharmony_ci qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); 13938c2ecf20Sopenharmony_ci default: 13948c2ecf20Sopenharmony_ci goto skip; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci WARN_ON(desc_cnt > 1); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci if (likely(rxbuf)) 13998c2ecf20Sopenharmony_ci list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]); 14008c2ecf20Sopenharmony_ci else 14018c2ecf20Sopenharmony_ci adapter->stats.null_rxbuf++; 14028c2ecf20Sopenharmony_ciskip: 14038c2ecf20Sopenharmony_ci for (; desc_cnt > 0; desc_cnt--) { 14048c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 14058c2ecf20Sopenharmony_ci desc->status_desc_data[0] = QLCNIC_DESC_OWNER_FW; 14068c2ecf20Sopenharmony_ci consumer = get_next_index(consumer, sds_ring->num_desc); 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci count++; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 14128c2ecf20Sopenharmony_ci rds_ring = &adapter->recv_ctx->rds_rings[ring]; 14138c2ecf20Sopenharmony_ci if (!list_empty(&sds_ring->free_list[ring])) { 14148c2ecf20Sopenharmony_ci list_for_each(cur, &sds_ring->free_list[ring]) { 14158c2ecf20Sopenharmony_ci rxbuf = list_entry(cur, struct qlcnic_rx_buffer, 14168c2ecf20Sopenharmony_ci list); 14178c2ecf20Sopenharmony_ci qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf); 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci spin_lock(&rds_ring->lock); 14208c2ecf20Sopenharmony_ci list_splice_tail_init(&sds_ring->free_list[ring], 14218c2ecf20Sopenharmony_ci &rds_ring->free_list); 14228c2ecf20Sopenharmony_ci spin_unlock(&rds_ring->lock); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring); 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (count) { 14298c2ecf20Sopenharmony_ci sds_ring->consumer = consumer; 14308c2ecf20Sopenharmony_ci writel(consumer, sds_ring->crb_sts_consumer); 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return count; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_civoid qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, 14378c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring, u8 ring_id) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct rcv_desc *pdesc; 14408c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 14418c2ecf20Sopenharmony_ci int count = 0; 14428c2ecf20Sopenharmony_ci u32 producer, handle; 14438c2ecf20Sopenharmony_ci struct list_head *head; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci producer = rds_ring->producer; 14468c2ecf20Sopenharmony_ci head = &rds_ring->free_list; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci while (!list_empty(head)) { 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (!buffer->skb) { 14538c2ecf20Sopenharmony_ci if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) 14548c2ecf20Sopenharmony_ci break; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci count++; 14588c2ecf20Sopenharmony_ci list_del(&buffer->list); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* make a rcv descriptor */ 14618c2ecf20Sopenharmony_ci pdesc = &rds_ring->desc_head[producer]; 14628c2ecf20Sopenharmony_ci pdesc->addr_buffer = cpu_to_le64(buffer->dma); 14638c2ecf20Sopenharmony_ci handle = qlcnic_get_ref_handle(adapter, buffer->ref_handle, 14648c2ecf20Sopenharmony_ci ring_id); 14658c2ecf20Sopenharmony_ci pdesc->reference_handle = cpu_to_le16(handle); 14668c2ecf20Sopenharmony_ci pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); 14678c2ecf20Sopenharmony_ci producer = get_next_index(producer, rds_ring->num_desc); 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if (count) { 14718c2ecf20Sopenharmony_ci rds_ring->producer = producer; 14728c2ecf20Sopenharmony_ci writel((producer-1) & (rds_ring->num_desc-1), 14738c2ecf20Sopenharmony_ci rds_ring->crb_rcv_producer); 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cistatic void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci if (adapter->ahw->msg_enable & NETIF_MSG_DRV) { 14808c2ecf20Sopenharmony_ci char prefix[30]; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci scnprintf(prefix, sizeof(prefix), "%s: %s: ", 14838c2ecf20Sopenharmony_ci dev_name(&adapter->pdev->dev), __func__); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci print_hex_dump_debug(prefix, DUMP_PREFIX_NONE, 16, 1, 14868c2ecf20Sopenharmony_ci skb->data, skb->len, true); 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, int ring, 14918c2ecf20Sopenharmony_ci u64 sts_data0) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 14948c2ecf20Sopenharmony_ci struct sk_buff *skb; 14958c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 14968c2ecf20Sopenharmony_ci int index, length, cksum, pkt_offset; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 14998c2ecf20Sopenharmony_ci return; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci index = qlcnic_get_sts_refhandle(sts_data0); 15048c2ecf20Sopenharmony_ci length = qlcnic_get_sts_totallength(sts_data0); 15058c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 15068c2ecf20Sopenharmony_ci return; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci cksum = qlcnic_get_sts_status(sts_data0); 15098c2ecf20Sopenharmony_ci pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); 15128c2ecf20Sopenharmony_ci if (!skb) 15138c2ecf20Sopenharmony_ci return; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (length > rds_ring->skb_size) 15168c2ecf20Sopenharmony_ci skb_put(skb, rds_ring->skb_size); 15178c2ecf20Sopenharmony_ci else 15188c2ecf20Sopenharmony_ci skb_put(skb, length); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (pkt_offset) 15218c2ecf20Sopenharmony_ci skb_pull(skb, pkt_offset); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) 15248c2ecf20Sopenharmony_ci adapter->ahw->diag_cnt++; 15258c2ecf20Sopenharmony_ci else 15268c2ecf20Sopenharmony_ci dump_skb(skb, adapter); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 15298c2ecf20Sopenharmony_ci adapter->stats.rx_pkts++; 15308c2ecf20Sopenharmony_ci adapter->stats.rxbytes += length; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci return; 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_civoid qlcnic_82xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter = sds_ring->adapter; 15388c2ecf20Sopenharmony_ci struct status_desc *desc; 15398c2ecf20Sopenharmony_ci u64 sts_data0; 15408c2ecf20Sopenharmony_ci int ring, opcode, desc_cnt; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci u32 consumer = sds_ring->consumer; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 15458c2ecf20Sopenharmony_ci sts_data0 = le64_to_cpu(desc->status_desc_data[0]); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci if (!(sts_data0 & STATUS_OWNER_HOST)) 15488c2ecf20Sopenharmony_ci return; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); 15518c2ecf20Sopenharmony_ci opcode = qlcnic_get_sts_opcode(sts_data0); 15528c2ecf20Sopenharmony_ci switch (opcode) { 15538c2ecf20Sopenharmony_ci case QLCNIC_RESPONSE_DESC: 15548c2ecf20Sopenharmony_ci qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); 15558c2ecf20Sopenharmony_ci break; 15568c2ecf20Sopenharmony_ci default: 15578c2ecf20Sopenharmony_ci ring = qlcnic_get_sts_type(sts_data0); 15588c2ecf20Sopenharmony_ci qlcnic_process_rcv_diag(adapter, ring, sts_data0); 15598c2ecf20Sopenharmony_ci break; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci for (; desc_cnt > 0; desc_cnt--) { 15638c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 15648c2ecf20Sopenharmony_ci desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); 15658c2ecf20Sopenharmony_ci consumer = get_next_index(consumer, sds_ring->num_desc); 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci sds_ring->consumer = consumer; 15698c2ecf20Sopenharmony_ci writel(consumer, sds_ring->crb_sts_consumer); 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ciint qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, 15738c2ecf20Sopenharmony_ci struct net_device *netdev) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci int ring; 15768c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 15778c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 15788c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings)) 15818c2ecf20Sopenharmony_ci return -ENOMEM; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 15848c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 15858c2ecf20Sopenharmony_ci if (qlcnic_check_multi_tx(adapter) && 15868c2ecf20Sopenharmony_ci !adapter->ahw->diag_test) { 15878c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll, 15888c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 15898c2ecf20Sopenharmony_ci } else { 15908c2ecf20Sopenharmony_ci if (ring == (adapter->drv_sds_rings - 1)) 15918c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, 15928c2ecf20Sopenharmony_ci qlcnic_poll, 15938c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 15948c2ecf20Sopenharmony_ci else 15958c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, 15968c2ecf20Sopenharmony_ci qlcnic_rx_poll, 15978c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (qlcnic_alloc_tx_rings(adapter, netdev)) { 16028c2ecf20Sopenharmony_ci qlcnic_free_sds_rings(recv_ctx); 16038c2ecf20Sopenharmony_ci return -ENOMEM; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { 16078c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 16088c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 16098c2ecf20Sopenharmony_ci netif_tx_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll, 16108c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci return 0; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_civoid qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter) 16188c2ecf20Sopenharmony_ci{ 16198c2ecf20Sopenharmony_ci int ring; 16208c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 16218c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 16228c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 16258c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 16268c2ecf20Sopenharmony_ci netif_napi_del(&sds_ring->napi); 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci qlcnic_free_sds_rings(adapter->recv_ctx); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { 16328c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 16338c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 16348c2ecf20Sopenharmony_ci netif_napi_del(&tx_ring->napi); 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci qlcnic_free_tx_rings(adapter); 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_civoid qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter) 16428c2ecf20Sopenharmony_ci{ 16438c2ecf20Sopenharmony_ci int ring; 16448c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 16458c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 16468c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) 16498c2ecf20Sopenharmony_ci return; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 16528c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 16538c2ecf20Sopenharmony_ci napi_enable(&sds_ring->napi); 16548c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (qlcnic_check_multi_tx(adapter) && 16588c2ecf20Sopenharmony_ci (adapter->flags & QLCNIC_MSIX_ENABLED) && 16598c2ecf20Sopenharmony_ci !adapter->ahw->diag_test) { 16608c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 16618c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 16628c2ecf20Sopenharmony_ci napi_enable(&tx_ring->napi); 16638c2ecf20Sopenharmony_ci qlcnic_enable_tx_intr(adapter, tx_ring); 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_civoid qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) 16698c2ecf20Sopenharmony_ci{ 16708c2ecf20Sopenharmony_ci int ring; 16718c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 16728c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 16738c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) 16768c2ecf20Sopenharmony_ci return; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 16798c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 16808c2ecf20Sopenharmony_ci qlcnic_disable_sds_intr(adapter, sds_ring); 16818c2ecf20Sopenharmony_ci napi_synchronize(&sds_ring->napi); 16828c2ecf20Sopenharmony_ci napi_disable(&sds_ring->napi); 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_MSIX_ENABLED) && 16868c2ecf20Sopenharmony_ci !adapter->ahw->diag_test && 16878c2ecf20Sopenharmony_ci qlcnic_check_multi_tx(adapter)) { 16888c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 16898c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 16908c2ecf20Sopenharmony_ci qlcnic_disable_tx_intr(adapter, tx_ring); 16918c2ecf20Sopenharmony_ci napi_synchronize(&tx_ring->napi); 16928c2ecf20Sopenharmony_ci napi_disable(&tx_ring->napi); 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci#define QLC_83XX_NORMAL_LB_PKT (1ULL << 36) 16988c2ecf20Sopenharmony_ci#define QLC_83XX_LRO_LB_PKT (1ULL << 46) 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic inline int qlcnic_83xx_is_lb_pkt(u64 sts_data, int lro_pkt) 17018c2ecf20Sopenharmony_ci{ 17028c2ecf20Sopenharmony_ci if (lro_pkt) 17038c2ecf20Sopenharmony_ci return (sts_data & QLC_83XX_LRO_LB_PKT) ? 1 : 0; 17048c2ecf20Sopenharmony_ci else 17058c2ecf20Sopenharmony_ci return (sts_data & QLC_83XX_NORMAL_LB_PKT) ? 1 : 0; 17068c2ecf20Sopenharmony_ci} 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci#define QLCNIC_ENCAP_LENGTH_MASK 0x7f 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic inline u8 qlcnic_encap_length(u64 sts_data) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci return sts_data & QLCNIC_ENCAP_LENGTH_MASK; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic struct qlcnic_rx_buffer * 17168c2ecf20Sopenharmony_ciqlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter, 17178c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring, 17188c2ecf20Sopenharmony_ci u8 ring, u64 sts_data[]) 17198c2ecf20Sopenharmony_ci{ 17208c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 17218c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 17228c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 17238c2ecf20Sopenharmony_ci struct sk_buff *skb; 17248c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 17258c2ecf20Sopenharmony_ci int index, length, cksum, is_lb_pkt; 17268c2ecf20Sopenharmony_ci u16 vid = 0xffff; 17278c2ecf20Sopenharmony_ci int err; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 17308c2ecf20Sopenharmony_ci return NULL; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci index = qlcnic_83xx_hndl(sts_data[0]); 17358c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 17368c2ecf20Sopenharmony_ci return NULL; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci buffer = &rds_ring->rx_buf_arr[index]; 17398c2ecf20Sopenharmony_ci length = qlcnic_83xx_pktln(sts_data[0]); 17408c2ecf20Sopenharmony_ci cksum = qlcnic_83xx_csum_status(sts_data[1]); 17418c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); 17428c2ecf20Sopenharmony_ci if (!skb) 17438c2ecf20Sopenharmony_ci return buffer; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (length > rds_ring->skb_size) 17468c2ecf20Sopenharmony_ci skb_put(skb, rds_ring->skb_size); 17478c2ecf20Sopenharmony_ci else 17488c2ecf20Sopenharmony_ci skb_put(skb, length); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci err = qlcnic_check_rx_tagging(adapter, skb, &vid); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (adapter->rx_mac_learn) { 17538c2ecf20Sopenharmony_ci is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0); 17548c2ecf20Sopenharmony_ci qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid); 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (unlikely(err)) { 17588c2ecf20Sopenharmony_ci adapter->stats.rxdropped++; 17598c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 17608c2ecf20Sopenharmony_ci return buffer; 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (qlcnic_encap_length(sts_data[1]) && 17668c2ecf20Sopenharmony_ci skb->ip_summed == CHECKSUM_UNNECESSARY) { 17678c2ecf20Sopenharmony_ci skb->csum_level = 1; 17688c2ecf20Sopenharmony_ci adapter->stats.encap_rx_csummed++; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (vid != 0xffff) 17728c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci napi_gro_receive(&sds_ring->napi, skb); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci adapter->stats.rx_pkts++; 17778c2ecf20Sopenharmony_ci adapter->stats.rxbytes += length; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci return buffer; 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic struct qlcnic_rx_buffer * 17838c2ecf20Sopenharmony_ciqlcnic_83xx_process_lro(struct qlcnic_adapter *adapter, 17848c2ecf20Sopenharmony_ci u8 ring, u64 sts_data[]) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 17878c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 17888c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *buffer; 17898c2ecf20Sopenharmony_ci struct sk_buff *skb; 17908c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 17918c2ecf20Sopenharmony_ci struct iphdr *iph; 17928c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h; 17938c2ecf20Sopenharmony_ci struct tcphdr *th; 17948c2ecf20Sopenharmony_ci bool push; 17958c2ecf20Sopenharmony_ci int l2_hdr_offset, l4_hdr_offset; 17968c2ecf20Sopenharmony_ci int index, is_lb_pkt; 17978c2ecf20Sopenharmony_ci u16 lro_length, length, data_offset, gso_size; 17988c2ecf20Sopenharmony_ci u16 vid = 0xffff; 17998c2ecf20Sopenharmony_ci int err; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 18028c2ecf20Sopenharmony_ci return NULL; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci index = qlcnic_83xx_hndl(sts_data[0]); 18078c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 18088c2ecf20Sopenharmony_ci return NULL; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci buffer = &rds_ring->rx_buf_arr[index]; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci lro_length = qlcnic_83xx_lro_pktln(sts_data[0]); 18138c2ecf20Sopenharmony_ci l2_hdr_offset = qlcnic_83xx_l2_hdr_off(sts_data[1]); 18148c2ecf20Sopenharmony_ci l4_hdr_offset = qlcnic_83xx_l4_hdr_off(sts_data[1]); 18158c2ecf20Sopenharmony_ci push = qlcnic_83xx_is_psh_bit(sts_data[1]); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); 18188c2ecf20Sopenharmony_ci if (!skb) 18198c2ecf20Sopenharmony_ci return buffer; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (qlcnic_83xx_is_tstamp(sts_data[1])) 18228c2ecf20Sopenharmony_ci data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE; 18238c2ecf20Sopenharmony_ci else 18248c2ecf20Sopenharmony_ci data_offset = l4_hdr_offset + QLCNIC_TCP_HDR_SIZE; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci skb_put(skb, lro_length + data_offset); 18278c2ecf20Sopenharmony_ci skb_pull(skb, l2_hdr_offset); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci err = qlcnic_check_rx_tagging(adapter, skb, &vid); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (adapter->rx_mac_learn) { 18328c2ecf20Sopenharmony_ci is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1); 18338c2ecf20Sopenharmony_ci qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid); 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (unlikely(err)) { 18378c2ecf20Sopenharmony_ci adapter->stats.rxdropped++; 18388c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 18398c2ecf20Sopenharmony_ci return buffer; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 18438c2ecf20Sopenharmony_ci if (ntohs(skb->protocol) == ETH_P_IPV6) { 18448c2ecf20Sopenharmony_ci ipv6h = (struct ipv6hdr *)skb->data; 18458c2ecf20Sopenharmony_ci th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr)); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci length = (th->doff << 2) + lro_length; 18488c2ecf20Sopenharmony_ci ipv6h->payload_len = htons(length); 18498c2ecf20Sopenharmony_ci } else { 18508c2ecf20Sopenharmony_ci iph = (struct iphdr *)skb->data; 18518c2ecf20Sopenharmony_ci th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); 18528c2ecf20Sopenharmony_ci length = (iph->ihl << 2) + (th->doff << 2) + lro_length; 18538c2ecf20Sopenharmony_ci csum_replace2(&iph->check, iph->tot_len, htons(length)); 18548c2ecf20Sopenharmony_ci iph->tot_len = htons(length); 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci th->psh = push; 18588c2ecf20Sopenharmony_ci length = skb->len; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) { 18618c2ecf20Sopenharmony_ci gso_size = qlcnic_83xx_get_lro_sts_mss(sts_data[0]); 18628c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = gso_size; 18638c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) 18648c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; 18658c2ecf20Sopenharmony_ci else 18668c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci if (vid != 0xffff) 18708c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci netif_receive_skb(skb); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci adapter->stats.lro_pkts++; 18758c2ecf20Sopenharmony_ci adapter->stats.lrobytes += length; 18768c2ecf20Sopenharmony_ci return buffer; 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic int qlcnic_83xx_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, 18808c2ecf20Sopenharmony_ci int max) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 18838c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter = sds_ring->adapter; 18848c2ecf20Sopenharmony_ci struct list_head *cur; 18858c2ecf20Sopenharmony_ci struct status_desc *desc; 18868c2ecf20Sopenharmony_ci struct qlcnic_rx_buffer *rxbuf = NULL; 18878c2ecf20Sopenharmony_ci u8 ring; 18888c2ecf20Sopenharmony_ci u64 sts_data[2]; 18898c2ecf20Sopenharmony_ci int count = 0, opcode; 18908c2ecf20Sopenharmony_ci u32 consumer = sds_ring->consumer; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci while (count < max) { 18938c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 18948c2ecf20Sopenharmony_ci sts_data[1] = le64_to_cpu(desc->status_desc_data[1]); 18958c2ecf20Sopenharmony_ci opcode = qlcnic_83xx_opcode(sts_data[1]); 18968c2ecf20Sopenharmony_ci if (!opcode) 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci sts_data[0] = le64_to_cpu(desc->status_desc_data[0]); 18998c2ecf20Sopenharmony_ci ring = QLCNIC_FETCH_RING_ID(sts_data[0]); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci switch (opcode) { 19028c2ecf20Sopenharmony_ci case QLC_83XX_REG_DESC: 19038c2ecf20Sopenharmony_ci rxbuf = qlcnic_83xx_process_rcv(adapter, sds_ring, 19048c2ecf20Sopenharmony_ci ring, sts_data); 19058c2ecf20Sopenharmony_ci break; 19068c2ecf20Sopenharmony_ci case QLC_83XX_LRO_DESC: 19078c2ecf20Sopenharmony_ci rxbuf = qlcnic_83xx_process_lro(adapter, ring, 19088c2ecf20Sopenharmony_ci sts_data); 19098c2ecf20Sopenharmony_ci break; 19108c2ecf20Sopenharmony_ci default: 19118c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 19128c2ecf20Sopenharmony_ci "Unknown opcode: 0x%x\n", opcode); 19138c2ecf20Sopenharmony_ci goto skip; 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci if (likely(rxbuf)) 19178c2ecf20Sopenharmony_ci list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]); 19188c2ecf20Sopenharmony_ci else 19198c2ecf20Sopenharmony_ci adapter->stats.null_rxbuf++; 19208c2ecf20Sopenharmony_ciskip: 19218c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 19228c2ecf20Sopenharmony_ci /* Reset the descriptor */ 19238c2ecf20Sopenharmony_ci desc->status_desc_data[1] = 0; 19248c2ecf20Sopenharmony_ci consumer = get_next_index(consumer, sds_ring->num_desc); 19258c2ecf20Sopenharmony_ci count++; 19268c2ecf20Sopenharmony_ci } 19278c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 19288c2ecf20Sopenharmony_ci rds_ring = &adapter->recv_ctx->rds_rings[ring]; 19298c2ecf20Sopenharmony_ci if (!list_empty(&sds_ring->free_list[ring])) { 19308c2ecf20Sopenharmony_ci list_for_each(cur, &sds_ring->free_list[ring]) { 19318c2ecf20Sopenharmony_ci rxbuf = list_entry(cur, struct qlcnic_rx_buffer, 19328c2ecf20Sopenharmony_ci list); 19338c2ecf20Sopenharmony_ci qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf); 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci spin_lock(&rds_ring->lock); 19368c2ecf20Sopenharmony_ci list_splice_tail_init(&sds_ring->free_list[ring], 19378c2ecf20Sopenharmony_ci &rds_ring->free_list); 19388c2ecf20Sopenharmony_ci spin_unlock(&rds_ring->lock); 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring); 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci if (count) { 19438c2ecf20Sopenharmony_ci sds_ring->consumer = consumer; 19448c2ecf20Sopenharmony_ci writel(consumer, sds_ring->crb_sts_consumer); 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci return count; 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci int tx_complete; 19528c2ecf20Sopenharmony_ci int work_done; 19538c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 19548c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 19558c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); 19588c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 19598c2ecf20Sopenharmony_ci /* tx ring count = 1 */ 19608c2ecf20Sopenharmony_ci tx_ring = adapter->tx_ring; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); 19638c2ecf20Sopenharmony_ci work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci /* Check if we need a repoll */ 19668c2ecf20Sopenharmony_ci if (!tx_complete) 19678c2ecf20Sopenharmony_ci work_done = budget; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (work_done < budget) { 19708c2ecf20Sopenharmony_ci napi_complete_done(&sds_ring->napi, work_done); 19718c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci return work_done; 19758c2ecf20Sopenharmony_ci} 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_cistatic int qlcnic_83xx_poll(struct napi_struct *napi, int budget) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci int tx_complete; 19808c2ecf20Sopenharmony_ci int work_done; 19818c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 19828c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 19838c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); 19868c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 19878c2ecf20Sopenharmony_ci /* tx ring count = 1 */ 19888c2ecf20Sopenharmony_ci tx_ring = adapter->tx_ring; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); 19918c2ecf20Sopenharmony_ci work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci /* Check if we need a repoll */ 19948c2ecf20Sopenharmony_ci if (!tx_complete) 19958c2ecf20Sopenharmony_ci work_done = budget; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci if (work_done < budget) { 19988c2ecf20Sopenharmony_ci napi_complete_done(&sds_ring->napi, work_done); 19998c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 20008c2ecf20Sopenharmony_ci } 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci return work_done; 20038c2ecf20Sopenharmony_ci} 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_cistatic int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget) 20068c2ecf20Sopenharmony_ci{ 20078c2ecf20Sopenharmony_ci int work_done; 20088c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 20098c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi); 20128c2ecf20Sopenharmony_ci adapter = tx_ring->adapter; 20138c2ecf20Sopenharmony_ci work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget); 20148c2ecf20Sopenharmony_ci if (work_done) { 20158c2ecf20Sopenharmony_ci napi_complete(&tx_ring->napi); 20168c2ecf20Sopenharmony_ci if (test_bit(__QLCNIC_DEV_UP , &adapter->state)) 20178c2ecf20Sopenharmony_ci qlcnic_enable_tx_intr(adapter, tx_ring); 20188c2ecf20Sopenharmony_ci } else { 20198c2ecf20Sopenharmony_ci /* need a repoll */ 20208c2ecf20Sopenharmony_ci work_done = budget; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci return work_done; 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_cistatic int qlcnic_83xx_rx_poll(struct napi_struct *napi, int budget) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci int work_done; 20298c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 20308c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); 20338c2ecf20Sopenharmony_ci adapter = sds_ring->adapter; 20348c2ecf20Sopenharmony_ci work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); 20358c2ecf20Sopenharmony_ci if (work_done < budget) { 20368c2ecf20Sopenharmony_ci napi_complete_done(&sds_ring->napi, work_done); 20378c2ecf20Sopenharmony_ci if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) 20388c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci return work_done; 20428c2ecf20Sopenharmony_ci} 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_civoid qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter) 20458c2ecf20Sopenharmony_ci{ 20468c2ecf20Sopenharmony_ci int ring; 20478c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 20488c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 20498c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) 20528c2ecf20Sopenharmony_ci return; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 20558c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 20568c2ecf20Sopenharmony_ci napi_enable(&sds_ring->napi); 20578c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_MSIX_ENABLED) 20588c2ecf20Sopenharmony_ci qlcnic_enable_sds_intr(adapter, sds_ring); 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_MSIX_ENABLED) && 20628c2ecf20Sopenharmony_ci !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { 20638c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 20648c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 20658c2ecf20Sopenharmony_ci napi_enable(&tx_ring->napi); 20668c2ecf20Sopenharmony_ci qlcnic_enable_tx_intr(adapter, tx_ring); 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci} 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_civoid qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci int ring; 20748c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 20758c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 20768c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) 20798c2ecf20Sopenharmony_ci return; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 20828c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 20838c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_MSIX_ENABLED) 20848c2ecf20Sopenharmony_ci qlcnic_disable_sds_intr(adapter, sds_ring); 20858c2ecf20Sopenharmony_ci napi_synchronize(&sds_ring->napi); 20868c2ecf20Sopenharmony_ci napi_disable(&sds_ring->napi); 20878c2ecf20Sopenharmony_ci } 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_MSIX_ENABLED) && 20908c2ecf20Sopenharmony_ci !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { 20918c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 20928c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 20938c2ecf20Sopenharmony_ci qlcnic_disable_tx_intr(adapter, tx_ring); 20948c2ecf20Sopenharmony_ci napi_synchronize(&tx_ring->napi); 20958c2ecf20Sopenharmony_ci napi_disable(&tx_ring->napi); 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci} 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ciint qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter, 21018c2ecf20Sopenharmony_ci struct net_device *netdev) 21028c2ecf20Sopenharmony_ci{ 21038c2ecf20Sopenharmony_ci int ring; 21048c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 21058c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 21068c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings)) 21098c2ecf20Sopenharmony_ci return -ENOMEM; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 21128c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 21138c2ecf20Sopenharmony_ci if (adapter->flags & QLCNIC_MSIX_ENABLED) { 21148c2ecf20Sopenharmony_ci if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) 21158c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, 21168c2ecf20Sopenharmony_ci qlcnic_83xx_rx_poll, 21178c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 21188c2ecf20Sopenharmony_ci else 21198c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, 21208c2ecf20Sopenharmony_ci qlcnic_83xx_msix_sriov_vf_poll, 21218c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci } else { 21248c2ecf20Sopenharmony_ci netif_napi_add(netdev, &sds_ring->napi, 21258c2ecf20Sopenharmony_ci qlcnic_83xx_poll, 21268c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci if (qlcnic_alloc_tx_rings(adapter, netdev)) { 21318c2ecf20Sopenharmony_ci qlcnic_free_sds_rings(recv_ctx); 21328c2ecf20Sopenharmony_ci return -ENOMEM; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_MSIX_ENABLED) && 21368c2ecf20Sopenharmony_ci !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { 21378c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 21388c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 21398c2ecf20Sopenharmony_ci netif_tx_napi_add(netdev, &tx_ring->napi, 21408c2ecf20Sopenharmony_ci qlcnic_83xx_msix_tx_poll, 21418c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci return 0; 21468c2ecf20Sopenharmony_ci} 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_civoid qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter) 21498c2ecf20Sopenharmony_ci{ 21508c2ecf20Sopenharmony_ci int ring; 21518c2ecf20Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 21528c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 21538c2ecf20Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 21568c2ecf20Sopenharmony_ci sds_ring = &recv_ctx->sds_rings[ring]; 21578c2ecf20Sopenharmony_ci netif_napi_del(&sds_ring->napi); 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci qlcnic_free_sds_rings(adapter->recv_ctx); 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci if ((adapter->flags & QLCNIC_MSIX_ENABLED) && 21638c2ecf20Sopenharmony_ci !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { 21648c2ecf20Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 21658c2ecf20Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 21668c2ecf20Sopenharmony_ci netif_napi_del(&tx_ring->napi); 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci qlcnic_free_tx_rings(adapter); 21718c2ecf20Sopenharmony_ci} 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_cistatic void qlcnic_83xx_process_rcv_diag(struct qlcnic_adapter *adapter, 21748c2ecf20Sopenharmony_ci int ring, u64 sts_data[]) 21758c2ecf20Sopenharmony_ci{ 21768c2ecf20Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 21778c2ecf20Sopenharmony_ci struct sk_buff *skb; 21788c2ecf20Sopenharmony_ci struct qlcnic_host_rds_ring *rds_ring; 21798c2ecf20Sopenharmony_ci int index, length; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci if (unlikely(ring >= adapter->max_rds_rings)) 21828c2ecf20Sopenharmony_ci return; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci rds_ring = &recv_ctx->rds_rings[ring]; 21858c2ecf20Sopenharmony_ci index = qlcnic_83xx_hndl(sts_data[0]); 21868c2ecf20Sopenharmony_ci if (unlikely(index >= rds_ring->num_desc)) 21878c2ecf20Sopenharmony_ci return; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci length = qlcnic_83xx_pktln(sts_data[0]); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); 21928c2ecf20Sopenharmony_ci if (!skb) 21938c2ecf20Sopenharmony_ci return; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci if (length > rds_ring->skb_size) 21968c2ecf20Sopenharmony_ci skb_put(skb, rds_ring->skb_size); 21978c2ecf20Sopenharmony_ci else 21988c2ecf20Sopenharmony_ci skb_put(skb, length); 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) 22018c2ecf20Sopenharmony_ci adapter->ahw->diag_cnt++; 22028c2ecf20Sopenharmony_ci else 22038c2ecf20Sopenharmony_ci dump_skb(skb, adapter); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 22068c2ecf20Sopenharmony_ci return; 22078c2ecf20Sopenharmony_ci} 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_civoid qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci struct qlcnic_adapter *adapter = sds_ring->adapter; 22128c2ecf20Sopenharmony_ci struct status_desc *desc; 22138c2ecf20Sopenharmony_ci u64 sts_data[2]; 22148c2ecf20Sopenharmony_ci int ring, opcode; 22158c2ecf20Sopenharmony_ci u32 consumer = sds_ring->consumer; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 22188c2ecf20Sopenharmony_ci sts_data[0] = le64_to_cpu(desc->status_desc_data[0]); 22198c2ecf20Sopenharmony_ci sts_data[1] = le64_to_cpu(desc->status_desc_data[1]); 22208c2ecf20Sopenharmony_ci opcode = qlcnic_83xx_opcode(sts_data[1]); 22218c2ecf20Sopenharmony_ci if (!opcode) 22228c2ecf20Sopenharmony_ci return; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci ring = QLCNIC_FETCH_RING_ID(sts_data[0]); 22258c2ecf20Sopenharmony_ci qlcnic_83xx_process_rcv_diag(adapter, ring, sts_data); 22268c2ecf20Sopenharmony_ci desc = &sds_ring->desc_head[consumer]; 22278c2ecf20Sopenharmony_ci desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); 22288c2ecf20Sopenharmony_ci consumer = get_next_index(consumer, sds_ring->num_desc); 22298c2ecf20Sopenharmony_ci sds_ring->consumer = consumer; 22308c2ecf20Sopenharmony_ci writel(consumer, sds_ring->crb_sts_consumer); 22318c2ecf20Sopenharmony_ci} 2232