Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  cxgb4_main.c   Sprache: C

 
/*
 * This file is part of the Chelsio T4 Ethernet driver for Linux.
 *
 * Copyright (c) 2003-2016 Chelsio Communications, Inc. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/bitmap.h>
#include <linux/crc32.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/log2.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/rtnetlink.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/sockios.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <net/neighbour.h>
#include <net/netevent.h>
#include <net/addrconf.h>
#include <net/bonding.h>
#include <linux/uaccess.h>
#include <linux/crash_dump.h>
#include <net/udp_tunnel.h>
#include <net/xfrm.h>
#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE)
#include <net/tls.h>
#endif

#include "cxgb4.h"
#include "cxgb4_filter.h"
#include "t4_regs.h"
#include "t4_values.h"
#include "t4_msg.h"
#include "t4fw_api.h"
#include "t4fw_version.h"
#include "cxgb4_dcb.h"
#include "srq.h"
#include "cxgb4_debugfs.h"
#include "clip_tbl.h"
#include "l2t.h"
#include "smt.h"
#include "sched.h"
#include "cxgb4_tc_u32.h"
#include "cxgb4_tc_flower.h"
#include "cxgb4_tc_mqprio.h"
#include "cxgb4_tc_matchall.h"
#include "cxgb4_ptp.h"
#include "cxgb4_cudbg.h"

char cxgb4_driver_name[] = KBUILD_MODNAME;

#define DRV_DESC "Chelsio T4/T5/T6 Network Driver"

#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
    NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
    NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)

/* Macros needed to support the PCI Device ID Table ...
 */

#define CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN \
 static const struct pci_device_id cxgb4_pci_tbl[] = {
#define CXGB4_UNIFIED_PF 0x4

#define CH_PCI_DEVICE_ID_FUNCTION CXGB4_UNIFIED_PF

/* Include PCI Device IDs for both PF4 and PF0-3 so our PCI probe() routine is
 * called for both.
 */

#define CH_PCI_DEVICE_ID_FUNCTION2 0x0

#define CH_PCI_ID_TABLE_ENTRY(devid) \
  {PCI_VDEVICE(CHELSIO, (devid)), CXGB4_UNIFIED_PF}

#define CH_PCI_DEVICE_ID_TABLE_DEFINE_END \
  { 0, } \
 }

#include "t4_pci_id_tbl.h"

#define FW4_FNAME "cxgb4/t4fw.bin"
#define FW5_FNAME "cxgb4/t5fw.bin"
#define FW6_FNAME "cxgb4/t6fw.bin"
#define FW4_CFNAME "cxgb4/t4-config.txt"
#define FW5_CFNAME "cxgb4/t5-config.txt"
#define FW6_CFNAME "cxgb4/t6-config.txt"
#define PHY_AQ1202_FIRMWARE "cxgb4/aq1202_fw.cld"
#define PHY_BCM84834_FIRMWARE "cxgb4/bcm8483.bin"
#define PHY_AQ1202_DEVICEID 0x4409
#define PHY_BCM84834_DEVICEID 0x4486

MODULE_DESCRIPTION(DRV_DESC);
MODULE_AUTHOR("Chelsio Communications");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl);
MODULE_FIRMWARE(FW4_FNAME);
MODULE_FIRMWARE(FW5_FNAME);
MODULE_FIRMWARE(FW6_FNAME);

/*
 * The driver uses the best interrupt scheme available on a platform in the
 * order MSI-X, MSI, legacy INTx interrupts.  This parameter determines which
 * of these schemes the driver may consider as follows:
 *
 * msi = 2: choose from among all three options
 * msi = 1: only consider MSI and INTx interrupts
 * msi = 0: force INTx interrupts
 */

static int msi = 2;

module_param(msi, int, 0644);
MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)");

/*
 * Normally we tell the chip to deliver Ingress Packets into our DMA buffers
 * offset by 2 bytes in order to have the IP headers line up on 4-byte
 * boundaries.  This is a requirement for many architectures which will throw
 * a machine check fault if an attempt is made to access one of the 4-byte IP
 * header fields on a non-4-byte boundary.  And it's a major performance issue
 * even on some architectures which allow it like some implementations of the
 * x86 ISA.  However, some architectures don't mind this and for some very
 * edge-case performance sensitive applications (like forwarding large volumes
 * of small packets), setting this DMA offset to 0 will decrease the number of
 * PCI-E Bus transfers enough to measurably affect performance.
 */

static int rx_dma_offset = 2;

/* TX Queue select used to determine what algorithm to use for selecting TX
 * queue. Select between the kernel provided function (select_queue=0) or user
 * cxgb_select_queue function (select_queue=1)
 *
 * Default: select_queue=0
 */

static int select_queue;
module_param(select_queue, int, 0644);
MODULE_PARM_DESC(select_queue,
   "Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method.");

static struct dentry *cxgb4_debugfs_root;

LIST_HEAD(adapter_list);
DEFINE_MUTEX(uld_mutex);
LIST_HEAD(uld_list);

static int cfg_queues(struct adapter *adap);

static void link_report(struct net_device *dev)
{
 if (!netif_carrier_ok(dev))
  netdev_info(dev, "link down\n");
 else {
  static const char *fc[] = { "no""Rx""Tx""Tx/Rx" };

  const char *s;
  const struct port_info *p = netdev_priv(dev);

  switch (p->link_cfg.speed) {
  case 100:
   s = "100Mbps";
   break;
  case 1000:
   s = "1Gbps";
   break;
  case 10000:
   s = "10Gbps";
   break;
  case 25000:
   s = "25Gbps";
   break;
  case 40000:
   s = "40Gbps";
   break;
  case 50000:
   s = "50Gbps";
   break;
  case 100000:
   s = "100Gbps";
   break;
  default:
   pr_info("%s: unsupported speed: %d\n",
    dev->name, p->link_cfg.speed);
   return;
  }

  netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s,
       fc[p->link_cfg.fc]);
 }
}

#ifdef CONFIG_CHELSIO_T4_DCB
/* Set up/tear down Data Center Bridging Priority mapping for a net device. */
static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable)
{
 struct port_info *pi = netdev_priv(dev);
 struct adapter *adap = pi->adapter;
 struct sge_eth_txq *txq = &adap->sge.ethtxq[pi->first_qset];
 int i;

 /* We use a simple mapping of Port TX Queue Index to DCB
 * Priority when we're enabling DCB.
 */

 for (i = 0; i < pi->nqsets; i++, txq++) {
  u32 name, value;
  int err;

  name = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
   FW_PARAMS_PARAM_X_V(
    FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH) |
   FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id));
  value = enable ? i : 0xffffffff;

  /* Since we can be called while atomic (from "interrupt
 * level") we need to issue the Set Parameters Commannd
 * without sleeping (timeout < 0).
 */

  err = t4_set_params_timeout(adap, adap->mbox, adap->pf, 0, 1,
         &name, &value,
         -FW_CMD_MAX_TIMEOUT);

  if (err)
   dev_err(adap->pdev_dev,
    "Can't %s DCB Priority on port %d, TX Queue %d: err=%d\n",
    enable ? "set" : "unset", pi->port_id, i, -err);
  else
   txq->dcb_prio = enable ? value : 0;
 }
}

int cxgb4_dcb_enabled(const struct net_device *dev)
{
 struct port_info *pi = netdev_priv(dev);

 if (!pi->dcb.enabled)
  return 0;

 return ((pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED) ||
  (pi->dcb.state == CXGB4_DCB_STATE_HOST));
}
#endif /* CONFIG_CHELSIO_T4_DCB */

void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat)
{
 struct net_device *dev = adapter->port[port_id];

 /* Skip changes from disabled ports. */
 if (netif_running(dev) && link_stat != netif_carrier_ok(dev)) {
  if (link_stat)
   netif_carrier_on(dev);
  else {
#ifdef CONFIG_CHELSIO_T4_DCB
   if (cxgb4_dcb_enabled(dev)) {
    cxgb4_dcb_reset(dev);
    dcb_tx_queue_prio_enable(dev, false);
   }
#endif /* CONFIG_CHELSIO_T4_DCB */
   netif_carrier_off(dev);
  }

  link_report(dev);
 }
}

void t4_os_portmod_changed(struct adapter *adap, int port_id)
{
 static const char *mod_str[] = {
  NULL, "LR""SR""ER""passive DA""active DA""LRM"
 };

 struct net_device *dev = adap->port[port_id];
 struct port_info *pi = netdev_priv(dev);

 if (pi->mod_type == FW_PORT_MOD_TYPE_NONE)
  netdev_info(dev, "port module unplugged\n");
 else if (pi->mod_type < ARRAY_SIZE(mod_str))
  netdev_info(dev, "%s module inserted\n", mod_str[pi->mod_type]);
 else if (pi->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED)
  netdev_info(dev, "%s: unsupported port module inserted\n",
       dev->name);
 else if (pi->mod_type == FW_PORT_MOD_TYPE_UNKNOWN)
  netdev_info(dev, "%s: unknown port module inserted\n",
       dev->name);
 else if (pi->mod_type == FW_PORT_MOD_TYPE_ERROR)
  netdev_info(dev, "%s: transceiver module error\n", dev->name);
 else
  netdev_info(dev, "%s: unknown module type %d inserted\n",
       dev->name, pi->mod_type);

 /* If the interface is running, then we'll need any "sticky" Link
 * Parameters redone with a new Transceiver Module.
 */

 pi->link_cfg.redo_l1cfg = netif_running(dev);
}

int dbfifo_int_thresh = 10; /* 10 == 640 entry threshold */
module_param(dbfifo_int_thresh, int, 0644);
MODULE_PARM_DESC(dbfifo_int_thresh, "doorbell fifo interrupt threshold");

/*
 * usecs to sleep while draining the dbfifo
 */

static int dbfifo_drain_delay = 1000;
module_param(dbfifo_drain_delay, int, 0644);
MODULE_PARM_DESC(dbfifo_drain_delay,
   "usecs to sleep while draining the dbfifo");

static inline int cxgb4_set_addr_hash(struct port_info *pi)
{
 struct adapter *adap = pi->adapter;
 u64 vec = 0;
 bool ucast = false;
 struct hash_mac_addr *entry;

 /* Calculate the hash vector for the updated list and program it */
 list_for_each_entry(entry, &adap->mac_hlist, list) {
  ucast |= is_unicast_ether_addr(entry->addr);
  vec |= (1ULL << hash_mac_addr(entry->addr));
 }
 return t4_set_addr_hash(adap, adap->mbox, pi->viid, ucast,
    vec, false);
}

static int cxgb4_mac_sync(struct net_device *netdev, const u8 *mac_addr)
{
 struct port_info *pi = netdev_priv(netdev);
 struct adapter *adap = pi->adapter;
 int ret;
 u64 mhash = 0;
 u64 uhash = 0;
 /* idx stores the index of allocated filters,
 * its size should be modified based on the number of
 * MAC addresses that we allocate filters for
 */


 u16 idx[1] = {};
 bool free = false;
 bool ucast = is_unicast_ether_addr(mac_addr);
 const u8 *maclist[1] = {mac_addr};
 struct hash_mac_addr *new_entry;

 ret = cxgb4_alloc_mac_filt(adap, pi->viid, free, 1, maclist,
       idx, ucast ? &uhash : &mhash, false);
 if (ret < 0)
  goto out;
 /* if hash != 0, then add the addr to hash addr list
 * so on the end we will calculate the hash for the
 * list and program it
 */

 if (uhash || mhash) {
  new_entry = kzalloc(sizeof(*new_entry), GFP_ATOMIC);
  if (!new_entry)
   return -ENOMEM;
  ether_addr_copy(new_entry->addr, mac_addr);
  list_add_tail(&new_entry->list, &adap->mac_hlist);
  ret = cxgb4_set_addr_hash(pi);
 }
out:
 return ret < 0 ? ret : 0;
}

static int cxgb4_mac_unsync(struct net_device *netdev, const u8 *mac_addr)
{
 struct port_info *pi = netdev_priv(netdev);
 struct adapter *adap = pi->adapter;
 int ret;
 const u8 *maclist[1] = {mac_addr};
 struct hash_mac_addr *entry, *tmp;

 /* If the MAC address to be removed is in the hash addr
 * list, delete it from the list and update hash vector
 */

 list_for_each_entry_safe(entry, tmp, &adap->mac_hlist, list) {
  if (ether_addr_equal(entry->addr, mac_addr)) {
   list_del(&entry->list);
   kfree(entry);
   return cxgb4_set_addr_hash(pi);
  }
 }

 ret = cxgb4_free_mac_filt(adap, pi->viid, 1, maclist, false);
 return ret < 0 ? -EINVAL : 0;
}

/*
 * Set Rx properties of a port, such as promiscruity, address filters, and MTU.
 * If @mtu is -1 it is left unchanged.
 */

static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok)
{
 struct port_info *pi = netdev_priv(dev);
 struct adapter *adapter = pi->adapter;

 __dev_uc_sync(dev, cxgb4_mac_sync, cxgb4_mac_unsync);
 __dev_mc_sync(dev, cxgb4_mac_sync, cxgb4_mac_unsync);

 return t4_set_rxmode(adapter, adapter->mbox, pi->viid, pi->viid_mirror,
        mtu, (dev->flags & IFF_PROMISC) ? 1 : 0,
        (dev->flags & IFF_ALLMULTI) ? 1 : 0, 1, -1,
        sleep_ok);
}

/**
 * cxgb4_change_mac - Update match filter for a MAC address.
 * @pi: the port_info
 * @viid: the VI id
 * @tcam_idx: TCAM index of existing filter for old value of MAC address,
 *    or -1
 * @addr: the new MAC address value
 * @persist: whether a new MAC allocation should be persistent
 * @smt_idx: the destination to store the new SMT index.
 *
 * Modifies an MPS filter and sets it to the new MAC address if
 * @tcam_idx >= 0, or adds the MAC address to a new filter if
 * @tcam_idx < 0. In the latter case the address is added persistently
 * if @persist is %true.
 * Addresses are programmed to hash region, if tcam runs out of entries.
 *
 */

int cxgb4_change_mac(struct port_info *pi, unsigned int viid,
       int *tcam_idx, const u8 *addr, bool persist,
       u8 *smt_idx)
{
 struct adapter *adapter = pi->adapter;
 struct hash_mac_addr *entry, *new_entry;
 int ret;

 ret = t4_change_mac(adapter, adapter->mbox, viid,
       *tcam_idx, addr, persist, smt_idx);
 /* We ran out of TCAM entries. try programming hash region. */
 if (ret == -ENOMEM) {
  /* If the MAC address to be updated is in the hash addr
 * list, update it from the list
 */

  list_for_each_entry(entry, &adapter->mac_hlist, list) {
   if (entry->iface_mac) {
    ether_addr_copy(entry->addr, addr);
    goto set_hash;
   }
  }
  new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
  if (!new_entry)
   return -ENOMEM;
  ether_addr_copy(new_entry->addr, addr);
  new_entry->iface_mac = true;
  list_add_tail(&new_entry->list, &adapter->mac_hlist);
set_hash:
  ret = cxgb4_set_addr_hash(pi);
 } else if (ret >= 0) {
  *tcam_idx = ret;
  ret = 0;
 }

 return ret;
}

/*
 * link_start - enable a port
 * @dev: the port to enable
 *
 * Performs the MAC and PHY actions needed to enable a port.
 */

static int link_start(struct net_device *dev)
{
 struct port_info *pi = netdev_priv(dev);
 unsigned int mb = pi->adapter->mbox;
 int ret;

 /*
 * We do not set address filters and promiscuity here, the stack does
 * that step explicitly.
 */

 ret = t4_set_rxmode(pi->adapter, mb, pi->viid, pi->viid_mirror,
       dev->mtu, -1, -1, -1,
       !!(dev->features & NETIF_F_HW_VLAN_CTAG_RX), true);
 if (ret == 0)
  ret = cxgb4_update_mac_filt(pi, pi->viid, &pi->xact_addr_filt,
         dev->dev_addr, true, &pi->smt_idx);
 if (ret == 0)
  ret = t4_link_l1cfg(pi->adapter, mb, pi->tx_chan,
        &pi->link_cfg);
 if (ret == 0) {
  local_bh_disable();
  ret = t4_enable_pi_params(pi->adapter, mb, pi, true,
       true, CXGB4_DCB_ENABLED);
  local_bh_enable();
 }

 return ret;
}

#ifdef CONFIG_CHELSIO_T4_DCB
/* Handle a Data Center Bridging update message from the firmware. */
static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd)
{
 int port = FW_PORT_CMD_PORTID_G(ntohl(pcmd->op_to_portid));
 struct net_device *dev = adap->port[adap->chan_map[port]];
 int old_dcb_enabled = cxgb4_dcb_enabled(dev);
 int new_dcb_enabled;

 cxgb4_dcb_handle_fw_update(adap, pcmd);
 new_dcb_enabled = cxgb4_dcb_enabled(dev);

 /* If the DCB has become enabled or disabled on the port then we're
 * going to need to set up/tear down DCB Priority parameters for the
 * TX Queues associated with the port.
 */

 if (new_dcb_enabled != old_dcb_enabled)
  dcb_tx_queue_prio_enable(dev, new_dcb_enabled);
}
#endif /* CONFIG_CHELSIO_T4_DCB */

/* Response queue handler for the FW event queue.
 */

static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
     const struct pkt_gl *gl)
{
 u8 opcode = ((const struct rss_header *)rsp)->opcode;

 rsp++;                                          /* skip RSS header */

 /* FW can send EGR_UPDATEs encapsulated in a CPL_FW4_MSG.
 */

 if (unlikely(opcode == CPL_FW4_MSG &&
    ((const struct cpl_fw4_msg *)rsp)->type == FW_TYPE_RSSCPL)) {
  rsp++;
  opcode = ((const struct rss_header *)rsp)->opcode;
  rsp++;
  if (opcode != CPL_SGE_EGR_UPDATE) {
   dev_err(q->adap->pdev_dev, "unexpected FW4/CPL %#x on FW event queue\n"
    , opcode);
   goto out;
  }
 }

 if (likely(opcode == CPL_SGE_EGR_UPDATE)) {
  const struct cpl_sge_egr_update *p = (void *)rsp;
  unsigned int qid = EGR_QID_G(ntohl(p->opcode_qid));
  struct sge_txq *txq;

  txq = q->adap->sge.egr_map[qid - q->adap->sge.egr_start];
  txq->restarts++;
  if (txq->q_type == CXGB4_TXQ_ETH) {
   struct sge_eth_txq *eq;

   eq = container_of(txq, struct sge_eth_txq, q);
   t4_sge_eth_txq_egress_update(q->adap, eq, -1);
  } else {
   struct sge_uld_txq *oq;

   oq = container_of(txq, struct sge_uld_txq, q);
   tasklet_schedule(&oq->qresume_tsk);
  }
 } else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) {
  const struct cpl_fw6_msg *p = (void *)rsp;

#ifdef CONFIG_CHELSIO_T4_DCB
  const struct fw_port_cmd *pcmd = (const void *)p->data;
  unsigned int cmd = FW_CMD_OP_G(ntohl(pcmd->op_to_portid));
  unsigned int action =
   FW_PORT_CMD_ACTION_G(ntohl(pcmd->action_to_len16));

  if (cmd == FW_PORT_CMD &&
      (action == FW_PORT_ACTION_GET_PORT_INFO ||
       action == FW_PORT_ACTION_GET_PORT_INFO32)) {
   int port = FW_PORT_CMD_PORTID_G(
     be32_to_cpu(pcmd->op_to_portid));
   struct net_device *dev;
   int dcbxdis, state_input;

   dev = q->adap->port[q->adap->chan_map[port]];
   dcbxdis = (action == FW_PORT_ACTION_GET_PORT_INFO
     ? !!(pcmd->u.info.dcbxdis_pkd & FW_PORT_CMD_DCBXDIS_F)
     : !!(be32_to_cpu(pcmd->u.info32.lstatus32_to_cbllen32)
          & FW_PORT_CMD_DCBXDIS32_F));
   state_input = (dcbxdis
           ? CXGB4_DCB_INPUT_FW_DISABLED
           : CXGB4_DCB_INPUT_FW_ENABLED);

   cxgb4_dcb_state_fsm(dev, state_input);
  }

  if (cmd == FW_PORT_CMD &&
      action == FW_PORT_ACTION_L2_DCB_CFG)
   dcb_rpl(q->adap, pcmd);
  else
#endif
   if (p->type == 0)
    t4_handle_fw_rpl(q->adap, p->data);
 } else if (opcode == CPL_L2T_WRITE_RPL) {
  const struct cpl_l2t_write_rpl *p = (void *)rsp;

  do_l2t_write_rpl(q->adap, p);
 } else if (opcode == CPL_SMT_WRITE_RPL) {
  const struct cpl_smt_write_rpl *p = (void *)rsp;

  do_smt_write_rpl(q->adap, p);
 } else if (opcode == CPL_SET_TCB_RPL) {
  const struct cpl_set_tcb_rpl *p = (void *)rsp;

  filter_rpl(q->adap, p);
 } else if (opcode == CPL_ACT_OPEN_RPL) {
  const struct cpl_act_open_rpl *p = (void *)rsp;

  hash_filter_rpl(q->adap, p);
 } else if (opcode == CPL_ABORT_RPL_RSS) {
  const struct cpl_abort_rpl_rss *p = (void *)rsp;

  hash_del_filter_rpl(q->adap, p);
 } else if (opcode == CPL_SRQ_TABLE_RPL) {
  const struct cpl_srq_table_rpl *p = (void *)rsp;

  do_srq_table_rpl(q->adap, p);
 } else
  dev_err(q->adap->pdev_dev,
   "unexpected CPL %#x on FW event queue\n", opcode);
out:
 return 0;
}

static void disable_msi(struct adapter *adapter)
{
 if (adapter->flags & CXGB4_USING_MSIX) {
  pci_disable_msix(adapter->pdev);
  adapter->flags &= ~CXGB4_USING_MSIX;
 } else if (adapter->flags & CXGB4_USING_MSI) {
  pci_disable_msi(adapter->pdev);
  adapter->flags &= ~CXGB4_USING_MSI;
 }
}

/*
 * Interrupt handler for non-data events used with MSI-X.
 */

static irqreturn_t t4_nondata_intr(int irq, void *cookie)
{
 struct adapter *adap = cookie;
 u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A));

 if (v & PFSW_F) {
  adap->swintr = 1;
  t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A), v);
 }
 if (adap->flags & CXGB4_MASTER_PF)
  t4_slow_intr_handler(adap);
 return IRQ_HANDLED;
}

int cxgb4_set_msix_aff(struct adapter *adap, unsigned short vec,
         cpumask_var_t *aff_mask, int idx)
{
 int rv;

 if (!zalloc_cpumask_var(aff_mask, GFP_KERNEL)) {
  dev_err(adap->pdev_dev, "alloc_cpumask_var failed\n");
  return -ENOMEM;
 }

 cpumask_set_cpu(cpumask_local_spread(idx, dev_to_node(adap->pdev_dev)),
   *aff_mask);

 rv = irq_set_affinity_hint(vec, *aff_mask);
 if (rv)
  dev_warn(adap->pdev_dev,
    "irq_set_affinity_hint %u failed %d\n",
    vec, rv);

 return 0;
}

void cxgb4_clear_msix_aff(unsigned short vec, cpumask_var_t aff_mask)
{
 irq_set_affinity_hint(vec, NULL);
 free_cpumask_var(aff_mask);
}

static int request_msix_queue_irqs(struct adapter *adap)
{
 struct sge *s = &adap->sge;
 struct msix_info *minfo;
 int err, ethqidx;

 if (s->fwevtq_msix_idx < 0)
  return -ENOMEM;

 err = request_irq(adap->msix_info[s->fwevtq_msix_idx].vec,
     t4_sge_intr_msix, 0,
     adap->msix_info[s->fwevtq_msix_idx].desc,
     &s->fw_evtq);
 if (err)
  return err;

 for_each_ethrxq(s, ethqidx) {
  minfo = s->ethrxq[ethqidx].msix;
  err = request_irq(minfo->vec,
      t4_sge_intr_msix, 0,
      minfo->desc,
      &s->ethrxq[ethqidx].rspq);
  if (err)
   goto unwind;

  cxgb4_set_msix_aff(adap, minfo->vec,
       &minfo->aff_mask, ethqidx);
 }
 return 0;

unwind:
 while (--ethqidx >= 0) {
  minfo = s->ethrxq[ethqidx].msix;
  cxgb4_clear_msix_aff(minfo->vec, minfo->aff_mask);
  free_irq(minfo->vec, &s->ethrxq[ethqidx].rspq);
 }
 free_irq(adap->msix_info[s->fwevtq_msix_idx].vec, &s->fw_evtq);
 return err;
}

static void free_msix_queue_irqs(struct adapter *adap)
{
 struct sge *s = &adap->sge;
 struct msix_info *minfo;
 int i;

 free_irq(adap->msix_info[s->fwevtq_msix_idx].vec, &s->fw_evtq);
 for_each_ethrxq(s, i) {
  minfo = s->ethrxq[i].msix;
  cxgb4_clear_msix_aff(minfo->vec, minfo->aff_mask);
  free_irq(minfo->vec, &s->ethrxq[i].rspq);
 }
}

static int setup_ppod_edram(struct adapter *adap)
{
 unsigned int param, val;
 int ret;

 /* Driver sends FW_PARAMS_PARAM_DEV_PPOD_EDRAM read command to check
 * if firmware supports ppod edram feature or not. If firmware
 * returns 1, then driver can enable this feature by sending
 * FW_PARAMS_PARAM_DEV_PPOD_EDRAM write command with value 1 to
 * enable ppod edram feature.
 */

 param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
  FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_PPOD_EDRAM));

 ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val);
 if (ret < 0) {
  dev_warn(adap->pdev_dev,
    "querying PPOD_EDRAM support failed: %d\n",
    ret);
  return -1;
 }

 if (val != 1)
  return -1;

 ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val);
 if (ret < 0) {
  dev_err(adap->pdev_dev,
   "setting PPOD_EDRAM failed: %d\n", ret);
  return -1;
 }
 return 0;
}

static void adap_config_hpfilter(struct adapter *adapter)
{
 u32 param, val = 0;
 int ret;

 /* Enable HP filter region. Older fw will fail this request and
 * it is fine.
 */

 param = FW_PARAM_DEV(HPFILTER_REGION_SUPPORT);
 ret = t4_set_params(adapter, adapter->mbox, adapter->pf, 0,
       1, ¶m, &val);

 /* An error means FW doesn't know about HP filter support,
 * it's not a problem, don't return an error.
 */

 if (ret < 0)
  dev_err(adapter->pdev_dev,
   "HP filter region isn't supported by FW\n");
}

static int cxgb4_config_rss(const struct port_info *pi, u16 *rss,
       u16 rss_size, u16 viid)
{
 struct adapter *adap = pi->adapter;
 int ret;

 ret = t4_config_rss_range(adap, adap->mbox, viid, 0, rss_size, rss,
      rss_size);
 if (ret)
  return ret;

 /* If Tunnel All Lookup isn't specified in the global RSS
 * Configuration, then we need to specify a default Ingress
 * Queue for any ingress packets which aren't hashed.  We'll
 * use our first ingress queue ...
 */

 return t4_config_vi_rss(adap, adap->mbox, viid,
    FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F |
    FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F |
    FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F |
    FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F |
    FW_RSS_VI_CONFIG_CMD_UDPEN_F,
    rss[0]);
}

/**
 * cxgb4_write_rss - write the RSS table for a given port
 * @pi: the port
 * @queues: array of queue indices for RSS
 *
 * Sets up the portion of the HW RSS table for the port's VI to distribute
 * packets to the Rx queues in @queues.
 * Should never be called before setting up sge eth rx queues
 */

int cxgb4_write_rss(const struct port_info *pi, const u16 *queues)
{
 struct adapter *adapter = pi->adapter;
 const struct sge_eth_rxq *rxq;
 int i, err;
 u16 *rss;

 rxq = &adapter->sge.ethrxq[pi->first_qset];
 rss = kmalloc_array(pi->rss_size, sizeof(u16), GFP_KERNEL);
 if (!rss)
  return -ENOMEM;

 /* map the queue indices to queue ids */
 for (i = 0; i < pi->rss_size; i++, queues++)
  rss[i] = rxq[*queues].rspq.abs_id;

 err = cxgb4_config_rss(pi, rss, pi->rss_size, pi->viid);
 kfree(rss);
 return err;
}

/**
 * setup_rss - configure RSS
 * @adap: the adapter
 *
 * Sets up RSS for each port.
 */

static int setup_rss(struct adapter *adap)
{
 int i, j, err;

 for_each_port(adap, i) {
  const struct port_info *pi = adap2pinfo(adap, i);

  /* Fill default values with equal distribution */
  for (j = 0; j < pi->rss_size; j++)
   pi->rss[j] = j % pi->nqsets;

  err = cxgb4_write_rss(pi, pi->rss);
  if (err)
   return err;
 }
 return 0;
}

/*
 * Return the channel of the ingress queue with the given qid.
 */

static unsigned int rxq_to_chan(const struct sge *p, unsigned int qid)
{
 qid -= p->ingr_start;
 return netdev2pinfo(p->ingr_map[qid]->netdev)->tx_chan;
}

void cxgb4_quiesce_rx(struct sge_rspq *q)
{
 if (q->handler)
  napi_disable(&q->napi);
}

/*
 * Wait until all NAPI handlers are descheduled.
 */

static void quiesce_rx(struct adapter *adap)
{
 int i;

 for (i = 0; i < adap->sge.ingr_sz; i++) {
  struct sge_rspq *q = adap->sge.ingr_map[i];

  if (!q)
   continue;

  cxgb4_quiesce_rx(q);
 }
}

/* Disable interrupt and napi handler */
static void disable_interrupts(struct adapter *adap)
{
 struct sge *s = &adap->sge;

 if (adap->flags & CXGB4_FULL_INIT_DONE) {
  t4_intr_disable(adap);
  if (adap->flags & CXGB4_USING_MSIX) {
   free_msix_queue_irqs(adap);
   free_irq(adap->msix_info[s->nd_msix_idx].vec,
     adap);
  } else {
   free_irq(adap->pdev->irq, adap);
  }
  quiesce_rx(adap);
 }
}

void cxgb4_enable_rx(struct adapter *adap, struct sge_rspq *q)
{
 if (q->handler)
  napi_enable(&q->napi);

 /* 0-increment GTS to start the timer and enable interrupts */
 t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A),
       SEINTARM_V(q->intr_params) |
       INGRESSQID_V(q->cntxt_id));
}

/*
 * Enable NAPI scheduling and interrupt generation for all Rx queues.
 */

static void enable_rx(struct adapter *adap)
{
 int i;

 for (i = 0; i < adap->sge.ingr_sz; i++) {
  struct sge_rspq *q = adap->sge.ingr_map[i];

  if (!q)
   continue;

  cxgb4_enable_rx(adap, q);
 }
}

static int setup_non_data_intr(struct adapter *adap)
{
 int msix;

 adap->sge.nd_msix_idx = -1;
 if (!(adap->flags & CXGB4_USING_MSIX))
  return 0;

 /* Request MSI-X vector for non-data interrupt */
 msix = cxgb4_get_msix_idx_from_bmap(adap);
 if (msix < 0)
  return -ENOMEM;

 snprintf(adap->msix_info[msix].desc,
   sizeof(adap->msix_info[msix].desc),
   "%s", adap->port[0]->name);

 adap->sge.nd_msix_idx = msix;
 return 0;
}

static int setup_fw_sge_queues(struct adapter *adap)
{
 struct sge *s = &adap->sge;
 int msix, err = 0;

 bitmap_zero(s->starving_fl, s->egr_sz);
 bitmap_zero(s->txq_maperr, s->egr_sz);

 if (adap->flags & CXGB4_USING_MSIX) {
  s->fwevtq_msix_idx = -1;
  msix = cxgb4_get_msix_idx_from_bmap(adap);
  if (msix < 0)
   return -ENOMEM;

  snprintf(adap->msix_info[msix].desc,
    sizeof(adap->msix_info[msix].desc),
    "%s-FWeventq", adap->port[0]->name);
 } else {
  err = t4_sge_alloc_rxq(adap, &s->intrq, false, adap->port[0], 0,
           NULL, NULL, NULL, -1);
  if (err)
   return err;
  msix = -((int)s->intrq.abs_id + 1);
 }

 err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
          msix, NULL, fwevtq_handler, NULL, -1);
 if (err && msix >= 0)
  cxgb4_free_msix_idx_in_bmap(adap, msix);

 s->fwevtq_msix_idx = msix;
 return err;
}

/**
 * setup_sge_queues - configure SGE Tx/Rx/response queues
 * @adap: the adapter
 *
 * Determines how many sets of SGE queues to use and initializes them.
 * We support multiple queue sets per port if we have MSI-X, otherwise
 * just one queue set per port.
 */

static int setup_sge_queues(struct adapter *adap)
{
 struct sge_uld_rxq_info *rxq_info = NULL;
 struct sge *s = &adap->sge;
 unsigned int cmplqid = 0;
 int err, i, j, msix = 0;

 if (is_uld(adap))
  rxq_info = s->uld_rxq_info[CXGB4_ULD_RDMA];

 if (!(adap->flags & CXGB4_USING_MSIX))
  msix = -((int)s->intrq.abs_id + 1);

 for_each_port(adap, i) {
  struct net_device *dev = adap->port[i];
  struct port_info *pi = netdev_priv(dev);
  struct sge_eth_rxq *q = &s->ethrxq[pi->first_qset];
  struct sge_eth_txq *t = &s->ethtxq[pi->first_qset];

  for (j = 0; j < pi->nqsets; j++, q++) {
   if (msix >= 0) {
    msix = cxgb4_get_msix_idx_from_bmap(adap);
    if (msix < 0) {
     err = msix;
     goto freeout;
    }

    snprintf(adap->msix_info[msix].desc,
      sizeof(adap->msix_info[msix].desc),
      "%s-Rx%d", dev->name, j);
    q->msix = &adap->msix_info[msix];
   }

   err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev,
            msix, &q->fl,
            t4_ethrx_handler,
            NULL,
            t4_get_tp_ch_map(adap,
        pi->tx_chan));
   if (err)
    goto freeout;
   q->rspq.idx = j;
   memset(&q->stats, 0, sizeof(q->stats));
  }

  q = &s->ethrxq[pi->first_qset];
  for (j = 0; j < pi->nqsets; j++, t++, q++) {
   err = t4_sge_alloc_eth_txq(adap, t, dev,
     netdev_get_tx_queue(dev, j),
     q->rspq.cntxt_id,
     !!(adap->flags & CXGB4_SGE_DBQ_TIMER));
   if (err)
    goto freeout;
  }
 }

 for_each_port(adap, i) {
  /* Note that cmplqid below is 0 if we don't
 * have RDMA queues, and that's the right value.
 */

  if (rxq_info)
   cmplqid = rxq_info->uldrxq[i].rspq.cntxt_id;

  err = t4_sge_alloc_ctrl_txq(adap, &s->ctrlq[i], adap->port[i],
         s->fw_evtq.cntxt_id, cmplqid);
  if (err)
   goto freeout;
 }

 if (!is_t4(adap->params.chip)) {
  err = t4_sge_alloc_eth_txq(adap, &s->ptptxq, adap->port[0],
        netdev_get_tx_queue(adap->port[0], 0)
        , s->fw_evtq.cntxt_id, false);
  if (err)
   goto freeout;
 }

 t4_write_reg(adap, is_t4(adap->params.chip) ?
    MPS_TRC_RSS_CONTROL_A :
    MPS_T5_TRC_RSS_CONTROL_A,
       RSSCONTROL_V(netdev2pinfo(adap->port[0])->tx_chan) |
       QUEUENUMBER_V(s->ethrxq[0].rspq.abs_id));
 return 0;
freeout:
 dev_err(adap->pdev_dev, "Can't allocate queues, err=%d\n", -err);
 t4_free_sge_resources(adap);
 return err;
}

static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb,
        struct net_device *sb_dev)
{
 int txq;

#ifdef CONFIG_CHELSIO_T4_DCB
 /* If a Data Center Bridging has been successfully negotiated on this
 * link then we'll use the skb's priority to map it to a TX Queue.
 * The skb's priority is determined via the VLAN Tag Priority Code
 * Point field.
 */

 if (cxgb4_dcb_enabled(dev) && !is_kdump_kernel()) {
  u16 vlan_tci;
  int err;

  err = vlan_get_tag(skb, &vlan_tci);
  if (unlikely(err)) {
   if (net_ratelimit())
    netdev_warn(dev,
         "TX Packet without VLAN Tag on DCB Link\n");
   txq = 0;
  } else {
   txq = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
#ifdef CONFIG_CHELSIO_T4_FCOE
   if (skb->protocol == htons(ETH_P_FCOE))
    txq = skb->priority & 0x7;
#endif /* CONFIG_CHELSIO_T4_FCOE */
  }
  return txq;
 }
#endif /* CONFIG_CHELSIO_T4_DCB */

 if (dev->num_tc) {
  struct port_info *pi = netdev2pinfo(dev);
  u8 ver, proto;

  ver = ip_hdr(skb)->version;
  proto = (ver == 6) ? ipv6_hdr(skb)->nexthdr :
         ip_hdr(skb)->protocol;

  /* Send unsupported traffic pattern to normal NIC queues. */
  txq = netdev_pick_tx(dev, skb, sb_dev);
  if (xfrm_offload(skb) || is_ptp_enabled(skb, dev) ||
      skb->encapsulation ||
      tls_is_skb_tx_device_offloaded(skb) ||
      (proto != IPPROTO_TCP && proto != IPPROTO_UDP))
   txq = txq % pi->nqsets;

  return txq;
 }

 if (select_queue) {
  txq = (skb_rx_queue_recorded(skb)
   ? skb_get_rx_queue(skb)
   : smp_processor_id());

  while (unlikely(txq >= dev->real_num_tx_queues))
   txq -= dev->real_num_tx_queues;

  return txq;
 }

 return netdev_pick_tx(dev, skb, NULL) % dev->real_num_tx_queues;
}

static int closest_timer(const struct sge *s, int time)
{
 int i, delta, match = 0, min_delta = INT_MAX;

 for (i = 0; i < ARRAY_SIZE(s->timer_val); i++) {
  delta = time - s->timer_val[i];
  if (delta < 0)
   delta = -delta;
  if (delta < min_delta) {
   min_delta = delta;
   match = i;
  }
 }
 return match;
}

static int closest_thres(const struct sge *s, int thres)
{
 int i, delta, match = 0, min_delta = INT_MAX;

 for (i = 0; i < ARRAY_SIZE(s->counter_val); i++) {
  delta = thres - s->counter_val[i];
  if (delta < 0)
   delta = -delta;
  if (delta < min_delta) {
   min_delta = delta;
   match = i;
  }
 }
 return match;
}

/**
 * cxgb4_set_rspq_intr_params - set a queue's interrupt holdoff parameters
 * @q: the Rx queue
 * @us: the hold-off time in us, or 0 to disable timer
 * @cnt: the hold-off packet count, or 0 to disable counter
 *
 * Sets an Rx queue's interrupt hold-off time and packet count.  At least
 * one of the two needs to be enabled for the queue to generate interrupts.
 */

int cxgb4_set_rspq_intr_params(struct sge_rspq *q,
          unsigned int us, unsigned int cnt)
{
 struct adapter *adap = q->adap;

 if ((us | cnt) == 0)
  cnt = 1;

 if (cnt) {
  int err;
  u32 v, new_idx;

  new_idx = closest_thres(&adap->sge, cnt);
  if (q->desc && q->pktcnt_idx != new_idx) {
   /* the queue has already been created, update it */
   v = FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
       FW_PARAMS_PARAM_X_V(
     FW_PARAMS_PARAM_DMAQ_IQ_INTCNTTHRESH) |
       FW_PARAMS_PARAM_YZ_V(q->cntxt_id);
   err = t4_set_params(adap, adap->mbox, adap->pf, 0, 1,
         &v, &new_idx);
   if (err)
    return err;
  }
  q->pktcnt_idx = new_idx;
 }

 us = us == 0 ? 6 : closest_timer(&adap->sge, us);
 q->intr_params = QINTR_TIMER_IDX_V(us) | QINTR_CNT_EN_V(cnt > 0);
 return 0;
}

static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
{
 netdev_features_t changed = dev->features ^ features;
 const struct port_info *pi = netdev_priv(dev);
 int err;

 if (!(changed & NETIF_F_HW_VLAN_CTAG_RX))
  return 0;

 err = t4_set_rxmode(pi->adapter, pi->adapter->mbox, pi->viid,
       pi->viid_mirror, -1, -1, -1, -1,
       !!(features & NETIF_F_HW_VLAN_CTAG_RX), true);
 if (unlikely(err))
  dev->features = features ^ NETIF_F_HW_VLAN_CTAG_RX;
 return err;
}

static int setup_debugfs(struct adapter *adap)
{
 if (IS_ERR_OR_NULL(adap->debugfs_root))
  return -1;

#ifdef CONFIG_DEBUG_FS
 t4_setup_debugfs(adap);
#endif
 return 0;
}

static void cxgb4_port_mirror_free_rxq(struct adapter *adap,
           struct sge_eth_rxq *mirror_rxq)
{
 if ((adap->flags & CXGB4_FULL_INIT_DONE) &&
     !(adap->flags & CXGB4_SHUTTING_DOWN))
  cxgb4_quiesce_rx(&mirror_rxq->rspq);

 if (adap->flags & CXGB4_USING_MSIX) {
  cxgb4_clear_msix_aff(mirror_rxq->msix->vec,
         mirror_rxq->msix->aff_mask);
  free_irq(mirror_rxq->msix->vec, &mirror_rxq->rspq);
  cxgb4_free_msix_idx_in_bmap(adap, mirror_rxq->msix->idx);
 }

 free_rspq_fl(adap, &mirror_rxq->rspq, &mirror_rxq->fl);
}

static int cxgb4_port_mirror_alloc_queues(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);
 struct sge_eth_rxq *mirror_rxq;
 struct sge *s = &adap->sge;
 int ret = 0, msix = 0;
 u16 i, rxqid;
 u16 *rss;

 if (!pi->vi_mirror_count)
  return 0;

 if (s->mirror_rxq[pi->port_id])
  return 0;

 mirror_rxq = kcalloc(pi->nmirrorqsets, sizeof(*mirror_rxq), GFP_KERNEL);
 if (!mirror_rxq)
  return -ENOMEM;

 s->mirror_rxq[pi->port_id] = mirror_rxq;

 if (!(adap->flags & CXGB4_USING_MSIX))
  msix = -((int)adap->sge.intrq.abs_id + 1);

 for (i = 0, rxqid = 0; i < pi->nmirrorqsets; i++, rxqid++) {
  mirror_rxq = &s->mirror_rxq[pi->port_id][i];

  /* Allocate Mirror Rxqs */
  if (msix >= 0) {
   msix = cxgb4_get_msix_idx_from_bmap(adap);
   if (msix < 0) {
    ret = msix;
    goto out_free_queues;
   }

   mirror_rxq->msix = &adap->msix_info[msix];
   snprintf(mirror_rxq->msix->desc,
     sizeof(mirror_rxq->msix->desc),
     "%s-mirrorrxq%d", dev->name, i);
  }

  init_rspq(adap, &mirror_rxq->rspq,
     CXGB4_MIRROR_RXQ_DEFAULT_INTR_USEC,
     CXGB4_MIRROR_RXQ_DEFAULT_PKT_CNT,
     CXGB4_MIRROR_RXQ_DEFAULT_DESC_NUM,
     CXGB4_MIRROR_RXQ_DEFAULT_DESC_SIZE);

  mirror_rxq->fl.size = CXGB4_MIRROR_FLQ_DEFAULT_DESC_NUM;

  ret = t4_sge_alloc_rxq(adap, &mirror_rxq->rspq, false,
           dev, msix, &mirror_rxq->fl,
           t4_ethrx_handler, NULL, 0);
  if (ret)
   goto out_free_msix_idx;

  /* Setup MSI-X vectors for Mirror Rxqs */
  if (adap->flags & CXGB4_USING_MSIX) {
   ret = request_irq(mirror_rxq->msix->vec,
       t4_sge_intr_msix, 0,
       mirror_rxq->msix->desc,
       &mirror_rxq->rspq);
   if (ret)
    goto out_free_rxq;

   cxgb4_set_msix_aff(adap, mirror_rxq->msix->vec,
        &mirror_rxq->msix->aff_mask, i);
  }

  /* Start NAPI for Mirror Rxqs */
  cxgb4_enable_rx(adap, &mirror_rxq->rspq);
 }

 /* Setup RSS for Mirror Rxqs */
 rss = kcalloc(pi->rss_size, sizeof(u16), GFP_KERNEL);
 if (!rss) {
  ret = -ENOMEM;
  goto out_free_queues;
 }

 mirror_rxq = &s->mirror_rxq[pi->port_id][0];
 for (i = 0; i < pi->rss_size; i++)
  rss[i] = mirror_rxq[i % pi->nmirrorqsets].rspq.abs_id;

 ret = cxgb4_config_rss(pi, rss, pi->rss_size, pi->viid_mirror);
 kfree(rss);
 if (ret)
  goto out_free_queues;

 return 0;

out_free_rxq:
 free_rspq_fl(adap, &mirror_rxq->rspq, &mirror_rxq->fl);

out_free_msix_idx:
 cxgb4_free_msix_idx_in_bmap(adap, mirror_rxq->msix->idx);

out_free_queues:
 while (rxqid-- > 0)
  cxgb4_port_mirror_free_rxq(adap,
        &s->mirror_rxq[pi->port_id][rxqid]);

 kfree(s->mirror_rxq[pi->port_id]);
 s->mirror_rxq[pi->port_id] = NULL;
 return ret;
}

static void cxgb4_port_mirror_free_queues(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);
 struct sge *s = &adap->sge;
 u16 i;

 if (!pi->vi_mirror_count)
  return;

 if (!s->mirror_rxq[pi->port_id])
  return;

 for (i = 0; i < pi->nmirrorqsets; i++)
  cxgb4_port_mirror_free_rxq(adap,
        &s->mirror_rxq[pi->port_id][i]);

 kfree(s->mirror_rxq[pi->port_id]);
 s->mirror_rxq[pi->port_id] = NULL;
}

static int cxgb4_port_mirror_start(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);
 int ret, idx = -1;

 if (!pi->vi_mirror_count)
  return 0;

 /* Mirror VIs can be created dynamically after stack had
 * already setup Rx modes like MTU, promisc, allmulti, etc.
 * on main VI. So, parse what the stack had setup on the
 * main VI and update the same on the mirror VI.
 */

 ret = t4_set_rxmode(adap, adap->mbox, pi->viid, pi->viid_mirror,
       dev->mtu, (dev->flags & IFF_PROMISC) ? 1 : 0,
       (dev->flags & IFF_ALLMULTI) ? 1 : 0, 1,
       !!(dev->features & NETIF_F_HW_VLAN_CTAG_RX), true);
 if (ret) {
  dev_err(adap->pdev_dev,
   "Failed start up Rx mode for Mirror VI 0x%x, ret: %d\n",
   pi->viid_mirror, ret);
  return ret;
 }

 /* Enable replication bit for the device's MAC address
 * in MPS TCAM, so that the packets for the main VI are
 * replicated to mirror VI.
 */

 ret = cxgb4_update_mac_filt(pi, pi->viid_mirror, &idx,
        dev->dev_addr, true, NULL);
 if (ret) {
  dev_err(adap->pdev_dev,
   "Failed updating MAC filter for Mirror VI 0x%x, ret: %d\n",
   pi->viid_mirror, ret);
  return ret;
 }

 /* Enabling a Virtual Interface can result in an interrupt
 * during the processing of the VI Enable command and, in some
 * paths, result in an attempt to issue another command in the
 * interrupt context. Thus, we disable interrupts during the
 * course of the VI Enable command ...
 */

 local_bh_disable();
 ret = t4_enable_vi_params(adap, adap->mbox, pi->viid_mirror, truetrue,
      false);
 local_bh_enable();
 if (ret)
  dev_err(adap->pdev_dev,
   "Failed starting Mirror VI 0x%x, ret: %d\n",
   pi->viid_mirror, ret);

 return ret;
}

static void cxgb4_port_mirror_stop(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);

 if (!pi->vi_mirror_count)
  return;

 t4_enable_vi_params(adap, adap->mbox, pi->viid_mirror, falsefalse,
       false);
}

int cxgb4_port_mirror_alloc(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);
 int ret = 0;

 if (!pi->nmirrorqsets)
  return -EOPNOTSUPP;

 mutex_lock(&pi->vi_mirror_mutex);
 if (pi->viid_mirror) {
  pi->vi_mirror_count++;
  goto out_unlock;
 }

 ret = t4_init_port_mirror(pi, adap->mbox, pi->port_id, adap->pf, 0,
      &pi->viid_mirror);
 if (ret)
  goto out_unlock;

 pi->vi_mirror_count = 1;

 if (adap->flags & CXGB4_FULL_INIT_DONE) {
  ret = cxgb4_port_mirror_alloc_queues(dev);
  if (ret)
   goto out_free_vi;

  ret = cxgb4_port_mirror_start(dev);
  if (ret)
   goto out_free_queues;
 }

 mutex_unlock(&pi->vi_mirror_mutex);
 return 0;

out_free_queues:
 cxgb4_port_mirror_free_queues(dev);

out_free_vi:
 pi->vi_mirror_count = 0;
 t4_free_vi(adap, adap->mbox, adap->pf, 0, pi->viid_mirror);
 pi->viid_mirror = 0;

out_unlock:
 mutex_unlock(&pi->vi_mirror_mutex);
 return ret;
}

void cxgb4_port_mirror_free(struct net_device *dev)
{
 struct port_info *pi = netdev2pinfo(dev);
 struct adapter *adap = netdev2adap(dev);

 mutex_lock(&pi->vi_mirror_mutex);
 if (!pi->viid_mirror)
  goto out_unlock;

 if (pi->vi_mirror_count > 1) {
  pi->vi_mirror_count--;
  goto out_unlock;
 }

 cxgb4_port_mirror_stop(dev);
 cxgb4_port_mirror_free_queues(dev);

 pi->vi_mirror_count = 0;
 t4_free_vi(adap, adap->mbox, adap->pf, 0, pi->viid_mirror);
 pi->viid_mirror = 0;

out_unlock:
 mutex_unlock(&pi->vi_mirror_mutex);
}

/*
 * upper-layer driver support
 */


/*
 * Allocate an active-open TID and set it to the supplied value.
 */

int cxgb4_alloc_atid(struct tid_info *t, void *data)
{
 int atid = -1;

 spin_lock_bh(&t->atid_lock);
 if (t->afree) {
  union aopen_entry *p = t->afree;

  atid = (p - t->atid_tab) + t->atid_base;
  t->afree = p->next;
  p->data = data;
  t->atids_in_use++;
 }
 spin_unlock_bh(&t->atid_lock);
 return atid;
}
EXPORT_SYMBOL(cxgb4_alloc_atid);

/*
 * Release an active-open TID.
 */

void cxgb4_free_atid(struct tid_info *t, unsigned int atid)
{
 union aopen_entry *p = &t->atid_tab[atid - t->atid_base];

 spin_lock_bh(&t->atid_lock);
 p->next = t->afree;
 t->afree = p;
 t->atids_in_use--;
 spin_unlock_bh(&t->atid_lock);
}
EXPORT_SYMBOL(cxgb4_free_atid);

/*
 * Allocate a server TID and set it to the supplied value.
 */

int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)
{
 int stid;

 spin_lock_bh(&t->stid_lock);
 if (family == PF_INET) {
  stid = find_first_zero_bit(t->stid_bmap, t->nstids);
  if (stid < t->nstids)
   __set_bit(stid, t->stid_bmap);
  else
   stid = -1;
 } else {
  stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 1);
  if (stid < 0)
   stid = -1;
 }
 if (stid >= 0) {
  t->stid_tab[stid].data = data;
  stid += t->stid_base;
  /* IPv6 requires max of 520 bits or 16 cells in TCAM
 * This is equivalent to 4 TIDs. With CLIP enabled it
 * needs 2 TIDs.
 */

  if (family == PF_INET6) {
   t->stids_in_use += 2;
   t->v6_stids_in_use += 2;
  } else {
   t->stids_in_use++;
  }
 }
 spin_unlock_bh(&t->stid_lock);
 return stid;
}
EXPORT_SYMBOL(cxgb4_alloc_stid);

/* Allocate a server filter TID and set it to the supplied value.
 */

int cxgb4_alloc_sftid(struct tid_info *t, int family, void *data)
{
 int stid;

 spin_lock_bh(&t->stid_lock);
 if (family == PF_INET) {
  stid = find_next_zero_bit(t->stid_bmap,
    t->nstids + t->nsftids, t->nstids);
  if (stid < (t->nstids + t->nsftids))
   __set_bit(stid, t->stid_bmap);
  else
   stid = -1;
 } else {
  stid = -1;
 }
 if (stid >= 0) {
  t->stid_tab[stid].data = data;
  stid -= t->nstids;
  stid += t->sftid_base;
  t->sftids_in_use++;
 }
 spin_unlock_bh(&t->stid_lock);
 return stid;
}
EXPORT_SYMBOL(cxgb4_alloc_sftid);

/* Release a server TID.
 */

void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family)
{
 /* Is it a server filter TID? */
 if (t->nsftids && (stid >= t->sftid_base)) {
  stid -= t->sftid_base;
  stid += t->nstids;
 } else {
  stid -= t->stid_base;
 }

 spin_lock_bh(&t->stid_lock);
 if (family == PF_INET)
  __clear_bit(stid, t->stid_bmap);
 else
  bitmap_release_region(t->stid_bmap, stid, 1);
 t->stid_tab[stid].data = NULL;
 if (stid < t->nstids) {
  if (family == PF_INET6) {
   t->stids_in_use -= 2;
   t->v6_stids_in_use -= 2;
  } else {
   t->stids_in_use--;
  }
 } else {
  t->sftids_in_use--;
 }

 spin_unlock_bh(&t->stid_lock);
}
EXPORT_SYMBOL(cxgb4_free_stid);

/*
 * Populate a TID_RELEASE WR.  Caller must properly size the skb.
 */

static void mk_tid_release(struct sk_buff *skb, unsigned int chan,
      unsigned int tid)
{
 struct cpl_tid_release *req;

 set_wr_txq(skb, CPL_PRIORITY_SETUP, chan);
 req = __skb_put(skb, sizeof(*req));
 INIT_TP_WR(req, tid);
 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_TID_RELEASE, tid));
}

/*
 * Queue a TID release request and if necessary schedule a work queue to
 * process it.
 */

static void cxgb4_queue_tid_release(struct tid_info *t, unsigned int chan,
        unsigned int tid)
{
 struct adapter *adap = container_of(t, struct adapter, tids);
 void **p = &t->tid_tab[tid - t->tid_base];

 spin_lock_bh(&adap->tid_release_lock);
 *p = adap->tid_release_head;
 /* Low 2 bits encode the Tx channel number */
 adap->tid_release_head = (void **)((uintptr_t)p | chan);
 if (!adap->tid_release_task_busy) {
  adap->tid_release_task_busy = true;
  queue_work(adap->workq, &adap->tid_release_task);
 }
 spin_unlock_bh(&adap->tid_release_lock);
}

/*
 * Process the list of pending TID release requests.
 */

static void process_tid_release_list(struct work_struct *work)
{
 struct sk_buff *skb;
 struct adapter *adap;

 adap = container_of(work, struct adapter, tid_release_task);

 spin_lock_bh(&adap->tid_release_lock);
 while (adap->tid_release_head) {
  void **p = adap->tid_release_head;
  unsigned int chan = (uintptr_t)p & 3;
  p = (void *)p - chan;

  adap->tid_release_head = *p;
  *p = NULL;
  spin_unlock_bh(&adap->tid_release_lock);

  while (!(skb = alloc_skb(sizeof(struct cpl_tid_release),
      GFP_KERNEL)))
   schedule_timeout_uninterruptible(1);

  mk_tid_release(skb, chan, p - adap->tids.tid_tab);
  t4_ofld_send(adap, skb);
  spin_lock_bh(&adap->tid_release_lock);
 }
 adap->tid_release_task_busy = false;
 spin_unlock_bh(&adap->tid_release_lock);
}

/*
 * Release a TID and inform HW.  If we are unable to allocate the release
 * message we defer to a work queue.
 */

void cxgb4_remove_tid(struct tid_info *t, unsigned int chan, unsigned int tid,
        unsigned short family)
{
 struct adapter *adap = container_of(t, struct adapter, tids);
 struct sk_buff *skb;

 if (tid_out_of_range(&adap->tids, tid)) {
  dev_err(adap->pdev_dev, "tid %d out of range\n", tid);
  return;
 }

 if (t->tid_tab[tid - adap->tids.tid_base]) {
  t->tid_tab[tid - adap->tids.tid_base] = NULL;
  atomic_dec(&t->conns_in_use);
  if (t->hash_base && (tid >= t->hash_base)) {
   if (family == AF_INET6)
    atomic_sub(2, &t->hash_tids_in_use);
   else
    atomic_dec(&t->hash_tids_in_use);
  } else {
   if (family == AF_INET6)
    atomic_sub(2, &t->tids_in_use);
   else
    atomic_dec(&t->tids_in_use);
  }
 }

 skb = alloc_skb(sizeof(struct cpl_tid_release), GFP_ATOMIC);
 if (likely(skb)) {
  mk_tid_release(skb, chan, tid);
  t4_ofld_send(adap, skb);
 } else
  cxgb4_queue_tid_release(t, chan, tid);
}
EXPORT_SYMBOL(cxgb4_remove_tid);

/*
 * Allocate and initialize the TID tables.  Returns 0 on success.
 */

static int tid_init(struct tid_info *t)
{
 struct adapter *adap = container_of(t, struct adapter, tids);
 unsigned int max_ftids = t->nftids + t->nsftids;
 unsigned int natids = t->natids;
 unsigned int hpftid_bmap_size;
 unsigned int eotid_bmap_size;
 unsigned int stid_bmap_size;
 unsigned int ftid_bmap_size;
 size_t size;

 stid_bmap_size = BITS_TO_LONGS(t->nstids + t->nsftids);
 ftid_bmap_size = BITS_TO_LONGS(t->nftids);
 hpftid_bmap_size = BITS_TO_LONGS(t->nhpftids);
 eotid_bmap_size = BITS_TO_LONGS(t->neotids);
 size = t->ntids * sizeof(*t->tid_tab) +
        natids * sizeof(*t->atid_tab) +
        t->nstids * sizeof(*t->stid_tab) +
        t->nsftids * sizeof(*t->stid_tab) +
        stid_bmap_size * sizeof(long) +
        t->nhpftids * sizeof(*t->hpftid_tab) +
        hpftid_bmap_size * sizeof(long) +
        max_ftids * sizeof(*t->ftid_tab) +
        ftid_bmap_size * sizeof(long) +
        t->neotids * sizeof(*t->eotid_tab) +
        eotid_bmap_size * sizeof(long);

 t->tid_tab = kvzalloc(size, GFP_KERNEL);
 if (!t->tid_tab)
  return -ENOMEM;

 t->atid_tab = (union aopen_entry *)&t->tid_tab[t->ntids];
 t->stid_tab = (struct serv_entry *)&t->atid_tab[natids];
 t->stid_bmap = (unsigned long *)&t->stid_tab[t->nstids + t->nsftids];
 t->hpftid_tab = (struct filter_entry *)&t->stid_bmap[stid_bmap_size];
 t->hpftid_bmap = (unsigned long *)&t->hpftid_tab[t->nhpftids];
 t->ftid_tab = (struct filter_entry *)&t->hpftid_bmap[hpftid_bmap_size];
 t->ftid_bmap = (unsigned long *)&t->ftid_tab[max_ftids];
 t->eotid_tab = (struct eotid_entry *)&t->ftid_bmap[ftid_bmap_size];
 t->eotid_bmap = (unsigned long *)&t->eotid_tab[t->neotids];
 spin_lock_init(&t->stid_lock);
 spin_lock_init(&t->atid_lock);
 spin_lock_init(&t->ftid_lock);

 t->stids_in_use = 0;
 t->v6_stids_in_use = 0;
 t->sftids_in_use = 0;
 t->afree = NULL;
 t->atids_in_use = 0;
 atomic_set(&t->tids_in_use, 0);
 atomic_set(&t->conns_in_use, 0);
 atomic_set(&t->hash_tids_in_use, 0);
 atomic_set(&t->eotids_in_use, 0);

 /* Setup the free list for atid_tab and clear the stid bitmap. */
 if (natids) {
  while (--natids)
   t->atid_tab[natids - 1].next = &t->atid_tab[natids];
  t->afree = t->atid_tab;
 }

 if (is_offload(adap)) {
  bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);
  /* Reserve stid 0 for T4/T5 adapters */
  if (!t->stid_base &&
      CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5)
   __set_bit(0, t->stid_bmap);

  if (t->neotids)
   bitmap_zero(t->eotid_bmap, t->neotids);
 }

 if (t->nhpftids)
  bitmap_zero(t->hpftid_bmap, t->nhpftids);
 bitmap_zero(t->ftid_bmap, t->nftids);
 return 0;
}

/**
 * cxgb4_create_server - create an IP server
 * @dev: the device
 * @stid: the server TID
 * @sip: local IP address to bind server to
 * @sport: the server's TCP port
 * @vlan: the VLAN header information
 * @queue: queue to direct messages from this server to
 *
 * Create an IP server for the given port and address.
 * Returns <0 on error and one of the %NET_XMIT_* values on success.
 */

int cxgb4_create_server(const struct net_device *dev, unsigned int stid,
   __be32 sip, __be16 sport, __be16 vlan,
   unsigned int queue)
{
 unsigned int chan;
 struct sk_buff *skb;
 struct adapter *adap;
 struct cpl_pass_open_req *req;
 int ret;

 skb = alloc_skb(sizeof(*req), GFP_KERNEL);
 if (!skb)
  return -ENOMEM;

 adap = netdev2adap(dev);
 req = __skb_put(skb, sizeof(*req));
 INIT_TP_WR(req, 0);
 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, stid));
 req->local_port = sport;
 req->peer_port = htons(0);
 req->local_ip = sip;
 req->peer_ip = htonl(0);
 chan = rxq_to_chan(&adap->sge, queue);
 req->opt0 = cpu_to_be64(TX_CHAN_V(chan));
 req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) |
    SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue));
 ret = t4_mgmt_tx(adap, skb);
 return net_xmit_eval(ret);
}
EXPORT_SYMBOL(cxgb4_create_server);

/* cxgb4_create_server6 - create an IPv6 server
 * @dev: the device
 * @stid: the server TID
 * @sip: local IPv6 address to bind server to
 * @sport: the server's TCP port
 * @queue: queue to direct messages from this server to
 *
 * Create an IPv6 server for the given port and address.
 * Returns <0 on error and one of the %NET_XMIT_* values on success.
 */

int cxgb4_create_server6(const struct net_device *dev, unsigned int stid,
    const struct in6_addr *sip, __be16 sport,
    unsigned int queue)
{
 unsigned int chan;
 struct sk_buff *skb;
 struct adapter *adap;
 struct cpl_pass_open_req6 *req;
 int ret;

 skb = alloc_skb(sizeof(*req), GFP_KERNEL);
 if (!skb)
  return -ENOMEM;

 adap = netdev2adap(dev);
 req = __skb_put(skb, sizeof(*req));
 INIT_TP_WR(req, 0);
 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ6, stid));
 req->local_port = sport;
 req->peer_port = htons(0);
 req->local_ip_hi = *(__be64 *)(sip->s6_addr);
 req->local_ip_lo = *(__be64 *)(sip->s6_addr + 8);
 req->peer_ip_hi = cpu_to_be64(0);
 req->peer_ip_lo = cpu_to_be64(0);
 chan = rxq_to_chan(&adap->sge, queue);
 req->opt0 = cpu_to_be64(TX_CHAN_V(chan));
 req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) |
    SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue));
 ret = t4_mgmt_tx(adap, skb);
 return net_xmit_eval(ret);
}
EXPORT_SYMBOL(cxgb4_create_server6);

int cxgb4_remove_server(const struct net_device *dev, unsigned int stid,
   unsigned int queue, bool ipv6)
{
 struct sk_buff *skb;
 struct adapter *adap;
 struct cpl_close_listsvr_req *req;
 int ret;

 adap = netdev2adap(dev);

 skb = alloc_skb(sizeof(*req), GFP_KERNEL);
 if (!skb)
  return -ENOMEM;

 req = __skb_put(skb, sizeof(*req));
 INIT_TP_WR(req, 0);
 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid));
 req->reply_ctrl = htons(NO_REPLY_V(0) | (ipv6 ? LISTSVR_IPV6_V(1) :
    LISTSVR_IPV6_V(0)) | QUEUENO_V(queue));
 ret = t4_mgmt_tx(adap, skb);
 return net_xmit_eval(ret);
}
EXPORT_SYMBOL(cxgb4_remove_server);

/**
 * cxgb4_best_mtu - find the entry in the MTU table closest to an MTU
 * @mtus: the HW MTU table
 * @mtu: the target MTU
 * @idx: index of selected entry in the MTU table
 *
 * Returns the index and the value in the HW MTU table that is closest to
 * but does not exceed @mtu, unless @mtu is smaller than any value in the
 * table, in which case that smallest available value is selected.
 */

unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
       unsigned int *idx)
{
 unsigned int i = 0;

 while (i < NMTUS - 1 && mtus[i + 1] <= mtu)
  ++i;
 if (idx)
  *idx = i;
 return mtus[i];
}
EXPORT_SYMBOL(cxgb4_best_mtu);

/**
 *     cxgb4_best_aligned_mtu - find best MTU, [hopefully] data size aligned
 *     @mtus: the HW MTU table
 *     @header_size: Header Size
 *     @data_size_max: maximum Data Segment Size
 *     @data_size_align: desired Data Segment Size Alignment (2^N)
 *     @mtu_idxp: HW MTU Table Index return value pointer (possibly NULL)
 *
 *     Similar to cxgb4_best_mtu() but instead of searching the Hardware
 *     MTU Table based solely on a Maximum MTU parameter, we break that
 *     parameter up into a Header Size and Maximum Data Segment Size, and
 *     provide a desired Data Segment Size Alignment.  If we find an MTU in
 *     the Hardware MTU Table which will result in a Data Segment Size with
 *     the requested alignment _and_ that MTU isn't "too far" from the
 *     closest MTU, then we'll return that rather than the closest MTU.
 */

unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
        unsigned short header_size,
        unsigned short data_size_max,
        unsigned short data_size_align,
        unsigned int *mtu_idxp)
{
 unsigned short max_mtu = header_size + data_size_max;
 unsigned short data_size_align_mask = data_size_align - 1;
 int mtu_idx, aligned_mtu_idx;

 /* Scan the MTU Table till we find an MTU which is larger than our
 * Maximum MTU or we reach the end of the table.  Along the way,
 * record the last MTU found, if any, which will result in a Data
 * Segment Length matching the requested alignment.
 */

 for (mtu_idx = 0, aligned_mtu_idx = -1; mtu_idx < NMTUS; mtu_idx++) {
  unsigned short data_size = mtus[mtu_idx] - header_size;

  /* If this MTU minus the Header Size would result in a
 * Data Segment Size of the desired alignment, remember it.
 */

  if ((data_size & data_size_align_mask) == 0)
   aligned_mtu_idx = mtu_idx;

  /* If we're not at the end of the Hardware MTU Table and the
 * next element is larger than our Maximum MTU, drop out of
 * the loop.
 */

  if (mtu_idx+1 < NMTUS && mtus[mtu_idx+1] > max_mtu)
   break;
 }

 /* If we fell out of the loop because we ran to the end of the table,
 * then we just have to use the last [largest] entry.
 */

 if (mtu_idx == NMTUS)
  mtu_idx--;

 /* If we found an MTU which resulted in the requested Data Segment
 * Length alignment and that's "not far" from the largest MTU which is
 * less than or equal to the maximum MTU, then use that.
 */

 if (aligned_mtu_idx >= 0 &&
     mtu_idx - aligned_mtu_idx <= 1)
  mtu_idx = aligned_mtu_idx;

 /* If the caller has passed in an MTU Index pointer, pass the
 * MTU Index back.  Return the MTU value.
 */

 if (mtu_idxp)
  *mtu_idxp = mtu_idx;
 return mtus[mtu_idx];
}
EXPORT_SYMBOL(cxgb4_best_aligned_mtu);

/**
 * cxgb4_port_chan - get the HW channel of a port
 * @dev: the net device for the port
 *
 * Return the HW Tx channel of the given port.
 */

unsigned int cxgb4_port_chan(const struct net_device *dev)
{
 return netdev2pinfo(dev)->tx_chan;
}
EXPORT_SYMBOL(cxgb4_port_chan);

/**
 *      cxgb4_port_e2cchan - get the HW c-channel of a port
 *      @dev: the net device for the port
 *
 *      Return the HW RX c-channel of the given port.
 */

unsigned int cxgb4_port_e2cchan(const struct net_device *dev)
{
 return netdev2pinfo(dev)->rx_cchan;
}
EXPORT_SYMBOL(cxgb4_port_e2cchan);

unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo)
{
 struct adapter *adap = netdev2adap(dev);
 u32 v1, v2, lp_count, hp_count;

 v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A);
 v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A);
 if (is_t4(adap->params.chip)) {
  lp_count = LP_COUNT_G(v1);
  hp_count = HP_COUNT_G(v1);
 } else {
  lp_count = LP_COUNT_T5_G(v1);
  hp_count = HP_COUNT_T5_G(v2);
 }
 return lpfifo ? lp_count : hp_count;
}
EXPORT_SYMBOL(cxgb4_dbfifo_count);

/**
 * cxgb4_port_viid - get the VI id of a port
 * @dev: the net device for the port
 *
 * Return the VI id of the given port.
 */

unsigned int cxgb4_port_viid(const struct net_device *dev)
{
 return netdev2pinfo(dev)->viid;
}
EXPORT_SYMBOL(cxgb4_port_viid);

/**
 * cxgb4_port_idx - get the index of a port
 * @dev: the net device for the port
 *
 * Return the index of the given port.
 */

unsigned int cxgb4_port_idx(const struct net_device *dev)
{
 return netdev2pinfo(dev)->port_id;
}
EXPORT_SYMBOL(cxgb4_port_idx);

void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4,
    struct tp_tcp_stats *v6)
{
 struct adapter *adap = pci_get_drvdata(pdev);

 spin_lock(&adap->stats_lock);
 t4_tp_get_tcp_stats(adap, v4, v6, false);
 spin_unlock(&adap->stats_lock);
}
EXPORT_SYMBOL(cxgb4_get_tcp_stats);

int cxgb4_flush_eq_cache(struct net_device *dev)
{
 struct adapter *adap = netdev2adap(dev);

 return t4_sge_ctxt_flush(adap, adap->mbox, CTXT_EGRESS);
}
EXPORT_SYMBOL(cxgb4_flush_eq_cache);

static int read_eq_indices(struct adapter *adap, u16 qid, u16 *pidx, u16 *cidx)
{
 u32 addr = t4_read_reg(adap, SGE_DBQ_CTXT_BADDR_A) + 24 * qid + 8;
 __be64 indices;
 int ret;

 spin_lock(&adap->win0_lock);
 ret = t4_memory_rw(adap, 0, MEM_EDC0, addr,
      sizeof(indices), (__be32 *)&indices,
      T4_MEMORY_READ);
 spin_unlock(&adap->win0_lock);
 if (!ret) {
  *cidx = (be64_to_cpu(indices) >> 25) & 0xffff;
  *pidx = (be64_to_cpu(indices) >> 9) & 0xffff;
 }
 return ret;
}

int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx,
   u16 size)
{
 struct adapter *adap = netdev2adap(dev);
 u16 hw_pidx, hw_cidx;
 int ret;

 ret = read_eq_indices(adap, qid, &hw_pidx, &hw_cidx);
 if (ret)
  goto out;

 if (pidx != hw_pidx) {
  u16 delta;
  u32 val;

  if (pidx >= hw_pidx)
   delta = pidx - hw_pidx;
  else
   delta = size - hw_pidx + pidx;

  if (is_t4(adap->params.chip))
   val = PIDX_V(delta);
  else
   val = PIDX_T5_V(delta);
  wmb();
  t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A),
        QID_V(qid) | val);
 }
out:
 return ret;
}
EXPORT_SYMBOL(cxgb4_sync_txq_pidx);

int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
{
 u32 edc0_size, edc1_size, mc0_size, mc1_size, size;
 u32 edc0_end, edc1_end, mc0_end, mc1_end;
 u32 offset, memtype, memaddr;
 struct adapter *adap;
 u32 hma_size = 0;
 int ret;

 adap = netdev2adap(dev);

 offset = ((stag >> 8) * 32) + adap->vres.stag.start;

 /* Figure out where the offset lands in the Memory Type/Address scheme.
 * This code assumes that the memory is laid out starting at offset 0
 * with no breaks as: EDC0, EDC1, MC0, MC1. All cards have both EDC0
 * and EDC1.  Some cards will have neither MC0 nor MC1, most cards have
 * MC0, and some have both MC0 and MC1.
 */

 size = t4_read_reg(adap, MA_EDRAM0_BAR_A);
 edc0_size = EDRAM0_SIZE_G(size) << 20;
 size = t4_read_reg(adap, MA_EDRAM1_BAR_A);
 edc1_size = EDRAM1_SIZE_G(size) << 20;
 size = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A);
 mc0_size = EXT_MEM0_SIZE_G(size) << 20;

 if (t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A) & HMA_MUX_F) {
  size = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A);
  hma_size = EXT_MEM1_SIZE_G(size) << 20;
 }
 edc0_end = edc0_size;
 edc1_end = edc0_end + edc1_size;
 mc0_end = edc1_end + mc0_size;

 if (offset < edc0_end) {
  memtype = MEM_EDC0;
  memaddr = offset;
 } else if (offset < edc1_end) {
  memtype = MEM_EDC1;
  memaddr = offset - edc0_end;
 } else {
  if (hma_size && (offset < (edc1_end + hma_size))) {
   memtype = MEM_HMA;
   memaddr = offset - edc1_end;
  } else if (offset < mc0_end) {
   memtype = MEM_MC0;
   memaddr = offset - edc1_end;
  } else if (is_t5(adap->params.chip)) {
   size = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A);
   mc1_size = EXT_MEM1_SIZE_G(size) << 20;
   mc1_end = mc0_end + mc1_size;
   if (offset < mc1_end) {
    memtype = MEM_MC1;
    memaddr = offset - mc0_end;
   } else {
    /* offset beyond the end of any memory */
    goto err;
   }
  } else {
   /* T4/T6 only has a single memory channel */
   goto err;
  }
 }

 spin_lock(&adap->win0_lock);
 ret = t4_memory_rw(adap, 0, memtype, memaddr, 32, tpte, T4_MEMORY_READ);
 spin_unlock(&adap->win0_lock);
 return ret;

err:
 dev_err(adap->pdev_dev, "stag %#x, offset %#x out of range\n",
  stag, offset);
 return -EINVAL;
}
EXPORT_SYMBOL(cxgb4_read_tpte);

u64 cxgb4_read_sge_timestamp(struct net_device *dev)
{
 u32 hi, lo;
 struct adapter *adap;

 adap = netdev2adap(dev);
 lo = t4_read_reg(adap, SGE_TIMESTAMP_LO_A);
 hi = TSVAL_G(t4_read_reg(adap, SGE_TIMESTAMP_HI_A));

 return ((u64)hi << 32) | (u64)lo;
}
EXPORT_SYMBOL(cxgb4_read_sge_timestamp);

int cxgb4_bar2_sge_qregs(struct net_device *dev,
    unsigned int qid,
    enum cxgb4_bar2_qtype qtype,
    int user,
    u64 *pbar2_qoffset,
    unsigned int *pbar2_qid)
{
 return t4_bar2_sge_qregs(netdev2adap(dev),
     qid,
     (qtype == CXGB4_BAR2_QTYPE_EGRESS
      ? T4_BAR2_QTYPE_EGRESS
      : T4_BAR2_QTYPE_INGRESS),
     user,
     pbar2_qoffset,
     pbar2_qid);
}
EXPORT_SYMBOL(cxgb4_bar2_sge_qregs);

static struct pci_driver cxgb4_driver;

static void check_neigh_update(struct neighbour *neigh)
{
 const struct device *parent;
 const struct net_device *netdev = neigh->dev;

 if (is_vlan_dev(netdev))
  netdev = vlan_dev_real_dev(netdev);
 parent = netdev->dev.parent;
 if (parent && parent->driver == &cxgb4_driver.driver)
  t4_l2t_update(dev_get_drvdata(parent), neigh);
}

static int netevent_cb(struct notifier_block *nb, unsigned long event,
         void *data)
{
 switch (event) {
 case NETEVENT_NEIGH_UPDATE:
  check_neigh_update(data);
  break;
 case NETEVENT_REDIRECT:
 default:
  break;
 }
 return 0;
}

static bool netevent_registered;
static struct notifier_block cxgb4_netevent_nb = {
 .notifier_call = netevent_cb
};

static void drain_db_fifo(struct adapter *adap, int usecs)
{
 u32 v1, v2, lp_count, hp_count;

 do {
  v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A);
  v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A);
  if (is_t4(adap->params.chip)) {
   lp_count = LP_COUNT_G(v1);
   hp_count = HP_COUNT_G(v1);
  } else {
   lp_count = LP_COUNT_T5_G(v1);
   hp_count = HP_COUNT_T5_G(v2);
  }

  if (lp_count == 0 && hp_count == 0)
   break;
  set_current_state(TASK_UNINTERRUPTIBLE);
  schedule_timeout(usecs_to_jiffies(usecs));
 } while (1);
}

static void disable_txq_db(struct sge_txq *q)
{
 unsigned long flags;

 spin_lock_irqsave(&q->db_lock, flags);
 q->db_disabled = 1;
 spin_unlock_irqrestore(&q->db_lock, flags);
}

static void enable_txq_db(struct adapter *adap, struct sge_txq *q)
{
 spin_lock_irq(&q->db_lock);
 if (q->db_pidx_inc) {
  /* Make sure that all writes to the TX descriptors
 * are committed before we tell HW about them.
 */

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge