Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/arch/arm64/boot/dts/renesas/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 1 kB image not shown  

Quelle  qlcnic_io.c   Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * QLogic qlcnic NIC Driver
 * Copyright (c) 2009-2013 QLogic Corporation
 */


#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <linux/ipv6.h>
#include <net/checksum.h>
#include <linux/printk.h>
#include <linux/jiffies.h>

#include "qlcnic.h"

#define QLCNIC_TX_ETHER_PKT  0x01
#define QLCNIC_TX_TCP_PKT  0x02
#define QLCNIC_TX_UDP_PKT  0x03
#define QLCNIC_TX_IP_PKT  0x04
#define QLCNIC_TX_TCP_LSO  0x05
#define QLCNIC_TX_TCP_LSO6  0x06
#define QLCNIC_TX_ENCAP_PKT  0x07
#define QLCNIC_TX_ENCAP_LSO  0x08
#define QLCNIC_TX_TCPV6_PKT  0x0b
#define QLCNIC_TX_UDPV6_PKT  0x0c

#define QLCNIC_FLAGS_VLAN_TAGGED 0x10
#define QLCNIC_FLAGS_VLAN_OOB  0x40

#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \
 (cmd_desc)->vlan_TCI = cpu_to_le16(v);
#define qlcnic_set_cmd_desc_port(cmd_desc, var) \
 ((cmd_desc)->port_ctxid |= ((var) & 0x0F))
#define qlcnic_set_cmd_desc_ctxid(cmd_desc, var) \
 ((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0))

#define qlcnic_set_tx_port(_desc, _port) \
 ((_desc)->port_ctxid = ((_port) & 0xf) | (((_port) << 4) & 0xf0))

#define qlcnic_set_tx_flags_opcode(_desc, _flags, _opcode) \
 ((_desc)->flags_opcode |= \
 cpu_to_le16(((_flags) & 0x7f) | (((_opcode) & 0x3f) << 7)))

#define qlcnic_set_tx_frags_len(_desc, _frags, _len) \
 ((_desc)->nfrags__length = \
 cpu_to_le32(((_frags) & 0xff) | (((_len) & 0xffffff) << 8)))

/* owner bits of status_desc */
#define STATUS_OWNER_HOST (0x1ULL << 56)
#define STATUS_OWNER_PHANTOM (0x2ULL << 56)

/* Status descriptor:
   0-3 port, 4-7 status, 8-11 type, 12-27 total_length
   28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset
   53-55 desc_cnt, 56-57 owner, 58-63 opcode
 */

#define qlcnic_get_sts_port(sts_data) \
 ((sts_data) & 0x0F)
#define qlcnic_get_sts_status(sts_data) \
 (((sts_data) >> 4) & 0x0F)
#define qlcnic_get_sts_type(sts_data) \
 (((sts_data) >> 8) & 0x0F)
#define qlcnic_get_sts_totallength(sts_data) \
 (((sts_data) >> 12) & 0xFFFF)
#define qlcnic_get_sts_refhandle(sts_data) \
 (((sts_data) >> 28) & 0xFFFF)
#define qlcnic_get_sts_prot(sts_data) \
 (((sts_data) >> 44) & 0x0F)
#define qlcnic_get_sts_pkt_offset(sts_data) \
 (((sts_data) >> 48) & 0x1F)
#define qlcnic_get_sts_desc_cnt(sts_data) \
 (((sts_data) >> 53) & 0x7)
#define qlcnic_get_sts_opcode(sts_data) \
 (((sts_data) >> 58) & 0x03F)

#define qlcnic_get_lro_sts_refhandle(sts_data)  \
 ((sts_data) & 0x07FFF)
#define qlcnic_get_lro_sts_length(sts_data) \
 (((sts_data) >> 16) & 0x0FFFF)
#define qlcnic_get_lro_sts_l2_hdr_offset(sts_data) \
 (((sts_data) >> 32) & 0x0FF)
#define qlcnic_get_lro_sts_l4_hdr_offset(sts_data) \
 (((sts_data) >> 40) & 0x0FF)
#define qlcnic_get_lro_sts_timestamp(sts_data) \
 (((sts_data) >> 48) & 0x1)
#define qlcnic_get_lro_sts_type(sts_data) \
 (((sts_data) >> 49) & 0x7)
#define qlcnic_get_lro_sts_push_flag(sts_data)  \
 (((sts_data) >> 52) & 0x1)
#define qlcnic_get_lro_sts_seq_number(sts_data)  \
 ((sts_data) & 0x0FFFFFFFF)
#define qlcnic_get_lro_sts_mss(sts_data1)  \
 ((sts_data1 >> 32) & 0x0FFFF)

#define qlcnic_83xx_get_lro_sts_mss(sts) ((sts) & 0xffff)

/* opcode field in status_desc */
#define QLCNIC_SYN_OFFLOAD 0x03
#define QLCNIC_RXPKT_DESC   0x04
#define QLCNIC_OLD_RXPKT_DESC 0x3f
#define QLCNIC_RESPONSE_DESC 0x05
#define QLCNIC_LRO_DESC   0x12

#define QLCNIC_TCP_HDR_SIZE  20
#define QLCNIC_TCP_TS_OPTION_SIZE 12
#define QLCNIC_FETCH_RING_ID(handle) ((handle) >> 63)
#define QLCNIC_DESC_OWNER_FW  cpu_to_le64(STATUS_OWNER_PHANTOM)

#define QLCNIC_TCP_TS_HDR_SIZE (QLCNIC_TCP_HDR_SIZE + QLCNIC_TCP_TS_OPTION_SIZE)

/* for status field in status_desc */
#define STATUS_CKSUM_LOOP 0
#define STATUS_CKSUM_OK  2

#define qlcnic_83xx_pktln(sts)  ((sts >> 32) & 0x3FFF)
#define qlcnic_83xx_hndl(sts)  ((sts >> 48) & 0x7FFF)
#define qlcnic_83xx_csum_status(sts) ((sts >> 39) & 7)
#define qlcnic_83xx_opcode(sts) ((sts >> 42) & 0xF)
#define qlcnic_83xx_vlan_tag(sts) (((sts) >> 48) & 0xFFFF)
#define qlcnic_83xx_lro_pktln(sts) (((sts) >> 32) & 0x3FFF)
#define qlcnic_83xx_l2_hdr_off(sts) (((sts) >> 16) & 0xFF)
#define qlcnic_83xx_l4_hdr_off(sts) (((sts) >> 24) & 0xFF)
#define qlcnic_83xx_pkt_cnt(sts) (((sts) >> 16) & 0x7)
#define qlcnic_83xx_is_tstamp(sts) (((sts) >> 40) & 1)
#define qlcnic_83xx_is_psh_bit(sts) (((sts) >> 41) & 1)
#define qlcnic_83xx_is_ip_align(sts) (((sts) >> 46) & 1)
#define qlcnic_83xx_has_vlan_tag(sts) (((sts) >> 47) & 1)

static int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring,
       int max);

static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *,
         struct qlcnic_host_rds_ring *,
         u16, u16);

static inline u8 qlcnic_mac_hash(u64 mac, u16 vlan)
{
 return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff) ^ (vlan & 0xff));
}

static inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter,
     u16 handle, u8 ring_id)
{
 if (qlcnic_83xx_check(adapter))
  return handle | (ring_id << 15);
 else
  return handle;
}

static inline int qlcnic_82xx_is_lb_pkt(u64 sts_data)
{
 return (qlcnic_get_sts_status(sts_data) == STATUS_CKSUM_LOOP) ? 1 : 0;
}

static void qlcnic_delete_rx_list_mac(struct qlcnic_adapter *adapter,
          struct qlcnic_filter *fil,
          void *addr, u16 vlan_id)
{
 int ret;
 u8 op;

 op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
 ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op);
 if (ret)
  return;

 op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL;
 ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op);
 if (!ret) {
  hlist_del(&fil->fnode);
  adapter->rx_fhash.fnum--;
 }
}

static struct qlcnic_filter *qlcnic_find_mac_filter(struct hlist_head *head,
          void *addr, u16 vlan_id)
{
 struct qlcnic_filter *tmp_fil = NULL;
 struct hlist_node *n;

 hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
  if (ether_addr_equal(tmp_fil->faddr, addr) &&
      tmp_fil->vlan_id == vlan_id)
   return tmp_fil;
 }

 return NULL;
}

static void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter,
     struct sk_buff *skb, int loopback_pkt, u16 vlan_id)
{
 struct ethhdr *phdr = (struct ethhdr *)(skb->data);
 struct qlcnic_filter *fil, *tmp_fil;
 struct hlist_head *head;
 unsigned long time;
 u64 src_addr = 0;
 u8 hindex, op;
 int ret;

 if (!qlcnic_sriov_pf_check(adapter) || (vlan_id == 0xffff))
  vlan_id = 0;

 memcpy(&src_addr, phdr->h_source, ETH_ALEN);
 hindex = qlcnic_mac_hash(src_addr, vlan_id) &
   (adapter->fhash.fbucket_size - 1);

 if (loopback_pkt) {
  if (adapter->rx_fhash.fnum >= adapter->rx_fhash.fmax)
   return;

  head = &(adapter->rx_fhash.fhead[hindex]);

  tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
  if (tmp_fil) {
   time = tmp_fil->ftime;
   if (time_after(jiffies, QLCNIC_READD_AGE * HZ + time))
    tmp_fil->ftime = jiffies;
   return;
  }

  fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
  if (!fil)
   return;

  fil->ftime = jiffies;
  memcpy(fil->faddr, &src_addr, ETH_ALEN);
  fil->vlan_id = vlan_id;
  spin_lock(&adapter->rx_mac_learn_lock);
  hlist_add_head(&(fil->fnode), head);
  adapter->rx_fhash.fnum++;
  spin_unlock(&adapter->rx_mac_learn_lock);
 } else {
  head = &adapter->fhash.fhead[hindex];

  spin_lock(&adapter->mac_learn_lock);

  tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
  if (tmp_fil) {
   op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL;
   ret = qlcnic_sre_macaddr_change(adapter,
       (u8 *)&src_addr,
       vlan_id, op);
   if (!ret) {
    hlist_del(&tmp_fil->fnode);
    adapter->fhash.fnum--;
   }

   spin_unlock(&adapter->mac_learn_lock);

   return;
  }

  spin_unlock(&adapter->mac_learn_lock);

  head = &adapter->rx_fhash.fhead[hindex];

  spin_lock(&adapter->rx_mac_learn_lock);

  tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
  if (tmp_fil)
   qlcnic_delete_rx_list_mac(adapter, tmp_fil, &src_addr,
        vlan_id);

  spin_unlock(&adapter->rx_mac_learn_lock);
 }
}

void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
          u16 vlan_id, struct qlcnic_host_tx_ring *tx_ring)
{
 struct cmd_desc_type0 *hwdesc;
 struct qlcnic_nic_req *req;
 struct qlcnic_mac_req *mac_req;
 struct qlcnic_vlan_req *vlan_req;
 u32 producer;
 u64 word;

 producer = tx_ring->producer;
 hwdesc = &tx_ring->desc_head[tx_ring->producer];

 req = (struct qlcnic_nic_req *)hwdesc;
 memset(req, 0, sizeof(struct qlcnic_nic_req));
 req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23);

 word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16);
 req->req_hdr = cpu_to_le64(word);

 mac_req = (struct qlcnic_mac_req *)&(req->words[0]);
 mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
 memcpy(mac_req->mac_addr, uaddr, ETH_ALEN);

 vlan_req = (struct qlcnic_vlan_req *)&req->words[1];
 vlan_req->vlan_id = cpu_to_le16(vlan_id);

 tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
 smp_mb();
}

static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
          struct cmd_desc_type0 *first_desc,
          struct sk_buff *skb,
          struct qlcnic_host_tx_ring *tx_ring)
{
 struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
 struct ethhdr *phdr = (struct ethhdr *)(skb->data);
 u16 protocol = ntohs(skb->protocol);
 struct qlcnic_filter *fil, *tmp_fil;
 struct hlist_head *head;
 struct hlist_node *n;
 u64 src_addr = 0;
 u16 vlan_id = 0;
 u8 hindex, hval;

 if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
  return;

 if (adapter->flags & QLCNIC_VLAN_FILTERING) {
  if (protocol == ETH_P_8021Q) {
   vh = skb_vlan_eth_hdr(skb);
   vlan_id = ntohs(vh->h_vlan_TCI);
  } else if (skb_vlan_tag_present(skb)) {
   vlan_id = skb_vlan_tag_get(skb);
  }
 }

 memcpy(&src_addr, phdr->h_source, ETH_ALEN);
 hval = qlcnic_mac_hash(src_addr, vlan_id);
 hindex = hval & (adapter->fhash.fbucket_size - 1);
 head = &(adapter->fhash.fhead[hindex]);

 hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
  if (ether_addr_equal(tmp_fil->faddr, (u8 *)&src_addr) &&
      tmp_fil->vlan_id == vlan_id) {
   if (time_is_before_jiffies(QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
    qlcnic_change_filter(adapter, &src_addr,
           vlan_id, tx_ring);
   tmp_fil->ftime = jiffies;
   return;
  }
 }

 if (unlikely(adapter->fhash.fnum >= adapter->fhash.fmax)) {
  adapter->stats.mac_filter_limit_overrun++;
  return;
 }

 fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
 if (!fil)
  return;

 qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring);
 fil->ftime = jiffies;
 fil->vlan_id = vlan_id;
 memcpy(fil->faddr, &src_addr, ETH_ALEN);
 spin_lock(&adapter->mac_learn_lock);
 hlist_add_head(&(fil->fnode), head);
 adapter->fhash.fnum++;
 spin_unlock(&adapter->mac_learn_lock);
}

#define QLCNIC_ENCAP_VXLAN_PKT  BIT_0
#define QLCNIC_ENCAP_OUTER_L3_IP6 BIT_1
#define QLCNIC_ENCAP_INNER_L3_IP6 BIT_2
#define QLCNIC_ENCAP_INNER_L4_UDP BIT_3
#define QLCNIC_ENCAP_DO_L3_CSUM  BIT_4
#define QLCNIC_ENCAP_DO_L4_CSUM  BIT_5

static int qlcnic_tx_encap_pkt(struct qlcnic_adapter *adapter,
          struct cmd_desc_type0 *first_desc,
          struct sk_buff *skb,
          struct qlcnic_host_tx_ring *tx_ring)
{
 u8 opcode = 0, inner_hdr_len = 0, outer_hdr_len = 0, total_hdr_len = 0;
 int copied, copy_len, descr_size;
 u32 producer = tx_ring->producer;
 struct cmd_desc_type0 *hwdesc;
 u16 flags = 0, encap_descr = 0;

 opcode = QLCNIC_TX_ETHER_PKT;
 encap_descr = QLCNIC_ENCAP_VXLAN_PKT;

 if (skb_is_gso(skb)) {
  inner_hdr_len = skb_inner_transport_header(skb) +
    inner_tcp_hdrlen(skb) -
    skb_inner_mac_header(skb);

  /* VXLAN header size = 8 */
  outer_hdr_len = skb_transport_offset(skb) + 8 +
    sizeof(struct udphdr);
  first_desc->outer_hdr_length = outer_hdr_len;
  total_hdr_len = inner_hdr_len + outer_hdr_len;
  encap_descr |= QLCNIC_ENCAP_DO_L3_CSUM |
          QLCNIC_ENCAP_DO_L4_CSUM;
  first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
  first_desc->hdr_length = inner_hdr_len;

  /* Copy inner and outer headers in Tx descriptor(s)
 * If total_hdr_len > cmd_desc_type0, use multiple
 * descriptors
 */

  copied = 0;
  descr_size = (int)sizeof(struct cmd_desc_type0);
  while (copied < total_hdr_len) {
   copy_len = min(descr_size, (total_hdr_len - copied));
   hwdesc = &tx_ring->desc_head[producer];
   tx_ring->cmd_buf_arr[producer].skb = NULL;
   skb_copy_from_linear_data_offset(skb, copied,
        (char *)hwdesc,
        copy_len);
   copied += copy_len;
   producer = get_next_index(producer, tx_ring->num_desc);
  }

  tx_ring->producer = producer;

  /* Make sure updated tx_ring->producer is visible
 * for qlcnic_tx_avail()
 */

  smp_mb();
  adapter->stats.encap_lso_frames++;

  opcode = QLCNIC_TX_ENCAP_LSO;
 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
  if (inner_ip_hdr(skb)->version == 6) {
   if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
    encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
  } else {
   if (inner_ip_hdr(skb)->protocol == IPPROTO_UDP)
    encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
  }

  adapter->stats.encap_tx_csummed++;
  opcode = QLCNIC_TX_ENCAP_PKT;
 }

 /* Prepare first 16 bits of byte offset 16 of Tx descriptor */
 if (ip_hdr(skb)->version == 6)
  encap_descr |= QLCNIC_ENCAP_OUTER_L3_IP6;

 /* outer IP header's size in 32bit words size*/
 encap_descr |= (skb_network_header_len(skb) >> 2) << 6;

 /* outer IP header offset */
 encap_descr |= skb_network_offset(skb) << 10;
 first_desc->encap_descr = cpu_to_le16(encap_descr);

 first_desc->tcp_hdr_offset = skb_inner_transport_offset(skb);
 first_desc->ip_hdr_offset = skb_inner_network_offset(skb);

 qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);

 return 0;
}

static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
    struct cmd_desc_type0 *first_desc, struct sk_buff *skb,
    struct qlcnic_host_tx_ring *tx_ring)
{
 u8 l4proto, opcode = 0, hdr_len = 0, tag_vlan = 0;
 u16 flags = 0, vlan_tci = 0;
 int copied, offset, copy_len, size;
 struct cmd_desc_type0 *hwdesc;
 struct vlan_ethhdr *vh;
 u16 protocol = ntohs(skb->protocol);
 u32 producer = tx_ring->producer;

 if (protocol == ETH_P_8021Q) {
  vh = skb_vlan_eth_hdr(skb);
  flags = QLCNIC_FLAGS_VLAN_TAGGED;
  vlan_tci = ntohs(vh->h_vlan_TCI);
  protocol = ntohs(vh->h_vlan_encapsulated_proto);
  tag_vlan = 1;
 } else if (skb_vlan_tag_present(skb)) {
  flags = QLCNIC_FLAGS_VLAN_OOB;
  vlan_tci = skb_vlan_tag_get(skb);
  tag_vlan = 1;
 }
 if (unlikely(adapter->tx_pvid)) {
  if (tag_vlan && !(adapter->flags & QLCNIC_TAGGING_ENABLED))
   return -EIO;
  if (tag_vlan && (adapter->flags & QLCNIC_TAGGING_ENABLED))
   goto set_flags;

  flags = QLCNIC_FLAGS_VLAN_OOB;
  vlan_tci = adapter->tx_pvid;
 }
set_flags:
 qlcnic_set_tx_vlan_tci(first_desc, vlan_tci);
 qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);

 if (*(skb->data) & BIT_0) {
  flags |= BIT_0;
  memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
 }
 opcode = QLCNIC_TX_ETHER_PKT;
 if (skb_is_gso(skb)) {
  hdr_len = skb_tcp_all_headers(skb);
  first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
  first_desc->hdr_length = hdr_len;
  opcode = (protocol == ETH_P_IPV6) ? QLCNIC_TX_TCP_LSO6 :
          QLCNIC_TX_TCP_LSO;

  /* For LSO, we need to copy the MAC/IP/TCP headers into
* the descriptor ring */

  copied = 0;
  offset = 2;

  if (flags & QLCNIC_FLAGS_VLAN_OOB) {
   first_desc->hdr_length += VLAN_HLEN;
   first_desc->tcp_hdr_offset = VLAN_HLEN;
   first_desc->ip_hdr_offset = VLAN_HLEN;

   /* Only in case of TSO on vlan device */
   flags |= QLCNIC_FLAGS_VLAN_TAGGED;

   /* Create a TSO vlan header template for firmware */
   hwdesc = &tx_ring->desc_head[producer];
   tx_ring->cmd_buf_arr[producer].skb = NULL;

   copy_len = min((int)sizeof(struct cmd_desc_type0) -
           offset, hdr_len + VLAN_HLEN);

   vh = (struct vlan_ethhdr *)((char *) hwdesc + 2);
   skb_copy_from_linear_data(skb, vh, 12);
   vh->h_vlan_proto = htons(ETH_P_8021Q);
   vh->h_vlan_TCI = htons(vlan_tci);

   skb_copy_from_linear_data_offset(skb, 12,
        (char *)vh + 16,
        copy_len - 16);
   copied = copy_len - VLAN_HLEN;
   offset = 0;
   producer = get_next_index(producer, tx_ring->num_desc);
  }

  while (copied < hdr_len) {
   size = (int)sizeof(struct cmd_desc_type0) - offset;
   copy_len = min(size, (hdr_len - copied));
   hwdesc = &tx_ring->desc_head[producer];
   tx_ring->cmd_buf_arr[producer].skb = NULL;
   skb_copy_from_linear_data_offset(skb, copied,
        (char *)hwdesc +
        offset, copy_len);
   copied += copy_len;
   offset = 0;
   producer = get_next_index(producer, tx_ring->num_desc);
  }

  tx_ring->producer = producer;
  smp_mb();
  adapter->stats.lso_frames++;

 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
  if (protocol == ETH_P_IP) {
   l4proto = ip_hdr(skb)->protocol;

   if (l4proto == IPPROTO_TCP)
    opcode = QLCNIC_TX_TCP_PKT;
   else if (l4proto == IPPROTO_UDP)
    opcode = QLCNIC_TX_UDP_PKT;
  } else if (protocol == ETH_P_IPV6) {
   l4proto = ipv6_hdr(skb)->nexthdr;

   if (l4proto == IPPROTO_TCP)
    opcode = QLCNIC_TX_TCPV6_PKT;
   else if (l4proto == IPPROTO_UDP)
    opcode = QLCNIC_TX_UDPV6_PKT;
  }
 }
 first_desc->tcp_hdr_offset += skb_transport_offset(skb);
 first_desc->ip_hdr_offset += skb_network_offset(skb);
 qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);

 return 0;
}

static int qlcnic_map_tx_skb(struct pci_dev *pdev, struct sk_buff *skb,
        struct qlcnic_cmd_buffer *pbuf)
{
 struct qlcnic_skb_frag *nf;
 skb_frag_t *frag;
 int i, nr_frags;
 dma_addr_t map;

 nr_frags = skb_shinfo(skb)->nr_frags;
 nf = &pbuf->frag_array[0];

 map = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb),
        DMA_TO_DEVICE);
 if (dma_mapping_error(&pdev->dev, map))
  goto out_err;

 nf->dma = map;
 nf->length = skb_headlen(skb);

 for (i = 0; i < nr_frags; i++) {
  frag = &skb_shinfo(skb)->frags[i];
  nf = &pbuf->frag_array[i+1];
  map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
           DMA_TO_DEVICE);
  if (dma_mapping_error(&pdev->dev, map))
   goto unwind;

  nf->dma = map;
  nf->length = skb_frag_size(frag);
 }

 return 0;

unwind:
 while (--i >= 0) {
  nf = &pbuf->frag_array[i+1];
  dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE);
 }

 nf = &pbuf->frag_array[0];
 dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE);

out_err:
 return -ENOMEM;
}

static void qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb,
     struct qlcnic_cmd_buffer *pbuf)
{
 struct qlcnic_skb_frag *nf = &pbuf->frag_array[0];
 int i, nr_frags = skb_shinfo(skb)->nr_frags;

 for (i = 0; i < nr_frags; i++) {
  nf = &pbuf->frag_array[i+1];
  dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE);
 }

 nf = &pbuf->frag_array[0];
 dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE);
 pbuf->skb = NULL;
}

static inline void qlcnic_clear_cmddesc(u64 *desc)
{
 desc[0] = 0ULL;
 desc[2] = 0ULL;
 desc[7] = 0ULL;
}

netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_cmd_buffer *pbuf;
 struct qlcnic_skb_frag *buffrag;
 struct cmd_desc_type0 *hwdesc, *first_desc;
 struct pci_dev *pdev;
 struct ethhdr *phdr;
 int i, k, frag_count, delta = 0;
 u32 producer, num_txd;
 u16 protocol;
 bool l4_is_udp = false;

 if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
  netif_tx_stop_all_queues(netdev);
  return NETDEV_TX_BUSY;
 }

 if (adapter->flags & QLCNIC_MACSPOOF) {
  phdr = (struct ethhdr *)skb->data;
  if (!ether_addr_equal(phdr->h_source, adapter->mac_addr))
   goto drop_packet;
 }

 tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
 num_txd = tx_ring->num_desc;

 frag_count = skb_shinfo(skb)->nr_frags + 1;

 /* 14 frags supported for normal packet and
 * 32 frags supported for TSO packet
 */

 if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
  for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
   delta += skb_frag_size(&skb_shinfo(skb)->frags[i]);

  if (!__pskb_pull_tail(skb, delta))
   goto drop_packet;

  frag_count = 1 + skb_shinfo(skb)->nr_frags;
 }

 if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) {
  netif_tx_stop_queue(tx_ring->txq);
  if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
   netif_tx_start_queue(tx_ring->txq);
  } else {
   tx_ring->tx_stats.xmit_off++;
   return NETDEV_TX_BUSY;
  }
 }

 producer = tx_ring->producer;
 pbuf = &tx_ring->cmd_buf_arr[producer];
 pdev = adapter->pdev;
 first_desc = &tx_ring->desc_head[producer];
 hwdesc = &tx_ring->desc_head[producer];
 qlcnic_clear_cmddesc((u64 *)hwdesc);

 if (qlcnic_map_tx_skb(pdev, skb, pbuf)) {
  adapter->stats.tx_dma_map_error++;
  goto drop_packet;
 }

 pbuf->skb = skb;
 pbuf->frag_count = frag_count;

 qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len);
 qlcnic_set_tx_port(first_desc, adapter->portnum);

 for (i = 0; i < frag_count; i++) {
  k = i % 4;

  if ((k == 0) && (i > 0)) {
   /* move to next desc.*/
   producer = get_next_index(producer, num_txd);
   hwdesc = &tx_ring->desc_head[producer];
   qlcnic_clear_cmddesc((u64 *)hwdesc);
   tx_ring->cmd_buf_arr[producer].skb = NULL;
  }

  buffrag = &pbuf->frag_array[i];
  hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length);
  switch (k) {
  case 0:
   hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
   break;
  case 1:
   hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma);
   break;
  case 2:
   hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma);
   break;
  case 3:
   hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma);
   break;
  }
 }

 tx_ring->producer = get_next_index(producer, num_txd);
 smp_mb();

 protocol = ntohs(skb->protocol);
 if (protocol == ETH_P_IP)
  l4_is_udp = ip_hdr(skb)->protocol == IPPROTO_UDP;
 else if (protocol == ETH_P_IPV6)
  l4_is_udp = ipv6_hdr(skb)->nexthdr == IPPROTO_UDP;

 /* Check if it is a VXLAN packet */
 if (!skb->encapsulation || !l4_is_udp ||
     !qlcnic_encap_tx_offload(adapter)) {
  if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb,
        tx_ring)))
   goto unwind_buff;
 } else {
  if (unlikely(qlcnic_tx_encap_pkt(adapter, first_desc,
       skb, tx_ring)))
   goto unwind_buff;
 }

 if (adapter->drv_mac_learn)
  qlcnic_send_filter(adapter, first_desc, skb, tx_ring);

 tx_ring->tx_stats.tx_bytes += skb->len;
 tx_ring->tx_stats.xmit_called++;

 /* Ensure writes are complete before HW fetches Tx descriptors */
 wmb();
 qlcnic_update_cmd_producer(tx_ring);

 return NETDEV_TX_OK;

unwind_buff:
 qlcnic_unmap_buffers(pdev, skb, pbuf);
drop_packet:
 adapter->stats.txdropped++;
 dev_kfree_skb_any(skb);
 return NETDEV_TX_OK;
}

void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup)
{
 struct net_device *netdev = adapter->netdev;

 if (adapter->ahw->linkup && !linkup) {
  netdev_info(netdev, "NIC Link is down\n");
  adapter->ahw->linkup = 0;
  netif_carrier_off(netdev);
 } else if (!adapter->ahw->linkup && linkup) {
  adapter->ahw->linkup = 1;

  /* Do not advertise Link up to the stack if device
 * is in loopback mode
 */

  if (qlcnic_83xx_check(adapter) && adapter->ahw->lb_mode) {
   netdev_info(netdev, "NIC Link is up for loopback test\n");
   return;
  }

  netdev_info(netdev, "NIC Link is up\n");
  netif_carrier_on(netdev);
 }
}

static int qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter,
          struct qlcnic_host_rds_ring *rds_ring,
          struct qlcnic_rx_buffer *buffer)
{
 struct sk_buff *skb;
 dma_addr_t dma;
 struct pci_dev *pdev = adapter->pdev;

 skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size);
 if (!skb) {
  adapter->stats.skb_alloc_failure++;
  return -ENOMEM;
 }

 skb_reserve(skb, NET_IP_ALIGN);
 dma = dma_map_single(&pdev->dev, skb->data, rds_ring->dma_size,
        DMA_FROM_DEVICE);

 if (dma_mapping_error(&pdev->dev, dma)) {
  adapter->stats.rx_dma_map_error++;
  dev_kfree_skb_any(skb);
  return -ENOMEM;
 }

 buffer->skb = skb;
 buffer->dma = dma;

 return 0;
}

static void qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
     struct qlcnic_host_rds_ring *rds_ring,
     u8 ring_id)
{
 struct rcv_desc *pdesc;
 struct qlcnic_rx_buffer *buffer;
 int  count = 0;
 uint32_t producer, handle;
 struct list_head *head;

 if (!spin_trylock(&rds_ring->lock))
  return;

 producer = rds_ring->producer;
 head = &rds_ring->free_list;
 while (!list_empty(head)) {
  buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);

  if (!buffer->skb) {
   if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
    break;
  }
  count++;
  list_del(&buffer->list);

  /* make a rcv descriptor  */
  pdesc = &rds_ring->desc_head[producer];
  handle = qlcnic_get_ref_handle(adapter,
            buffer->ref_handle, ring_id);
  pdesc->reference_handle = cpu_to_le16(handle);
  pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
  pdesc->addr_buffer = cpu_to_le64(buffer->dma);
  producer = get_next_index(producer, rds_ring->num_desc);
 }
 if (count) {
  rds_ring->producer = producer;
  writel((producer - 1) & (rds_ring->num_desc - 1),
         rds_ring->crb_rcv_producer);
 }
 spin_unlock(&rds_ring->lock);
}

static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
       struct qlcnic_host_tx_ring *tx_ring,
       int budget)
{
 u32 sw_consumer, hw_consumer;
 int i, done, count = 0;
 struct qlcnic_cmd_buffer *buffer;
 struct pci_dev *pdev = adapter->pdev;
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_skb_frag *frag;

 if (!spin_trylock(&tx_ring->tx_clean_lock))
  return 1;

 sw_consumer = tx_ring->sw_consumer;
 hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));

 while (sw_consumer != hw_consumer) {
  buffer = &tx_ring->cmd_buf_arr[sw_consumer];
  if (buffer->skb) {
   frag = &buffer->frag_array[0];
   dma_unmap_single(&pdev->dev, frag->dma, frag->length,
      DMA_TO_DEVICE);
   frag->dma = 0ULL;
   for (i = 1; i < buffer->frag_count; i++) {
    frag++;
    dma_unmap_page(&pdev->dev, frag->dma,
            frag->length, DMA_TO_DEVICE);
    frag->dma = 0ULL;
   }
   tx_ring->tx_stats.xmit_finished++;
   dev_kfree_skb_any(buffer->skb);
   buffer->skb = NULL;
  }

  sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc);
  if (++count >= budget)
   break;
 }

 tx_ring->sw_consumer = sw_consumer;

 if (count && netif_running(netdev)) {
  smp_mb();
  if (netif_tx_queue_stopped(tx_ring->txq) &&
      netif_carrier_ok(netdev)) {
   if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
    netif_tx_wake_queue(tx_ring->txq);
    tx_ring->tx_stats.xmit_on++;
   }
  }
  adapter->tx_timeo_cnt = 0;
 }
 /*
 * If everything is freed up to consumer then check if the ring is full
 * If the ring is full then check if more needs to be freed and
 * schedule the call back again.
 *
 * This happens when there are 2 CPUs. One could be freeing and the
 * other filling it. If the ring is full when we get out of here and
 * the card has already interrupted the host then the host can miss the
 * interrupt.
 *
 * There is still a possible race condition and the host could miss an
 * interrupt. The card has to take care of this.
 */

 hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
 done = (sw_consumer == hw_consumer);

 spin_unlock(&tx_ring->tx_clean_lock);

 return done;
}

static int qlcnic_poll(struct napi_struct *napi, int budget)
{
 int tx_complete, work_done;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_adapter *adapter;
 struct qlcnic_host_tx_ring *tx_ring;

 sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
 adapter = sds_ring->adapter;
 tx_ring = sds_ring->tx_ring;

 tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring,
           budget);
 work_done = qlcnic_process_rcv_ring(sds_ring, budget);

 /* Check if we need a repoll */
 if (!tx_complete)
  work_done = budget;

 if (work_done < budget) {
  napi_complete_done(&sds_ring->napi, work_done);
  if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
   qlcnic_enable_sds_intr(adapter, sds_ring);
   qlcnic_enable_tx_intr(adapter, tx_ring);
  }
 }

 return work_done;
}

static int qlcnic_tx_poll(struct napi_struct *napi, int budget)
{
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_adapter *adapter;
 int work_done;

 tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi);
 adapter = tx_ring->adapter;

 work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
 if (work_done) {
  napi_complete(&tx_ring->napi);
  if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
   qlcnic_enable_tx_intr(adapter, tx_ring);
 } else {
  /* As qlcnic_process_cmd_ring() returned 0, we need a repoll*/
  work_done = budget;
 }

 return work_done;
}

static int qlcnic_rx_poll(struct napi_struct *napi, int budget)
{
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_adapter *adapter;
 int work_done;

 sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
 adapter = sds_ring->adapter;

 work_done = qlcnic_process_rcv_ring(sds_ring, budget);

 if (work_done < budget) {
  napi_complete_done(&sds_ring->napi, work_done);
  if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
   qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 return work_done;
}

static void qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
        struct qlcnic_fw_msg *msg)
{
 u32 cable_OUI;
 u16 cable_len, link_speed;
 u8  link_status, module, duplex, autoneg, lb_status = 0;
 struct net_device *netdev = adapter->netdev;

 adapter->ahw->has_link_events = 1;

 cable_OUI = msg->body[1] & 0xffffffff;
 cable_len = (msg->body[1] >> 32) & 0xffff;
 link_speed = (msg->body[1] >> 48) & 0xffff;

 link_status = msg->body[2] & 0xff;
 duplex = (msg->body[2] >> 16) & 0xff;
 autoneg = (msg->body[2] >> 24) & 0xff;
 lb_status = (msg->body[2] >> 32) & 0x3;

 module = (msg->body[2] >> 8) & 0xff;
 if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE)
  dev_info(&netdev->dev,
    "unsupported cable: OUI 0x%x, length %d\n",
    cable_OUI, cable_len);
 else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN)
  dev_info(&netdev->dev, "unsupported cable length %d\n",
    cable_len);

 if (!link_status && (lb_status == QLCNIC_ILB_MODE ||
     lb_status == QLCNIC_ELB_MODE))
  adapter->ahw->loopback_state |= QLCNIC_LINKEVENT;

 qlcnic_advert_link_change(adapter, link_status);

 if (duplex == LINKEVENT_FULL_DUPLEX)
  adapter->ahw->link_duplex = DUPLEX_FULL;
 else
  adapter->ahw->link_duplex = DUPLEX_HALF;

 adapter->ahw->module_type = module;
 adapter->ahw->link_autoneg = autoneg;

 if (link_status) {
  adapter->ahw->link_speed = link_speed;
 } else {
  adapter->ahw->link_speed = SPEED_UNKNOWN;
  adapter->ahw->link_duplex = DUPLEX_UNKNOWN;
 }
}

static void qlcnic_handle_fw_message(int desc_cnt, int index,
         struct qlcnic_host_sds_ring *sds_ring)
{
 struct qlcnic_fw_msg msg;
 struct status_desc *desc;
 struct qlcnic_adapter *adapter;
 struct device *dev;
 int i = 0, opcode, ret;

 while (desc_cnt > 0 && i < 8) {
  desc = &sds_ring->desc_head[index];
  msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]);
  msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]);

  index = get_next_index(index, sds_ring->num_desc);
  desc_cnt--;
 }

 adapter = sds_ring->adapter;
 dev = &adapter->pdev->dev;
 opcode = qlcnic_get_nic_msg_opcode(msg.body[0]);

 switch (opcode) {
 case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE:
  qlcnic_handle_linkevent(adapter, &msg);
  break;
 case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK:
  ret = (u32)(msg.body[1]);
  switch (ret) {
  case 0:
   adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE;
   break;
  case 1:
   dev_info(dev, "loopback already in progress\n");
   adapter->ahw->diag_cnt = -EINPROGRESS;
   break;
  case 2:
   dev_info(dev, "loopback cable is not connected\n");
   adapter->ahw->diag_cnt = -ENODEV;
   break;
  default:
   dev_info(dev,
     "loopback configure request failed, err %x\n",
     ret);
   adapter->ahw->diag_cnt = -EIO;
   break;
  }
  break;
 case QLCNIC_C2H_OPCODE_GET_DCB_AEN:
  qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg);
  break;
 default:
  break;
 }
}

static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter,
         struct qlcnic_host_rds_ring *ring,
         u16 index, u16 cksum)
{
 struct qlcnic_rx_buffer *buffer;
 struct sk_buff *skb;

 buffer = &ring->rx_buf_arr[index];
 if (unlikely(buffer->skb == NULL)) {
  WARN_ON(1);
  return NULL;
 }

 dma_unmap_single(&adapter->pdev->dev, buffer->dma, ring->dma_size,
    DMA_FROM_DEVICE);

 skb = buffer->skb;
 if (likely((adapter->netdev->features & NETIF_F_RXCSUM) &&
     (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) {
  adapter->stats.csummed++;
  skb->ip_summed = CHECKSUM_UNNECESSARY;
 } else {
  skb_checksum_none_assert(skb);
 }


 buffer->skb = NULL;

 return skb;
}

static inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter,
       struct sk_buff *skb, u16 *vlan_tag)
{
 struct ethhdr *eth_hdr;

 if (!__vlan_get_tag(skb, vlan_tag)) {
  eth_hdr = (struct ethhdr *)skb->data;
  memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
  skb_pull(skb, VLAN_HLEN);
 }
 if (!adapter->rx_pvid)
  return 0;

 if (*vlan_tag == adapter->rx_pvid) {
  /* Outer vlan tag. Packet should follow non-vlan path */
  *vlan_tag = 0xffff;
  return 0;
 }
 if (adapter->flags & QLCNIC_TAGGING_ENABLED)
  return 0;

 return -EINVAL;
}

static struct qlcnic_rx_buffer *
qlcnic_process_rcv(struct qlcnic_adapter *adapter,
     struct qlcnic_host_sds_ring *sds_ring, int ring,
     u64 sts_data0)
{
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_rx_buffer *buffer;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 int index, length, cksum, pkt_offset, is_lb_pkt;
 u16 vid = 0xffff, t_vid;

 if (unlikely(ring >= adapter->max_rds_rings))
  return NULL;

 rds_ring = &recv_ctx->rds_rings[ring];

 index = qlcnic_get_sts_refhandle(sts_data0);
 if (unlikely(index >= rds_ring->num_desc))
  return NULL;

 buffer = &rds_ring->rx_buf_arr[index];
 length = qlcnic_get_sts_totallength(sts_data0);
 cksum  = qlcnic_get_sts_status(sts_data0);
 pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);

 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
 if (!skb)
  return buffer;

 if (adapter->rx_mac_learn) {
  t_vid = 0;
  is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
  qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
 }

 if (length > rds_ring->skb_size)
  skb_put(skb, rds_ring->skb_size);
 else
  skb_put(skb, length);

 if (pkt_offset)
  skb_pull(skb, pkt_offset);

 if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
  adapter->stats.rxdropped++;
  dev_kfree_skb(skb);
  return buffer;
 }

 skb->protocol = eth_type_trans(skb, netdev);

 if (vid != 0xffff)
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);

 napi_gro_receive(&sds_ring->napi, skb);

 adapter->stats.rx_pkts++;
 adapter->stats.rxbytes += length;

 return buffer;
}

#define QLC_TCP_HDR_SIZE            20
#define QLC_TCP_TS_OPTION_SIZE      12
#define QLC_TCP_TS_HDR_SIZE         (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE)

static struct qlcnic_rx_buffer *
qlcnic_process_lro(struct qlcnic_adapter *adapter,
     int ring, u64 sts_data0, u64 sts_data1)
{
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_rx_buffer *buffer;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 struct iphdr *iph;
 struct ipv6hdr *ipv6h;
 struct tcphdr *th;
 bool push, timestamp;
 int index, l2_hdr_offset, l4_hdr_offset, is_lb_pkt;
 u16 lro_length, length, data_offset, t_vid, vid = 0xffff;
 u32 seq_number;

 if (unlikely(ring >= adapter->max_rds_rings))
  return NULL;

 rds_ring = &recv_ctx->rds_rings[ring];

 index = qlcnic_get_lro_sts_refhandle(sts_data0);
 if (unlikely(index >= rds_ring->num_desc))
  return NULL;

 buffer = &rds_ring->rx_buf_arr[index];

 timestamp = qlcnic_get_lro_sts_timestamp(sts_data0);
 lro_length = qlcnic_get_lro_sts_length(sts_data0);
 l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0);
 l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0);
 push = qlcnic_get_lro_sts_push_flag(sts_data0);
 seq_number = qlcnic_get_lro_sts_seq_number(sts_data1);

 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
 if (!skb)
  return buffer;

 if (adapter->rx_mac_learn) {
  t_vid = 0;
  is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
  qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
 }

 if (timestamp)
  data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE;
 else
  data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE;

 skb_put(skb, lro_length + data_offset);
 skb_pull(skb, l2_hdr_offset);

 if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
  adapter->stats.rxdropped++;
  dev_kfree_skb(skb);
  return buffer;
 }

 skb->protocol = eth_type_trans(skb, netdev);

 if (ntohs(skb->protocol) == ETH_P_IPV6) {
  ipv6h = (struct ipv6hdr *)skb->data;
  th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr));
  length = (th->doff << 2) + lro_length;
  ipv6h->payload_len = htons(length);
 } else {
  iph = (struct iphdr *)skb->data;
  th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
  length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
  csum_replace2(&iph->check, iph->tot_len, htons(length));
  iph->tot_len = htons(length);
 }

 th->psh = push;
 th->seq = htonl(seq_number);
 length = skb->len;

 if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) {
  skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1);
  if (skb->protocol == htons(ETH_P_IPV6))
   skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
  else
   skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
 }

 if (vid != 0xffff)
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
 netif_receive_skb(skb);

 adapter->stats.lro_pkts++;
 adapter->stats.lrobytes += length;

 return buffer;
}

static int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max)
{
 struct qlcnic_host_rds_ring *rds_ring;
 struct qlcnic_adapter *adapter = sds_ring->adapter;
 struct list_head *cur;
 struct status_desc *desc;
 struct qlcnic_rx_buffer *rxbuf;
 int opcode, desc_cnt, count = 0;
 u64 sts_data0, sts_data1;
 u8 ring;
 u32 consumer = sds_ring->consumer;

 while (count < max) {
  desc = &sds_ring->desc_head[consumer];
  sts_data0 = le64_to_cpu(desc->status_desc_data[0]);

  if (!(sts_data0 & STATUS_OWNER_HOST))
   break;

  desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
  opcode = qlcnic_get_sts_opcode(sts_data0);
  switch (opcode) {
  case QLCNIC_RXPKT_DESC:
  case QLCNIC_OLD_RXPKT_DESC:
  case QLCNIC_SYN_OFFLOAD:
   ring = qlcnic_get_sts_type(sts_data0);
   rxbuf = qlcnic_process_rcv(adapter, sds_ring, ring,
         sts_data0);
   break;
  case QLCNIC_LRO_DESC:
   ring = qlcnic_get_lro_sts_type(sts_data0);
   sts_data1 = le64_to_cpu(desc->status_desc_data[1]);
   rxbuf = qlcnic_process_lro(adapter, ring, sts_data0,
         sts_data1);
   break;
  case QLCNIC_RESPONSE_DESC:
   qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
   goto skip;
  default:
   goto skip;
  }
  WARN_ON(desc_cnt > 1);

  if (likely(rxbuf))
   list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
  else
   adapter->stats.null_rxbuf++;
skip:
  for (; desc_cnt > 0; desc_cnt--) {
   desc = &sds_ring->desc_head[consumer];
   desc->status_desc_data[0] = QLCNIC_DESC_OWNER_FW;
   consumer = get_next_index(consumer, sds_ring->num_desc);
  }
  count++;
 }

 for (ring = 0; ring < adapter->max_rds_rings; ring++) {
  rds_ring = &adapter->recv_ctx->rds_rings[ring];
  if (!list_empty(&sds_ring->free_list[ring])) {
   list_for_each(cur, &sds_ring->free_list[ring]) {
    rxbuf = list_entry(cur, struct qlcnic_rx_buffer,
         list);
    qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
   }
   spin_lock(&rds_ring->lock);
   list_splice_tail_init(&sds_ring->free_list[ring],
           &rds_ring->free_list);
   spin_unlock(&rds_ring->lock);
  }

  qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring);
 }

 if (count) {
  sds_ring->consumer = consumer;
  writel(consumer, sds_ring->crb_sts_consumer);
 }

 return count;
}

void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
       struct qlcnic_host_rds_ring *rds_ring, u8 ring_id)
{
 struct rcv_desc *pdesc;
 struct qlcnic_rx_buffer *buffer;
 int count = 0;
 u32 producer, handle;
 struct list_head *head;

 producer = rds_ring->producer;
 head = &rds_ring->free_list;

 while (!list_empty(head)) {

  buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);

  if (!buffer->skb) {
   if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
    break;
  }

  count++;
  list_del(&buffer->list);

  /* make a rcv descriptor  */
  pdesc = &rds_ring->desc_head[producer];
  pdesc->addr_buffer = cpu_to_le64(buffer->dma);
  handle = qlcnic_get_ref_handle(adapter, buffer->ref_handle,
            ring_id);
  pdesc->reference_handle = cpu_to_le16(handle);
  pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
  producer = get_next_index(producer, rds_ring->num_desc);
 }

 if (count) {
  rds_ring->producer = producer;
  writel((producer-1) & (rds_ring->num_desc-1),
         rds_ring->crb_rcv_producer);
 }
}

static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter)
{
 if (adapter->ahw->msg_enable & NETIF_MSG_DRV) {
  char prefix[30];

  scnprintf(prefix, sizeof(prefix), "%s: %s: ",
     dev_name(&adapter->pdev->dev), __func__);

  print_hex_dump_debug(prefix, DUMP_PREFIX_NONE, 16, 1,
         skb->data, skb->len, true);
 }
}

static void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, int ring,
        u64 sts_data0)
{
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 int index, length, cksum, pkt_offset;

 if (unlikely(ring >= adapter->max_rds_rings))
  return;

 rds_ring = &recv_ctx->rds_rings[ring];

 index = qlcnic_get_sts_refhandle(sts_data0);
 length = qlcnic_get_sts_totallength(sts_data0);
 if (unlikely(index >= rds_ring->num_desc))
  return;

 cksum  = qlcnic_get_sts_status(sts_data0);
 pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);

 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
 if (!skb)
  return;

 if (length > rds_ring->skb_size)
  skb_put(skb, rds_ring->skb_size);
 else
  skb_put(skb, length);

 if (pkt_offset)
  skb_pull(skb, pkt_offset);

 if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
  adapter->ahw->diag_cnt++;
 else
  dump_skb(skb, adapter);

 dev_kfree_skb_any(skb);
 adapter->stats.rx_pkts++;
 adapter->stats.rxbytes += length;

 return;
}

void qlcnic_82xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
{
 struct qlcnic_adapter *adapter = sds_ring->adapter;
 struct status_desc *desc;
 u64 sts_data0;
 int ring, opcode, desc_cnt;

 u32 consumer = sds_ring->consumer;

 desc = &sds_ring->desc_head[consumer];
 sts_data0 = le64_to_cpu(desc->status_desc_data[0]);

 if (!(sts_data0 & STATUS_OWNER_HOST))
  return;

 desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
 opcode = qlcnic_get_sts_opcode(sts_data0);
 switch (opcode) {
 case QLCNIC_RESPONSE_DESC:
  qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
  break;
 default:
  ring = qlcnic_get_sts_type(sts_data0);
  qlcnic_process_rcv_diag(adapter, ring, sts_data0);
  break;
 }

 for (; desc_cnt > 0; desc_cnt--) {
  desc = &sds_ring->desc_head[consumer];
  desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
  consumer = get_next_index(consumer, sds_ring->num_desc);
 }

 sds_ring->consumer = consumer;
 writel(consumer, sds_ring->crb_sts_consumer);
}

int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
    struct net_device *netdev)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_host_tx_ring *tx_ring;

 if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
  return -ENOMEM;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  if (qlcnic_check_multi_tx(adapter) &&
      !adapter->ahw->diag_test) {
   netif_napi_add(netdev, &sds_ring->napi,
           qlcnic_rx_poll);
  } else {
   if (ring == (adapter->drv_sds_rings - 1))
    netif_napi_add(netdev, &sds_ring->napi,
            qlcnic_poll);
   else
    netif_napi_add(netdev, &sds_ring->napi,
            qlcnic_rx_poll);
  }
 }

 if (qlcnic_alloc_tx_rings(adapter, netdev)) {
  qlcnic_free_sds_rings(recv_ctx);
  return -ENOMEM;
 }

 if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   netif_napi_add_tx(netdev, &tx_ring->napi,
       qlcnic_tx_poll);
  }
 }

 return 0;
}

void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_host_tx_ring *tx_ring;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  netif_napi_del(&sds_ring->napi);
 }

 qlcnic_free_sds_rings(adapter->recv_ctx);

 if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   netif_napi_del(&tx_ring->napi);
  }
 }

 qlcnic_free_tx_rings(adapter);
}

void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  napi_enable(&sds_ring->napi);
  qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 if (qlcnic_check_multi_tx(adapter) &&
     (adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !adapter->ahw->diag_test) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   napi_enable(&tx_ring->napi);
   qlcnic_enable_tx_intr(adapter, tx_ring);
  }
 }
}

void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  qlcnic_disable_sds_intr(adapter, sds_ring);
  napi_synchronize(&sds_ring->napi);
  napi_disable(&sds_ring->napi);
 }

 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !adapter->ahw->diag_test &&
     qlcnic_check_multi_tx(adapter)) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   qlcnic_disable_tx_intr(adapter, tx_ring);
   napi_synchronize(&tx_ring->napi);
   napi_disable(&tx_ring->napi);
  }
 }
}

#define QLC_83XX_NORMAL_LB_PKT (1ULL << 36)
#define QLC_83XX_LRO_LB_PKT (1ULL << 46)

static inline int qlcnic_83xx_is_lb_pkt(u64 sts_data, int lro_pkt)
{
 if (lro_pkt)
  return (sts_data & QLC_83XX_LRO_LB_PKT) ? 1 : 0;
 else
  return (sts_data & QLC_83XX_NORMAL_LB_PKT) ? 1 : 0;
}

#define QLCNIC_ENCAP_LENGTH_MASK 0x7f

static inline u8 qlcnic_encap_length(u64 sts_data)
{
 return sts_data & QLCNIC_ENCAP_LENGTH_MASK;
}

static struct qlcnic_rx_buffer *
qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
   struct qlcnic_host_sds_ring *sds_ring,
   u8 ring, u64 sts_data[])
{
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_rx_buffer *buffer;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 int index, length, cksum, is_lb_pkt;
 u16 vid = 0xffff;
 int err;

 if (unlikely(ring >= adapter->max_rds_rings))
  return NULL;

 rds_ring = &recv_ctx->rds_rings[ring];

 index = qlcnic_83xx_hndl(sts_data[0]);
 if (unlikely(index >= rds_ring->num_desc))
  return NULL;

 buffer = &rds_ring->rx_buf_arr[index];
 length = qlcnic_83xx_pktln(sts_data[0]);
 cksum  = qlcnic_83xx_csum_status(sts_data[1]);
 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
 if (!skb)
  return buffer;

 if (length > rds_ring->skb_size)
  skb_put(skb, rds_ring->skb_size);
 else
  skb_put(skb, length);

 err = qlcnic_check_rx_tagging(adapter, skb, &vid);

 if (adapter->rx_mac_learn) {
  is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0);
  qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid);
 }

 if (unlikely(err)) {
  adapter->stats.rxdropped++;
  dev_kfree_skb(skb);
  return buffer;
 }

 skb->protocol = eth_type_trans(skb, netdev);

 if (qlcnic_encap_length(sts_data[1]) &&
     skb->ip_summed == CHECKSUM_UNNECESSARY) {
  skb->csum_level = 1;
  adapter->stats.encap_rx_csummed++;
 }

 if (vid != 0xffff)
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);

 napi_gro_receive(&sds_ring->napi, skb);

 adapter->stats.rx_pkts++;
 adapter->stats.rxbytes += length;

 return buffer;
}

static struct qlcnic_rx_buffer *
qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,
   u8 ring, u64 sts_data[])
{
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_rx_buffer *buffer;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 struct iphdr *iph;
 struct ipv6hdr *ipv6h;
 struct tcphdr *th;
 bool push;
 int l2_hdr_offset, l4_hdr_offset;
 int index, is_lb_pkt;
 u16 lro_length, length, data_offset, gso_size;
 u16 vid = 0xffff;
 int err;

 if (unlikely(ring >= adapter->max_rds_rings))
  return NULL;

 rds_ring = &recv_ctx->rds_rings[ring];

 index = qlcnic_83xx_hndl(sts_data[0]);
 if (unlikely(index >= rds_ring->num_desc))
  return NULL;

 buffer = &rds_ring->rx_buf_arr[index];

 lro_length = qlcnic_83xx_lro_pktln(sts_data[0]);
 l2_hdr_offset = qlcnic_83xx_l2_hdr_off(sts_data[1]);
 l4_hdr_offset = qlcnic_83xx_l4_hdr_off(sts_data[1]);
 push = qlcnic_83xx_is_psh_bit(sts_data[1]);

 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
 if (!skb)
  return buffer;

 if (qlcnic_83xx_is_tstamp(sts_data[1]))
  data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE;
 else
  data_offset = l4_hdr_offset + QLCNIC_TCP_HDR_SIZE;

 skb_put(skb, lro_length + data_offset);
 skb_pull(skb, l2_hdr_offset);

 err = qlcnic_check_rx_tagging(adapter, skb, &vid);

 if (adapter->rx_mac_learn) {
  is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1);
  qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid);
 }

 if (unlikely(err)) {
  adapter->stats.rxdropped++;
  dev_kfree_skb(skb);
  return buffer;
 }

 skb->protocol = eth_type_trans(skb, netdev);
 if (ntohs(skb->protocol) == ETH_P_IPV6) {
  ipv6h = (struct ipv6hdr *)skb->data;
  th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr));

  length = (th->doff << 2) + lro_length;
  ipv6h->payload_len = htons(length);
 } else {
  iph = (struct iphdr *)skb->data;
  th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
  length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
  csum_replace2(&iph->check, iph->tot_len, htons(length));
  iph->tot_len = htons(length);
 }

 th->psh = push;
 length = skb->len;

 if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) {
  gso_size = qlcnic_83xx_get_lro_sts_mss(sts_data[0]);
  skb_shinfo(skb)->gso_size = gso_size;
  if (skb->protocol == htons(ETH_P_IPV6))
   skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
  else
   skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
 }

 if (vid != 0xffff)
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);

 netif_receive_skb(skb);

 adapter->stats.lro_pkts++;
 adapter->stats.lrobytes += length;
 return buffer;
}

static int qlcnic_83xx_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring,
     int max)
{
 struct qlcnic_host_rds_ring *rds_ring;
 struct qlcnic_adapter *adapter = sds_ring->adapter;
 struct list_head *cur;
 struct status_desc *desc;
 struct qlcnic_rx_buffer *rxbuf = NULL;
 u8 ring;
 u64 sts_data[2];
 int count = 0, opcode;
 u32 consumer = sds_ring->consumer;

 while (count < max) {
  desc = &sds_ring->desc_head[consumer];
  sts_data[1] = le64_to_cpu(desc->status_desc_data[1]);
  opcode = qlcnic_83xx_opcode(sts_data[1]);
  if (!opcode)
   break;
  sts_data[0] = le64_to_cpu(desc->status_desc_data[0]);
  ring = QLCNIC_FETCH_RING_ID(sts_data[0]);

  switch (opcode) {
  case QLC_83XX_REG_DESC:
   rxbuf = qlcnic_83xx_process_rcv(adapter, sds_ring,
       ring, sts_data);
   break;
  case QLC_83XX_LRO_DESC:
   rxbuf = qlcnic_83xx_process_lro(adapter, ring,
       sts_data);
   break;
  default:
   dev_info(&adapter->pdev->dev,
     "Unknown opcode: 0x%x\n", opcode);
   goto skip;
  }

  if (likely(rxbuf))
   list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
  else
   adapter->stats.null_rxbuf++;
skip:
  desc = &sds_ring->desc_head[consumer];
  /* Reset the descriptor */
  desc->status_desc_data[1] = 0;
  consumer = get_next_index(consumer, sds_ring->num_desc);
  count++;
 }
 for (ring = 0; ring < adapter->max_rds_rings; ring++) {
  rds_ring = &adapter->recv_ctx->rds_rings[ring];
  if (!list_empty(&sds_ring->free_list[ring])) {
   list_for_each(cur, &sds_ring->free_list[ring]) {
    rxbuf = list_entry(cur, struct qlcnic_rx_buffer,
         list);
    qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
   }
   spin_lock(&rds_ring->lock);
   list_splice_tail_init(&sds_ring->free_list[ring],
           &rds_ring->free_list);
   spin_unlock(&rds_ring->lock);
  }
  qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring);
 }
 if (count) {
  sds_ring->consumer = consumer;
  writel(consumer, sds_ring->crb_sts_consumer);
 }
 return count;
}

static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget)
{
 int tx_complete;
 int work_done;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_adapter *adapter;
 struct qlcnic_host_tx_ring *tx_ring;

 sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
 adapter = sds_ring->adapter;
 /* tx ring count = 1 */
 tx_ring = adapter->tx_ring;

 tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
 work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);

 /* Check if we need a repoll */
 if (!tx_complete)
  work_done = budget;

 if (work_done < budget) {
  napi_complete_done(&sds_ring->napi, work_done);
  qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 return work_done;
}

static int qlcnic_83xx_poll(struct napi_struct *napi, int budget)
{
 int tx_complete;
 int work_done;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_adapter *adapter;
 struct qlcnic_host_tx_ring *tx_ring;

 sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
 adapter = sds_ring->adapter;
 /* tx ring count = 1 */
 tx_ring = adapter->tx_ring;

 tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
 work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);

 /* Check if we need a repoll */
 if (!tx_complete)
  work_done = budget;

 if (work_done < budget) {
  napi_complete_done(&sds_ring->napi, work_done);
  qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 return work_done;
}

static int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget)
{
 int work_done;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_adapter *adapter;

 tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi);
 adapter = tx_ring->adapter;
 work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
 if (work_done) {
  napi_complete(&tx_ring->napi);
  if (test_bit(__QLCNIC_DEV_UP , &adapter->state))
   qlcnic_enable_tx_intr(adapter, tx_ring);
 } else {
  /* need a repoll */
  work_done = budget;
 }

 return work_done;
}

static int qlcnic_83xx_rx_poll(struct napi_struct *napi, int budget)
{
 int work_done;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_adapter *adapter;

 sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
 adapter = sds_ring->adapter;
 work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
 if (work_done < budget) {
  napi_complete_done(&sds_ring->napi, work_done);
  if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
   qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 return work_done;
}

void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  napi_enable(&sds_ring->napi);
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   qlcnic_enable_sds_intr(adapter, sds_ring);
 }

 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   napi_enable(&tx_ring->napi);
   qlcnic_enable_tx_intr(adapter, tx_ring);
  }
 }
}

void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_host_tx_ring *tx_ring;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   qlcnic_disable_sds_intr(adapter, sds_ring);
  napi_synchronize(&sds_ring->napi);
  napi_disable(&sds_ring->napi);
 }

 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   qlcnic_disable_tx_intr(adapter, tx_ring);
   napi_synchronize(&tx_ring->napi);
   napi_disable(&tx_ring->napi);
  }
 }
}

int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
    struct net_device *netdev)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
  return -ENOMEM;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  if (adapter->flags & QLCNIC_MSIX_ENABLED) {
   if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
    netif_napi_add(netdev, &sds_ring->napi,
            qlcnic_83xx_rx_poll);
   else
    netif_napi_add(netdev, &sds_ring->napi,
            qlcnic_83xx_msix_sriov_vf_poll);

  } else {
   netif_napi_add(netdev, &sds_ring->napi,
           qlcnic_83xx_poll);
  }
 }

 if (qlcnic_alloc_tx_rings(adapter, netdev)) {
  qlcnic_free_sds_rings(recv_ctx);
  return -ENOMEM;
 }

 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   netif_napi_add_tx(netdev, &tx_ring->napi,
       qlcnic_83xx_msix_tx_poll);
  }
 }

 return 0;
}

void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_host_tx_ring *tx_ring;

 for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
  sds_ring = &recv_ctx->sds_rings[ring];
  netif_napi_del(&sds_ring->napi);
 }

 qlcnic_free_sds_rings(adapter->recv_ctx);

 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   netif_napi_del(&tx_ring->napi);
  }
 }

 qlcnic_free_tx_rings(adapter);
}

static void qlcnic_83xx_process_rcv_diag(struct qlcnic_adapter *adapter,
      int ring, u64 sts_data[])
{
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct sk_buff *skb;
 struct qlcnic_host_rds_ring *rds_ring;
 int index, length;

 if (unlikely(ring >= adapter->max_rds_rings))
  return;

 rds_ring = &recv_ctx->rds_rings[ring];
 index = qlcnic_83xx_hndl(sts_data[0]);
 if (unlikely(index >= rds_ring->num_desc))
  return;

 length = qlcnic_83xx_pktln(sts_data[0]);

 skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
 if (!skb)
  return;

 if (length > rds_ring->skb_size)
  skb_put(skb, rds_ring->skb_size);
 else
  skb_put(skb, length);

 if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
  adapter->ahw->diag_cnt++;
 else
  dump_skb(skb, adapter);

 dev_kfree_skb_any(skb);
 return;
}

void qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
{
 struct qlcnic_adapter *adapter = sds_ring->adapter;
 struct status_desc *desc;
 u64 sts_data[2];
 int ring, opcode;
 u32 consumer = sds_ring->consumer;

 desc = &sds_ring->desc_head[consumer];
 sts_data[0] = le64_to_cpu(desc->status_desc_data[0]);
 sts_data[1] = le64_to_cpu(desc->status_desc_data[1]);
 opcode = qlcnic_83xx_opcode(sts_data[1]);
 if (!opcode)
  return;

 ring = QLCNIC_FETCH_RING_ID(sts_data[0]);
 qlcnic_83xx_process_rcv_diag(adapter, ring, sts_data);
 desc = &sds_ring->desc_head[consumer];
 desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
 consumer = get_next_index(consumer, sds_ring->num_desc);
 sds_ring->consumer = consumer;
 writel(consumer, sds_ring->crb_sts_consumer);
}

Messung V0.5
C=99 H=91 G=94

[ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ]