Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/cavium/liquidio/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 47 kB image not shown  

Quelle  lio_core.c   Sprache: C

 
/**********************************************************************
 * Author: Cavium, Inc.
 *
 * Contact: support@cavium.com
 *          Please include "LiquidIO" in the subject.
 *
 * Copyright (c) 2003-2016 Cavium, Inc.
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, Version 2, as
 * published by the Free Software Foundation.
 *
 * This file is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT.  See the GNU General Public License for more details.
 ***********************************************************************/

#include <linux/pci.h>
#include <linux/if_vlan.h>
#include "liquidio_common.h"
#include "octeon_droq.h"
#include "octeon_iq.h"
#include "response_manager.h"
#include "octeon_device.h"
#include "octeon_nic.h"
#include "octeon_main.h"
#include "octeon_network.h"

MODULE_AUTHOR("Cavium Networks, ");
MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Core");
MODULE_LICENSE("GPL");

/* OOM task polling interval */
#define LIO_OOM_POLL_INTERVAL_MS 250

#define OCTNIC_MAX_SG  MAX_SKB_FRAGS

/**
 * lio_delete_glists - Delete gather lists
 * @lio: per-network private data
 */

void lio_delete_glists(struct lio *lio)
{
 struct octnic_gather *g;
 int i;

 kfree(lio->glist_lock);
 lio->glist_lock = NULL;

 if (!lio->glist)
  return;

 for (i = 0; i < lio->oct_dev->num_iqs; i++) {
  do {
   g = (struct octnic_gather *)
       lio_list_delete_head(&lio->glist[i]);
   kfree(g);
  } while (g);

  if (lio->glists_virt_base && lio->glists_virt_base[i] &&
      lio->glists_dma_base && lio->glists_dma_base[i]) {
   lio_dma_free(lio->oct_dev,
         lio->glist_entry_size * lio->tx_qsize,
         lio->glists_virt_base[i],
         lio->glists_dma_base[i]);
  }
 }

 kfree(lio->glists_virt_base);
 lio->glists_virt_base = NULL;

 kfree(lio->glists_dma_base);
 lio->glists_dma_base = NULL;

 kfree(lio->glist);
 lio->glist = NULL;
}
EXPORT_SYMBOL_GPL(lio_delete_glists);

/**
 * lio_setup_glists - Setup gather lists
 * @oct: octeon_device
 * @lio: per-network private data
 * @num_iqs: count of iqs to allocate
 */

int lio_setup_glists(struct octeon_device *oct, struct lio *lio, int num_iqs)
{
 struct octnic_gather *g;
 int i, j;

 lio->glist_lock =
     kcalloc(num_iqs, sizeof(*lio->glist_lock), GFP_KERNEL);
 if (!lio->glist_lock)
  return -ENOMEM;

 lio->glist =
     kcalloc(num_iqs, sizeof(*lio->glist), GFP_KERNEL);
 if (!lio->glist) {
  kfree(lio->glist_lock);
  lio->glist_lock = NULL;
  return -ENOMEM;
 }

 lio->glist_entry_size =
  ROUNDUP8((ROUNDUP4(OCTNIC_MAX_SG) >> 2) * OCT_SG_ENTRY_SIZE);

 /* allocate memory to store virtual and dma base address of
 * per glist consistent memory
 */

 lio->glists_virt_base = kcalloc(num_iqs, sizeof(*lio->glists_virt_base),
     GFP_KERNEL);
 lio->glists_dma_base = kcalloc(num_iqs, sizeof(*lio->glists_dma_base),
           GFP_KERNEL);

 if (!lio->glists_virt_base || !lio->glists_dma_base) {
  lio_delete_glists(lio);
  return -ENOMEM;
 }

 for (i = 0; i < num_iqs; i++) {
  int numa_node = dev_to_node(&oct->pci_dev->dev);

  spin_lock_init(&lio->glist_lock[i]);

  INIT_LIST_HEAD(&lio->glist[i]);

  lio->glists_virt_base[i] =
   lio_dma_alloc(oct,
          lio->glist_entry_size * lio->tx_qsize,
          &lio->glists_dma_base[i]);

  if (!lio->glists_virt_base[i]) {
   lio_delete_glists(lio);
   return -ENOMEM;
  }

  for (j = 0; j < lio->tx_qsize; j++) {
   g = kzalloc_node(sizeof(*g), GFP_KERNEL,
      numa_node);
   if (!g)
    g = kzalloc(sizeof(*g), GFP_KERNEL);
   if (!g)
    break;

   g->sg = lio->glists_virt_base[i] +
    (j * lio->glist_entry_size);

   g->sg_dma_ptr = lio->glists_dma_base[i] +
     (j * lio->glist_entry_size);

   list_add_tail(&g->list, &lio->glist[i]);
  }

  if (j != lio->tx_qsize) {
   lio_delete_glists(lio);
   return -ENOMEM;
  }
 }

 return 0;
}
EXPORT_SYMBOL_GPL(lio_setup_glists);

int liquidio_set_feature(struct net_device *netdev, int cmd, u16 param1)
{
 struct lio *lio = GET_LIO(netdev);
 struct octeon_device *oct = lio->oct_dev;
 struct octnic_ctrl_pkt nctrl;
 int ret = 0;

 memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));

 nctrl.ncmd.u64 = 0;
 nctrl.ncmd.s.cmd = cmd;
 nctrl.ncmd.s.param1 = param1;
 nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
 nctrl.netpndev = (u64)netdev;
 nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;

 ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
 if (ret) {
  dev_err(&oct->pci_dev->dev, "Feature change failed in core (ret: 0x%x)\n",
   ret);
  if (ret > 0)
   ret = -EIO;
 }
 return ret;
}
EXPORT_SYMBOL_GPL(liquidio_set_feature);

void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl,
     unsigned int bytes_compl)
{
 struct netdev_queue *netdev_queue = txq;

 netdev_tx_completed_queue(netdev_queue, pkts_compl, bytes_compl);
}

void octeon_update_tx_completion_counters(void *buf, int reqtype,
       unsigned int *pkts_compl,
       unsigned int *bytes_compl)
{
 struct octnet_buf_free_info *finfo;
 struct sk_buff *skb = NULL;
 struct octeon_soft_command *sc;

 switch (reqtype) {
 case REQTYPE_NORESP_NET:
 case REQTYPE_NORESP_NET_SG:
  finfo = buf;
  skb = finfo->skb;
  break;

 case REQTYPE_RESP_NET_SG:
 case REQTYPE_RESP_NET:
  sc = buf;
  skb = sc->callback_arg;
  break;

 default:
  return;
 }

 (*pkts_compl)++;
 *bytes_compl += skb->len;
}

int octeon_report_sent_bytes_to_bql(void *buf, int reqtype)
{
 struct octnet_buf_free_info *finfo;
 struct sk_buff *skb;
 struct octeon_soft_command *sc;
 struct netdev_queue *txq;

 switch (reqtype) {
 case REQTYPE_NORESP_NET:
 case REQTYPE_NORESP_NET_SG:
  finfo = buf;
  skb = finfo->skb;
  break;

 case REQTYPE_RESP_NET_SG:
 case REQTYPE_RESP_NET:
  sc = buf;
  skb = sc->callback_arg;
  break;

 default:
  return 0;
 }

 txq = netdev_get_tx_queue(skb->dev, skb_get_queue_mapping(skb));
 netdev_tx_sent_queue(txq, skb->len);

 return netif_xmit_stopped(txq);
}

void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr)
{
 struct octnic_ctrl_pkt *nctrl = (struct octnic_ctrl_pkt *)nctrl_ptr;
 struct net_device *netdev = (struct net_device *)nctrl->netpndev;
 struct lio *lio = GET_LIO(netdev);
 struct octeon_device *oct = lio->oct_dev;
 u8 *mac;

 if (nctrl->sc_status)
  return;

 switch (nctrl->ncmd.s.cmd) {
 case OCTNET_CMD_CHANGE_DEVFLAGS:
 case OCTNET_CMD_SET_MULTI_LIST:
 case OCTNET_CMD_SET_UC_LIST:
  break;

 case OCTNET_CMD_CHANGE_MACADDR:
  mac = ((u8 *)&nctrl->udd[0]) + 2;
  if (nctrl->ncmd.s.param1) {
   /* vfidx is 0 based, but vf_num (param1) is 1 based */
   int vfidx = nctrl->ncmd.s.param1 - 1;
   bool mac_is_admin_assigned = nctrl->ncmd.s.param2;

   if (mac_is_admin_assigned)
    netif_info(lio, probe, lio->netdev,
        "MAC Address %pM is configured for VF %d\n",
        mac, vfidx);
  } else {
   netif_info(lio, probe, lio->netdev,
       " MACAddr changed to %pM\n",
       mac);
  }
  break;

 case OCTNET_CMD_GPIO_ACCESS:
  netif_info(lio, probe, lio->netdev, "LED Flashing visual identification\n");

  break;

 case OCTNET_CMD_ID_ACTIVE:
  netif_info(lio, probe, lio->netdev, "LED Flashing visual identification\n");

  break;

 case OCTNET_CMD_LRO_ENABLE:
  dev_info(&oct->pci_dev->dev, "%s LRO Enabled\n", netdev->name);
  break;

 case OCTNET_CMD_LRO_DISABLE:
  dev_info(&oct->pci_dev->dev, "%s LRO Disabled\n",
    netdev->name);
  break;

 case OCTNET_CMD_VERBOSE_ENABLE:
  dev_info(&oct->pci_dev->dev, "%s Firmware debug enabled\n",
    netdev->name);
  break;

 case OCTNET_CMD_VERBOSE_DISABLE:
  dev_info(&oct->pci_dev->dev, "%s Firmware debug disabled\n",
    netdev->name);
  break;

 case OCTNET_CMD_VLAN_FILTER_CTL:
  if (nctrl->ncmd.s.param1)
   dev_info(&oct->pci_dev->dev,
     "%s VLAN filter enabled\n", netdev->name);
  else
   dev_info(&oct->pci_dev->dev,
     "%s VLAN filter disabled\n", netdev->name);
  break;

 case OCTNET_CMD_ADD_VLAN_FILTER:
  dev_info(&oct->pci_dev->dev, "%s VLAN filter %d added\n",
    netdev->name, nctrl->ncmd.s.param1);
  break;

 case OCTNET_CMD_DEL_VLAN_FILTER:
  dev_info(&oct->pci_dev->dev, "%s VLAN filter %d removed\n",
    netdev->name, nctrl->ncmd.s.param1);
  break;

 case OCTNET_CMD_SET_SETTINGS:
  dev_info(&oct->pci_dev->dev, "%s settings changed\n",
    netdev->name);

  break;

 /* Case to handle "OCTNET_CMD_TNL_RX_CSUM_CTL"
 * Command passed by NIC driver
 */

 case OCTNET_CMD_TNL_RX_CSUM_CTL:
  if (nctrl->ncmd.s.param1 == OCTNET_CMD_RXCSUM_ENABLE) {
   netif_info(lio, probe, lio->netdev,
       "RX Checksum Offload Enabled\n");
  } else if (nctrl->ncmd.s.param1 ==
      OCTNET_CMD_RXCSUM_DISABLE) {
   netif_info(lio, probe, lio->netdev,
       "RX Checksum Offload Disabled\n");
  }
  break;

  /* Case to handle "OCTNET_CMD_TNL_TX_CSUM_CTL"
 * Command passed by NIC driver
 */

 case OCTNET_CMD_TNL_TX_CSUM_CTL:
  if (nctrl->ncmd.s.param1 == OCTNET_CMD_TXCSUM_ENABLE) {
   netif_info(lio, probe, lio->netdev,
       "TX Checksum Offload Enabled\n");
  } else if (nctrl->ncmd.s.param1 ==
      OCTNET_CMD_TXCSUM_DISABLE) {
   netif_info(lio, probe, lio->netdev,
       "TX Checksum Offload Disabled\n");
  }
  break;

  /* Case to handle "OCTNET_CMD_VXLAN_PORT_CONFIG"
 * Command passed by NIC driver
 */

 case OCTNET_CMD_VXLAN_PORT_CONFIG:
  if (nctrl->ncmd.s.more == OCTNET_CMD_VXLAN_PORT_ADD) {
   netif_info(lio, probe, lio->netdev,
       "VxLAN Destination UDP PORT:%d ADDED\n",
       nctrl->ncmd.s.param1);
  } else if (nctrl->ncmd.s.more ==
      OCTNET_CMD_VXLAN_PORT_DEL) {
   netif_info(lio, probe, lio->netdev,
       "VxLAN Destination UDP PORT:%d DELETED\n",
       nctrl->ncmd.s.param1);
  }
  break;

 case OCTNET_CMD_SET_FLOW_CTL:
  netif_info(lio, probe, lio->netdev, "Set RX/TX flow control parameters\n");
  break;

 case OCTNET_CMD_QUEUE_COUNT_CTL:
  netif_info(lio, probe, lio->netdev, "Queue count updated to %d\n",
      nctrl->ncmd.s.param1);
  break;

 default:
  dev_err(&oct->pci_dev->dev, "%s Unknown cmd %d\n", __func__,
   nctrl->ncmd.s.cmd);
 }
}
EXPORT_SYMBOL_GPL(liquidio_link_ctrl_cmd_completion);

void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac)
{
 bool macaddr_changed = false;
 struct net_device *netdev;
 struct lio *lio;

 rtnl_lock();

 netdev = oct->props[0].netdev;
 lio = GET_LIO(netdev);

 lio->linfo.macaddr_is_admin_asgnd = true;

 if (!ether_addr_equal(netdev->dev_addr, mac)) {
  macaddr_changed = true;
  eth_hw_addr_set(netdev, mac);
  ether_addr_copy(((u8 *)&lio->linfo.hw_addr) + 2, mac);
  call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev);
 }

 rtnl_unlock();

 if (macaddr_changed)
  dev_info(&oct->pci_dev->dev,
    "PF changed VF's MAC address to %pM\n", mac);

 /* no need to notify the firmware of the macaddr change because
 * the PF did that already
 */

}

void octeon_schedule_rxq_oom_work(struct octeon_device *oct,
      struct octeon_droq *droq)
{
 struct net_device *netdev = oct->props[0].netdev;
 struct lio *lio = GET_LIO(netdev);
 struct cavium_wq *wq = &lio->rxq_status_wq[droq->q_no];

 queue_delayed_work(wq->wq, &wq->wk.work,
      msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS));
}

static void octnet_poll_check_rxq_oom_status(struct work_struct *work)
{
 struct cavium_wk *wk = (struct cavium_wk *)work;
 struct lio *lio = (struct lio *)wk->ctxptr;
 struct octeon_device *oct = lio->oct_dev;
 int q_no = wk->ctxul;
 struct octeon_droq *droq = oct->droq[q_no];

 if (!ifstate_check(lio, LIO_IFSTATE_RUNNING) || !droq)
  return;

 if (octeon_retry_droq_refill(droq))
  octeon_schedule_rxq_oom_work(oct, droq);
}

int setup_rx_oom_poll_fn(struct net_device *netdev)
{
 struct lio *lio = GET_LIO(netdev);
 struct octeon_device *oct = lio->oct_dev;
 struct cavium_wq *wq;
 int q, q_no;

 for (q = 0; q < oct->num_oqs; q++) {
  q_no = lio->linfo.rxpciq[q].s.q_no;
  wq = &lio->rxq_status_wq[q_no];
  wq->wq = alloc_workqueue("rxq-oom-status",
      WQ_MEM_RECLAIM, 0);
  if (!wq->wq) {
   dev_err(&oct->pci_dev->dev, "unable to create cavium rxq oom status wq\n");
   return -ENOMEM;
  }

  INIT_DELAYED_WORK(&wq->wk.work,
      octnet_poll_check_rxq_oom_status);
  wq->wk.ctxptr = lio;
  wq->wk.ctxul = q_no;
 }

 return 0;
}
EXPORT_SYMBOL_GPL(setup_rx_oom_poll_fn);

void cleanup_rx_oom_poll_fn(struct net_device *netdev)
{
 struct lio *lio = GET_LIO(netdev);
 struct octeon_device *oct = lio->oct_dev;
 struct cavium_wq *wq;
 int q_no;

 for (q_no = 0; q_no < oct->num_oqs; q_no++) {
  wq = &lio->rxq_status_wq[q_no];
  if (wq->wq) {
   cancel_delayed_work_sync(&wq->wk.work);
   destroy_workqueue(wq->wq);
   wq->wq = NULL;
  }
 }
}
EXPORT_SYMBOL_GPL(cleanup_rx_oom_poll_fn);

/* Runs in interrupt context. */
static void lio_update_txq_status(struct octeon_device *oct, int iq_num)
{
 struct octeon_instr_queue *iq = oct->instr_queue[iq_num];
 struct net_device *netdev;
 struct lio *lio;

 netdev = oct->props[iq->ifidx].netdev;

 /* This is needed because the first IQ does not have
 * a netdev associated with it.
 */

 if (!netdev)
  return;

 lio = GET_LIO(netdev);
 if (__netif_subqueue_stopped(netdev, iq->q_index) &&
     lio->linfo.link.s.link_up &&
     (!octnet_iq_is_full(oct, iq_num))) {
  netif_wake_subqueue(netdev, iq->q_index);
  INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num,
       tx_restart, 1);
 }
}

/**
 * octeon_setup_droq - Setup output queue
 * @oct: octeon device
 * @q_no: which queue
 * @num_descs: how many descriptors
 * @desc_size: size of each descriptor
 * @app_ctx: application context
 */

static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs,
        int desc_size, void *app_ctx)
{
 int ret_val;

 dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no);
 /* droq creation and local register settings. */
 ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx);
 if (ret_val < 0)
  return ret_val;

 if (ret_val == 1) {
  dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no);
  return 0;
 }

 /* Enable the droq queues */
 octeon_set_droq_pkt_op(oct, q_no, 1);

 /* Send Credit for Octeon Output queues. Credits are always
 * sent after the output queue is enabled.
 */

 writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg);

 return ret_val;
}

/**
 * liquidio_push_packet - Routine to push packets arriving on Octeon interface upto network layer.
 * @octeon_id:octeon device id.
 * @skbuff:   skbuff struct to be passed to network layer.
 * @len:      size of total data received.
 * @rh:       Control header associated with the packet
 * @param:    additional control data with the packet
 * @arg:      farg registered in droq_ops
 */

static void
liquidio_push_packet(u32 __maybe_unused octeon_id,
       void *skbuff,
       u32 len,
       union octeon_rh *rh,
       void *param,
       void *arg)
{
 struct net_device *netdev = (struct net_device *)arg;
 struct octeon_droq *droq =
     container_of(param, struct octeon_droq, napi);
 struct sk_buff *skb = (struct sk_buff *)skbuff;
 struct skb_shared_hwtstamps *shhwtstamps;
 struct napi_struct *napi = param;
 u16 vtag = 0;
 u32 r_dh_off;
 u64 ns;

 if (netdev) {
  struct lio *lio = GET_LIO(netdev);
  struct octeon_device *oct = lio->oct_dev;

  /* Do not proceed if the interface is not in RUNNING state. */
  if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) {
   recv_buffer_free(skb);
   droq->stats.rx_dropped++;
   return;
  }

  skb->dev = netdev;

  skb_record_rx_queue(skb, droq->q_no);
  if (likely(len > MIN_SKB_SIZE)) {
   struct octeon_skb_page_info *pg_info;
   unsigned char *va;

   pg_info = ((struct octeon_skb_page_info *)(skb->cb));
   if (pg_info->page) {
    /* For Paged allocation use the frags */
    va = page_address(pg_info->page) +
     pg_info->page_offset;
    memcpy(skb->data, va, MIN_SKB_SIZE);
    skb_put(skb, MIN_SKB_SIZE);
    skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
      pg_info->page,
      pg_info->page_offset +
      MIN_SKB_SIZE,
      len - MIN_SKB_SIZE,
      LIO_RXBUFFER_SZ);
   }
  } else {
   struct octeon_skb_page_info *pg_info =
    ((struct octeon_skb_page_info *)(skb->cb));
   skb_copy_to_linear_data(skb, page_address(pg_info->page)
      + pg_info->page_offset, len);
   skb_put(skb, len);
   put_page(pg_info->page);
  }

  r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT;

  if (oct->ptp_enable) {
   if (rh->r_dh.has_hwtstamp) {
    /* timestamp is included from the hardware at
 * the beginning of the packet.
 */

    if (ifstate_check
     (lio,
      LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) {
     /* Nanoseconds are in the first 64-bits
 * of the packet.
 */

     memcpy(&ns, (skb->data + r_dh_off),
            sizeof(ns));
     r_dh_off -= BYTES_PER_DHLEN_UNIT;
     shhwtstamps = skb_hwtstamps(skb);
     shhwtstamps->hwtstamp =
      ns_to_ktime(ns +
           lio->ptp_adjust);
    }
   }
  }

  if (rh->r_dh.has_hash) {
   __be32 *hash_be = (__be32 *)(skb->data + r_dh_off);
   u32 hash = be32_to_cpu(*hash_be);

   skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
   r_dh_off -= BYTES_PER_DHLEN_UNIT;
  }

  skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT);
  skb->protocol = eth_type_trans(skb, skb->dev);

  if ((netdev->features & NETIF_F_RXCSUM) &&
      (((rh->r_dh.encap_on) &&
        (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) ||
       (!(rh->r_dh.encap_on) &&
        ((rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED) ==
   CNNIC_CSUM_VERIFIED))))
   /* checksum has already been verified */
   skb->ip_summed = CHECKSUM_UNNECESSARY;
  else
   skb->ip_summed = CHECKSUM_NONE;

  /* Setting Encapsulation field on basis of status received
 * from the firmware
 */

  if (rh->r_dh.encap_on) {
   skb->encapsulation = 1;
   skb->csum_level = 1;
   droq->stats.rx_vxlan++;
  }

  /* inbound VLAN tag */
  if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
      rh->r_dh.vlan) {
   u16 priority = rh->r_dh.priority;
   u16 vid = rh->r_dh.vlan;

   vtag = (priority << VLAN_PRIO_SHIFT) | vid;
   __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
  }

  napi_gro_receive(napi, skb);

  droq->stats.rx_bytes_received += len -
   rh->r_dh.len * BYTES_PER_DHLEN_UNIT;
  droq->stats.rx_pkts_received++;
 } else {
  recv_buffer_free(skb);
 }
}

/**
 * napi_schedule_wrapper - wrapper for calling napi_schedule
 * @param: parameters to pass to napi_schedule
 *
 * Used when scheduling on different CPUs
 */

static void napi_schedule_wrapper(void *param)
{
 struct napi_struct *napi = param;

 napi_schedule(napi);
}

/**
 * liquidio_napi_drv_callback - callback when receive interrupt occurs and we are in NAPI mode
 * @arg: pointer to octeon output queue
 */

static void liquidio_napi_drv_callback(void *arg)
{
 struct octeon_device *oct;
 struct octeon_droq *droq = arg;
 int this_cpu = smp_processor_id();

 oct = droq->oct_dev;

 if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct) ||
     droq->cpu_id == this_cpu) {
  napi_schedule_irqoff(&droq->napi);
 } else {
  INIT_CSD(&droq->csd, napi_schedule_wrapper, &droq->napi);
  smp_call_function_single_async(droq->cpu_id, &droq->csd);
 }
}

/**
 * liquidio_napi_poll - Entry point for NAPI polling
 * @napi: NAPI structure
 * @budget: maximum number of items to process
 */

static int liquidio_napi_poll(struct napi_struct *napi, int budget)
{
 struct octeon_instr_queue *iq;
 struct octeon_device *oct;
 struct octeon_droq *droq;
 int tx_done = 0, iq_no;
 int work_done;

 droq = container_of(napi, struct octeon_droq, napi);
 oct = droq->oct_dev;
 iq_no = droq->q_no;

 /* Handle Droq descriptors */
 work_done = octeon_droq_process_poll_pkts(oct, droq, budget);

 /* Flush the instruction queue */
 iq = oct->instr_queue[iq_no];
 if (iq) {
  /* TODO: move this check to inside octeon_flush_iq,
 * once check_db_timeout is removed
 */

  if (atomic_read(&iq->instr_pending))
   /* Process iq buffers with in the budget limits */
   tx_done = octeon_flush_iq(oct, iq, budget);
  else
   tx_done = 1;
  /* Update iq read-index rather than waiting for next interrupt.
 * Return back if tx_done is false.
 */

  /* sub-queue status update */
  lio_update_txq_status(oct, iq_no);
 } else {
  dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n",
   __func__, iq_no);
 }

#define MAX_REG_CNT  2000000U
 /* force enable interrupt if reg cnts are high to avoid wraparound */
 if ((work_done < budget && tx_done) ||
     (iq && iq->pkt_in_done >= MAX_REG_CNT) ||
     (droq->pkt_count >= MAX_REG_CNT)) {
  napi_complete_done(napi, work_done);

  octeon_enable_irq(droq->oct_dev, droq->q_no);
  return 0;
 }

 return (!tx_done) ? (budget) : (work_done);
}

/**
 * liquidio_setup_io_queues - Setup input and output queues
 * @octeon_dev: octeon device
 * @ifidx: Interface index
 * @num_iqs: input io queue count
 * @num_oqs: output io queue count
 *
 * Note: Queues are with respect to the octeon device. Thus
 * an input queue is for egress packets, and output queues
 * are for ingress packets.
 */

int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx,
        u32 num_iqs, u32 num_oqs)
{
 struct octeon_droq_ops droq_ops;
 struct net_device *netdev;
 struct octeon_droq *droq;
 struct napi_struct *napi;
 int cpu_id_modulus;
 int num_tx_descs;
 struct lio *lio;
 int retval = 0;
 int q, q_no;
 int cpu_id;

 netdev = octeon_dev->props[ifidx].netdev;

 lio = GET_LIO(netdev);

 memset(&droq_ops, 0, sizeof(struct octeon_droq_ops));

 droq_ops.fptr = liquidio_push_packet;
 droq_ops.farg = netdev;

 droq_ops.poll_mode = 1;
 droq_ops.napi_fn = liquidio_napi_drv_callback;
 cpu_id = 0;
 cpu_id_modulus = num_present_cpus();

 /* set up DROQs. */
 for (q = 0; q < num_oqs; q++) {
  q_no = lio->linfo.rxpciq[q].s.q_no;
  dev_dbg(&octeon_dev->pci_dev->dev,
   "%s index:%d linfo.rxpciq.s.q_no:%d\n",
   __func__, q, q_no);
  retval = octeon_setup_droq(
      octeon_dev, q_no,
      CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev),
      lio->ifidx),
      CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev),
         lio->ifidx),
      NULL);
  if (retval) {
   dev_err(&octeon_dev->pci_dev->dev,
    "%s : Runtime DROQ(RxQ) creation failed.\n",
    __func__);
   return 1;
  }

  droq = octeon_dev->droq[q_no];
  napi = &droq->napi;
  dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx\n",
   (u64)netdev, (u64)octeon_dev);
  netif_napi_add(netdev, napi, liquidio_napi_poll);

  /* designate a CPU for this droq */
  droq->cpu_id = cpu_id;
  cpu_id++;
  if (cpu_id >= cpu_id_modulus)
   cpu_id = 0;

  octeon_register_droq_ops(octeon_dev, q_no, &droq_ops);
 }

 if (OCTEON_CN23XX_PF(octeon_dev) || OCTEON_CN23XX_VF(octeon_dev)) {
  /* 23XX PF/VF can send/recv control messages (via the first
 * PF/VF-owned droq) from the firmware even if the ethX
 * interface is down, so that's why poll_mode must be off
 * for the first droq.
 */

  octeon_dev->droq[0]->ops.poll_mode = 0;
 }

 /* set up IQs. */
 for (q = 0; q < num_iqs; q++) {
  num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF(
      octeon_get_conf(octeon_dev), lio->ifidx);
  retval = octeon_setup_iq(octeon_dev, ifidx, q,
      lio->linfo.txpciq[q], num_tx_descs,
      netdev_get_tx_queue(netdev, q));
  if (retval) {
   dev_err(&octeon_dev->pci_dev->dev,
    " %s : Runtime IQ(TxQ) creation failed.\n",
    __func__);
   return 1;
  }

  /* XPS */
  if (!OCTEON_CN23XX_VF(octeon_dev) && octeon_dev->msix_on &&
      octeon_dev->ioq_vector) {
   struct octeon_ioq_vector    *ioq_vector;

   ioq_vector = &octeon_dev->ioq_vector[q];
   netif_set_xps_queue(netdev,
         &ioq_vector->affinity_mask,
         ioq_vector->iq_index);
  }
 }

 return 0;
}
EXPORT_SYMBOL_GPL(liquidio_setup_io_queues);

static
int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret)
{
 struct octeon_device *oct = droq->oct_dev;
 struct octeon_device_priv *oct_priv =
     (struct octeon_device_priv *)oct->priv;

 if (droq->ops.poll_mode) {
  droq->ops.napi_fn(droq);
 } else {
  if (ret & MSIX_PO_INT) {
   if (OCTEON_CN23XX_VF(oct))
    dev_err(&oct->pci_dev->dev,
     "should not come here should not get rx when poll mode = 0 for vf\n");
   tasklet_schedule(&oct_priv->droq_tasklet);
   return 1;
  }
  /* this will be flushed periodically by check iq db */
  if (ret & MSIX_PI_INT)
   return 0;
 }

 return 0;
}

irqreturn_t
liquidio_msix_intr_handler(int __maybe_unused irq, void *dev)
{
 struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev;
 struct octeon_device *oct = ioq_vector->oct_dev;
 struct octeon_droq *droq = oct->droq[ioq_vector->droq_index];
 u64 ret;

 ret = oct->fn_list.msix_interrupt_handler(ioq_vector);

 if (ret & MSIX_PO_INT || ret & MSIX_PI_INT)
  liquidio_schedule_msix_droq_pkt_handler(droq, ret);

 return IRQ_HANDLED;
}

/**
 * liquidio_schedule_droq_pkt_handlers - Droq packet processor sceduler
 * @oct: octeon device
 */

static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct)
{
 struct octeon_device_priv *oct_priv =
  (struct octeon_device_priv *)oct->priv;
 struct octeon_droq *droq;
 u64 oq_no;

 if (oct->int_status & OCT_DEV_INTR_PKT_DATA) {
  for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct);
       oq_no++) {
   if (!(oct->droq_intr & BIT_ULL(oq_no)))
    continue;

   droq = oct->droq[oq_no];

   if (droq->ops.poll_mode) {
    droq->ops.napi_fn(droq);
    oct_priv->napi_mask |= BIT_ULL(oq_no);
   } else {
    tasklet_schedule(&oct_priv->droq_tasklet);
   }
  }
 }
}

/**
 * liquidio_legacy_intr_handler - Interrupt handler for octeon
 * @irq: unused
 * @dev: octeon device
 */

static
irqreturn_t liquidio_legacy_intr_handler(int __maybe_unused irq, void *dev)
{
 struct octeon_device *oct = (struct octeon_device *)dev;
 irqreturn_t ret;

 /* Disable our interrupts for the duration of ISR */
 oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR);

 ret = oct->fn_list.process_interrupt_regs(oct);

 if (ret == IRQ_HANDLED)
  liquidio_schedule_droq_pkt_handlers(oct);

 /* Re-enable our interrupts  */
 if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET))
  oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR);

 return ret;
}

/**
 * octeon_setup_interrupt - Setup interrupt for octeon device
 * @oct: octeon device
 * @num_ioqs: number of queues
 *
 *  Enable interrupt in Octeon device as given in the PCI interrupt mask.
 */

int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs)
{
 struct msix_entry *msix_entries;
 char *queue_irq_names = NULL;
 int i, num_interrupts = 0;
 int num_alloc_ioq_vectors;
 char *aux_irq_name = NULL;
 int num_ioq_vectors;
 int irqret, err;

 if (oct->msix_on) {
  oct->num_msix_irqs = num_ioqs;
  if (OCTEON_CN23XX_PF(oct)) {
   num_interrupts = MAX_IOQ_INTERRUPTS_PER_PF + 1;

   /* one non ioq interrupt for handling
 * sli_mac_pf_int_sum
 */

   oct->num_msix_irqs += 1;
  } else if (OCTEON_CN23XX_VF(oct)) {
   num_interrupts = MAX_IOQ_INTERRUPTS_PER_VF;
  }

  /* allocate storage for the names assigned to each irq */
  oct->irq_name_storage =
   kcalloc(num_interrupts, INTRNAMSIZ, GFP_KERNEL);
  if (!oct->irq_name_storage) {
   dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n");
   return -ENOMEM;
  }

  queue_irq_names = oct->irq_name_storage;

  if (OCTEON_CN23XX_PF(oct))
   aux_irq_name = &queue_irq_names
    [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)];

  oct->msix_entries = kcalloc(oct->num_msix_irqs,
         sizeof(struct msix_entry),
         GFP_KERNEL);
  if (!oct->msix_entries) {
   dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n");
   kfree(oct->irq_name_storage);
   oct->irq_name_storage = NULL;
   return -ENOMEM;
  }

  msix_entries = (struct msix_entry *)oct->msix_entries;

  /*Assumption is that pf msix vectors start from pf srn to pf to
 * trs and not from 0. if not change this code
 */

  if (OCTEON_CN23XX_PF(oct)) {
   for (i = 0; i < oct->num_msix_irqs - 1; i++)
    msix_entries[i].entry =
     oct->sriov_info.pf_srn + i;

   msix_entries[oct->num_msix_irqs - 1].entry =
    oct->sriov_info.trs;
  } else if (OCTEON_CN23XX_VF(oct)) {
   for (i = 0; i < oct->num_msix_irqs; i++)
    msix_entries[i].entry = i;
  }
  num_alloc_ioq_vectors = pci_enable_msix_range(
      oct->pci_dev, msix_entries,
      oct->num_msix_irqs,
      oct->num_msix_irqs);
  if (num_alloc_ioq_vectors < 0) {
   dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n");
   kfree(oct->msix_entries);
   oct->msix_entries = NULL;
   kfree(oct->irq_name_storage);
   oct->irq_name_storage = NULL;
   return num_alloc_ioq_vectors;
  }

  dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n");

  num_ioq_vectors = oct->num_msix_irqs;
  /* For PF, there is one non-ioq interrupt handler */
  if (OCTEON_CN23XX_PF(oct)) {
   num_ioq_vectors -= 1;

   snprintf(aux_irq_name, INTRNAMSIZ,
     "LiquidIO%u-pf%u-aux", oct->octeon_id,
     oct->pf_num);
   irqret = request_irq(
     msix_entries[num_ioq_vectors].vector,
     liquidio_legacy_intr_handler, 0,
     aux_irq_name, oct);
   if (irqret) {
    dev_err(&oct->pci_dev->dev,
     "Request_irq failed for MSIX interrupt Error: %d\n",
     irqret);
    pci_disable_msix(oct->pci_dev);
    kfree(oct->msix_entries);
    kfree(oct->irq_name_storage);
    oct->irq_name_storage = NULL;
    oct->msix_entries = NULL;
    return irqret;
   }
  }
  for (i = 0 ; i < num_ioq_vectors ; i++) {
   if (OCTEON_CN23XX_PF(oct))
    snprintf(&queue_irq_names[IRQ_NAME_OFF(i)],
      INTRNAMSIZ, "LiquidIO%u-pf%u-rxtx-%u",
      oct->octeon_id, oct->pf_num, i);

   if (OCTEON_CN23XX_VF(oct))
    snprintf(&queue_irq_names[IRQ_NAME_OFF(i)],
      INTRNAMSIZ, "LiquidIO%u-vf%u-rxtx-%u",
      oct->octeon_id, oct->vf_num, i);

   irqret = request_irq(msix_entries[i].vector,
          liquidio_msix_intr_handler, 0,
          &queue_irq_names[IRQ_NAME_OFF(i)],
          &oct->ioq_vector[i]);

   if (irqret) {
    dev_err(&oct->pci_dev->dev,
     "Request_irq failed for MSIX interrupt Error: %d\n",
     irqret);
    /* Freeing the non-ioq irq vector here . */
    free_irq(msix_entries[num_ioq_vectors].vector,
      oct);

    while (i) {
     i--;
     /* clearing affinity mask. */
     irq_set_affinity_hint(
            msix_entries[i].vector,
            NULL);
     free_irq(msix_entries[i].vector,
       &oct->ioq_vector[i]);
    }
    pci_disable_msix(oct->pci_dev);
    kfree(oct->msix_entries);
    kfree(oct->irq_name_storage);
    oct->irq_name_storage = NULL;
    oct->msix_entries = NULL;
    return irqret;
   }
   oct->ioq_vector[i].vector = msix_entries[i].vector;
   /* assign the cpu mask for this msix interrupt vector */
   irq_set_affinity_hint(msix_entries[i].vector,
           &oct->ioq_vector[i].affinity_mask
           );
  }
  dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n",
   oct->octeon_id);
 } else {
  err = pci_enable_msi(oct->pci_dev);
  if (err)
   dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n",
     err);
  else
   oct->flags |= LIO_FLAG_MSI_ENABLED;

  /* allocate storage for the names assigned to the irq */
  oct->irq_name_storage = kzalloc(INTRNAMSIZ, GFP_KERNEL);
  if (!oct->irq_name_storage)
   return -ENOMEM;

  queue_irq_names = oct->irq_name_storage;

  if (OCTEON_CN23XX_PF(oct))
   snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ,
     "LiquidIO%u-pf%u-rxtx-%u",
     oct->octeon_id, oct->pf_num, 0);

  if (OCTEON_CN23XX_VF(oct))
   snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ,
     "LiquidIO%u-vf%u-rxtx-%u",
     oct->octeon_id, oct->vf_num, 0);

  irqret = request_irq(oct->pci_dev->irq,
         liquidio_legacy_intr_handler,
         IRQF_SHARED,
         &queue_irq_names[IRQ_NAME_OFF(0)], oct);
  if (irqret) {
   if (oct->flags & LIO_FLAG_MSI_ENABLED)
    pci_disable_msi(oct->pci_dev);
   dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n",
    irqret);
   kfree(oct->irq_name_storage);
   oct->irq_name_storage = NULL;
   return irqret;
  }
 }
 return 0;
}
EXPORT_SYMBOL_GPL(octeon_setup_interrupt);

/**
 * liquidio_change_mtu - Net device change_mtu
 * @netdev: network device
 * @new_mtu: the new max transmit unit size
 */

int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
{
 struct lio *lio = GET_LIO(netdev);
 struct octeon_device *oct = lio->oct_dev;
 struct octeon_soft_command *sc;
 union octnet_cmd *ncmd;
 int ret = 0;

 sc = (struct octeon_soft_command *)
  octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, 0);
 if (!sc) {
  netif_info(lio, rx_err, lio->netdev,
      "Failed to allocate soft command\n");
  return -ENOMEM;
 }

 ncmd = (union octnet_cmd *)sc->virtdptr;

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 ncmd->u64 = 0;
 ncmd->s.cmd = OCTNET_CMD_CHANGE_MTU;
 ncmd->s.param1 = new_mtu;

 octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
        OPCODE_NIC_CMD, 0, 0, 0);

 ret = octeon_send_soft_command(oct, sc);
 if (ret == IQ_SEND_FAILED) {
  netif_info(lio, rx_err, lio->netdev, "Failed to change MTU\n");
  octeon_free_soft_command(oct, sc);
  return -EINVAL;
 }
 /* Sleep on a wait queue till the cond flag indicates that the
 * response arrived or timed-out.
 */

 ret = wait_for_sc_completion_timeout(oct, sc, 0);
 if (ret)
  return ret;

 if (sc->sc_status) {
  WRITE_ONCE(sc->caller_is_done, true);
  return -EINVAL;
 }

 WRITE_ONCE(netdev->mtu, new_mtu);
 lio->mtu = new_mtu;

 WRITE_ONCE(sc->caller_is_done, true);
 return 0;
}
EXPORT_SYMBOL_GPL(liquidio_change_mtu);

int lio_wait_for_clean_oq(struct octeon_device *oct)
{
 int retry = 100, pending_pkts = 0;
 int idx;

 do {
  pending_pkts = 0;

  for (idx = 0; idx < MAX_OCTEON_OUTPUT_QUEUES(oct); idx++) {
   if (!(oct->io_qmask.oq & BIT_ULL(idx)))
    continue;
   pending_pkts +=
    atomic_read(&oct->droq[idx]->pkts_pending);
  }

  if (pending_pkts > 0)
   schedule_timeout_uninterruptible(1);

 } while (retry-- && pending_pkts);

 return pending_pkts;
}
EXPORT_SYMBOL_GPL(lio_wait_for_clean_oq);

static void
octnet_nic_stats_callback(struct octeon_device *oct_dev,
     u32 status, void *ptr)
{
 struct octeon_soft_command *sc = (struct octeon_soft_command *)ptr;
 struct oct_nic_stats_resp *resp =
     (struct oct_nic_stats_resp *)sc->virtrptr;
 struct nic_rx_stats *rsp_rstats = &resp->stats.fromwire;
 struct nic_tx_stats *rsp_tstats = &resp->stats.fromhost;
 struct nic_rx_stats *rstats = &oct_dev->link_stats.fromwire;
 struct nic_tx_stats *tstats = &oct_dev->link_stats.fromhost;

 if (status != OCTEON_REQUEST_TIMEOUT && !resp->status) {
  octeon_swap_8B_data((u64 *)&resp->stats,
        (sizeof(struct oct_link_stats)) >> 3);

  /* RX link-level stats */
  rstats->total_rcvd = rsp_rstats->total_rcvd;
  rstats->bytes_rcvd = rsp_rstats->bytes_rcvd;
  rstats->total_bcst = rsp_rstats->total_bcst;
  rstats->total_mcst = rsp_rstats->total_mcst;
  rstats->runts      = rsp_rstats->runts;
  rstats->ctl_rcvd   = rsp_rstats->ctl_rcvd;
  /* Accounts for over/under-run of buffers */
  rstats->fifo_err  = rsp_rstats->fifo_err;
  rstats->dmac_drop = rsp_rstats->dmac_drop;
  rstats->fcs_err   = rsp_rstats->fcs_err;
  rstats->jabber_err = rsp_rstats->jabber_err;
  rstats->l2_err    = rsp_rstats->l2_err;
  rstats->frame_err = rsp_rstats->frame_err;
  rstats->red_drops = rsp_rstats->red_drops;

  /* RX firmware stats */
  rstats->fw_total_rcvd = rsp_rstats->fw_total_rcvd;
  rstats->fw_total_fwd = rsp_rstats->fw_total_fwd;
  rstats->fw_total_mcast = rsp_rstats->fw_total_mcast;
  rstats->fw_total_bcast = rsp_rstats->fw_total_bcast;
  rstats->fw_err_pko = rsp_rstats->fw_err_pko;
  rstats->fw_err_link = rsp_rstats->fw_err_link;
  rstats->fw_err_drop = rsp_rstats->fw_err_drop;
  rstats->fw_rx_vxlan = rsp_rstats->fw_rx_vxlan;
  rstats->fw_rx_vxlan_err = rsp_rstats->fw_rx_vxlan_err;

  /* Number of packets that are LROed      */
  rstats->fw_lro_pkts = rsp_rstats->fw_lro_pkts;
  /* Number of octets that are LROed       */
  rstats->fw_lro_octs = rsp_rstats->fw_lro_octs;
  /* Number of LRO packets formed          */
  rstats->fw_total_lro = rsp_rstats->fw_total_lro;
  /* Number of times lRO of packet aborted */
  rstats->fw_lro_aborts = rsp_rstats->fw_lro_aborts;
  rstats->fw_lro_aborts_port = rsp_rstats->fw_lro_aborts_port;
  rstats->fw_lro_aborts_seq = rsp_rstats->fw_lro_aborts_seq;
  rstats->fw_lro_aborts_tsval = rsp_rstats->fw_lro_aborts_tsval;
  rstats->fw_lro_aborts_timer = rsp_rstats->fw_lro_aborts_timer;
  /* intrmod: packet forward rate */
  rstats->fwd_rate = rsp_rstats->fwd_rate;

  /* TX link-level stats */
  tstats->total_pkts_sent = rsp_tstats->total_pkts_sent;
  tstats->total_bytes_sent = rsp_tstats->total_bytes_sent;
  tstats->mcast_pkts_sent = rsp_tstats->mcast_pkts_sent;
  tstats->bcast_pkts_sent = rsp_tstats->bcast_pkts_sent;
  tstats->ctl_sent = rsp_tstats->ctl_sent;
  /* Packets sent after one collision*/
  tstats->one_collision_sent = rsp_tstats->one_collision_sent;
  /* Packets sent after multiple collision*/
  tstats->multi_collision_sent = rsp_tstats->multi_collision_sent;
  /* Packets not sent due to max collisions */
  tstats->max_collision_fail = rsp_tstats->max_collision_fail;
  /* Packets not sent due to max deferrals */
  tstats->max_deferral_fail = rsp_tstats->max_deferral_fail;
  /* Accounts for over/under-run of buffers */
  tstats->fifo_err = rsp_tstats->fifo_err;
  tstats->runts = rsp_tstats->runts;
  /* Total number of collisions detected */
  tstats->total_collisions = rsp_tstats->total_collisions;

  /* firmware stats */
  tstats->fw_total_sent = rsp_tstats->fw_total_sent;
  tstats->fw_total_fwd = rsp_tstats->fw_total_fwd;
  tstats->fw_total_mcast_sent = rsp_tstats->fw_total_mcast_sent;
  tstats->fw_total_bcast_sent = rsp_tstats->fw_total_bcast_sent;
  tstats->fw_err_pko = rsp_tstats->fw_err_pko;
  tstats->fw_err_pki = rsp_tstats->fw_err_pki;
  tstats->fw_err_link = rsp_tstats->fw_err_link;
  tstats->fw_err_drop = rsp_tstats->fw_err_drop;
  tstats->fw_tso = rsp_tstats->fw_tso;
  tstats->fw_tso_fwd = rsp_tstats->fw_tso_fwd;
  tstats->fw_err_tso = rsp_tstats->fw_err_tso;
  tstats->fw_tx_vxlan = rsp_tstats->fw_tx_vxlan;

  resp->status = 1;
 } else {
  dev_err(&oct_dev->pci_dev->dev, "sc OPCODE_NIC_PORT_STATS command failed\n");
  resp->status = -1;
 }
}

static int lio_fetch_vf_stats(struct lio *lio)
{
 struct octeon_device *oct_dev = lio->oct_dev;
 struct octeon_soft_command *sc;
 struct oct_nic_vf_stats_resp *resp;

 int retval;

 /* Alloc soft command */
 sc = (struct octeon_soft_command *)
  octeon_alloc_soft_command(oct_dev,
       0,
       sizeof(struct oct_nic_vf_stats_resp),
       0);

 if (!sc) {
  dev_err(&oct_dev->pci_dev->dev, "Soft command allocation failed\n");
  retval = -ENOMEM;
  goto lio_fetch_vf_stats_exit;
 }

 resp = (struct oct_nic_vf_stats_resp *)sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_vf_stats_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct_dev, sc, OPCODE_NIC,
        OPCODE_NIC_VF_PORT_STATS, 0, 0, 0);

 retval = octeon_send_soft_command(oct_dev, sc);
 if (retval == IQ_SEND_FAILED) {
  octeon_free_soft_command(oct_dev, sc);
  goto lio_fetch_vf_stats_exit;
 }

 retval =
  wait_for_sc_completion_timeout(oct_dev, sc,
            (2 * LIO_SC_MAX_TMO_MS));
 if (retval)  {
  dev_err(&oct_dev->pci_dev->dev,
   "sc OPCODE_NIC_VF_PORT_STATS command failed\n");
  goto lio_fetch_vf_stats_exit;
 }

 if (sc->sc_status != OCTEON_REQUEST_TIMEOUT && !resp->status) {
  octeon_swap_8B_data((u64 *)&resp->spoofmac_cnt,
        (sizeof(u64)) >> 3);

  if (resp->spoofmac_cnt != 0) {
   dev_warn(&oct_dev->pci_dev->dev,
     "%llu Spoofed packets detected\n",
     resp->spoofmac_cnt);
  }
 }
 WRITE_ONCE(sc->caller_is_done, 1);

lio_fetch_vf_stats_exit:
 return retval;
}

void lio_fetch_stats(struct work_struct *work)
{
 struct cavium_wk *wk = (struct cavium_wk *)work;
 struct lio *lio = wk->ctxptr;
 struct octeon_device *oct_dev = lio->oct_dev;
 struct octeon_soft_command *sc;
 struct oct_nic_stats_resp *resp;
 unsigned long time_in_jiffies;
 int retval;

 if (OCTEON_CN23XX_PF(oct_dev)) {
  /* report spoofchk every 2 seconds */
  if (!(oct_dev->vfstats_poll % LIO_VFSTATS_POLL) &&
      (oct_dev->fw_info.app_cap_flags & LIQUIDIO_SPOOFCHK_CAP) &&
      oct_dev->sriov_info.num_vfs_alloced) {
   lio_fetch_vf_stats(lio);
  }

  oct_dev->vfstats_poll++;
 }

 /* Alloc soft command */
 sc = (struct octeon_soft_command *)
  octeon_alloc_soft_command(oct_dev,
       0,
       sizeof(struct oct_nic_stats_resp),
       0);

 if (!sc) {
  dev_err(&oct_dev->pci_dev->dev, "Soft command allocation failed\n");
  goto lio_fetch_stats_exit;
 }

 resp = (struct oct_nic_stats_resp *)sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_stats_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct_dev, sc, OPCODE_NIC,
        OPCODE_NIC_PORT_STATS, 0, 0, 0);

 retval = octeon_send_soft_command(oct_dev, sc);
 if (retval == IQ_SEND_FAILED) {
  octeon_free_soft_command(oct_dev, sc);
  goto lio_fetch_stats_exit;
 }

 retval = wait_for_sc_completion_timeout(oct_dev, sc,
      (2 * LIO_SC_MAX_TMO_MS));
 if (retval)  {
  dev_err(&oct_dev->pci_dev->dev, "sc OPCODE_NIC_PORT_STATS command failed\n");
  goto lio_fetch_stats_exit;
 }

 octnet_nic_stats_callback(oct_dev, sc->sc_status, sc);
 WRITE_ONCE(sc->caller_is_done, true);

lio_fetch_stats_exit:
 time_in_jiffies = msecs_to_jiffies(LIQUIDIO_NDEV_STATS_POLL_TIME_MS);
 if (ifstate_check(lio, LIO_IFSTATE_RUNNING))
  schedule_delayed_work(&lio->stats_wk.work, time_in_jiffies);

 return;
}
EXPORT_SYMBOL_GPL(lio_fetch_stats);

int liquidio_set_speed(struct lio *lio, int speed)
{
 struct octeon_device *oct = lio->oct_dev;
 struct oct_nic_seapi_resp *resp;
 struct octeon_soft_command *sc;
 union octnet_cmd *ncmd;
 int retval;
 u32 var;

 if (oct->speed_setting == speed)
  return 0;

 if (!OCTEON_CN23XX_PF(oct)) {
  dev_err(&oct->pci_dev->dev, "%s: SET SPEED only for PF\n",
   __func__);
  return -EOPNOTSUPP;
 }

 sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
           sizeof(struct oct_nic_seapi_resp),
           0);
 if (!sc)
  return -ENOMEM;

 ncmd = sc->virtdptr;
 resp = sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 ncmd->u64 = 0;
 ncmd->s.cmd = SEAPI_CMD_SPEED_SET;
 ncmd->s.param1 = speed;

 octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
        OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

 retval = octeon_send_soft_command(oct, sc);
 if (retval == IQ_SEND_FAILED) {
  dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
  octeon_free_soft_command(oct, sc);
  retval = -EBUSY;
 } else {
  /* Wait for response or timeout */
  retval = wait_for_sc_completion_timeout(oct, sc, 0);
  if (retval)
   return retval;

  retval = resp->status;

  if (retval) {
   dev_err(&oct->pci_dev->dev, "%s failed, retval=%d\n",
    __func__, retval);
   WRITE_ONCE(sc->caller_is_done, true);

   return -EIO;
  }

  var = be32_to_cpu((__force __be32)resp->speed);
  if (var != speed) {
   dev_err(&oct->pci_dev->dev,
    "%s: setting failed speed= %x, expect %x\n",
    __func__, var, speed);
  }

  oct->speed_setting = var;
  WRITE_ONCE(sc->caller_is_done, true);
 }

 return retval;
}

int liquidio_get_speed(struct lio *lio)
{
 struct octeon_device *oct = lio->oct_dev;
 struct oct_nic_seapi_resp *resp;
 struct octeon_soft_command *sc;
 union octnet_cmd *ncmd;
 int retval;

 sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
           sizeof(struct oct_nic_seapi_resp),
           0);
 if (!sc)
  return -ENOMEM;

 ncmd = sc->virtdptr;
 resp = sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 ncmd->u64 = 0;
 ncmd->s.cmd = SEAPI_CMD_SPEED_GET;

 octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
        OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

 retval = octeon_send_soft_command(oct, sc);
 if (retval == IQ_SEND_FAILED) {
  dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
  octeon_free_soft_command(oct, sc);
  retval = -EIO;
 } else {
  retval = wait_for_sc_completion_timeout(oct, sc, 0);
  if (retval)
   return retval;

  retval = resp->status;
  if (retval) {
   dev_err(&oct->pci_dev->dev,
    "%s failed retval=%d\n", __func__, retval);
   retval = -EIO;
  } else {
   u32 var;

   var = be32_to_cpu((__force __be32)resp->speed);
   oct->speed_setting = var;
   if (var == 0xffff) {
    /* unable to access boot variables
 * get the default value based on the NIC type
 */

    if (oct->subsystem_id ==
      OCTEON_CN2350_25GB_SUBSYS_ID ||
        oct->subsystem_id ==
      OCTEON_CN2360_25GB_SUBSYS_ID) {
     oct->no_speed_setting = 1;
     oct->speed_setting = 25;
    } else {
     oct->speed_setting = 10;
    }
   }

  }
  WRITE_ONCE(sc->caller_is_done, true);
 }

 return retval;
}
EXPORT_SYMBOL_GPL(liquidio_get_speed);

int liquidio_set_fec(struct lio *lio, int on_off)
{
 struct oct_nic_seapi_resp *resp;
 struct octeon_soft_command *sc;
 struct octeon_device *oct;
 union octnet_cmd *ncmd;
 int retval;
 u32 var;

 oct = lio->oct_dev;

 if (oct->props[lio->ifidx].fec == on_off)
  return 0;

 if (!OCTEON_CN23XX_PF(oct)) {
  dev_err(&oct->pci_dev->dev, "%s: SET FEC only for PF\n",
   __func__);
  return -1;
 }

 if (oct->speed_boot != 25)  {
  dev_err(&oct->pci_dev->dev,
   "Set FEC only when link speed is 25G during insmod\n");
  return -1;
 }

 sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
           sizeof(struct oct_nic_seapi_resp), 0);
 if (!sc) {
  dev_err(&oct->pci_dev->dev,
   "Failed to allocate soft command\n");
  return -ENOMEM;
 }

 ncmd = sc->virtdptr;
 resp = sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 ncmd->u64 = 0;
 ncmd->s.cmd = SEAPI_CMD_FEC_SET;
 ncmd->s.param1 = on_off;
 /* SEAPI_CMD_FEC_DISABLE(0) or SEAPI_CMD_FEC_RS(1) */

 octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
        OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

 retval = octeon_send_soft_command(oct, sc);
 if (retval == IQ_SEND_FAILED) {
  dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
  octeon_free_soft_command(oct, sc);
  return -EIO;
 }

 retval = wait_for_sc_completion_timeout(oct, sc, 0);
 if (retval)
  return (-EIO);

 var = be32_to_cpu(resp->fec_setting);
 resp->fec_setting = var;
 if (var != on_off) {
  dev_err(&oct->pci_dev->dev,
   "Setting failed fec= %x, expect %x\n",
   var, on_off);
  oct->props[lio->ifidx].fec = var;
  if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS)
   oct->props[lio->ifidx].fec = 1;
  else
   oct->props[lio->ifidx].fec = 0;
 }

 WRITE_ONCE(sc->caller_is_done, true);

 if (oct->props[lio->ifidx].fec !=
     oct->props[lio->ifidx].fec_boot) {
  dev_dbg(&oct->pci_dev->dev,
   "Reload driver to change fec to %s\n",
   oct->props[lio->ifidx].fec ? "on" : "off");
 }

 return retval;
}

int liquidio_get_fec(struct lio *lio)
{
 struct oct_nic_seapi_resp *resp;
 struct octeon_soft_command *sc;
 struct octeon_device *oct;
 union octnet_cmd *ncmd;
 int retval;
 u32 var;

 oct = lio->oct_dev;

 sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
           sizeof(struct oct_nic_seapi_resp), 0);
 if (!sc)
  return -ENOMEM;

 ncmd = sc->virtdptr;
 resp = sc->virtrptr;
 memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

 init_completion(&sc->complete);
 sc->sc_status = OCTEON_REQUEST_PENDING;

 ncmd->u64 = 0;
 ncmd->s.cmd = SEAPI_CMD_FEC_GET;

 octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

 sc->iq_no = lio->linfo.txpciq[0].s.q_no;

 octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
        OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

 retval = octeon_send_soft_command(oct, sc);
 if (retval == IQ_SEND_FAILED) {
  dev_info(&oct->pci_dev->dev,
    "%s: Failed to send soft command\n", __func__);
  octeon_free_soft_command(oct, sc);
  return -EIO;
 }

 retval = wait_for_sc_completion_timeout(oct, sc, 0);
 if (retval)
  return retval;

 var = be32_to_cpu(resp->fec_setting);
 resp->fec_setting = var;
 if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS)
  oct->props[lio->ifidx].fec = 1;
 else
  oct->props[lio->ifidx].fec = 0;

 WRITE_ONCE(sc->caller_is_done, true);

 if (oct->props[lio->ifidx].fec !=
     oct->props[lio->ifidx].fec_boot) {
  dev_dbg(&oct->pci_dev->dev,
   "Reload driver to change fec to %s\n",
   oct->props[lio->ifidx].fec ? "on" : "off");
 }

 return retval;
}
EXPORT_SYMBOL_GPL(liquidio_get_fec);

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

¤ Dauer der Verarbeitung: 0.7 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.