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

Quelle  ionic_lif.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */

#include <linux/ethtool.h>
#include <linux/printk.h>
#include <linux/dynamic_debug.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/cpumask.h>
#include <linux/crash_dump.h>
#include <linux/vmalloc.h>
#include <net/page_pool/helpers.h>

#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_dev.h"
#include "ionic_lif.h"
#include "ionic_txrx.h"
#include "ionic_ethtool.h"
#include "ionic_debugfs.h"

/* queuetype support level */
static const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = {
 [IONIC_QTYPE_ADMINQ]  = 0,   /* 0 = Base version with CQ support */
 [IONIC_QTYPE_NOTIFYQ] = 0,   /* 0 = Base version */
 [IONIC_QTYPE_RXQ]     = 2,   /* 0 = Base version with CQ+SG support
      * 2 =       ... with CMB rings
      */

 [IONIC_QTYPE_TXQ]     = 3,   /* 0 = Base version with CQ+SG support
      * 1 =       ... with Tx SG version 1
      * 3 =       ... with CMB rings
      */

};

static void ionic_link_status_check(struct ionic_lif *lif);
static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
static void ionic_lif_set_netdev_info(struct ionic_lif *lif);

static void ionic_txrx_deinit(struct ionic_lif *lif);
static int ionic_txrx_init(struct ionic_lif *lif);
static int ionic_start_queues(struct ionic_lif *lif);
static void ionic_stop_queues(struct ionic_lif *lif);
static void ionic_lif_queue_identify(struct ionic_lif *lif);

static void ionic_xdp_rxqs_prog_update(struct ionic_lif *lif);
static void ionic_unregister_rxq_info(struct ionic_queue *q);
static int ionic_register_rxq_info(struct ionic_queue *q, unsigned int napi_id);

static void ionic_dim_work(struct work_struct *work)
{
 struct dim *dim = container_of(work, struct dim, work);
 struct dim_cq_moder cur_moder;
 struct ionic_intr_info *intr;
 struct ionic_qcq *qcq;
 struct ionic_lif *lif;
 struct ionic_queue *q;
 u32 new_coal;

 qcq = container_of(dim, struct ionic_qcq, dim);
 q = &qcq->q;
 if (q->type == IONIC_QTYPE_RXQ)
  cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
 else
  cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix);
 lif = q->lif;
 new_coal = ionic_coal_usec_to_hw(lif->ionic, cur_moder.usec);
 new_coal = new_coal ? new_coal : 1;

 intr = &qcq->intr;
 if (intr->dim_coal_hw != new_coal) {
  intr->dim_coal_hw = new_coal;

  ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
         intr->index, intr->dim_coal_hw);
 }

 dim->state = DIM_START_MEASURE;
}

static void ionic_lif_deferred_work(struct work_struct *work)
{
 struct ionic_lif *lif = container_of(work, struct ionic_lif, deferred.work);
 struct ionic_deferred *def = &lif->deferred;
 struct ionic_deferred_work *w = NULL;

 do {
  spin_lock_bh(&def->lock);
  if (!list_empty(&def->list)) {
   w = list_first_entry(&def->list,
          struct ionic_deferred_work, list);
   list_del(&w->list);
  }
  spin_unlock_bh(&def->lock);

  if (!w)
   break;

  switch (w->type) {
  case IONIC_DW_TYPE_RX_MODE:
   ionic_lif_rx_mode(lif);
   break;
  case IONIC_DW_TYPE_LINK_STATUS:
   ionic_link_status_check(lif);
   break;
  case IONIC_DW_TYPE_LIF_RESET:
   if (w->fw_status) {
    ionic_lif_handle_fw_up(lif);
   } else {
    ionic_lif_handle_fw_down(lif);

    /* Fire off another watchdog to see
 * if the FW is already back rather than
 * waiting another whole cycle
 */

    mod_timer(&lif->ionic->watchdog_timer, jiffies + 1);
   }
   break;
  default:
   break;
  }
  kfree(w);
  w = NULL;
 } while (true);
}

void ionic_lif_deferred_enqueue(struct ionic_lif *lif,
    struct ionic_deferred_work *work)
{
 spin_lock_bh(&lif->deferred.lock);
 list_add_tail(&work->list, &lif->deferred.list);
 spin_unlock_bh(&lif->deferred.lock);
 queue_work(lif->ionic->wq, &lif->deferred.work);
}

static void ionic_link_status_check(struct ionic_lif *lif)
{
 struct net_device *netdev = lif->netdev;
 u16 link_status;
 bool link_up;

 if (!test_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state))
  return;

 /* Don't put carrier back up if we're in a broken state */
 if (test_bit(IONIC_LIF_F_BROKEN, lif->state)) {
  clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state);
  return;
 }

 link_status = le16_to_cpu(lif->info->status.link_status);
 link_up = link_status == IONIC_PORT_OPER_STATUS_UP;

 if (link_up) {
  int err = 0;

  if (netdev->flags & IFF_UP && netif_running(netdev)) {
   mutex_lock(&lif->queue_lock);
   err = ionic_start_queues(lif);
   if (err && err != -EBUSY) {
    netdev_err(netdev,
        "Failed to start queues: %d\n", err);
    set_bit(IONIC_LIF_F_BROKEN, lif->state);
    netif_carrier_off(lif->netdev);
   }
   mutex_unlock(&lif->queue_lock);
  }

  if (!err && !netif_carrier_ok(netdev)) {
   ionic_port_identify(lif->ionic);
   netdev_info(netdev, "Link up - %d Gbps\n",
        le32_to_cpu(lif->info->status.link_speed) / 1000);
   netif_carrier_on(netdev);
  }
 } else {
  if (netif_carrier_ok(netdev)) {
   lif->link_down_count++;
   netdev_info(netdev, "Link down\n");
   netif_carrier_off(netdev);
  }

  if (netdev->flags & IFF_UP && netif_running(netdev)) {
   mutex_lock(&lif->queue_lock);
   ionic_stop_queues(lif);
   mutex_unlock(&lif->queue_lock);
  }
 }

 clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state);
}

void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep)
{
 struct ionic_deferred_work *work;

 /* we only need one request outstanding at a time */
 if (test_and_set_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state))
  return;

 if (!can_sleep) {
  work = kzalloc(sizeof(*work), GFP_ATOMIC);
  if (!work) {
   clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state);
   return;
  }

  work->type = IONIC_DW_TYPE_LINK_STATUS;
  ionic_lif_deferred_enqueue(lif, work);
 } else {
  ionic_link_status_check(lif);
 }
}

static irqreturn_t ionic_isr(int irq, void *data)
{
 struct napi_struct *napi = data;

 napi_schedule_irqoff(napi);

 return IRQ_HANDLED;
}

static int ionic_request_irq(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 struct ionic_intr_info *intr = &qcq->intr;
 struct device *dev = lif->ionic->dev;
 struct ionic_queue *q = &qcq->q;
 const char *name;

 if (lif->registered)
  name = netdev_name(lif->netdev);
 else
  name = dev_name(dev);

 snprintf(intr->name, sizeof(intr->name),
   "%.5s-%.16s-%.8s", IONIC_DRV_NAME, name, q->name);

 return devm_request_irq(dev, intr->vector, ionic_isr,
    0, intr->name, &qcq->napi);
}

static int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr)
{
 struct ionic *ionic = lif->ionic;
 int index;

 index = find_first_zero_bit(ionic->intrs, ionic->nintrs);
 if (index == ionic->nintrs) {
  netdev_warn(lif->netdev, "%s: no intr, index=%d nintrs=%d\n",
       __func__, index, ionic->nintrs);
  return -ENOSPC;
 }

 set_bit(index, ionic->intrs);
 ionic_intr_init(&ionic->idev, intr, index);

 return 0;
}

static void ionic_intr_free(struct ionic *ionic, int index)
{
 if (index != IONIC_INTR_INDEX_NOT_ASSIGNED && index < ionic->nintrs)
  clear_bit(index, ionic->intrs);
}

static void ionic_irq_aff_notify(struct irq_affinity_notify *notify,
     const cpumask_t *mask)
{
 struct ionic_intr_info *intr = container_of(notify, struct ionic_intr_info, aff_notify);

 cpumask_copy(*intr->affinity_mask, mask);
}

static void ionic_irq_aff_release(struct kref __always_unused *ref)
{
}

static int ionic_qcq_enable(struct ionic_qcq *qcq)
{
 struct ionic_queue *q = &qcq->q;
 struct ionic_lif *lif = q->lif;
 struct ionic_dev *idev;
 struct device *dev;

 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.q_control = {
   .opcode = IONIC_CMD_Q_CONTROL,
   .lif_index = cpu_to_le16(lif->index),
   .type = q->type,
   .index = cpu_to_le32(q->index),
   .oper = IONIC_Q_ENABLE,
  },
 };
 int ret;

 idev = &lif->ionic->idev;
 dev = lif->ionic->dev;

 dev_dbg(dev, "q_enable.index %d q_enable.qtype %d\n",
  ctx.cmd.q_control.index, ctx.cmd.q_control.type);

 if (qcq->flags & IONIC_QCQ_F_INTR)
  ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);

 ret = ionic_adminq_post_wait(lif, &ctx);
 if (ret)
  return ret;

 if (qcq->flags & IONIC_QCQ_F_INTR) {
  napi_enable(&qcq->napi);
  irq_set_affinity_notifier(qcq->intr.vector,
       &qcq->intr.aff_notify);
  irq_set_affinity_hint(qcq->intr.vector,
          *qcq->intr.affinity_mask);
  ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
    IONIC_INTR_MASK_CLEAR);
 }

 return 0;
}

static int ionic_qcq_disable(struct ionic_lif *lif, struct ionic_qcq *qcq, int fw_err)
{
 struct ionic_queue *q;

 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.q_control = {
   .opcode = IONIC_CMD_Q_CONTROL,
   .oper = IONIC_Q_DISABLE,
  },
 };

 if (!qcq) {
  netdev_err(lif->netdev, "%s: bad qcq\n", __func__);
  return -ENXIO;
 }

 q = &qcq->q;

 if (qcq->flags & IONIC_QCQ_F_INTR) {
  struct ionic_dev *idev = &lif->ionic->idev;

  if (lif->doorbell_wa)
   cancel_work_sync(&qcq->doorbell_napi_work);
  cancel_work_sync(&qcq->dim.work);
  ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
    IONIC_INTR_MASK_SET);
  synchronize_irq(qcq->intr.vector);
  irq_set_affinity_notifier(qcq->intr.vector, NULL);
  irq_set_affinity_hint(qcq->intr.vector, NULL);
  napi_disable(&qcq->napi);
 }

 /* If there was a previous fw communcation error, don't bother with
 * sending the adminq command and just return the same error value.
 */

 if (fw_err == -ETIMEDOUT || fw_err == -ENXIO)
  return fw_err;

 ctx.cmd.q_control.lif_index = cpu_to_le16(lif->index);
 ctx.cmd.q_control.type = q->type;
 ctx.cmd.q_control.index = cpu_to_le32(q->index);
 dev_dbg(lif->ionic->dev, "q_disable.index %d q_disable.qtype %d\n",
  ctx.cmd.q_control.index, ctx.cmd.q_control.type);

 return ionic_adminq_post_wait(lif, &ctx);
}

static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 struct ionic_dev *idev = &lif->ionic->idev;

 if (!qcq)
  return;

 if (!(qcq->flags & IONIC_QCQ_F_INITED))
  return;

 ionic_unregister_rxq_info(&qcq->q);
 if (qcq->flags & IONIC_QCQ_F_INTR) {
  ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
    IONIC_INTR_MASK_SET);
  netif_napi_del(&qcq->napi);
 }

 qcq->flags &= ~IONIC_QCQ_F_INITED;
}

static void ionic_qcq_intr_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 if (!(qcq->flags & IONIC_QCQ_F_INTR) || qcq->intr.vector == 0)
  return;

 irq_set_affinity_hint(qcq->intr.vector, NULL);
 devm_free_irq(lif->ionic->dev, qcq->intr.vector, &qcq->napi);
 qcq->intr.vector = 0;
 ionic_intr_free(lif->ionic, qcq->intr.index);
 qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
}

static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 struct device *dev = lif->ionic->dev;

 if (!qcq)
  return;

 ionic_debugfs_del_qcq(qcq);

 if (qcq->q_base) {
  dma_free_coherent(dev, qcq->q_size, qcq->q_base, qcq->q_base_pa);
  qcq->q_base = NULL;
  qcq->q_base_pa = 0;
 }

 if (qcq->cmb_q_base) {
  iounmap(qcq->cmb_q_base);
  ionic_put_cmb(lif, qcq->cmb_pgid, qcq->cmb_order);
  qcq->cmb_pgid = 0;
  qcq->cmb_order = 0;
  qcq->cmb_q_base = NULL;
  qcq->cmb_q_base_pa = 0;
 }

 if (qcq->cq_base) {
  dma_free_coherent(dev, qcq->cq_size, qcq->cq_base, qcq->cq_base_pa);
  qcq->cq_base = NULL;
  qcq->cq_base_pa = 0;
 }

 if (qcq->sg_base) {
  dma_free_coherent(dev, qcq->sg_size, qcq->sg_base, qcq->sg_base_pa);
  qcq->sg_base = NULL;
  qcq->sg_base_pa = 0;
 }

 page_pool_destroy(qcq->q.page_pool);
 qcq->q.page_pool = NULL;

 ionic_qcq_intr_free(lif, qcq);
 vfree(qcq->q.info);
 qcq->q.info = NULL;
}

void ionic_qcqs_free(struct ionic_lif *lif)
{
 struct device *dev = lif->ionic->dev;
 struct ionic_qcq *adminqcq;
 unsigned long irqflags;

 if (lif->notifyqcq) {
  ionic_qcq_free(lif, lif->notifyqcq);
  devm_kfree(dev, lif->notifyqcq);
  lif->notifyqcq = NULL;
 }

 if (lif->adminqcq) {
  spin_lock_irqsave(&lif->adminq_lock, irqflags);
  adminqcq = READ_ONCE(lif->adminqcq);
  lif->adminqcq = NULL;
  spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
  if (adminqcq) {
   ionic_qcq_free(lif, adminqcq);
   devm_kfree(dev, adminqcq);
  }
 }

 if (lif->rxqcqs) {
  devm_kfree(dev, lif->rxqstats);
  lif->rxqstats = NULL;
  devm_kfree(dev, lif->rxqcqs);
  lif->rxqcqs = NULL;
 }

 if (lif->txqcqs) {
  devm_kfree(dev, lif->txqstats);
  lif->txqstats = NULL;
  devm_kfree(dev, lif->txqcqs);
  lif->txqcqs = NULL;
 }
}

static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq,
          struct ionic_qcq *n_qcq)
{
 n_qcq->intr.vector = src_qcq->intr.vector;
 n_qcq->intr.index = src_qcq->intr.index;
}

static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 cpumask_var_t *affinity_mask;
 int err;

 if (!(qcq->flags & IONIC_QCQ_F_INTR)) {
  qcq->intr.index = IONIC_INTR_INDEX_NOT_ASSIGNED;
  return 0;
 }

 err = ionic_intr_alloc(lif, &qcq->intr);
 if (err) {
  netdev_warn(lif->netdev, "no intr for %s: %d\n",
       qcq->q.name, err);
  goto err_out;
 }

 err = ionic_bus_get_irq(lif->ionic, qcq->intr.index);
 if (err < 0) {
  netdev_warn(lif->netdev, "no vector for %s: %d\n",
       qcq->q.name, err);
  goto err_out_free_intr;
 }
 qcq->intr.vector = err;
 ionic_intr_mask_assert(lif->ionic->idev.intr_ctrl, qcq->intr.index,
          IONIC_INTR_MASK_SET);

 err = ionic_request_irq(lif, qcq);
 if (err) {
  netdev_warn(lif->netdev, "irq request failed %d\n", err);
  goto err_out_free_intr;
 }

 /* try to get the irq on the local numa node first */
 affinity_mask = &lif->ionic->affinity_masks[qcq->intr.index];
 if (cpumask_empty(*affinity_mask)) {
  unsigned int cpu;

  cpu = cpumask_local_spread(qcq->intr.index,
        dev_to_node(lif->ionic->dev));
  if (cpu != -1)
   cpumask_set_cpu(cpu, *affinity_mask);
 }

 qcq->intr.affinity_mask = affinity_mask;
 qcq->intr.aff_notify.notify = ionic_irq_aff_notify;
 qcq->intr.aff_notify.release = ionic_irq_aff_release;

 netdev_dbg(lif->netdev, "%s: Interrupt index %d\n", qcq->q.name, qcq->intr.index);
 return 0;

err_out_free_intr:
 ionic_intr_free(lif->ionic, qcq->intr.index);
err_out:
 return err;
}

static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
      unsigned int index,
      const char *name, unsigned int flags,
      unsigned int num_descs, unsigned int desc_size,
      unsigned int cq_desc_size,
      unsigned int sg_desc_size,
      unsigned int desc_info_size,
      unsigned int pid, struct bpf_prog *xdp_prog,
      struct ionic_qcq **qcq)
{
 struct ionic_dev *idev = &lif->ionic->idev;
 struct device *dev = lif->ionic->dev;
 struct ionic_qcq *new;
 int err;

 *qcq = NULL;

 new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL);
 if (!new) {
  netdev_err(lif->netdev, "Cannot allocate queue structure\n");
  err = -ENOMEM;
  goto err_out;
 }

 new->q.dev = dev;
 new->flags = flags;

 new->q.info = vcalloc(num_descs, desc_info_size);
 if (!new->q.info) {
  netdev_err(lif->netdev, "Cannot allocate queue info\n");
  err = -ENOMEM;
  goto err_out_free_qcq;
 }

 if (type == IONIC_QTYPE_RXQ) {
  struct page_pool_params pp_params = {
   .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
   .order = 0,
   .pool_size = num_descs,
   .nid = NUMA_NO_NODE,
   .dev = lif->ionic->dev,
   .napi = &new->napi,
   .dma_dir = DMA_FROM_DEVICE,
   .max_len = PAGE_SIZE,
   .netdev = lif->netdev,
  };

  if (xdp_prog)
   pp_params.dma_dir = DMA_BIDIRECTIONAL;

  new->q.page_pool = page_pool_create(&pp_params);
  if (IS_ERR(new->q.page_pool)) {
   netdev_err(lif->netdev, "Cannot create page_pool\n");
   err = PTR_ERR(new->q.page_pool);
   new->q.page_pool = NULL;
   goto err_out_free_q_info;
  }
 }

 new->q.type = type;
 new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems;

 err = ionic_q_init(lif, idev, &new->q, index, name, num_descs,
      desc_size, sg_desc_size, pid);
 if (err) {
  netdev_err(lif->netdev, "Cannot initialize queue\n");
  goto err_out_free_page_pool;
 }

 err = ionic_alloc_qcq_interrupt(lif, new);
 if (err)
  goto err_out_free_page_pool;

 err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size);
 if (err) {
  netdev_err(lif->netdev, "Cannot initialize completion queue\n");
  goto err_out_free_irq;
 }

 if (flags & IONIC_QCQ_F_NOTIFYQ) {
  int q_size;

  /* q & cq need to be contiguous in NotifyQ, so alloc it all in q
 * and don't alloc qc.  We leave new->qc_size and new->qc_base
 * as 0 to be sure we don't try to free it later.
 */

  q_size = ALIGN(num_descs * desc_size, PAGE_SIZE);
  new->q_size = PAGE_SIZE + q_size +
         ALIGN(num_descs * cq_desc_size, PAGE_SIZE);
  new->q_base = dma_alloc_coherent(dev, new->q_size,
       &new->q_base_pa, GFP_KERNEL);
  if (!new->q_base) {
   netdev_err(lif->netdev, "Cannot allocate qcq DMA memory\n");
   err = -ENOMEM;
   goto err_out_free_irq;
  }
  new->q.base = PTR_ALIGN(new->q_base, PAGE_SIZE);
  new->q.base_pa = ALIGN(new->q_base_pa, PAGE_SIZE);

  /* Base the NotifyQ cq.base off of the ALIGNed q.base */
  new->cq.base = PTR_ALIGN(new->q.base + q_size, PAGE_SIZE);
  new->cq.base_pa = ALIGN(new->q_base_pa + q_size, PAGE_SIZE);
  new->cq.bound_q = &new->q;
 } else {
  /* regular DMA q descriptors */
  new->q_size = PAGE_SIZE + (num_descs * desc_size);
  new->q_base = dma_alloc_coherent(dev, new->q_size, &new->q_base_pa,
       GFP_KERNEL);
  if (!new->q_base) {
   netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n");
   err = -ENOMEM;
   goto err_out_free_irq;
  }
  new->q.base = PTR_ALIGN(new->q_base, PAGE_SIZE);
  new->q.base_pa = ALIGN(new->q_base_pa, PAGE_SIZE);

  if (flags & IONIC_QCQ_F_CMB_RINGS) {
   /* on-chip CMB q descriptors */
   new->cmb_q_size = num_descs * desc_size;
   new->cmb_order = order_base_2(new->cmb_q_size / PAGE_SIZE);

   err = ionic_get_cmb(lif, &new->cmb_pgid, &new->cmb_q_base_pa,
         new->cmb_order);
   if (err) {
    netdev_err(lif->netdev,
        "Cannot allocate queue order %d from cmb: err %d\n",
        new->cmb_order, err);
    goto err_out_free_q;
   }

   new->cmb_q_base = ioremap_wc(new->cmb_q_base_pa, new->cmb_q_size);
   if (!new->cmb_q_base) {
    netdev_err(lif->netdev, "Cannot map queue from cmb\n");
    ionic_put_cmb(lif, new->cmb_pgid, new->cmb_order);
    err = -ENOMEM;
    goto err_out_free_q;
   }

   new->cmb_q_base_pa -= idev->phy_cmb_pages;
   new->q.cmb_base = new->cmb_q_base;
   new->q.cmb_base_pa = new->cmb_q_base_pa;
  }

  /* cq DMA descriptors */
  new->cq_size = PAGE_SIZE + (num_descs * cq_desc_size);
  new->cq_base = dma_alloc_coherent(dev, new->cq_size, &new->cq_base_pa,
        GFP_KERNEL);
  if (!new->cq_base) {
   netdev_err(lif->netdev, "Cannot allocate cq DMA memory\n");
   err = -ENOMEM;
   goto err_out_free_q;
  }
  new->cq.base = PTR_ALIGN(new->cq_base, PAGE_SIZE);
  new->cq.base_pa = ALIGN(new->cq_base_pa, PAGE_SIZE);
  new->cq.bound_q = &new->q;
 }

 if (flags & IONIC_QCQ_F_SG) {
  new->sg_size = PAGE_SIZE + (num_descs * sg_desc_size);
  new->sg_base = dma_alloc_coherent(dev, new->sg_size, &new->sg_base_pa,
        GFP_KERNEL);
  if (!new->sg_base) {
   netdev_err(lif->netdev, "Cannot allocate sg DMA memory\n");
   err = -ENOMEM;
   goto err_out_free_cq;
  }
  new->q.sg_base = PTR_ALIGN(new->sg_base, PAGE_SIZE);
  new->q.sg_base_pa = ALIGN(new->sg_base_pa, PAGE_SIZE);
 }

 INIT_WORK(&new->dim.work, ionic_dim_work);
 new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE;
 if (lif->doorbell_wa)
  INIT_WORK(&new->doorbell_napi_work, ionic_doorbell_napi_work);

 *qcq = new;

 return 0;

err_out_free_cq:
 dma_free_coherent(dev, new->cq_size, new->cq_base, new->cq_base_pa);
err_out_free_q:
 if (new->cmb_q_base) {
  iounmap(new->cmb_q_base);
  ionic_put_cmb(lif, new->cmb_pgid, new->cmb_order);
 }
 dma_free_coherent(dev, new->q_size, new->q_base, new->q_base_pa);
err_out_free_irq:
 if (flags & IONIC_QCQ_F_INTR) {
  devm_free_irq(dev, new->intr.vector, &new->napi);
  ionic_intr_free(lif->ionic, new->intr.index);
 }
err_out_free_page_pool:
 page_pool_destroy(new->q.page_pool);
err_out_free_q_info:
 vfree(new->q.info);
err_out_free_qcq:
 devm_kfree(dev, new);
err_out:
 dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err);
 return err;
}

static int ionic_qcqs_alloc(struct ionic_lif *lif)
{
 struct device *dev = lif->ionic->dev;
 unsigned int flags;
 int err;

 flags = IONIC_QCQ_F_INTR;
 err = ionic_qcq_alloc(lif, IONIC_QTYPE_ADMINQ, 0, "admin", flags,
         IONIC_ADMINQ_LENGTH,
         sizeof(struct ionic_admin_cmd),
         sizeof(struct ionic_admin_comp),
         0,
         sizeof(struct ionic_admin_desc_info),
         lif->kern_pid, NULL, &lif->adminqcq);
 if (err)
  return err;
 ionic_debugfs_add_qcq(lif, lif->adminqcq);

 if (lif->ionic->nnqs_per_lif) {
  flags = IONIC_QCQ_F_NOTIFYQ;
  err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0, "notifyq",
          flags, IONIC_NOTIFYQ_LENGTH,
          sizeof(struct ionic_notifyq_cmd),
          sizeof(union ionic_notifyq_comp),
          0,
          sizeof(struct ionic_admin_desc_info),
          lif->kern_pid, NULL, &lif->notifyqcq);
  if (err)
   goto err_out;
  ionic_debugfs_add_qcq(lif, lif->notifyqcq);

  /* Let the notifyq ride on the adminq interrupt */
  ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
 }

 err = -ENOMEM;
 lif->txqcqs = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif,
       sizeof(*lif->txqcqs), GFP_KERNEL);
 if (!lif->txqcqs)
  goto err_out;
 lif->rxqcqs = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif,
       sizeof(*lif->rxqcqs), GFP_KERNEL);
 if (!lif->rxqcqs)
  goto err_out;

 lif->txqstats = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif + 1,
         sizeof(*lif->txqstats), GFP_KERNEL);
 if (!lif->txqstats)
  goto err_out;
 lif->rxqstats = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif + 1,
         sizeof(*lif->rxqstats), GFP_KERNEL);
 if (!lif->rxqstats)
  goto err_out;

 return 0;

err_out:
 ionic_qcqs_free(lif);
 return err;
}

static void ionic_qcq_sanitize(struct ionic_qcq *qcq)
{
 qcq->q.tail_idx = 0;
 qcq->q.head_idx = 0;
 qcq->cq.tail_idx = 0;
 qcq->cq.done_color = 1;
 memset(qcq->q_base, 0, qcq->q_size);
 if (qcq->cmb_q_base)
  memset_io(qcq->cmb_q_base, 0, qcq->cmb_q_size);
 memset(qcq->cq_base, 0, qcq->cq_size);
 memset(qcq->sg_base, 0, qcq->sg_size);
}

static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 struct device *dev = lif->ionic->dev;
 struct ionic_queue *q = &qcq->q;
 struct ionic_cq *cq = &qcq->cq;
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.q_init = {
   .opcode = IONIC_CMD_Q_INIT,
   .lif_index = cpu_to_le16(lif->index),
   .type = q->type,
   .ver = lif->qtype_info[q->type].version,
   .index = cpu_to_le32(q->index),
   .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
          IONIC_QINIT_F_SG),
   .intr_index = cpu_to_le16(qcq->intr.index),
   .pid = cpu_to_le16(q->pid),
   .ring_size = ilog2(q->num_descs),
   .ring_base = cpu_to_le64(q->base_pa),
   .cq_ring_base = cpu_to_le64(cq->base_pa),
   .sg_ring_base = cpu_to_le64(q->sg_base_pa),
   .features = cpu_to_le64(q->features),
  },
 };
 int err;

 if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) {
  ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB);
  ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa);
 }

 dev_dbg(dev, "txq_init.pid %d\n", ctx.cmd.q_init.pid);
 dev_dbg(dev, "txq_init.index %d\n", ctx.cmd.q_init.index);
 dev_dbg(dev, "txq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
 dev_dbg(dev, "txq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
 dev_dbg(dev, "txq_init.cq_ring_base 0x%llx\n", ctx.cmd.q_init.cq_ring_base);
 dev_dbg(dev, "txq_init.sg_ring_base 0x%llx\n", ctx.cmd.q_init.sg_ring_base);
 dev_dbg(dev, "txq_init.flags 0x%x\n", ctx.cmd.q_init.flags);
 dev_dbg(dev, "txq_init.ver %d\n", ctx.cmd.q_init.ver);
 dev_dbg(dev, "txq_init.intr_index %d\n", ctx.cmd.q_init.intr_index);

 ionic_qcq_sanitize(qcq);

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  return err;

 q->hw_type = ctx.comp.q_init.hw_type;
 q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
 q->dbval = IONIC_DBELL_QID(q->hw_index);

 dev_dbg(dev, "txq->hw_type %d\n", q->hw_type);
 dev_dbg(dev, "txq->hw_index %d\n", q->hw_index);

 q->dbell_deadline = IONIC_TX_DOORBELL_DEADLINE;
 q->dbell_jiffies = jiffies;

 if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
  netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi);

 qcq->flags |= IONIC_QCQ_F_INITED;

 return 0;
}

static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
 struct device *dev = lif->ionic->dev;
 struct ionic_queue *q = &qcq->q;
 struct ionic_cq *cq = &qcq->cq;
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.q_init = {
   .opcode = IONIC_CMD_Q_INIT,
   .lif_index = cpu_to_le16(lif->index),
   .type = q->type,
   .ver = lif->qtype_info[q->type].version,
   .index = cpu_to_le32(q->index),
   .flags = cpu_to_le16(IONIC_QINIT_F_IRQ),
   .intr_index = cpu_to_le16(cq->bound_intr->index),
   .pid = cpu_to_le16(q->pid),
   .ring_size = ilog2(q->num_descs),
   .ring_base = cpu_to_le64(q->base_pa),
   .cq_ring_base = cpu_to_le64(cq->base_pa),
   .sg_ring_base = cpu_to_le64(q->sg_base_pa),
   .features = cpu_to_le64(q->features),
  },
 };
 int err;

 q->partner = &lif->txqcqs[q->index]->q;
 q->partner->partner = q;

 if (!lif->xdp_prog ||
     (lif->xdp_prog->aux && lif->xdp_prog->aux->xdp_has_frags))
  ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_SG);

 if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) {
  ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB);
  ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa);
 }

 dev_dbg(dev, "rxq_init.pid %d\n", ctx.cmd.q_init.pid);
 dev_dbg(dev, "rxq_init.index %d\n", ctx.cmd.q_init.index);
 dev_dbg(dev, "rxq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
 dev_dbg(dev, "rxq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
 dev_dbg(dev, "rxq_init.flags 0x%x\n", ctx.cmd.q_init.flags);
 dev_dbg(dev, "rxq_init.ver %d\n", ctx.cmd.q_init.ver);
 dev_dbg(dev, "rxq_init.intr_index %d\n", ctx.cmd.q_init.intr_index);

 ionic_qcq_sanitize(qcq);

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  return err;

 q->hw_type = ctx.comp.q_init.hw_type;
 q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
 q->dbval = IONIC_DBELL_QID(q->hw_index);

 dev_dbg(dev, "rxq->hw_type %d\n", q->hw_type);
 dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index);

 q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE;
 q->dbell_jiffies = jiffies;

 if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
  netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi);
 else
  netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi);
 err = ionic_register_rxq_info(q, qcq->napi.napi_id);
 if (err) {
  netif_napi_del(&qcq->napi);
  return err;
 }

 qcq->flags |= IONIC_QCQ_F_INITED;

 return 0;
}

int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif)
{
 unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz;
 unsigned int txq_i, flags;
 struct ionic_qcq *txq;
 u64 features;
 int err;

 if (lif->hwstamp_txq)
  return 0;

 features = IONIC_Q_F_2X_CQ_DESC | IONIC_TXQ_F_HWSTAMP;

 num_desc = IONIC_MIN_TXRX_DESC;
 desc_sz = sizeof(struct ionic_txq_desc);
 comp_sz = 2 * sizeof(struct ionic_txq_comp);

 if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 &&
     lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == sizeof(struct ionic_txq_sg_desc_v1))
  sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1);
 else
  sg_desc_sz = sizeof(struct ionic_txq_sg_desc);

 txq_i = lif->ionic->ntxqs_per_lif;
 flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG;

 err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags,
         num_desc, desc_sz, comp_sz, sg_desc_sz,
         sizeof(struct ionic_tx_desc_info),
         lif->kern_pid, NULL, &txq);
 if (err)
  goto err_qcq_alloc;

 txq->q.features = features;

 ionic_link_qcq_interrupts(lif->adminqcq, txq);
 ionic_debugfs_add_qcq(lif, txq);

 lif->hwstamp_txq = txq;

 if (netif_running(lif->netdev)) {
  err = ionic_lif_txq_init(lif, txq);
  if (err)
   goto err_qcq_init;

  if (test_bit(IONIC_LIF_F_UP, lif->state)) {
   err = ionic_qcq_enable(txq);
   if (err)
    goto err_qcq_enable;
  }
 }

 return 0;

err_qcq_enable:
 ionic_lif_qcq_deinit(lif, txq);
err_qcq_init:
 lif->hwstamp_txq = NULL;
 ionic_debugfs_del_qcq(txq);
 ionic_qcq_free(lif, txq);
 devm_kfree(lif->ionic->dev, txq);
err_qcq_alloc:
 return err;
}

int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif)
{
 unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz;
 unsigned int rxq_i, flags;
 struct ionic_qcq *rxq;
 u64 features;
 int err;

 if (lif->hwstamp_rxq)
  return 0;

 features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP;

 num_desc = IONIC_MIN_TXRX_DESC;
 desc_sz = sizeof(struct ionic_rxq_desc);
 comp_sz = 2 * sizeof(struct ionic_rxq_comp);
 sg_desc_sz = sizeof(struct ionic_rxq_sg_desc);

 rxq_i = lif->ionic->nrxqs_per_lif;
 flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG;

 err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags,
         num_desc, desc_sz, comp_sz, sg_desc_sz,
         sizeof(struct ionic_rx_desc_info),
         lif->kern_pid, NULL, &rxq);
 if (err)
  goto err_qcq_alloc;

 rxq->q.features = features;

 ionic_link_qcq_interrupts(lif->adminqcq, rxq);
 ionic_debugfs_add_qcq(lif, rxq);

 lif->hwstamp_rxq = rxq;

 if (netif_running(lif->netdev)) {
  err = ionic_lif_rxq_init(lif, rxq);
  if (err)
   goto err_qcq_init;

  if (test_bit(IONIC_LIF_F_UP, lif->state)) {
   ionic_rx_fill(&rxq->q, NULL);
   err = ionic_qcq_enable(rxq);
   if (err)
    goto err_qcq_enable;
  }
 }

 return 0;

err_qcq_enable:
 ionic_lif_qcq_deinit(lif, rxq);
err_qcq_init:
 lif->hwstamp_rxq = NULL;
 ionic_debugfs_del_qcq(rxq);
 ionic_qcq_free(lif, rxq);
 devm_kfree(lif->ionic->dev, rxq);
err_qcq_alloc:
 return err;
}

int ionic_lif_config_hwstamp_rxq_all(struct ionic_lif *lif, bool rx_all)
{
 struct ionic_queue_params qparam;

 ionic_init_queue_params(lif, &qparam);

 if (rx_all)
  qparam.rxq_features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP;
 else
  qparam.rxq_features = 0;

 /* if we're not running, just set the values and return */
 if (!netif_running(lif->netdev)) {
  lif->rxq_features = qparam.rxq_features;
  return 0;
 }

 return ionic_reconfigure_queues(lif, &qparam);
}

int ionic_lif_set_hwstamp_txmode(struct ionic_lif *lif, u16 txstamp_mode)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_TXSTAMP,
   .txstamp_mode = cpu_to_le16(txstamp_mode),
  },
 };

 return ionic_adminq_post_wait(lif, &ctx);
}

static void ionic_lif_del_hwstamp_rxfilt(struct ionic_lif *lif)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.rx_filter_del = {
   .opcode = IONIC_CMD_RX_FILTER_DEL,
   .lif_index = cpu_to_le16(lif->index),
  },
 };
 struct ionic_rx_filter *f;
 u32 filter_id;
 int err;

 spin_lock_bh(&lif->rx_filters.lock);

 f = ionic_rx_filter_rxsteer(lif);
 if (!f) {
  spin_unlock_bh(&lif->rx_filters.lock);
  return;
 }

 filter_id = f->filter_id;
 ionic_rx_filter_free(lif, f);

 spin_unlock_bh(&lif->rx_filters.lock);

 netdev_dbg(lif->netdev, "rx_filter del RXSTEER (id %d)\n", filter_id);

 ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(filter_id);

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err && err != -EEXIST)
  netdev_dbg(lif->netdev, "failed to delete rx_filter RXSTEER (id %d)\n", filter_id);
}

static int ionic_lif_add_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.rx_filter_add = {
   .opcode = IONIC_CMD_RX_FILTER_ADD,
   .lif_index = cpu_to_le16(lif->index),
   .match = cpu_to_le16(IONIC_RX_FILTER_STEER_PKTCLASS),
   .pkt_class = cpu_to_le64(pkt_class),
  },
 };
 u8 qtype;
 u32 qid;
 int err;

 if (!lif->hwstamp_rxq)
  return -EINVAL;

 qtype = lif->hwstamp_rxq->q.type;
 ctx.cmd.rx_filter_add.qtype = qtype;

 qid = lif->hwstamp_rxq->q.index;
 ctx.cmd.rx_filter_add.qid = cpu_to_le32(qid);

 netdev_dbg(lif->netdev, "rx_filter add RXSTEER\n");
 err = ionic_adminq_post_wait(lif, &ctx);
 if (err && err != -EEXIST)
  return err;

 spin_lock_bh(&lif->rx_filters.lock);
 err = ionic_rx_filter_save(lif, 0, qid, 0, &ctx, IONIC_FILTER_STATE_SYNCED);
 spin_unlock_bh(&lif->rx_filters.lock);

 return err;
}

int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class)
{
 ionic_lif_del_hwstamp_rxfilt(lif);

 if (!pkt_class)
  return 0;

 return ionic_lif_add_hwstamp_rxfilt(lif, pkt_class);
}

static int ionic_adminq_napi(struct napi_struct *napi, int budget)
{
 struct ionic_intr_info *intr = napi_to_cq(napi)->bound_intr;
 struct ionic_lif *lif = napi_to_cq(napi)->lif;
 struct ionic_dev *idev = &lif->ionic->idev;
 unsigned long irqflags;
 unsigned int flags = 0;
 int rx_work = 0;
 int tx_work = 0;
 int n_work = 0;
 int a_work = 0;
 int work_done;
 int credits;

 if (lif->notifyqcq && lif->notifyqcq->flags & IONIC_QCQ_F_INITED)
  n_work = ionic_cq_service(&lif->notifyqcq->cq, budget,
       ionic_notifyq_service, NULL, NULL);

 spin_lock_irqsave(&lif->adminq_lock, irqflags);
 if (lif->adminqcq && lif->adminqcq->flags & IONIC_QCQ_F_INITED)
  a_work = ionic_cq_service(&lif->adminqcq->cq, budget,
       ionic_adminq_service, NULL, NULL);

 spin_unlock_irqrestore(&lif->adminq_lock, irqflags);

 if (lif->hwstamp_rxq)
  rx_work = ionic_cq_service(&lif->hwstamp_rxq->cq, budget,
        ionic_rx_service, NULL, NULL);

 if (lif->hwstamp_txq)
  tx_work = ionic_tx_cq_service(&lif->hwstamp_txq->cq, budget, !!budget);

 work_done = max(max(n_work, a_work), max(rx_work, tx_work));
 if (work_done < budget && napi_complete_done(napi, work_done)) {
  flags |= IONIC_INTR_CRED_UNMASK;
  intr->rearm_count++;
 }

 if (work_done || flags) {
  flags |= IONIC_INTR_CRED_RESET_COALESCE;
  credits = n_work + a_work + rx_work + tx_work;
  ionic_intr_credits(idev->intr_ctrl, intr->index, credits, flags);
 }

 if (lif->doorbell_wa) {
  if (!a_work)
   ionic_adminq_poke_doorbell(&lif->adminqcq->q);
  if (lif->hwstamp_rxq && !rx_work)
   ionic_rxq_poke_doorbell(&lif->hwstamp_rxq->q);
  if (lif->hwstamp_txq && !tx_work)
   ionic_txq_poke_doorbell(&lif->hwstamp_txq->q);
 }

 return work_done;
}

void ionic_get_stats64(struct net_device *netdev,
         struct rtnl_link_stats64 *ns)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic_lif_stats *ls;

 memset(ns, 0, sizeof(*ns));
 ls = &lif->info->stats;

 ns->rx_packets = le64_to_cpu(ls->rx_ucast_packets) +
    le64_to_cpu(ls->rx_mcast_packets) +
    le64_to_cpu(ls->rx_bcast_packets);

 ns->tx_packets = le64_to_cpu(ls->tx_ucast_packets) +
    le64_to_cpu(ls->tx_mcast_packets) +
    le64_to_cpu(ls->tx_bcast_packets);

 ns->rx_bytes = le64_to_cpu(ls->rx_ucast_bytes) +
         le64_to_cpu(ls->rx_mcast_bytes) +
         le64_to_cpu(ls->rx_bcast_bytes);

 ns->tx_bytes = le64_to_cpu(ls->tx_ucast_bytes) +
         le64_to_cpu(ls->tx_mcast_bytes) +
         le64_to_cpu(ls->tx_bcast_bytes);

 ns->rx_dropped = le64_to_cpu(ls->rx_ucast_drop_packets) +
    le64_to_cpu(ls->rx_mcast_drop_packets) +
    le64_to_cpu(ls->rx_bcast_drop_packets);

 ns->tx_dropped = le64_to_cpu(ls->tx_ucast_drop_packets) +
    le64_to_cpu(ls->tx_mcast_drop_packets) +
    le64_to_cpu(ls->tx_bcast_drop_packets);

 ns->multicast = le64_to_cpu(ls->rx_mcast_packets);

 ns->rx_over_errors = le64_to_cpu(ls->rx_queue_empty);

 ns->rx_missed_errors = le64_to_cpu(ls->rx_dma_error) +
          le64_to_cpu(ls->rx_queue_disabled) +
          le64_to_cpu(ls->rx_desc_fetch_error) +
          le64_to_cpu(ls->rx_desc_data_error);

 ns->tx_aborted_errors = le64_to_cpu(ls->tx_dma_error) +
    le64_to_cpu(ls->tx_queue_disabled) +
    le64_to_cpu(ls->tx_desc_fetch_error) +
    le64_to_cpu(ls->tx_desc_data_error);

 ns->rx_errors = ns->rx_over_errors +
   ns->rx_missed_errors;

 ns->tx_errors = ns->tx_aborted_errors;
}

static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
{
 return ionic_lif_list_addr(netdev_priv(netdev), addr, ADD_ADDR);
}

static int ionic_addr_del(struct net_device *netdev, const u8 *addr)
{
 /* Don't delete our own address from the uc list */
 if (ether_addr_equal(addr, netdev->dev_addr))
  return 0;

 return ionic_lif_list_addr(netdev_priv(netdev), addr, DEL_ADDR);
}

void ionic_lif_rx_mode(struct ionic_lif *lif)
{
 struct net_device *netdev = lif->netdev;
 unsigned int nfilters;
 unsigned int nd_flags;
 char buf[128];
 u16 rx_mode;
 int i;
#define REMAIN(__x) (sizeof(buf) - (__x))

 mutex_lock(&lif->config_lock);

 /* grab the flags once for local use */
 nd_flags = netdev->flags;

 rx_mode = IONIC_RX_MODE_F_UNICAST;
 rx_mode |= (nd_flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0;
 rx_mode |= (nd_flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0;
 rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0;
 rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;

 /* sync the filters */
 ionic_rx_filter_sync(lif);

 /* check for overflow state
 *    if so, we track that we overflowed and enable NIC PROMISC
 *    else if the overflow is set and not needed
 *       we remove our overflow flag and check the netdev flags
 *       to see if we can disable NIC PROMISC
 */

 nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);

 if (((lif->nucast + lif->nmcast) >= nfilters) ||
     (lif->max_vlans && lif->nvlans >= lif->max_vlans)) {
  rx_mode |= IONIC_RX_MODE_F_PROMISC;
  rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
 } else {
  if (!(nd_flags & IFF_PROMISC))
   rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
  if (!(nd_flags & IFF_ALLMULTI))
   rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI;
 }

 i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
        lif->rx_mode, rx_mode);
 if (rx_mode & IONIC_RX_MODE_F_UNICAST)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST");
 if (rx_mode & IONIC_RX_MODE_F_MULTICAST)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST");
 if (rx_mode & IONIC_RX_MODE_F_BROADCAST)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST");
 if (rx_mode & IONIC_RX_MODE_F_PROMISC)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
 if (rx_mode & IONIC_RX_MODE_F_ALLMULTI)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
 if (rx_mode & IONIC_RX_MODE_F_RDMA_SNIFFER)
  i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_RDMA_SNIFFER");
 netdev_dbg(netdev, "lif%d %s\n", lif->index, buf);

 if (lif->rx_mode != rx_mode) {
  struct ionic_admin_ctx ctx = {
   .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
   .cmd.rx_mode_set = {
    .opcode = IONIC_CMD_RX_MODE_SET,
    .lif_index = cpu_to_le16(lif->index),
   },
  };
  int err;

  ctx.cmd.rx_mode_set.rx_mode = cpu_to_le16(rx_mode);
  err = ionic_adminq_post_wait(lif, &ctx);
  if (err)
   netdev_warn(netdev, "set rx_mode 0x%04x failed: %d\n",
        rx_mode, err);
  else
   lif->rx_mode = rx_mode;
 }

 mutex_unlock(&lif->config_lock);
}

static void ionic_ndo_set_rx_mode(struct net_device *netdev)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic_deferred_work *work;

 /* Sync the kernel filter list with the driver filter list */
 __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
 __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);

 /* Shove off the rest of the rxmode work to the work task
 * which will include syncing the filters to the firmware.
 */

 work = kzalloc(sizeof(*work), GFP_ATOMIC);
 if (!work) {
  netdev_err(lif->netdev, "rxmode change dropped\n");
  return;
 }
 work->type = IONIC_DW_TYPE_RX_MODE;
 netdev_dbg(lif->netdev, "deferred: rx_mode\n");
 ionic_lif_deferred_enqueue(lif, work);
}

static __le64 ionic_netdev_features_to_nic(netdev_features_t features)
{
 u64 wanted = 0;

 if (features & NETIF_F_HW_VLAN_CTAG_TX)
  wanted |= IONIC_ETH_HW_VLAN_TX_TAG;
 if (features & NETIF_F_HW_VLAN_CTAG_RX)
  wanted |= IONIC_ETH_HW_VLAN_RX_STRIP;
 if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
  wanted |= IONIC_ETH_HW_VLAN_RX_FILTER;
 if (features & NETIF_F_RXHASH)
  wanted |= IONIC_ETH_HW_RX_HASH;
 if (features & NETIF_F_RXCSUM)
  wanted |= IONIC_ETH_HW_RX_CSUM;
 if (features & NETIF_F_SG)
  wanted |= IONIC_ETH_HW_TX_SG;
 if (features & NETIF_F_HW_CSUM)
  wanted |= IONIC_ETH_HW_TX_CSUM;
 if (features & NETIF_F_TSO)
  wanted |= IONIC_ETH_HW_TSO;
 if (features & NETIF_F_TSO6)
  wanted |= IONIC_ETH_HW_TSO_IPV6;
 if (features & NETIF_F_TSO_ECN)
  wanted |= IONIC_ETH_HW_TSO_ECN;
 if (features & NETIF_F_GSO_GRE)
  wanted |= IONIC_ETH_HW_TSO_GRE;
 if (features & NETIF_F_GSO_GRE_CSUM)
  wanted |= IONIC_ETH_HW_TSO_GRE_CSUM;
 if (features & NETIF_F_GSO_IPXIP4)
  wanted |= IONIC_ETH_HW_TSO_IPXIP4;
 if (features & NETIF_F_GSO_IPXIP6)
  wanted |= IONIC_ETH_HW_TSO_IPXIP6;
 if (features & NETIF_F_GSO_UDP_TUNNEL)
  wanted |= IONIC_ETH_HW_TSO_UDP;
 if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM)
  wanted |= IONIC_ETH_HW_TSO_UDP_CSUM;

 return cpu_to_le64(wanted);
}

static int ionic_set_nic_features(struct ionic_lif *lif,
      netdev_features_t features)
{
 struct device *dev = lif->ionic->dev;
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_FEATURES,
  },
 };
 u64 vlan_flags = IONIC_ETH_HW_VLAN_TX_TAG |
    IONIC_ETH_HW_VLAN_RX_STRIP |
    IONIC_ETH_HW_VLAN_RX_FILTER;
 u64 old_hw_features;
 int err;

 ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features);

 if (lif->phc)
  ctx.cmd.lif_setattr.features |= cpu_to_le64(IONIC_ETH_HW_TIMESTAMP);

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  return err;

 old_hw_features = lif->hw_features;
 lif->hw_features = le64_to_cpu(ctx.cmd.lif_setattr.features &
           ctx.comp.lif_setattr.features);

 if ((old_hw_features ^ lif->hw_features) & IONIC_ETH_HW_RX_HASH)
  ionic_lif_rss_config(lif, lif->rss_types, NULL, NULL);

 if ((vlan_flags & le64_to_cpu(ctx.cmd.lif_setattr.features)) &&
     !(vlan_flags & le64_to_cpu(ctx.comp.lif_setattr.features)))
  dev_info_once(lif->ionic->dev, "NIC is not supporting vlan offload, likely in SmartNIC mode\n");

 if (lif->hw_features & IONIC_ETH_HW_VLAN_TX_TAG)
  dev_dbg(dev, "feature ETH_HW_VLAN_TX_TAG\n");
 if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_STRIP)
  dev_dbg(dev, "feature ETH_HW_VLAN_RX_STRIP\n");
 if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_FILTER)
  dev_dbg(dev, "feature ETH_HW_VLAN_RX_FILTER\n");
 if (lif->hw_features & IONIC_ETH_HW_RX_HASH)
  dev_dbg(dev, "feature ETH_HW_RX_HASH\n");
 if (lif->hw_features & IONIC_ETH_HW_TX_SG)
  dev_dbg(dev, "feature ETH_HW_TX_SG\n");
 if (lif->hw_features & IONIC_ETH_HW_TX_CSUM)
  dev_dbg(dev, "feature ETH_HW_TX_CSUM\n");
 if (lif->hw_features & IONIC_ETH_HW_RX_CSUM)
  dev_dbg(dev, "feature ETH_HW_RX_CSUM\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO)
  dev_dbg(dev, "feature ETH_HW_TSO\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPV6)
  dev_dbg(dev, "feature ETH_HW_TSO_IPV6\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_ECN)
  dev_dbg(dev, "feature ETH_HW_TSO_ECN\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_GRE)
  dev_dbg(dev, "feature ETH_HW_TSO_GRE\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_GRE_CSUM)
  dev_dbg(dev, "feature ETH_HW_TSO_GRE_CSUM\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP4)
  dev_dbg(dev, "feature ETH_HW_TSO_IPXIP4\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP6)
  dev_dbg(dev, "feature ETH_HW_TSO_IPXIP6\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_UDP)
  dev_dbg(dev, "feature ETH_HW_TSO_UDP\n");
 if (lif->hw_features & IONIC_ETH_HW_TSO_UDP_CSUM)
  dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n");
 if (lif->hw_features & IONIC_ETH_HW_TIMESTAMP)
  dev_dbg(dev, "feature ETH_HW_TIMESTAMP\n");

 return 0;
}

static int ionic_init_nic_features(struct ionic_lif *lif)
{
 struct net_device *netdev = lif->netdev;
 netdev_features_t features;
 int err;

 /* set up what we expect to support by default */
 features = NETIF_F_HW_VLAN_CTAG_TX |
     NETIF_F_HW_VLAN_CTAG_RX |
     NETIF_F_HW_VLAN_CTAG_FILTER |
     NETIF_F_SG |
     NETIF_F_HW_CSUM |
     NETIF_F_RXCSUM |
     NETIF_F_TSO |
     NETIF_F_TSO6 |
     NETIF_F_TSO_ECN |
     NETIF_F_GSO_GRE |
     NETIF_F_GSO_GRE_CSUM |
     NETIF_F_GSO_IPXIP4 |
     NETIF_F_GSO_IPXIP6 |
     NETIF_F_GSO_UDP_TUNNEL |
     NETIF_F_GSO_UDP_TUNNEL_CSUM;

 if (lif->nxqs > 1)
  features |= NETIF_F_RXHASH;

 err = ionic_set_nic_features(lif, features);
 if (err)
  return err;

 /* tell the netdev what we actually can support */
 netdev->features |= NETIF_F_HIGHDMA;

 if (lif->hw_features & IONIC_ETH_HW_VLAN_TX_TAG)
  netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
 if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_STRIP)
  netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
 if (lif->hw_features & IONIC_ETH_HW_VLAN_RX_FILTER)
  netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 if (lif->hw_features & IONIC_ETH_HW_RX_HASH)
  netdev->hw_features |= NETIF_F_RXHASH;
 if (lif->hw_features & IONIC_ETH_HW_TX_SG)
  netdev->hw_features |= NETIF_F_SG;

 if (lif->hw_features & IONIC_ETH_HW_TX_CSUM)
  netdev->hw_enc_features |= NETIF_F_HW_CSUM;
 if (lif->hw_features & IONIC_ETH_HW_RX_CSUM)
  netdev->hw_enc_features |= NETIF_F_RXCSUM;
 if (lif->hw_features & IONIC_ETH_HW_TSO)
  netdev->hw_enc_features |= NETIF_F_TSO;
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPV6)
  netdev->hw_enc_features |= NETIF_F_TSO6;
 if (lif->hw_features & IONIC_ETH_HW_TSO_ECN)
  netdev->hw_enc_features |= NETIF_F_TSO_ECN;
 if (lif->hw_features & IONIC_ETH_HW_TSO_GRE)
  netdev->hw_enc_features |= NETIF_F_GSO_GRE;
 if (lif->hw_features & IONIC_ETH_HW_TSO_GRE_CSUM)
  netdev->hw_enc_features |= NETIF_F_GSO_GRE_CSUM;
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP4)
  netdev->hw_enc_features |= NETIF_F_GSO_IPXIP4;
 if (lif->hw_features & IONIC_ETH_HW_TSO_IPXIP6)
  netdev->hw_enc_features |= NETIF_F_GSO_IPXIP6;
 if (lif->hw_features & IONIC_ETH_HW_TSO_UDP)
  netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL;
 if (lif->hw_features & IONIC_ETH_HW_TSO_UDP_CSUM)
  netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;

 netdev->hw_features |= netdev->hw_enc_features;
 netdev->features |= netdev->hw_features;
 netdev->vlan_features |= netdev->features & ~NETIF_F_VLAN_FEATURES;

 netdev->priv_flags |= IFF_UNICAST_FLT |
         IFF_LIVE_ADDR_CHANGE;

 netdev->xdp_features = NETDEV_XDP_ACT_BASIC    |
          NETDEV_XDP_ACT_REDIRECT |
          NETDEV_XDP_ACT_RX_SG    |
          NETDEV_XDP_ACT_NDO_XMIT |
          NETDEV_XDP_ACT_NDO_XMIT_SG;

 return 0;
}

static int ionic_set_features(struct net_device *netdev,
         netdev_features_t features)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 int err;

 netdev_dbg(netdev, "%s: lif->features=0x%08llx new_features=0x%08llx\n",
     __func__, (u64)lif->netdev->features, (u64)features);

 err = ionic_set_nic_features(lif, features);

 return err;
}

static int ionic_set_attr_mac(struct ionic_lif *lif, u8 *mac)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_MAC,
  },
 };

 ether_addr_copy(ctx.cmd.lif_setattr.mac, mac);
 return ionic_adminq_post_wait(lif, &ctx);
}

static int ionic_get_attr_mac(struct ionic_lif *lif, u8 *mac_addr)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_getattr = {
   .opcode = IONIC_CMD_LIF_GETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_MAC,
  },
 };
 int err;

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  return err;

 ether_addr_copy(mac_addr, ctx.comp.lif_getattr.mac);
 return 0;
}

static int ionic_program_mac(struct ionic_lif *lif, u8 *mac)
{
 u8  get_mac[ETH_ALEN];
 int err;

 err = ionic_set_attr_mac(lif, mac);
 if (err)
  return err;

 err = ionic_get_attr_mac(lif, get_mac);
 if (err)
  return err;

 /* To deal with older firmware that silently ignores the set attr mac:
 * doesn't actually change the mac and doesn't return an error, so we
 * do the get attr to verify whether or not the set actually happened
 */

 if (!ether_addr_equal(get_mac, mac))
  return 1;

 return 0;
}

static int ionic_set_mac_address(struct net_device *netdev, void *sa)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct sockaddr *addr = sa;
 u8 *mac;
 int err;

 mac = (u8 *)addr->sa_data;
 if (ether_addr_equal(netdev->dev_addr, mac))
  return 0;

 err = ionic_program_mac(lif, mac);
 if (err < 0)
  return err;

 if (err > 0)
  netdev_dbg(netdev, "%s: SET and GET ATTR Mac are not equal-due to old FW running\n",
      __func__);

 err = eth_prepare_mac_addr_change(netdev, addr);
 if (err)
  return err;

 if (!is_zero_ether_addr(netdev->dev_addr)) {
  netdev_info(netdev, "deleting mac addr %pM\n",
       netdev->dev_addr);
  ionic_lif_addr_del(netdev_priv(netdev), netdev->dev_addr);
 }

 eth_commit_mac_addr_change(netdev, addr);
 netdev_info(netdev, "updating mac addr %pM\n", mac);

 return ionic_lif_addr_add(netdev_priv(netdev), mac);
}

void ionic_stop_queues_reconfig(struct ionic_lif *lif)
{
 /* Stop and clean the queues before reconfiguration */
 netif_device_detach(lif->netdev);
 ionic_stop_queues(lif);
 ionic_txrx_deinit(lif);
}

static int ionic_start_queues_reconfig(struct ionic_lif *lif)
{
 int err;

 /* Re-init the queues after reconfiguration */

 /* The only way txrx_init can fail here is if communication
 * with FW is suddenly broken.  There's not much we can do
 * at this point - error messages have already been printed,
 * so we can continue on and the user can eventually do a
 * DOWN and UP to try to reset and clear the issue.
 */

 err = ionic_txrx_init(lif);
 ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
 netif_device_attach(lif->netdev);

 return err;
}

static bool ionic_xdp_is_valid_mtu(struct ionic_lif *lif, u32 mtu,
       struct bpf_prog *xdp_prog)
{
 if (!xdp_prog)
  return true;

 if (mtu <= IONIC_XDP_MAX_LINEAR_MTU)
  return true;

 if (xdp_prog->aux && xdp_prog->aux->xdp_has_frags)
  return true;

 return false;
}

static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_MTU,
   .mtu = cpu_to_le32(new_mtu),
  },
 };
 struct bpf_prog *xdp_prog;
 int err;

 xdp_prog = READ_ONCE(lif->xdp_prog);
 if (!ionic_xdp_is_valid_mtu(lif, new_mtu, xdp_prog))
  return -EINVAL;

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  return err;

 /* if we're not running, nothing more to do */
 if (!netif_running(netdev)) {
  WRITE_ONCE(netdev->mtu, new_mtu);
  return 0;
 }

 mutex_lock(&lif->queue_lock);
 ionic_stop_queues_reconfig(lif);
 WRITE_ONCE(netdev->mtu, new_mtu);
 err = ionic_start_queues_reconfig(lif);
 mutex_unlock(&lif->queue_lock);

 return err;
}

static void ionic_tx_timeout_work(struct work_struct *ws)
{
 struct ionic_lif *lif = container_of(ws, struct ionic_lif, tx_timeout_work);
 int err;

 if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
  return;

 /* if we were stopped before this scheduled job was launched,
 * don't bother the queues as they are already stopped.
 */

 if (!netif_running(lif->netdev))
  return;

 mutex_lock(&lif->queue_lock);
 ionic_stop_queues_reconfig(lif);
 err = ionic_start_queues_reconfig(lif);
 mutex_unlock(&lif->queue_lock);

 if (err)
  dev_err(lif->ionic->dev, "%s: Restarting queues failed\n", __func__);
}

static void ionic_tx_timeout(struct net_device *netdev, unsigned int txqueue)
{
 struct ionic_lif *lif = netdev_priv(netdev);

 netdev_info(lif->netdev, "Tx Timeout triggered - txq %d\n", txqueue);
 schedule_work(&lif->tx_timeout_work);
}

static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
     u16 vid)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 int err;

 err = ionic_lif_vlan_add(lif, vid);
 if (err)
  return err;

 ionic_lif_rx_mode(lif);

 return 0;
}

static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
      u16 vid)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 int err;

 err = ionic_lif_vlan_del(lif, vid);
 if (err)
  return err;

 ionic_lif_rx_mode(lif);

 return 0;
}

int ionic_lif_rss_config(struct ionic_lif *lif, const u16 types,
    const u8 *key, const u32 *indir)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .attr = IONIC_LIF_ATTR_RSS,
   .rss.addr = cpu_to_le64(lif->rss_ind_tbl_pa),
  },
 };
 unsigned int i, tbl_sz;

 if (lif->hw_features & IONIC_ETH_HW_RX_HASH) {
  lif->rss_types = types;
  ctx.cmd.lif_setattr.rss.types = cpu_to_le16(types);
 }

 if (key)
  memcpy(lif->rss_hash_key, key, IONIC_RSS_HASH_KEY_SIZE);

 if (indir) {
  tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
  for (i = 0; i < tbl_sz; i++)
   lif->rss_ind_tbl[i] = indir[i];
 }

 memcpy(ctx.cmd.lif_setattr.rss.key, lif->rss_hash_key,
        IONIC_RSS_HASH_KEY_SIZE);

 return ionic_adminq_post_wait(lif, &ctx);
}

static int ionic_lif_rss_init(struct ionic_lif *lif)
{
 unsigned int tbl_sz;
 unsigned int i;

 lif->rss_types = IONIC_RSS_TYPE_IPV4     |
    IONIC_RSS_TYPE_IPV4_TCP |
    IONIC_RSS_TYPE_IPV4_UDP |
    IONIC_RSS_TYPE_IPV6     |
    IONIC_RSS_TYPE_IPV6_TCP |
    IONIC_RSS_TYPE_IPV6_UDP;

 /* Fill indirection table with 'default' values */
 tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
 for (i = 0; i < tbl_sz; i++)
  lif->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i, lif->nxqs);

 return ionic_lif_rss_config(lif, lif->rss_types, NULL, NULL);
}

static void ionic_lif_rss_deinit(struct ionic_lif *lif)
{
 int tbl_sz;

 tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
 memset(lif->rss_ind_tbl, 0, tbl_sz);
 memset(lif->rss_hash_key, 0, IONIC_RSS_HASH_KEY_SIZE);

 ionic_lif_rss_config(lif, 0x0, NULL, NULL);
}

static void ionic_lif_quiesce(struct ionic_lif *lif)
{
 struct ionic_admin_ctx ctx = {
  .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
  .cmd.lif_setattr = {
   .opcode = IONIC_CMD_LIF_SETATTR,
   .index = cpu_to_le16(lif->index),
   .attr = IONIC_LIF_ATTR_STATE,
   .state = IONIC_LIF_QUIESCE,
  },
 };
 int err;

 err = ionic_adminq_post_wait(lif, &ctx);
 if (err)
  netdev_dbg(lif->netdev, "lif quiesce failed %d\n", err);
}

static void ionic_txrx_disable(struct ionic_lif *lif)
{
 unsigned int i;
 int err = 0;

 if (lif->txqcqs) {
  for (i = 0; i < lif->nxqs; i++)
   err = ionic_qcq_disable(lif, lif->txqcqs[i], err);
 }

 if (lif->hwstamp_txq)
  err = ionic_qcq_disable(lif, lif->hwstamp_txq, err);

 if (lif->rxqcqs) {
  for (i = 0; i < lif->nxqs; i++)
   err = ionic_qcq_disable(lif, lif->rxqcqs[i], err);
 }

 if (lif->hwstamp_rxq)
  err = ionic_qcq_disable(lif, lif->hwstamp_rxq, err);

 ionic_lif_quiesce(lif);
}

static void ionic_txrx_deinit(struct ionic_lif *lif)
{
 unsigned int i;

 if (lif->txqcqs) {
  for (i = 0; i < lif->nxqs && lif->txqcqs[i]; i++) {
   ionic_lif_qcq_deinit(lif, lif->txqcqs[i]);
   ionic_tx_flush(&lif->txqcqs[i]->cq);
   ionic_tx_empty(&lif->txqcqs[i]->q);
  }
 }

 if (lif->rxqcqs) {
  for (i = 0; i < lif->nxqs && lif->rxqcqs[i]; i++) {
   ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]);
   ionic_rx_empty(&lif->rxqcqs[i]->q);
  }
 }
 lif->rx_mode = 0;

 if (lif->hwstamp_txq) {
  ionic_lif_qcq_deinit(lif, lif->hwstamp_txq);
  ionic_tx_flush(&lif->hwstamp_txq->cq);
  ionic_tx_empty(&lif->hwstamp_txq->q);
 }

 if (lif->hwstamp_rxq) {
  ionic_lif_qcq_deinit(lif, lif->hwstamp_rxq);
  ionic_rx_empty(&lif->hwstamp_rxq->q);
 }
}

void ionic_txrx_free(struct ionic_lif *lif)
{
 unsigned int i;

 if (lif->txqcqs) {
  for (i = 0; i < lif->ionic->ntxqs_per_lif && lif->txqcqs[i]; i++) {
   ionic_qcq_free(lif, lif->txqcqs[i]);
   devm_kfree(lif->ionic->dev, lif->txqcqs[i]);
   lif->txqcqs[i] = NULL;
  }
 }

 if (lif->rxqcqs) {
  for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) {
   ionic_qcq_free(lif, lif->rxqcqs[i]);
   devm_kfree(lif->ionic->dev, lif->rxqcqs[i]);
   lif->rxqcqs[i] = NULL;
  }
 }

 if (lif->hwstamp_txq) {
  ionic_qcq_free(lif, lif->hwstamp_txq);
  devm_kfree(lif->ionic->dev, lif->hwstamp_txq);
  lif->hwstamp_txq = NULL;
 }

 if (lif->hwstamp_rxq) {
  ionic_qcq_free(lif, lif->hwstamp_rxq);
  devm_kfree(lif->ionic->dev, lif->hwstamp_rxq);
  lif->hwstamp_rxq = NULL;
 }
}

static int ionic_txrx_alloc(struct ionic_lif *lif)
{
 unsigned int comp_sz, desc_sz, num_desc, sg_desc_sz;
 unsigned int flags, i;
 int err = 0;

 num_desc = lif->ntxq_descs;
 desc_sz = sizeof(struct ionic_txq_desc);
 comp_sz = sizeof(struct ionic_txq_comp);

 if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 &&
     lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz ==
       sizeof(struct ionic_txq_sg_desc_v1))
  sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1);
 else
  sg_desc_sz = sizeof(struct ionic_txq_sg_desc);

 flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG;

 if (test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state))
  flags |= IONIC_QCQ_F_CMB_RINGS;

 if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
  flags |= IONIC_QCQ_F_INTR;

 for (i = 0; i < lif->nxqs; i++) {
  err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
          num_desc, desc_sz, comp_sz, sg_desc_sz,
          sizeof(struct ionic_tx_desc_info),
          lif->kern_pid, NULL, &lif->txqcqs[i]);
  if (err)
   goto err_out;

  if (flags & IONIC_QCQ_F_INTR) {
   ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
          lif->txqcqs[i]->intr.index,
          lif->tx_coalesce_hw);
   if (test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state))
    lif->txqcqs[i]->intr.dim_coal_hw = lif->tx_coalesce_hw;
  }

  ionic_debugfs_add_qcq(lif, lif->txqcqs[i]);
 }

 flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG | IONIC_QCQ_F_INTR;

 if (test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state))
  flags |= IONIC_QCQ_F_CMB_RINGS;

 num_desc = lif->nrxq_descs;
 desc_sz = sizeof(struct ionic_rxq_desc);
 comp_sz = sizeof(struct ionic_rxq_comp);
 sg_desc_sz = sizeof(struct ionic_rxq_sg_desc);

 if (lif->rxq_features & IONIC_Q_F_2X_CQ_DESC)
  comp_sz *= 2;

 for (i = 0; i < lif->nxqs; i++) {
  err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
          num_desc, desc_sz, comp_sz, sg_desc_sz,
          sizeof(struct ionic_rx_desc_info),
          lif->kern_pid, lif->xdp_prog,
          &lif->rxqcqs[i]);
  if (err)
   goto err_out;

  lif->rxqcqs[i]->q.features = lif->rxq_features;

  ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
         lif->rxqcqs[i]->intr.index,
         lif->rx_coalesce_hw);
  if (test_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state))
   lif->rxqcqs[i]->intr.dim_coal_hw = lif->rx_coalesce_hw;

  if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
   ionic_link_qcq_interrupts(lif->rxqcqs[i],
        lif->txqcqs[i]);

  ionic_debugfs_add_qcq(lif, lif->rxqcqs[i]);
 }

 return 0;

err_out:
 ionic_txrx_free(lif);

 return err;
}

static int ionic_txrx_init(struct ionic_lif *lif)
{
 unsigned int i;
 int err;

 for (i = 0; i < lif->nxqs; i++) {
  err = ionic_lif_txq_init(lif, lif->txqcqs[i]);
  if (err)
   goto err_out;

  err = ionic_lif_rxq_init(lif, lif->rxqcqs[i]);
  if (err) {
   ionic_lif_qcq_deinit(lif, lif->txqcqs[i]);
   goto err_out;
  }
 }

 if (lif->netdev->features & NETIF_F_RXHASH)
  ionic_lif_rss_init(lif);

 ionic_lif_rx_mode(lif);

 return 0;

err_out:
 while (i--) {
  ionic_lif_qcq_deinit(lif, lif->txqcqs[i]);
  ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]);
 }

 return err;
}

static int ionic_txrx_enable(struct ionic_lif *lif)
{
 int derr = 0;
 int i, err;

 ionic_xdp_rxqs_prog_update(lif);

 for (i = 0; i < lif->nxqs; i++) {
  if (!(lif->rxqcqs[i] && lif->txqcqs[i])) {
   dev_err(lif->ionic->dev, "%s: bad qcq %d\n", __func__, i);
   err = -ENXIO;
   goto err_out;
  }

  ionic_rx_fill(&lif->rxqcqs[i]->q,
         READ_ONCE(lif->rxqcqs[i]->q.xdp_prog));
  err = ionic_qcq_enable(lif->rxqcqs[i]);
  if (err)
   goto err_out;

  err = ionic_qcq_enable(lif->txqcqs[i]);
  if (err) {
   derr = ionic_qcq_disable(lif, lif->rxqcqs[i], err);
   goto err_out;
  }
 }

 if (lif->hwstamp_rxq) {
  ionic_rx_fill(&lif->hwstamp_rxq->q, NULL);
  err = ionic_qcq_enable(lif->hwstamp_rxq);
  if (err)
   goto err_out_hwstamp_rx;
 }

 if (lif->hwstamp_txq) {
  err = ionic_qcq_enable(lif->hwstamp_txq);
  if (err)
   goto err_out_hwstamp_tx;
 }

 return 0;

err_out_hwstamp_tx:
 if (lif->hwstamp_rxq)
  derr = ionic_qcq_disable(lif, lif->hwstamp_rxq, derr);
err_out_hwstamp_rx:
 i = lif->nxqs;
err_out:
 while (i--) {
  derr = ionic_qcq_disable(lif, lif->txqcqs[i], derr);
  derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr);
 }

 ionic_xdp_rxqs_prog_update(lif);

 return err;
}

static int ionic_start_queues(struct ionic_lif *lif)
{
 int err;

 if (test_bit(IONIC_LIF_F_BROKEN, lif->state))
  return -EIO;

 if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
  return -EBUSY;

 if (test_and_set_bit(IONIC_LIF_F_UP, lif->state))
  return 0;

 err = ionic_txrx_enable(lif);
 if (err) {
  clear_bit(IONIC_LIF_F_UP, lif->state);
  return err;
 }
 netif_tx_wake_all_queues(lif->netdev);

 return 0;
}

static int ionic_open(struct net_device *netdev)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 int err;

 /* If recovering from a broken state, clear the bit and we'll try again */
 if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state))
  netdev_info(netdev, "clearing broken state\n");

 mutex_lock(&lif->queue_lock);

 err = ionic_txrx_alloc(lif);
 if (err)
  goto err_unlock;

 err = ionic_txrx_init(lif);
 if (err)
  goto err_txrx_free;

 err = netif_set_real_num_tx_queues(netdev, lif->nxqs);
 if (err)
  goto err_txrx_deinit;

 err = netif_set_real_num_rx_queues(netdev, lif->nxqs);
 if (err)
  goto err_txrx_deinit;

 /* don't start the queues until we have link */
 if (netif_carrier_ok(netdev)) {
  err = ionic_start_queues(lif);
  if (err)
   goto err_txrx_deinit;
 }

 /* If hardware timestamping is enabled, but the queues were freed by
 * ionic_stop, those need to be reallocated and initialized, too.
 */

 ionic_lif_hwstamp_recreate_queues(lif);

 mutex_unlock(&lif->queue_lock);

 return 0;

err_txrx_deinit:
 ionic_txrx_deinit(lif);
err_txrx_free:
 ionic_txrx_free(lif);
err_unlock:
 mutex_unlock(&lif->queue_lock);
 return err;
}

static void ionic_stop_queues(struct ionic_lif *lif)
{
 if (!test_and_clear_bit(IONIC_LIF_F_UP, lif->state))
  return;

 netif_tx_disable(lif->netdev);
 ionic_txrx_disable(lif);
}

static int ionic_stop(struct net_device *netdev)
{
 struct ionic_lif *lif = netdev_priv(netdev);

 if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
  return 0;

 mutex_lock(&lif->queue_lock);
 ionic_stop_queues(lif);
 ionic_txrx_deinit(lif);
 ionic_txrx_free(lif);
 mutex_unlock(&lif->queue_lock);

 return 0;
}

static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
 struct ionic_lif *lif = netdev_priv(netdev);

 switch (cmd) {
 case SIOCSHWTSTAMP:
  return ionic_lif_hwstamp_set(lif, ifr);
 case SIOCGHWTSTAMP:
  return ionic_lif_hwstamp_get(lif, ifr);
 default:
  return -EOPNOTSUPP;
 }
}

static int ionic_get_vf_config(struct net_device *netdev,
          int vf, struct ifla_vf_info *ivf)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic *ionic = lif->ionic;
 int ret = 0;

 if (!netif_device_present(netdev))
  return -EBUSY;

 down_read(&ionic->vf_op_lock);

 if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
  ret = -EINVAL;
 } else {
  struct ionic_vf *vfdata = &ionic->vfs[vf];

  ivf->vf    = vf;
  ivf->qos   = 0;
  ivf->vlan         = le16_to_cpu(vfdata->vlanid);
  ivf->spoofchk     = vfdata->spoofchk;
  ivf->linkstate    = vfdata->linkstate;
  ivf->max_tx_rate  = le32_to_cpu(vfdata->maxrate);
  ivf->trusted      = vfdata->trusted;
  ether_addr_copy(ivf->mac, vfdata->macaddr);
 }

 up_read(&ionic->vf_op_lock);
 return ret;
}

static int ionic_get_vf_stats(struct net_device *netdev, int vf,
         struct ifla_vf_stats *vf_stats)
{
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic *ionic = lif->ionic;
 struct ionic_lif_stats *vs;
 int ret = 0;

 if (!netif_device_present(netdev))
  return -EBUSY;

 down_read(&ionic->vf_op_lock);

 if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
  ret = -EINVAL;
 } else {
  memset(vf_stats, 0, sizeof(*vf_stats));
  vs = &ionic->vfs[vf].stats;

  vf_stats->rx_packets = le64_to_cpu(vs->rx_ucast_packets);
  vf_stats->tx_packets = le64_to_cpu(vs->tx_ucast_packets);
  vf_stats->rx_bytes   = le64_to_cpu(vs->rx_ucast_bytes);
  vf_stats->tx_bytes   = le64_to_cpu(vs->tx_ucast_bytes);
  vf_stats->broadcast  = le64_to_cpu(vs->rx_bcast_packets);
  vf_stats->multicast  = le64_to_cpu(vs->rx_mcast_packets);
  vf_stats->rx_dropped = le64_to_cpu(vs->rx_ucast_drop_packets) +
           le64_to_cpu(vs->rx_mcast_drop_packets) +
           le64_to_cpu(vs->rx_bcast_drop_packets);
  vf_stats->tx_dropped = le64_to_cpu(vs->tx_ucast_drop_packets) +
           le64_to_cpu(vs->tx_mcast_drop_packets) +
           le64_to_cpu(vs->tx_bcast_drop_packets);
 }

 up_read(&ionic->vf_op_lock);
 return ret;
}

static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
{
 struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_MAC };
 struct ionic_lif *lif = netdev_priv(netdev);
 struct ionic *ionic = lif->ionic;
 int ret;

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

--> maximum size reached

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

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

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