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

Impressum en_main.c

  Sprache: C
 

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


#include <linux/dim.h>
#include <net/tc_act/tc_gact.h>
#include <linux/mlx5/fs.h>
#include <net/vxlan.h>
#include <net/geneve.h>
#include <linux/bpf.h>
#include <linux/debugfs.h>
#include <linux/if_bridge.h>
#include <linux/filter.h>
#include <net/netdev_lock.h>
#include <net/netdev_queues.h>
#include <net/netdev_rx_queue.h>
#include <net/page_pool/types.h>
#include <net/pkt_sched.h>
#include <net/xdp_sock_drv.h>
#include "eswitch.h"
#include "en.h"
#include "en/dim.h"
#include "en/txrx.h"
#include "en_tc.h"
#include "en_rep.h"
#include "en_accel/ipsec.h"
#include "en_accel/macsec.h"
#include "en_accel/en_accel.h"
#include "en_accel/ktls.h"
#include "lib/vxlan.h"
#include "lib/clock.h"
#include "en/port.h"
#include "en/xdp.h"
#include "lib/eq.h"
#include "en/monitor_stats.h"
#include "en/health.h"
#include "en/params.h"
#include "en/xsk/pool.h"
#include "en/xsk/setup.h"
#include "en/xsk/rx.h"
#include "en/xsk/tx.h"
#include "en/hv_vhca_stats.h"
#include "en/devlink.h"
#include "lib/mlx5.h"
#include "en/ptp.h"
#include "en/htb.h"
#include "qos.h"
#include "en/trap.h"
#include "lib/devcom.h"
#include "lib/sd.h"
#include "en/pcie_cong_event.h"

static bool mlx5e_hw_gro_supported(struct mlx5_core_dev *mdev)
{
 if (!MLX5_CAP_GEN(mdev, shampo) ||
     !MLX5_CAP_SHAMPO(mdev, shampo_header_split_data_merge))
  return false;

 /* Our HW-GRO implementation relies on "KSM Mkey" for
 * SHAMPO headers buffer mapping
 */

 if (!MLX5_CAP_GEN(mdev, fixed_buffer_size))
  return false;

 if (!MLX5_CAP_GEN_2(mdev, min_mkey_log_entity_size_fixed_buffer_valid))
  return false;

 if (MLX5_CAP_GEN_2(mdev, min_mkey_log_entity_size_fixed_buffer) >
     MLX5E_SHAMPO_LOG_HEADER_ENTRY_SIZE)
  return false;

 return true;
}

bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev, u8 page_shift,
         enum mlx5e_mpwrq_umr_mode umr_mode)
{
 u16 umr_wqebbs, max_wqebbs;
 bool striding_rq_umr;

 striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && MLX5_CAP_GEN(mdev, umr_ptr_rlky) &&
     MLX5_CAP_ETH(mdev, reg_umr_sq);
 if (!striding_rq_umr)
  return false;

 umr_wqebbs = mlx5e_mpwrq_umr_wqebbs(mdev, page_shift, umr_mode);
 max_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev);
 /* Sanity check; should never happen, because mlx5e_mpwrq_umr_wqebbs is
 * calculated from mlx5e_get_max_sq_aligned_wqebbs.
 */

 if (WARN_ON(umr_wqebbs > max_wqebbs))
  return false;

 return true;
}

void mlx5e_update_carrier(struct mlx5e_priv *priv)
{
 struct mlx5_core_dev *mdev = priv->mdev;
 u8 port_state;
 bool up;

 port_state = mlx5_query_vport_state(mdev,
         MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT,
         0);

 up = port_state == VPORT_STATE_UP;
 if (up == netif_carrier_ok(priv->netdev))
  netif_carrier_event(priv->netdev);
 if (up) {
  netdev_info(priv->netdev, "Link up\n");
  netif_carrier_on(priv->netdev);
 } else {
  netdev_info(priv->netdev, "Link down\n");
  netif_carrier_off(priv->netdev);
 }
}

static void mlx5e_update_carrier_work(struct work_struct *work)
{
 struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
            update_carrier_work);

 mutex_lock(&priv->state_lock);
 if (test_bit(MLX5E_STATE_OPENED, &priv->state))
  if (priv->profile->update_carrier)
   priv->profile->update_carrier(priv);
 mutex_unlock(&priv->state_lock);
}

static void mlx5e_update_stats_work(struct work_struct *work)
{
 struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
            update_stats_work);

 mutex_lock(&priv->state_lock);
 priv->profile->update_stats(priv);
 mutex_unlock(&priv->state_lock);
}

void mlx5e_queue_update_stats(struct mlx5e_priv *priv)
{
 if (!priv->profile->update_stats)
  return;

 if (unlikely(test_bit(MLX5E_STATE_DESTROYING, &priv->state)))
  return;

 queue_work(priv->wq, &priv->update_stats_work);
}

static int async_event(struct notifier_block *nb, unsigned long event, void *data)
{
 struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, events_nb);
 struct mlx5_eqe   *eqe = data;

 if (event != MLX5_EVENT_TYPE_PORT_CHANGE)
  return NOTIFY_DONE;

 switch (eqe->sub_type) {
 case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
 case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
  queue_work(priv->wq, &priv->update_carrier_work);
  break;
 default:
  return NOTIFY_DONE;
 }

 return NOTIFY_OK;
}

static void mlx5e_enable_async_events(struct mlx5e_priv *priv)
{
 priv->events_nb.notifier_call = async_event;
 mlx5_notifier_register(priv->mdev, &priv->events_nb);
}

static void mlx5e_disable_async_events(struct mlx5e_priv *priv)
{
 mlx5_notifier_unregister(priv->mdev, &priv->events_nb);
}

static int mlx5e_devcom_event_mpv(int event, void *my_data, void *event_data)
{
 struct mlx5e_priv *slave_priv = my_data;

 switch (event) {
 case MPV_DEVCOM_MASTER_UP:
  mlx5_devcom_comp_set_ready(slave_priv->devcom, true);
  break;
 case MPV_DEVCOM_MASTER_DOWN:
  /* no need for comp set ready false since we unregister after
 * and it hurts cleanup flow.
 */

  break;
 case MPV_DEVCOM_IPSEC_MASTER_UP:
 case MPV_DEVCOM_IPSEC_MASTER_DOWN:
  mlx5e_ipsec_handle_mpv_event(event, my_data, event_data);
  break;
 }

 return 0;
}

static int mlx5e_devcom_init_mpv(struct mlx5e_priv *priv, u64 *data)
{
 priv->devcom = mlx5_devcom_register_component(priv->mdev->priv.devc,
            MLX5_DEVCOM_MPV,
            *data,
            mlx5e_devcom_event_mpv,
            priv);
 if (IS_ERR(priv->devcom))
  return PTR_ERR(priv->devcom);

 if (mlx5_core_is_mp_master(priv->mdev)) {
  mlx5_devcom_send_event(priv->devcom, MPV_DEVCOM_MASTER_UP,
           MPV_DEVCOM_MASTER_UP, priv);
  mlx5e_ipsec_send_event(priv, MPV_DEVCOM_IPSEC_MASTER_UP);
 }

 return 0;
}

static void mlx5e_devcom_cleanup_mpv(struct mlx5e_priv *priv)
{
 if (IS_ERR_OR_NULL(priv->devcom))
  return;

 if (mlx5_core_is_mp_master(priv->mdev)) {
  mlx5_devcom_send_event(priv->devcom, MPV_DEVCOM_MASTER_DOWN,
           MPV_DEVCOM_MASTER_DOWN, priv);
  mlx5e_ipsec_send_event(priv, MPV_DEVCOM_IPSEC_MASTER_DOWN);
 }

 mlx5_devcom_unregister_component(priv->devcom);
 priv->devcom = NULL;
}

static int blocking_event(struct notifier_block *nb, unsigned long event, void *data)
{
 struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, blocking_events_nb);
 struct mlx5_devlink_trap_event_ctx *trap_event_ctx = data;
 int err;

 switch (event) {
 case MLX5_DRIVER_EVENT_TYPE_TRAP:
  err = mlx5e_handle_trap_event(priv, trap_event_ctx->trap);
  if (err) {
   trap_event_ctx->err = err;
   return NOTIFY_BAD;
  }
  break;
 case MLX5_DRIVER_EVENT_AFFILIATION_DONE:
  if (mlx5e_devcom_init_mpv(priv, data))
   return NOTIFY_BAD;
  break;
 case MLX5_DRIVER_EVENT_AFFILIATION_REMOVED:
  mlx5e_devcom_cleanup_mpv(priv);
  break;
 default:
  return NOTIFY_DONE;
 }
 return NOTIFY_OK;
}

static void mlx5e_enable_blocking_events(struct mlx5e_priv *priv)
{
 priv->blocking_events_nb.notifier_call = blocking_event;
 mlx5_blocking_notifier_register(priv->mdev, &priv->blocking_events_nb);
}

static void mlx5e_disable_blocking_events(struct mlx5e_priv *priv)
{
 mlx5_blocking_notifier_unregister(priv->mdev, &priv->blocking_events_nb);
}

static u16 mlx5e_mpwrq_umr_octowords(u32 entries, enum mlx5e_mpwrq_umr_mode umr_mode)
{
 u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode);
 u32 sz;

 sz = ALIGN(entries * umr_entry_size, MLX5_UMR_FLEX_ALIGNMENT);

 return sz / MLX5_OCTWORD;
}

static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq,
           struct mlx5e_icosq *sq,
           struct mlx5e_umr_wqe *wqe)
{
 struct mlx5_wqe_ctrl_seg      *cseg = &wqe->hdr.ctrl;
 struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->hdr.uctrl;
 u16 octowords;
 u8 ds_cnt;

 ds_cnt = DIV_ROUND_UP(mlx5e_mpwrq_umr_wqe_sz(rq->mdev, rq->mpwqe.page_shift,
           rq->mpwqe.umr_mode),
         MLX5_SEND_WQE_DS);

 cseg->qpn_ds    = cpu_to_be32((sq->sqn << MLX5_WQE_CTRL_QPN_SHIFT) |
          ds_cnt);
 cseg->umr_mkey  = rq->mpwqe.umr_mkey_be;

 ucseg->flags = MLX5_UMR_TRANSLATION_OFFSET_EN | MLX5_UMR_INLINE;
 octowords = mlx5e_mpwrq_umr_octowords(rq->mpwqe.pages_per_wqe, rq->mpwqe.umr_mode);
 ucseg->xlt_octowords = cpu_to_be16(octowords);
 ucseg->mkey_mask     = cpu_to_be64(MLX5_MKEY_MASK_FREE);
}

static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq, int node)
{
 int wq_sz = mlx5_wq_ll_get_size(&rq->mpwqe.wq);
 size_t alloc_size;

 alloc_size = array_size(wq_sz, struct_size(rq->mpwqe.info,
         alloc_units.frag_pages,
         rq->mpwqe.pages_per_wqe));

 rq->mpwqe.info = kvzalloc_node(alloc_size, GFP_KERNEL, node);
 if (!rq->mpwqe.info)
  return -ENOMEM;

 /* For deferred page release (release right before alloc), make sure
 * that on first round release is not called.
 */

 for (int i = 0; i < wq_sz; i++) {
  struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, i);

  bitmap_fill(wi->skip_release_bitmap, rq->mpwqe.pages_per_wqe);
 }

 mlx5e_build_umr_wqe(rq, rq->icosq,
       container_of(&rq->mpwqe.umr_wqe,
      struct mlx5e_umr_wqe, hdr));

 return 0;
}


static u8 mlx5e_mpwrq_access_mode(enum mlx5e_mpwrq_umr_mode umr_mode)
{
 switch (umr_mode) {
 case MLX5E_MPWRQ_UMR_MODE_ALIGNED:
  return MLX5_MKC_ACCESS_MODE_MTT;
 case MLX5E_MPWRQ_UMR_MODE_UNALIGNED:
  return MLX5_MKC_ACCESS_MODE_KSM;
 case MLX5E_MPWRQ_UMR_MODE_OVERSIZED:
  return MLX5_MKC_ACCESS_MODE_KLMS;
 case MLX5E_MPWRQ_UMR_MODE_TRIPLE:
  return MLX5_MKC_ACCESS_MODE_KSM;
 }
 WARN_ONCE(1, "MPWRQ UMR mode %d is not known\n", umr_mode);
 return 0;
}

static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev,
     u32 npages, u8 page_shift, u32 *umr_mkey,
     dma_addr_t filler_addr,
     enum mlx5e_mpwrq_umr_mode umr_mode,
     u32 xsk_chunk_size)
{
 struct mlx5_mtt *mtt;
 struct mlx5_ksm *ksm;
 struct mlx5_klm *klm;
 u32 octwords;
 int inlen;
 void *mkc;
 u32 *in;
 int err;
 int i;

 if ((umr_mode == MLX5E_MPWRQ_UMR_MODE_UNALIGNED ||
      umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE) &&
     !MLX5_CAP_GEN(mdev, fixed_buffer_size)) {
  mlx5_core_warn(mdev, "Unaligned AF_XDP requires fixed_buffer_size capability\n");
  return -EINVAL;
 }

 octwords = mlx5e_mpwrq_umr_octowords(npages, umr_mode);

 inlen = MLX5_FLEXIBLE_INLEN(mdev, MLX5_ST_SZ_BYTES(create_mkey_in),
        MLX5_OCTWORD, octwords);
 if (inlen < 0)
  return inlen;

 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);

 MLX5_SET(mkc, mkc, free, 1);
 MLX5_SET(mkc, mkc, umr_en, 1);
 MLX5_SET(mkc, mkc, lw, 1);
 MLX5_SET(mkc, mkc, lr, 1);
 MLX5_SET(mkc, mkc, access_mode_1_0, mlx5e_mpwrq_access_mode(umr_mode));
 mlx5e_mkey_set_relaxed_ordering(mdev, mkc);
 MLX5_SET(mkc, mkc, qpn, 0xffffff);
 MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.hw_objs.pdn);
 MLX5_SET64(mkc, mkc, len, npages << page_shift);
 MLX5_SET(mkc, mkc, translations_octword_size, octwords);
 if (umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE)
  MLX5_SET(mkc, mkc, log_page_size, page_shift - 2);
 else if (umr_mode != MLX5E_MPWRQ_UMR_MODE_OVERSIZED)
  MLX5_SET(mkc, mkc, log_page_size, page_shift);
 MLX5_SET(create_mkey_in, in, translations_octword_actual_size, octwords);

 /* Initialize the mkey with all MTTs pointing to a default
 * page (filler_addr). When the channels are activated, UMR
 * WQEs will redirect the RX WQEs to the actual memory from
 * the RQ's pool, while the gaps (wqe_overflow) remain mapped
 * to the default page.
 */

 switch (umr_mode) {
 case MLX5E_MPWRQ_UMR_MODE_OVERSIZED:
  klm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
  for (i = 0; i < npages; i++) {
   klm[i << 1] = (struct mlx5_klm) {
    .va = cpu_to_be64(filler_addr),
    .bcount = cpu_to_be32(xsk_chunk_size),
    .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey),
   };
   klm[(i << 1) + 1] = (struct mlx5_klm) {
    .va = cpu_to_be64(filler_addr),
    .bcount = cpu_to_be32((1 << page_shift) - xsk_chunk_size),
    .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey),
   };
  }
  break;
 case MLX5E_MPWRQ_UMR_MODE_UNALIGNED:
  ksm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
  for (i = 0; i < npages; i++)
   ksm[i] = (struct mlx5_ksm) {
    .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey),
    .va = cpu_to_be64(filler_addr),
   };
  break;
 case MLX5E_MPWRQ_UMR_MODE_ALIGNED:
  mtt = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
  for (i = 0; i < npages; i++)
   mtt[i] = (struct mlx5_mtt) {
    .ptag = cpu_to_be64(filler_addr),
   };
  break;
 case MLX5E_MPWRQ_UMR_MODE_TRIPLE:
  ksm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
  for (i = 0; i < npages * 4; i++) {
   ksm[i] = (struct mlx5_ksm) {
    .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey),
    .va = cpu_to_be64(filler_addr),
   };
  }
  break;
 }

 err = mlx5_core_create_mkey(mdev, umr_mkey, in, inlen);

 kvfree(in);
 return err;
}

static int mlx5e_create_umr_ksm_mkey(struct mlx5_core_dev *mdev,
         u64 nentries, u8 log_entry_size,
         u32 *umr_mkey)
{
 int inlen;
 void *mkc;
 u32 *in;
 int err;

 inlen = MLX5_ST_SZ_BYTES(create_mkey_in);

 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);

 MLX5_SET(mkc, mkc, free, 1);
 MLX5_SET(mkc, mkc, umr_en, 1);
 MLX5_SET(mkc, mkc, lw, 1);
 MLX5_SET(mkc, mkc, lr, 1);
 MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_KSM);
 mlx5e_mkey_set_relaxed_ordering(mdev, mkc);
 MLX5_SET(mkc, mkc, qpn, 0xffffff);
 MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.hw_objs.pdn);
 MLX5_SET(mkc, mkc, translations_octword_size, nentries);
 MLX5_SET(mkc, mkc, log_page_size, log_entry_size);
 MLX5_SET64(mkc, mkc, len, nentries << log_entry_size);
 err = mlx5_core_create_mkey(mdev, umr_mkey, in, inlen);

 kvfree(in);
 return err;
}

static int mlx5e_create_rq_umr_mkey(struct mlx5_core_dev *mdev, struct mlx5e_rq *rq)
{
 u32 xsk_chunk_size = rq->xsk_pool ? rq->xsk_pool->chunk_size : 0;
 u32 wq_size = mlx5_wq_ll_get_size(&rq->mpwqe.wq);
 u32 num_entries, max_num_entries;
 u32 umr_mkey;
 int err;

 max_num_entries = mlx5e_mpwrq_max_num_entries(mdev, rq->mpwqe.umr_mode);

 /* Shouldn't overflow, the result is at most MLX5E_MAX_RQ_NUM_MTTS. */
 if (WARN_ON_ONCE(check_mul_overflow(wq_size, (u32)rq->mpwqe.mtts_per_wqe,
         &num_entries) ||
    num_entries > max_num_entries))
  mlx5_core_err(mdev, "%s: multiplication overflow: %u * %u > %u\n",
         __func__, wq_size, rq->mpwqe.mtts_per_wqe,
         max_num_entries);

 err = mlx5e_create_umr_mkey(mdev, num_entries, rq->mpwqe.page_shift,
        &umr_mkey, rq->wqe_overflow.addr,
        rq->mpwqe.umr_mode, xsk_chunk_size);
 rq->mpwqe.umr_mkey_be = cpu_to_be32(umr_mkey);
 return err;
}

static int mlx5e_create_rq_hd_umr_mkey(struct mlx5_core_dev *mdev,
           u16 hd_per_wq, __be32 *umr_mkey)
{
 u32 max_ksm_size = BIT(MLX5_CAP_GEN(mdev, log_max_klm_list_size));
 u32 mkey;
 int err;

 if (max_ksm_size < hd_per_wq) {
  mlx5_core_err(mdev, "max ksm list size 0x%x is smaller than shampo header buffer list size 0x%x\n",
         max_ksm_size, hd_per_wq);
  return -EINVAL;
 }

 err = mlx5e_create_umr_ksm_mkey(mdev, hd_per_wq,
     MLX5E_SHAMPO_LOG_HEADER_ENTRY_SIZE,
     &mkey);
 if (err)
  return err;

 *umr_mkey = cpu_to_be32(mkey);
 return 0;
}

static void mlx5e_init_frags_partition(struct mlx5e_rq *rq)
{
 struct mlx5e_wqe_frag_info next_frag = {};
 struct mlx5e_wqe_frag_info *prev = NULL;
 int i;

 WARN_ON(rq->xsk_pool);

 next_frag.frag_page = &rq->wqe.alloc_units->frag_pages[0];

 /* Skip first release due to deferred release. */
 next_frag.flags = BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);

 for (i = 0; i < mlx5_wq_cyc_get_size(&rq->wqe.wq); i++) {
  struct mlx5e_rq_frag_info *frag_info = &rq->wqe.info.arr[0];
  struct mlx5e_wqe_frag_info *frag =
   &rq->wqe.frags[i << rq->wqe.info.log_num_frags];
  int f;

  for (f = 0; f < rq->wqe.info.num_frags; f++, frag++) {
   if (next_frag.offset + frag_info[f].frag_stride > PAGE_SIZE) {
    /* Pages are assigned at runtime. */
    next_frag.frag_page++;
    next_frag.offset = 0;
    if (prev)
     prev->flags |= BIT(MLX5E_WQE_FRAG_LAST_IN_PAGE);
   }
   *frag = next_frag;

   /* prepare next */
   next_frag.offset += frag_info[f].frag_stride;
   prev = frag;
  }
 }

 if (prev)
  prev->flags |= BIT(MLX5E_WQE_FRAG_LAST_IN_PAGE);
}

static void mlx5e_init_xsk_buffs(struct mlx5e_rq *rq)
{
 int i;

 /* Assumptions used by XSK batched allocator. */
 WARN_ON(rq->wqe.info.num_frags != 1);
 WARN_ON(rq->wqe.info.log_num_frags != 0);
 WARN_ON(rq->wqe.info.arr[0].frag_stride != PAGE_SIZE);

 /* Considering the above assumptions a fragment maps to a single
 * xsk_buff.
 */

 for (i = 0; i < mlx5_wq_cyc_get_size(&rq->wqe.wq); i++) {
  rq->wqe.frags[i].xskp = &rq->wqe.alloc_units->xsk_buffs[i];

  /* Skip first release due to deferred release as WQES are
 * not allocated yet.
 */

  rq->wqe.frags[i].flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);
 }
}

static int mlx5e_init_wqe_alloc_info(struct mlx5e_rq *rq, int node)
{
 int wq_sz = mlx5_wq_cyc_get_size(&rq->wqe.wq);
 int len = wq_sz << rq->wqe.info.log_num_frags;
 struct mlx5e_wqe_frag_info *frags;
 union mlx5e_alloc_units *aus;
 int aus_sz;

 if (rq->xsk_pool)
  aus_sz = sizeof(*aus->xsk_buffs);
 else
  aus_sz = sizeof(*aus->frag_pages);

 aus = kvzalloc_node(array_size(len, aus_sz), GFP_KERNEL, node);
 if (!aus)
  return -ENOMEM;

 frags = kvzalloc_node(array_size(len, sizeof(*frags)), GFP_KERNEL, node);
 if (!frags) {
  kvfree(aus);
  return -ENOMEM;
 }

 rq->wqe.alloc_units = aus;
 rq->wqe.frags = frags;

 if (rq->xsk_pool)
  mlx5e_init_xsk_buffs(rq);
 else
  mlx5e_init_frags_partition(rq);

 return 0;
}

static void mlx5e_free_wqe_alloc_info(struct mlx5e_rq *rq)
{
 kvfree(rq->wqe.frags);
 kvfree(rq->wqe.alloc_units);
}

static void mlx5e_rq_err_cqe_work(struct work_struct *recover_work)
{
 struct mlx5e_rq *rq = container_of(recover_work, struct mlx5e_rq, recover_work);

 mlx5e_reporter_rq_cqe_err(rq);
}

static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
{
 struct mlx5e_rq *rq = container_of(timeout_work,
        struct mlx5e_rq,
        rx_timeout_work);

 /* Acquire netdev instance lock to synchronize with channel close and
 * reopen flows. Either successfully obtain the lock, or detect that
 * channels are closing for another reason, making this work no longer
 * necessary.
 */

 while (!netdev_trylock(rq->netdev)) {
  if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
   return;
  msleep(20);
 }

 mlx5e_reporter_rx_timeout(rq);
 netdev_unlock(rq->netdev);
}

static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
{
 rq->wqe_overflow.page = alloc_page(GFP_KERNEL);
 if (!rq->wqe_overflow.page)
  return -ENOMEM;

 rq->wqe_overflow.addr = dma_map_page(rq->pdev, rq->wqe_overflow.page, 0,
          PAGE_SIZE, rq->buff.map_dir);
 if (dma_mapping_error(rq->pdev, rq->wqe_overflow.addr)) {
  __free_page(rq->wqe_overflow.page);
  return -ENOMEM;
 }
 return 0;
}

static void mlx5e_free_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
{
  dma_unmap_page(rq->pdev, rq->wqe_overflow.addr, PAGE_SIZE,
   rq->buff.map_dir);
  __free_page(rq->wqe_overflow.page);
}

static int mlx5e_init_rxq_rq(struct mlx5e_channel *c, struct mlx5e_params *params,
        u32 xdp_frag_size, struct mlx5e_rq *rq)
{
 struct mlx5_core_dev *mdev = c->mdev;
 int err;

 rq->wq_type      = params->rq_wq_type;
 rq->pdev         = c->pdev;
 rq->netdev       = c->netdev;
 rq->priv         = c->priv;
 rq->tstamp       = c->tstamp;
 rq->clock        = mdev->clock;
 rq->icosq        = &c->icosq;
 rq->ix           = c->ix;
 rq->channel      = c;
 rq->mdev         = mdev;
 rq->hw_mtu =
  MLX5E_SW2HW_MTU(params, params->sw_mtu) - ETH_FCS_LEN * !params->scatter_fcs_en;
 rq->xdpsq        = &c->rq_xdpsq;
 rq->stats        = &c->priv->channel_stats[c->ix]->rq;
 rq->ptp_cyc2time = mlx5_rq_ts_translator(mdev);
 err = mlx5e_rq_set_handlers(rq, params, NULL);
 if (err)
  return err;

 return __xdp_rxq_info_reg(&rq->xdp_rxq, rq->netdev, rq->ix, c->napi.napi_id,
      xdp_frag_size);
}

static int mlx5e_rq_shampo_hd_info_alloc(struct mlx5e_rq *rq, u16 hd_per_wq,
      int node)
{
 struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;

 shampo->hd_per_wq = hd_per_wq;

 shampo->bitmap = bitmap_zalloc_node(hd_per_wq, GFP_KERNEL, node);
 shampo->pages = kvzalloc_node(array_size(hd_per_wq,
       sizeof(*shampo->pages)),
          GFP_KERNEL, node);
 if (!shampo->bitmap || !shampo->pages)
  goto err_nomem;

 return 0;

err_nomem:
 kvfree(shampo->pages);
 bitmap_free(shampo->bitmap);

 return -ENOMEM;
}

static void mlx5e_rq_shampo_hd_info_free(struct mlx5e_rq *rq)
{
 kvfree(rq->mpwqe.shampo->pages);
 bitmap_free(rq->mpwqe.shampo->bitmap);
}

static bool mlx5_rq_needs_separate_hd_pool(struct mlx5e_rq *rq)
{
 struct netdev_rx_queue *rxq = __netif_get_rx_queue(rq->netdev, rq->ix);

 return !!rxq->mp_params.mp_ops;
}

static int mlx5_rq_shampo_alloc(struct mlx5_core_dev *mdev,
    struct mlx5e_params *params,
    struct mlx5e_rq_param *rqp,
    struct mlx5e_rq *rq,
    u32 *pool_size,
    int node)
{
 void *wqc = MLX5_ADDR_OF(rqc, rqp->rqc, wq);
 u8 log_hd_per_page, log_hd_entry_size;
 u16 hd_per_wq, hd_per_wqe;
 u32 hd_pool_size;
 int wq_size;
 int err;

 if (!test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
  return 0;

 rq->mpwqe.shampo = kvzalloc_node(sizeof(*rq->mpwqe.shampo),
      GFP_KERNEL, node);
 if (!rq->mpwqe.shampo)
  return -ENOMEM;

 /* split headers data structures */
 hd_per_wq = mlx5e_shampo_hd_per_wq(mdev, params, rqp);
 err = mlx5e_rq_shampo_hd_info_alloc(rq, hd_per_wq, node);
 if (err)
  goto err_shampo_hd_info_alloc;

 err = mlx5e_create_rq_hd_umr_mkey(mdev, hd_per_wq,
       &rq->mpwqe.shampo->mkey_be);
 if (err)
  goto err_umr_mkey;

 hd_per_wqe = mlx5e_shampo_hd_per_wqe(mdev, params, rqp);
 wq_size = BIT(MLX5_GET(wq, wqc, log_wq_sz));

 BUILD_BUG_ON(MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE > PAGE_SHIFT);
 if (hd_per_wqe >= MLX5E_SHAMPO_WQ_HEADER_PER_PAGE) {
  log_hd_per_page = MLX5E_SHAMPO_LOG_WQ_HEADER_PER_PAGE;
  log_hd_entry_size = MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE;
 } else {
  log_hd_per_page = order_base_2(hd_per_wqe);
  log_hd_entry_size = order_base_2(PAGE_SIZE / hd_per_wqe);
 }

 rq->mpwqe.shampo->hd_per_wqe = hd_per_wqe;
 rq->mpwqe.shampo->hd_per_page = BIT(log_hd_per_page);
 rq->mpwqe.shampo->log_hd_per_page = log_hd_per_page;
 rq->mpwqe.shampo->log_hd_entry_size = log_hd_entry_size;

 hd_pool_size = (hd_per_wqe * wq_size) >> log_hd_per_page;

 if (mlx5_rq_needs_separate_hd_pool(rq)) {
  /* Separate page pool for shampo headers */
  struct page_pool_params pp_params = { };

  pp_params.order     = 0;
  pp_params.flags     = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
  pp_params.pool_size = hd_pool_size;
  pp_params.nid       = node;
  pp_params.dev       = rq->pdev;
  pp_params.napi      = rq->cq.napi;
  pp_params.netdev    = rq->netdev;
  pp_params.dma_dir   = rq->buff.map_dir;
  pp_params.max_len   = PAGE_SIZE;

  rq->hd_page_pool = page_pool_create(&pp_params);
  if (IS_ERR(rq->hd_page_pool)) {
   err = PTR_ERR(rq->hd_page_pool);
   rq->hd_page_pool = NULL;
   goto err_hds_page_pool;
  }
 } else {
  /* Common page pool, reserve space for headers. */
  *pool_size += hd_pool_size;
  rq->hd_page_pool = NULL;
 }

 /* gro only data structures */
 rq->hw_gro_data = kvzalloc_node(sizeof(*rq->hw_gro_data), GFP_KERNEL, node);
 if (!rq->hw_gro_data) {
  err = -ENOMEM;
  goto err_hw_gro_data;
 }

 return 0;

err_hw_gro_data:
 page_pool_destroy(rq->hd_page_pool);
err_hds_page_pool:
 mlx5_core_destroy_mkey(mdev, be32_to_cpu(rq->mpwqe.shampo->mkey_be));
err_umr_mkey:
 mlx5e_rq_shampo_hd_info_free(rq);
err_shampo_hd_info_alloc:
 kvfree(rq->mpwqe.shampo);
 return err;
}

static void mlx5e_rq_free_shampo(struct mlx5e_rq *rq)
{
 if (!test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
  return;

 kvfree(rq->hw_gro_data);
 if (rq->hd_page_pool != rq->page_pool)
  page_pool_destroy(rq->hd_page_pool);
 mlx5e_rq_shampo_hd_info_free(rq);
 mlx5_core_destroy_mkey(rq->mdev,
          be32_to_cpu(rq->mpwqe.shampo->mkey_be));
 kvfree(rq->mpwqe.shampo);
}

static int mlx5e_alloc_rq(struct mlx5e_params *params,
     struct mlx5e_xsk_param *xsk,
     struct mlx5e_rq_param *rqp,
     int node, struct mlx5e_rq *rq)
{
 struct mlx5_core_dev *mdev = rq->mdev;
 void *rqc = rqp->rqc;
 void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq);
 u32 pool_size;
 int wq_sz;
 int err;
 int i;

 rqp->wq.db_numa_node = node;
 INIT_WORK(&rq->recover_work, mlx5e_rq_err_cqe_work);
 INIT_WORK(&rq->rx_timeout_work, mlx5e_rq_timeout_work);

 if (params->xdp_prog)
  bpf_prog_inc(params->xdp_prog);
 RCU_INIT_POINTER(rq->xdp_prog, params->xdp_prog);

 rq->buff.map_dir = params->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
 rq->buff.headroom = mlx5e_get_rq_headroom(mdev, params, xsk);
 pool_size = 1 << params->log_rq_mtu_frames;

 rq->mkey_be = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey);

 switch (rq->wq_type) {
 case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
  err = mlx5_wq_ll_create(mdev, &rqp->wq, rqc_wq, &rq->mpwqe.wq,
     &rq->wq_ctrl);
  if (err)
   goto err_rq_xdp_prog;

  err = mlx5e_alloc_mpwqe_rq_drop_page(rq);
  if (err)
   goto err_rq_wq_destroy;

  rq->mpwqe.wq.db = &rq->mpwqe.wq.db[MLX5_RCV_DBR];

  wq_sz = mlx5_wq_ll_get_size(&rq->mpwqe.wq);

  rq->mpwqe.page_shift = mlx5e_mpwrq_page_shift(mdev, xsk);
  rq->mpwqe.umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk);
  rq->mpwqe.pages_per_wqe =
   mlx5e_mpwrq_pages_per_wqe(mdev, rq->mpwqe.page_shift,
        rq->mpwqe.umr_mode);
  rq->mpwqe.umr_wqebbs =
   mlx5e_mpwrq_umr_wqebbs(mdev, rq->mpwqe.page_shift,
            rq->mpwqe.umr_mode);
  rq->mpwqe.mtts_per_wqe =
   mlx5e_mpwrq_mtts_per_wqe(mdev, rq->mpwqe.page_shift,
       rq->mpwqe.umr_mode);

  pool_size = rq->mpwqe.pages_per_wqe <<
   mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk);

  if (!mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk) && params->xdp_prog)
   pool_size *= 2; /* additional page per packet for the linear part */

  rq->mpwqe.log_stride_sz = mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk);
  rq->mpwqe.num_strides =
   BIT(mlx5e_mpwqe_get_log_num_strides(mdev, params, xsk));
  rq->mpwqe.min_wqe_bulk = mlx5e_mpwqe_get_min_wqe_bulk(wq_sz);

  rq->buff.frame0_sz = (1 << rq->mpwqe.log_stride_sz);

  err = mlx5e_create_rq_umr_mkey(mdev, rq);
  if (err)
   goto err_rq_drop_page;

  err = mlx5e_rq_alloc_mpwqe_info(rq, node);
  if (err)
   goto err_rq_mkey;

  err = mlx5_rq_shampo_alloc(mdev, params, rqp, rq, &pool_size, node);
  if (err)
   goto err_free_mpwqe_info;

  break;
 default/* MLX5_WQ_TYPE_CYCLIC */
  err = mlx5_wq_cyc_create(mdev, &rqp->wq, rqc_wq, &rq->wqe.wq,
      &rq->wq_ctrl);
  if (err)
   goto err_rq_xdp_prog;

  rq->wqe.wq.db = &rq->wqe.wq.db[MLX5_RCV_DBR];

  wq_sz = mlx5_wq_cyc_get_size(&rq->wqe.wq);

  rq->wqe.info = rqp->frags_info;
  rq->buff.frame0_sz = rq->wqe.info.arr[0].frag_stride;

  err = mlx5e_init_wqe_alloc_info(rq, node);
  if (err)
   goto err_rq_wq_destroy;
 }

 if (xsk) {
  err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq,
       MEM_TYPE_XSK_BUFF_POOL, NULL);
  if (err)
   goto err_free_by_rq_type;
  xsk_pool_set_rxq_info(rq->xsk_pool, &rq->xdp_rxq);
 } else {
  /* Create a page_pool and register it with rxq */
  struct page_pool_params pp_params = { 0 };

  pp_params.order     = 0;
  pp_params.flags     = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
  pp_params.pool_size = pool_size;
  pp_params.nid       = node;
  pp_params.dev       = rq->pdev;
  pp_params.napi      = rq->cq.napi;
  pp_params.netdev    = rq->netdev;
  pp_params.dma_dir   = rq->buff.map_dir;
  pp_params.max_len   = PAGE_SIZE;
  pp_params.queue_idx = rq->ix;

  /* Shampo header data split allow for unreadable netmem */
  if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
   pp_params.flags |= PP_FLAG_ALLOW_UNREADABLE_NETMEM;

  /* page_pool can be used even when there is no rq->xdp_prog,
 * given page_pool does not handle DMA mapping there is no
 * required state to clear. And page_pool gracefully handle
 * elevated refcnt.
 */

  rq->page_pool = page_pool_create(&pp_params);
  if (IS_ERR(rq->page_pool)) {
   err = PTR_ERR(rq->page_pool);
   rq->page_pool = NULL;
   goto err_free_by_rq_type;
  }
  if (!rq->hd_page_pool)
   rq->hd_page_pool = rq->page_pool;
  if (xdp_rxq_info_is_reg(&rq->xdp_rxq)) {
   err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq,
        MEM_TYPE_PAGE_POOL, rq->page_pool);
   if (err)
    goto err_destroy_page_pool;
  }
 }

 for (i = 0; i < wq_sz; i++) {
  if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) {
   struct mlx5e_rx_wqe_ll *wqe =
    mlx5_wq_ll_get_wqe(&rq->mpwqe.wq, i);
   u32 byte_count =
    rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz;
   u64 dma_offset = mul_u32_u32(i, rq->mpwqe.mtts_per_wqe) <<
    rq->mpwqe.page_shift;
   u16 headroom = test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state) ?
           0 : rq->buff.headroom;

   wqe->data[0].addr = cpu_to_be64(dma_offset + headroom);
   wqe->data[0].byte_count = cpu_to_be32(byte_count);
   wqe->data[0].lkey = rq->mpwqe.umr_mkey_be;
  } else {
   struct mlx5e_rx_wqe_cyc *wqe =
    mlx5_wq_cyc_get_wqe(&rq->wqe.wq, i);
   int f;

   for (f = 0; f < rq->wqe.info.num_frags; f++) {
    u32 frag_size = rq->wqe.info.arr[f].frag_size |
     MLX5_HW_START_PADDING;

    wqe->data[f].byte_count = cpu_to_be32(frag_size);
    wqe->data[f].lkey = rq->mkey_be;
   }
   /* check if num_frags is not a pow of two */
   if (rq->wqe.info.num_frags < (1 << rq->wqe.info.log_num_frags)) {
    wqe->data[f].byte_count = 0;
    wqe->data[f].lkey = params->terminate_lkey_be;
    wqe->data[f].addr = 0;
   }
  }
 }

 return 0;

err_destroy_page_pool:
 page_pool_destroy(rq->page_pool);
err_free_by_rq_type:
 switch (rq->wq_type) {
 case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
  mlx5e_rq_free_shampo(rq);
err_free_mpwqe_info:
  kvfree(rq->mpwqe.info);
err_rq_mkey:
  mlx5_core_destroy_mkey(mdev, be32_to_cpu(rq->mpwqe.umr_mkey_be));
err_rq_drop_page:
  mlx5e_free_mpwqe_rq_drop_page(rq);
  break;
 default/* MLX5_WQ_TYPE_CYCLIC */
  mlx5e_free_wqe_alloc_info(rq);
 }
err_rq_wq_destroy:
 mlx5_wq_destroy(&rq->wq_ctrl);
err_rq_xdp_prog:
 if (params->xdp_prog)
  bpf_prog_put(params->xdp_prog);

 return err;
}

static void mlx5e_free_rq(struct mlx5e_rq *rq)
{
 kvfree(rq->dim);
 page_pool_destroy(rq->page_pool);

 switch (rq->wq_type) {
 case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
  mlx5e_rq_free_shampo(rq);
  kvfree(rq->mpwqe.info);
  mlx5_core_destroy_mkey(rq->mdev, be32_to_cpu(rq->mpwqe.umr_mkey_be));
  mlx5e_free_mpwqe_rq_drop_page(rq);
  break;
 default/* MLX5_WQ_TYPE_CYCLIC */
  mlx5e_free_wqe_alloc_info(rq);
 }

 mlx5_wq_destroy(&rq->wq_ctrl);

 if (xdp_rxq_info_is_reg(&rq->xdp_rxq)) {
  struct bpf_prog *old_prog;

  old_prog = rcu_dereference_protected(rq->xdp_prog,
           lockdep_is_held(&rq->priv->state_lock));
  if (old_prog)
   bpf_prog_put(old_prog);
 }
 xdp_rxq_info_unreg(&rq->xdp_rxq);
}

int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param, u16 q_counter)
{
 struct mlx5_core_dev *mdev = rq->mdev;
 u8 ts_format;
 void *in;
 void *rqc;
 void *wq;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(create_rq_in) +
  sizeof(u64) * rq->wq_ctrl.buf.npages;
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 ts_format = mlx5_is_real_time_rq(mdev) ?
       MLX5_TIMESTAMP_FORMAT_REAL_TIME :
       MLX5_TIMESTAMP_FORMAT_FREE_RUNNING;
 rqc = MLX5_ADDR_OF(create_rq_in, in, ctx);
 wq  = MLX5_ADDR_OF(rqc, rqc, wq);

 memcpy(rqc, param->rqc, sizeof(param->rqc));

 MLX5_SET(rqc,  rqc, cqn,  rq->cq.mcq.cqn);
 MLX5_SET(rqc,  rqc, state,  MLX5_RQC_STATE_RST);
 MLX5_SET(rqc,  rqc, ts_format,  ts_format);
 MLX5_SET(rqc,  rqc, counter_set_id,     q_counter);
 MLX5_SET(wq,   wq,  log_wq_pg_sz, rq->wq_ctrl.buf.page_shift -
      MLX5_ADAPTER_PAGE_SHIFT);
 MLX5_SET64(wq, wq,  dbr_addr,  rq->wq_ctrl.db.dma);

 if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state)) {
  MLX5_SET(wq, wq, log_headers_buffer_entry_num,
    order_base_2(rq->mpwqe.shampo->hd_per_wq));
  MLX5_SET(wq, wq, headers_mkey,
    be32_to_cpu(rq->mpwqe.shampo->mkey_be));
 }

 mlx5_fill_page_frag_array(&rq->wq_ctrl.buf,
      (__be64 *)MLX5_ADDR_OF(wq, wq, pas));

 err = mlx5_core_create_rq(mdev, in, inlen, &rq->rqn);

 kvfree(in);

 return err;
}

static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state)
{
 struct mlx5_core_dev *mdev = rq->mdev;

 void *in;
 void *rqc;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(modify_rq_in);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 if (curr_state == MLX5_RQC_STATE_RST && next_state == MLX5_RQC_STATE_RDY)
  mlx5e_rqwq_reset(rq);

 rqc = MLX5_ADDR_OF(modify_rq_in, in, ctx);

 MLX5_SET(modify_rq_in, in, rq_state, curr_state);
 MLX5_SET(rqc, rqc, state, next_state);

 err = mlx5_core_modify_rq(mdev, rq->rqn, in);

 kvfree(in);

 return err;
}

static void mlx5e_flush_rq_cq(struct mlx5e_rq *rq)
{
 struct mlx5_cqwq *cqwq = &rq->cq.wq;
 struct mlx5_cqe64 *cqe;

 if (test_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state)) {
  while ((cqe = mlx5_cqwq_get_cqe_enhanced_comp(cqwq)))
   mlx5_cqwq_pop(cqwq);
 } else {
  while ((cqe = mlx5_cqwq_get_cqe(cqwq)))
   mlx5_cqwq_pop(cqwq);
 }

 mlx5_cqwq_update_db_record(cqwq);
}

int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state)
{
 struct net_device *dev = rq->netdev;
 int err;

 err = mlx5e_modify_rq_state(rq, curr_state, MLX5_RQC_STATE_RST);
 if (err) {
  netdev_err(dev, "Failed to move rq 0x%x to reset\n", rq->rqn);
  return err;
 }

 mlx5e_free_rx_descs(rq);
 mlx5e_flush_rq_cq(rq);

 err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY);
 if (err) {
  netdev_err(dev, "Failed to move rq 0x%x to ready\n", rq->rqn);
  return err;
 }

 return 0;
}

static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd)
{
 struct mlx5_core_dev *mdev = rq->mdev;
 void *in;
 void *rqc;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(modify_rq_in);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 rqc = MLX5_ADDR_OF(modify_rq_in, in, ctx);

 MLX5_SET(modify_rq_in, in, rq_state, MLX5_RQC_STATE_RDY);
 MLX5_SET64(modify_rq_in, in, modify_bitmask,
     MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_VSD);
 MLX5_SET(rqc, rqc, vsd, vsd);
 MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RDY);

 err = mlx5_core_modify_rq(mdev, rq->rqn, in);

 kvfree(in);

 return err;
}

void mlx5e_destroy_rq(struct mlx5e_rq *rq)
{
 mlx5_core_destroy_rq(rq->mdev, rq->rqn);
}

int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time)
{
 unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time);

 u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq));

 do {
  if (mlx5e_rqwq_get_cur_sz(rq) >= min_wqes)
   return 0;

  msleep(20);
 } while (time_before(jiffies, exp_time));

 netdev_warn(rq->netdev, "Failed to get min RX wqes on Channel[%d] RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n",
      rq->ix, rq->rqn, mlx5e_rqwq_get_cur_sz(rq), min_wqes);

 queue_work(rq->priv->wq, &rq->rx_timeout_work);

 return -ETIMEDOUT;
}

void mlx5e_free_rx_missing_descs(struct mlx5e_rq *rq)
{
 struct mlx5_wq_ll *wq;
 u16 head;
 int i;

 if (rq->wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
  return;

 wq = &rq->mpwqe.wq;
 head = wq->head;

 /* Release WQEs that are in missing state: they have been
 * popped from the list after completion but were not freed
 * due to deferred release.
 * Also free the linked-list reserved entry, hence the "+ 1".
 */

 for (i = 0; i < mlx5_wq_ll_missing(wq) + 1; i++) {
  rq->dealloc_wqe(rq, head);
  head = mlx5_wq_ll_get_wqe_next_ix(wq, head);
 }

 rq->mpwqe.actual_wq_head = wq->head;
 rq->mpwqe.umr_in_progress = 0;
 rq->mpwqe.umr_completed = 0;

 if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state)) {
  struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
  u16 len;

  len = (shampo->pi - shampo->ci) & shampo->hd_per_wq;
  mlx5e_shampo_fill_umr(rq, len);
 }
}

void mlx5e_free_rx_descs(struct mlx5e_rq *rq)
{
 __be16 wqe_ix_be;
 u16 wqe_ix;

 if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) {
  struct mlx5_wq_ll *wq = &rq->mpwqe.wq;

  mlx5e_free_rx_missing_descs(rq);

  while (!mlx5_wq_ll_is_empty(wq)) {
   struct mlx5e_rx_wqe_ll *wqe;

   wqe_ix_be = *wq->tail_next;
   wqe_ix    = be16_to_cpu(wqe_ix_be);
   wqe       = mlx5_wq_ll_get_wqe(wq, wqe_ix);
   rq->dealloc_wqe(rq, wqe_ix);
   mlx5_wq_ll_pop(wq, wqe_ix_be,
           &wqe->next.next_wqe_index);
  }

  if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
   mlx5e_shampo_dealloc_hd(rq);
 } else {
  struct mlx5_wq_cyc *wq = &rq->wqe.wq;
  u16 missing = mlx5_wq_cyc_missing(wq);
  u16 head = mlx5_wq_cyc_get_head(wq);

  while (!mlx5_wq_cyc_is_empty(wq)) {
   wqe_ix = mlx5_wq_cyc_get_tail(wq);
   rq->dealloc_wqe(rq, wqe_ix);
   mlx5_wq_cyc_pop(wq);
  }
  /* Missing slots might also contain unreleased pages due to
 * deferred release.
 */

  while (missing--) {
   wqe_ix = mlx5_wq_cyc_ctr2ix(wq, head++);
   rq->dealloc_wqe(rq, wqe_ix);
  }
 }

}

int mlx5e_open_rq(struct mlx5e_params *params, struct mlx5e_rq_param *param,
    struct mlx5e_xsk_param *xsk, int node, u16 q_counter,
    struct mlx5e_rq *rq)
{
 struct mlx5_core_dev *mdev = rq->mdev;
 int err;

 if (params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO)
  __set_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state);

 err = mlx5e_alloc_rq(params, xsk, param, node, rq);
 if (err)
  return err;

 err = mlx5e_create_rq(rq, param, q_counter);
 if (err)
  goto err_free_rq;

 err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY);
 if (err)
  goto err_destroy_rq;

 if (MLX5_CAP_ETH(mdev, cqe_checksum_full))
  __set_bit(MLX5E_RQ_STATE_CSUM_FULL, &rq->state);

 if (rq->channel && !params->rx_dim_enabled) {
  rq->channel->rx_cq_moder = params->rx_cq_moderation;
 } else if (rq->channel) {
  u8 cq_period_mode;

  cq_period_mode = params->rx_moder_use_cqe_mode ?
      DIM_CQ_PERIOD_MODE_START_FROM_CQE :
      DIM_CQ_PERIOD_MODE_START_FROM_EQE;
  mlx5e_reset_rx_moderation(&rq->channel->rx_cq_moder, cq_period_mode,
       params->rx_dim_enabled);

  err = mlx5e_dim_rx_change(rq, params->rx_dim_enabled);
  if (err)
   goto err_destroy_rq;
 }

 /* We disable csum_complete when XDP is enabled since
 * XDP programs might manipulate packets which will render
 * skb->checksum incorrect.
 */

 if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_NO_CSUM_COMPLETE) || params->xdp_prog)
  __set_bit(MLX5E_RQ_STATE_NO_CSUM_COMPLETE, &rq->state);

 /* For CQE compression on striding RQ, use stride index provided by
 * HW if capability is supported.
 */

 if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ) &&
     MLX5_CAP_GEN(mdev, mini_cqe_resp_stride_index))
  __set_bit(MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, &rq->state);

 /* For enhanced CQE compression packet processing. decompress
 * session according to the enhanced layout.
 */

 if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS) &&
     MLX5_CAP_GEN(mdev, enhanced_cqe_compression))
  __set_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state);

 return 0;

err_destroy_rq:
 mlx5e_destroy_rq(rq);
err_free_rq:
 mlx5e_free_rq(rq);

 return err;
}

void mlx5e_activate_rq(struct mlx5e_rq *rq)
{
 set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state);
}

void mlx5e_deactivate_rq(struct mlx5e_rq *rq)
{
 clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state);
 synchronize_net(); /* Sync with NAPI to prevent mlx5e_post_rx_wqes. */
}

void mlx5e_close_rq(struct mlx5e_rq *rq)
{
 if (rq->dim)
  cancel_work_sync(&rq->dim->work);
 cancel_work_sync(&rq->recover_work);
 cancel_work_sync(&rq->rx_timeout_work);
 mlx5e_destroy_rq(rq);
 mlx5e_free_rx_descs(rq);
 mlx5e_free_rq(rq);
}

u32 mlx5e_profile_get_tisn(struct mlx5_core_dev *mdev,
      struct mlx5e_priv *priv,
      const struct mlx5e_profile *profile,
      u8 lag_port, u8 tc)
{
 if (profile->get_tisn)
  return profile->get_tisn(mdev, priv, lag_port, tc);

 return mdev->mlx5e_res.hw_objs.tisn[lag_port][tc];
}

static void mlx5e_free_xdpsq_db(struct mlx5e_xdpsq *sq)
{
 kvfree(sq->db.xdpi_fifo.xi);
 kvfree(sq->db.wqe_info);
}

static int mlx5e_alloc_xdpsq_fifo(struct mlx5e_xdpsq *sq, int numa)
{
 struct mlx5e_xdp_info_fifo *xdpi_fifo = &sq->db.xdpi_fifo;
 int wq_sz        = mlx5_wq_cyc_get_size(&sq->wq);
 int entries;
 size_t size;

 /* upper bound for maximum num of entries of all xmit_modes. */
 entries = roundup_pow_of_two(wq_sz * MLX5_SEND_WQEBB_NUM_DS *
         MLX5E_XDP_FIFO_ENTRIES2DS_MAX_RATIO);

 size = array_size(sizeof(*xdpi_fifo->xi), entries);
 xdpi_fifo->xi = kvzalloc_node(size, GFP_KERNEL, numa);
 if (!xdpi_fifo->xi)
  return -ENOMEM;

 xdpi_fifo->pc   = &sq->xdpi_fifo_pc;
 xdpi_fifo->cc   = &sq->xdpi_fifo_cc;
 xdpi_fifo->mask = entries - 1;

 return 0;
}

static int mlx5e_alloc_xdpsq_db(struct mlx5e_xdpsq *sq, int numa)
{
 int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
 size_t size;
 int err;

 size = array_size(sizeof(*sq->db.wqe_info), wq_sz);
 sq->db.wqe_info = kvzalloc_node(size, GFP_KERNEL, numa);
 if (!sq->db.wqe_info)
  return -ENOMEM;

 err = mlx5e_alloc_xdpsq_fifo(sq, numa);
 if (err) {
  mlx5e_free_xdpsq_db(sq);
  return err;
 }

 return 0;
}

static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c,
        struct mlx5e_params *params,
        struct xsk_buff_pool *xsk_pool,
        struct mlx5e_sq_param *param,
        struct mlx5e_xdpsq *sq,
        bool is_redirect)
{
 void *sqc_wq               = MLX5_ADDR_OF(sqc, param->sqc, wq);
 struct mlx5_core_dev *mdev = c->mdev;
 struct mlx5_wq_cyc *wq = &sq->wq;
 int err;

 sq->pdev      = c->pdev;
 sq->mkey_be   = c->mkey_be;
 sq->channel   = c;
 sq->uar_map   = mdev->priv.bfreg.map;
 sq->min_inline_mode = params->tx_min_inline_mode;
 sq->hw_mtu    = MLX5E_SW2HW_MTU(params, params->sw_mtu) - ETH_FCS_LEN;
 sq->xsk_pool  = xsk_pool;

 sq->stats = sq->xsk_pool ?
  &c->priv->channel_stats[c->ix]->xsksq :
  is_redirect ?
   &c->priv->channel_stats[c->ix]->xdpsq :
   &c->priv->channel_stats[c->ix]->rq_xdpsq;
 sq->stop_room = param->is_mpw ? mlx5e_stop_room_for_mpwqe(mdev) :
     mlx5e_stop_room_for_max_wqe(mdev);
 sq->max_sq_mpw_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev);

 param->wq.db_numa_node = cpu_to_node(c->cpu);
 err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl);
 if (err)
  return err;
 wq->db = &wq->db[MLX5_SND_DBR];

 err = mlx5e_alloc_xdpsq_db(sq, cpu_to_node(c->cpu));
 if (err)
  goto err_sq_wq_destroy;

 return 0;

err_sq_wq_destroy:
 mlx5_wq_destroy(&sq->wq_ctrl);

 return err;
}

static void mlx5e_free_xdpsq(struct mlx5e_xdpsq *sq)
{
 mlx5e_free_xdpsq_db(sq);
 mlx5_wq_destroy(&sq->wq_ctrl);
}

static void mlx5e_free_icosq_db(struct mlx5e_icosq *sq)
{
 kvfree(sq->db.wqe_info);
}

static int mlx5e_alloc_icosq_db(struct mlx5e_icosq *sq, int numa)
{
 int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
 size_t size;

 size = array_size(wq_sz, sizeof(*sq->db.wqe_info));
 sq->db.wqe_info = kvzalloc_node(size, GFP_KERNEL, numa);
 if (!sq->db.wqe_info)
  return -ENOMEM;

 return 0;
}

static void mlx5e_icosq_err_cqe_work(struct work_struct *recover_work)
{
 struct mlx5e_icosq *sq = container_of(recover_work, struct mlx5e_icosq,
           recover_work);

 mlx5e_reporter_icosq_cqe_err(sq);
}

static void mlx5e_async_icosq_err_cqe_work(struct work_struct *recover_work)
{
 struct mlx5e_icosq *sq = container_of(recover_work, struct mlx5e_icosq,
           recover_work);

 /* Not implemented yet. */

 netdev_warn(sq->channel->netdev, "async_icosq recovery is not implemented\n");
}

static int mlx5e_alloc_icosq(struct mlx5e_channel *c,
        struct mlx5e_sq_param *param,
        struct mlx5e_icosq *sq,
        work_func_t recover_work_func)
{
 void *sqc_wq               = MLX5_ADDR_OF(sqc, param->sqc, wq);
 struct mlx5_core_dev *mdev = c->mdev;
 struct mlx5_wq_cyc *wq = &sq->wq;
 int err;

 sq->channel   = c;
 sq->uar_map   = mdev->priv.bfreg.map;
 sq->reserved_room = param->stop_room;

 param->wq.db_numa_node = cpu_to_node(c->cpu);
 err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl);
 if (err)
  return err;
 wq->db = &wq->db[MLX5_SND_DBR];

 err = mlx5e_alloc_icosq_db(sq, cpu_to_node(c->cpu));
 if (err)
  goto err_sq_wq_destroy;

 INIT_WORK(&sq->recover_work, recover_work_func);

 return 0;

err_sq_wq_destroy:
 mlx5_wq_destroy(&sq->wq_ctrl);

 return err;
}

static void mlx5e_free_icosq(struct mlx5e_icosq *sq)
{
 mlx5e_free_icosq_db(sq);
 mlx5_wq_destroy(&sq->wq_ctrl);
}

void mlx5e_free_txqsq_db(struct mlx5e_txqsq *sq)
{
 kvfree(sq->db.wqe_info);
 kvfree(sq->db.skb_fifo.fifo);
 kvfree(sq->db.dma_fifo);
}

int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa)
{
 int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
 int df_sz = wq_sz * MLX5_SEND_WQEBB_NUM_DS;

 sq->db.dma_fifo = kvzalloc_node(array_size(df_sz,
         sizeof(*sq->db.dma_fifo)),
     GFP_KERNEL, numa);
 sq->db.skb_fifo.fifo = kvzalloc_node(array_size(df_sz,
       sizeof(*sq->db.skb_fifo.fifo)),
     GFP_KERNEL, numa);
 sq->db.wqe_info = kvzalloc_node(array_size(wq_sz,
         sizeof(*sq->db.wqe_info)),
     GFP_KERNEL, numa);
 if (!sq->db.dma_fifo || !sq->db.skb_fifo.fifo || !sq->db.wqe_info) {
  mlx5e_free_txqsq_db(sq);
  return -ENOMEM;
 }

 sq->dma_fifo_mask = df_sz - 1;

 sq->db.skb_fifo.pc   = &sq->skb_fifo_pc;
 sq->db.skb_fifo.cc   = &sq->skb_fifo_cc;
 sq->db.skb_fifo.mask = df_sz - 1;

 return 0;
}

static int mlx5e_alloc_txqsq(struct mlx5e_channel *c,
        int txq_ix,
        struct mlx5e_params *params,
        struct mlx5e_sq_param *param,
        struct mlx5e_txqsq *sq,
        int tc)
{
 void *sqc_wq               = MLX5_ADDR_OF(sqc, param->sqc, wq);
 struct mlx5_core_dev *mdev = c->mdev;
 struct mlx5_wq_cyc *wq = &sq->wq;
 int err;

 sq->pdev      = c->pdev;
 sq->clock     = mdev->clock;
 sq->mkey_be   = c->mkey_be;
 sq->netdev    = c->netdev;
 sq->mdev      = c->mdev;
 sq->channel   = c;
 sq->priv      = c->priv;
 sq->ch_ix     = c->ix;
 sq->txq_ix    = txq_ix;
 sq->uar_map   = mdev->priv.bfreg.map;
 sq->min_inline_mode = params->tx_min_inline_mode;
 sq->hw_mtu    = MLX5E_SW2HW_MTU(params, params->sw_mtu);
 sq->max_sq_mpw_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev);
 INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work);
 if (mlx5_ipsec_device_caps(c->priv->mdev))
  set_bit(MLX5E_SQ_STATE_IPSEC, &sq->state);
 if (param->is_mpw)
  set_bit(MLX5E_SQ_STATE_MPWQE, &sq->state);
 sq->stop_room = param->stop_room;
 sq->ptp_cyc2time = mlx5_sq_ts_translator(mdev);

 param->wq.db_numa_node = cpu_to_node(c->cpu);
 err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl);
 if (err)
  return err;
 wq->db    = &wq->db[MLX5_SND_DBR];

 err = mlx5e_alloc_txqsq_db(sq, cpu_to_node(c->cpu));
 if (err)
  goto err_sq_wq_destroy;

 return 0;

err_sq_wq_destroy:
 mlx5_wq_destroy(&sq->wq_ctrl);

 return err;
}

void mlx5e_free_txqsq(struct mlx5e_txqsq *sq)
{
 kvfree(sq->dim);
 mlx5e_free_txqsq_db(sq);
 mlx5_wq_destroy(&sq->wq_ctrl);
}

static int mlx5e_create_sq(struct mlx5_core_dev *mdev,
      struct mlx5e_sq_param *param,
      struct mlx5e_create_sq_param *csp,
      u32 *sqn)
{
 u8 ts_format;
 void *in;
 void *sqc;
 void *wq;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(create_sq_in) +
  sizeof(u64) * csp->wq_ctrl->buf.npages;
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 ts_format = mlx5_is_real_time_sq(mdev) ?
       MLX5_TIMESTAMP_FORMAT_REAL_TIME :
       MLX5_TIMESTAMP_FORMAT_FREE_RUNNING;
 sqc = MLX5_ADDR_OF(create_sq_in, in, ctx);
 wq = MLX5_ADDR_OF(sqc, sqc, wq);

 memcpy(sqc, param->sqc, sizeof(param->sqc));
 MLX5_SET(sqc,  sqc, tis_lst_sz, csp->tis_lst_sz);
 MLX5_SET(sqc,  sqc, tis_num_0, csp->tisn);
 MLX5_SET(sqc,  sqc, cqn, csp->cqn);
 MLX5_SET(sqc,  sqc, ts_cqe_to_dest_cqn, csp->ts_cqe_to_dest_cqn);
 MLX5_SET(sqc,  sqc, ts_format, ts_format);


 if (MLX5_CAP_ETH(mdev, wqe_inline_mode) == MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
  MLX5_SET(sqc,  sqc, min_wqe_inline_mode, csp->min_inline_mode);

 MLX5_SET(sqc,  sqc, state, MLX5_SQC_STATE_RST);
 MLX5_SET(sqc,  sqc, flush_in_error_en, 1);

 MLX5_SET(wq,   wq, wq_type,       MLX5_WQ_TYPE_CYCLIC);
 MLX5_SET(wq,   wq, uar_page,      mdev->priv.bfreg.index);
 MLX5_SET(wq,   wq, log_wq_pg_sz,  csp->wq_ctrl->buf.page_shift -
       MLX5_ADAPTER_PAGE_SHIFT);
 MLX5_SET64(wq, wq, dbr_addr,      csp->wq_ctrl->db.dma);

 mlx5_fill_page_frag_array(&csp->wq_ctrl->buf,
      (__be64 *)MLX5_ADDR_OF(wq, wq, pas));

 err = mlx5_core_create_sq(mdev, in, inlen, sqn);

 kvfree(in);

 return err;
}

int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
      struct mlx5e_modify_sq_param *p)
{
 u64 bitmask = 0;
 void *in;
 void *sqc;
 int inlen;
 int err;

 inlen = MLX5_ST_SZ_BYTES(modify_sq_in);
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 sqc = MLX5_ADDR_OF(modify_sq_in, in, ctx);

 MLX5_SET(modify_sq_in, in, sq_state, p->curr_state);
 MLX5_SET(sqc, sqc, state, p->next_state);
 if (p->rl_update && p->next_state == MLX5_SQC_STATE_RDY) {
  bitmask |= 1;
  MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, p->rl_index);
 }
 if (p->qos_update && p->next_state == MLX5_SQC_STATE_RDY) {
  bitmask |= 1 << 2;
  MLX5_SET(sqc, sqc, qos_queue_group_id, p->qos_queue_group_id);
 }
 MLX5_SET64(modify_sq_in, in, modify_bitmask, bitmask);

 err = mlx5_core_modify_sq(mdev, sqn, in);

 kvfree(in);

 return err;
}

static void mlx5e_destroy_sq(struct mlx5_core_dev *mdev, u32 sqn)
{
 mlx5_core_destroy_sq(mdev, sqn);
}

int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev,
   struct mlx5e_sq_param *param,
   struct mlx5e_create_sq_param *csp,
   u16 qos_queue_group_id,
   u32 *sqn)
{
 struct mlx5e_modify_sq_param msp = {0};
 int err;

 err = mlx5e_create_sq(mdev, param, csp, sqn);
 if (err)
  return err;

 msp.curr_state = MLX5_SQC_STATE_RST;
 msp.next_state = MLX5_SQC_STATE_RDY;
 if (qos_queue_group_id) {
  msp.qos_update = true;
  msp.qos_queue_group_id = qos_queue_group_id;
 }
 err = mlx5e_modify_sq(mdev, *sqn, &msp);
 if (err)
  mlx5e_destroy_sq(mdev, *sqn);

 return err;
}

static int mlx5e_set_sq_maxrate(struct net_device *dev,
    struct mlx5e_txqsq *sq, u32 rate);

int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix,
       struct mlx5e_params *params, struct mlx5e_sq_param *param,
       struct mlx5e_txqsq *sq, int tc, u16 qos_queue_group_id,
       struct mlx5e_sq_stats *sq_stats)
{
 struct mlx5e_create_sq_param csp = {};
 u32 tx_rate;
 int err;

 err = mlx5e_alloc_txqsq(c, txq_ix, params, param, sq, tc);
 if (err)
  return err;

 sq->stats = sq_stats;

 csp.tisn            = tisn;
 csp.tis_lst_sz      = 1;
 csp.cqn             = sq->cq.mcq.cqn;
 csp.wq_ctrl         = &sq->wq_ctrl;
 csp.min_inline_mode = sq->min_inline_mode;
 err = mlx5e_create_sq_rdy(c->mdev, param, &csp, qos_queue_group_id, &sq->sqn);
 if (err)
  goto err_free_txqsq;

 tx_rate = c->priv->tx_rates[sq->txq_ix];
 if (tx_rate)
  mlx5e_set_sq_maxrate(c->netdev, sq, tx_rate);

 if (sq->channel && !params->tx_dim_enabled) {
  sq->channel->tx_cq_moder = params->tx_cq_moderation;
 } else if (sq->channel) {
  u8 cq_period_mode;

  cq_period_mode = params->tx_moder_use_cqe_mode ?
      DIM_CQ_PERIOD_MODE_START_FROM_CQE :
      DIM_CQ_PERIOD_MODE_START_FROM_EQE;
  mlx5e_reset_tx_moderation(&sq->channel->tx_cq_moder,
       cq_period_mode,
       params->tx_dim_enabled);

  err = mlx5e_dim_tx_change(sq, params->tx_dim_enabled);
  if (err)
   goto err_destroy_sq;
 }

 return 0;

err_destroy_sq:
 mlx5e_destroy_sq(c->mdev, sq->sqn);
err_free_txqsq:
 mlx5e_free_txqsq(sq);

 return err;
}

void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
{
 sq->txq = netdev_get_tx_queue(sq->netdev, sq->txq_ix);
 set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
 netdev_tx_reset_queue(sq->txq);
 netif_tx_start_queue(sq->txq);
 netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi);
}

void mlx5e_tx_disable_queue(struct netdev_queue *txq)
{
 __netif_tx_lock_bh(txq);
 netif_tx_stop_queue(txq);
 __netif_tx_unlock_bh(txq);
}

void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq)
{
 struct mlx5_wq_cyc *wq = &sq->wq;

 netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, NULL);
 clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
 synchronize_net(); /* Sync with NAPI to prevent netif_tx_wake_queue. */

 mlx5e_tx_disable_queue(sq->txq);

 /* last doorbell out, godspeed .. */
 if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) {
  u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
  struct mlx5e_tx_wqe *nop;

  sq->db.wqe_info[pi] = (struct mlx5e_tx_wqe_info) {
   .num_wqebbs = 1,
  };

  nop = mlx5e_post_nop(wq, sq->sqn, &sq->pc);
  mlx5e_notify_hw(wq, sq->pc, sq->uar_map, &nop->ctrl);
 }
}

void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
{
 struct mlx5_core_dev *mdev = sq->mdev;
 struct mlx5_rate_limit rl = {0};

 if (sq->dim)
  cancel_work_sync(&sq->dim->work);
 cancel_work_sync(&sq->recover_work);
 mlx5e_destroy_sq(mdev, sq->sqn);
 if (sq->rate_limit) {
  rl.rate = sq->rate_limit;
  mlx5_rl_remove_rate(mdev, &rl);
 }
 mlx5e_free_txqsq_descs(sq);
 mlx5e_free_txqsq(sq);
}

void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
{
 struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
           recover_work);

 /* Recovering queues means re-enabling NAPI, which requires the netdev
 * instance lock. However, SQ closing flows have to wait for work tasks
 * to finish while also holding the netdev instance lock. So either get
 * the lock or find that the SQ is no longer enabled and thus this work
 * is not relevant anymore.
 */

 while (!netdev_trylock(sq->netdev)) {
  if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
   return;
  msleep(20);
 }

 mlx5e_reporter_tx_err_cqe(sq);
 netdev_unlock(sq->netdev);
}

static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
{
 return (struct dim_cq_moder) {
  .cq_period_mode = cq_period_mode,
  .pkts = MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_PKTS,
  .usec = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
    MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_USEC_FROM_CQE :
    MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_USEC,
 };
}

bool mlx5e_reset_tx_moderation(struct dim_cq_moder *cq_moder, u8 cq_period_mode,
          bool dim_enabled)
{
 bool reset_needed = cq_moder->cq_period_mode != cq_period_mode;

 if (dim_enabled)
  *cq_moder = net_dim_get_def_tx_moderation(cq_period_mode);
 else
  *cq_moder = mlx5e_get_def_tx_moderation(cq_period_mode);

 return reset_needed;
}

bool mlx5e_reset_tx_channels_moderation(struct mlx5e_channels *chs, u8 cq_period_mode,
     bool dim_enabled, bool keep_dim_state)
{
 bool reset = false;
 int i, tc;

 for (i = 0; i < chs->num; i++) {
  for (tc = 0; tc < mlx5e_get_dcb_num_tc(&chs->params); tc++) {
   if (keep_dim_state)
    dim_enabled = !!chs->c[i]->sq[tc].dim;

   reset |= mlx5e_reset_tx_moderation(&chs->c[i]->tx_cq_moder,
          cq_period_mode, dim_enabled);
  }
 }

 return reset;
}

static int mlx5e_open_icosq(struct mlx5e_channel *c, struct mlx5e_params *params,
       struct mlx5e_sq_param *param, struct mlx5e_icosq *sq,
       work_func_t recover_work_func)
{
 struct mlx5e_create_sq_param csp = {};
 int err;

 err = mlx5e_alloc_icosq(c, param, sq, recover_work_func);
 if (err)
  return err;

 csp.cqn             = sq->cq.mcq.cqn;
 csp.wq_ctrl         = &sq->wq_ctrl;
 csp.min_inline_mode = params->tx_min_inline_mode;
 err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn);
 if (err)
  goto err_free_icosq;

 if (param->is_tls) {
  sq->ktls_resync = mlx5e_ktls_rx_resync_create_resp_list();
  if (IS_ERR(sq->ktls_resync)) {
   err = PTR_ERR(sq->ktls_resync);
   goto err_destroy_icosq;
  }
 }
 return 0;

err_destroy_icosq:
 mlx5e_destroy_sq(c->mdev, sq->sqn);
err_free_icosq:
 mlx5e_free_icosq(sq);

 return err;
}

void mlx5e_activate_icosq(struct mlx5e_icosq *icosq)
{
 set_bit(MLX5E_SQ_STATE_ENABLED, &icosq->state);
}

void mlx5e_deactivate_icosq(struct mlx5e_icosq *icosq)
{
 clear_bit(MLX5E_SQ_STATE_ENABLED, &icosq->state);
 synchronize_net(); /* Sync with NAPI. */
}

static void mlx5e_close_icosq(struct mlx5e_icosq *sq)
{
 struct mlx5e_channel *c = sq->channel;

 if (sq->ktls_resync)
  mlx5e_ktls_rx_resync_destroy_resp_list(sq->ktls_resync);
 mlx5e_destroy_sq(c->mdev, sq->sqn);
 mlx5e_free_icosq_descs(sq);
 mlx5e_free_icosq(sq);
}

int mlx5e_open_xdpsq(struct mlx5e_channel *c, struct mlx5e_params *params,
       struct mlx5e_sq_param *param, struct xsk_buff_pool *xsk_pool,
       struct mlx5e_xdpsq *sq, bool is_redirect)
{
 struct mlx5e_create_sq_param csp = {};
 int err;

 err = mlx5e_alloc_xdpsq(c, params, xsk_pool, param, sq, is_redirect);
 if (err)
  return err;

 csp.tis_lst_sz      = 1;
 csp.tisn            = mlx5e_profile_get_tisn(c->mdev, c->priv, c->priv->profile,
           c->lag_port, 0); /* tc = 0 */
 csp.cqn             = sq->cq.mcq.cqn;
 csp.wq_ctrl         = &sq->wq_ctrl;
 csp.min_inline_mode = sq->min_inline_mode;
 set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);

 err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn);
 if (err)
  goto err_free_xdpsq;

 mlx5e_set_xmit_fp(sq, param->is_mpw);

 return 0;

err_free_xdpsq:
 clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
 mlx5e_free_xdpsq(sq);

 return err;
}

void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq)
{
 struct mlx5e_channel *c = sq->channel;

 clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
 synchronize_net(); /* Sync with NAPI. */

 mlx5e_destroy_sq(c->mdev, sq->sqn);
 mlx5e_free_xdpsq_descs(sq);
 mlx5e_free_xdpsq(sq);
}

static struct mlx5e_xdpsq *mlx5e_open_xdpredirect_sq(struct mlx5e_channel *c,
           struct mlx5e_params *params,
           struct mlx5e_channel_param *cparam,
           struct mlx5e_create_cq_param *ccp)
{
 struct mlx5e_xdpsq *xdpsq;
 int err;

 xdpsq = kvzalloc_node(sizeof(*xdpsq), GFP_KERNEL, cpu_to_node(c->cpu));
 if (!xdpsq)
  return ERR_PTR(-ENOMEM);

 err = mlx5e_open_cq(c->mdev, params->tx_cq_moderation,
       &cparam->xdp_sq.cqp, ccp, &xdpsq->cq);
 if (err)
  goto err_free_xdpsq;

 err = mlx5e_open_xdpsq(c, params, &cparam->xdp_sq, NULL, xdpsq, true);
 if (err)
  goto err_close_xdpsq_cq;

 return xdpsq;

err_close_xdpsq_cq:
 mlx5e_close_cq(&xdpsq->cq);
err_free_xdpsq:
 kvfree(xdpsq);

 return ERR_PTR(err);
}

static void mlx5e_close_xdpredirect_sq(struct mlx5e_xdpsq *xdpsq)
{
 mlx5e_close_xdpsq(xdpsq);
 mlx5e_close_cq(&xdpsq->cq);
 kvfree(xdpsq);
}

static int mlx5e_alloc_cq_common(struct mlx5_core_dev *mdev,
     struct net_device *netdev,
     struct workqueue_struct *workqueue,
     struct mlx5_uars_page *uar,
     struct mlx5e_cq_param *param,
     struct mlx5e_cq *cq)
{
 struct mlx5_core_cq *mcq = &cq->mcq;
 int err;
 u32 i;

 err = mlx5_cqwq_create(mdev, ¶m->wq, param->cqc, &cq->wq,
          &cq->wq_ctrl);
 if (err)
  return err;

 mcq->cqe_sz     = 64;
 mcq->set_ci_db  = cq->wq_ctrl.db.db;
 mcq->arm_db     = cq->wq_ctrl.db.db + 1;
 *mcq->set_ci_db = 0;
 mcq->vector     = param->eq_ix;
 mcq->comp       = mlx5e_completion_event;
 mcq->event      = mlx5e_cq_error_event;

 for (i = 0; i < mlx5_cqwq_get_size(&cq->wq); i++) {
  struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(&cq->wq, i);

  cqe->op_own = 0xf1;
  cqe->validity_iteration_count = 0xff;
 }

 cq->mdev = mdev;
 cq->netdev = netdev;
 cq->workqueue = workqueue;
 cq->uar = uar;

 return 0;
}

static int mlx5e_alloc_cq(struct mlx5_core_dev *mdev,
     struct mlx5e_cq_param *param,
     struct mlx5e_create_cq_param *ccp,
     struct mlx5e_cq *cq)
{
 int err;

 param->wq.buf_numa_node = ccp->node;
 param->wq.db_numa_node  = ccp->node;
 param->eq_ix            = ccp->ix;

 err = mlx5e_alloc_cq_common(mdev, ccp->netdev, ccp->wq,
        mdev->priv.bfreg.up, param, cq);

 cq->napi     = ccp->napi;
 cq->ch_stats = ccp->ch_stats;

 return err;
}

static void mlx5e_free_cq(struct mlx5e_cq *cq)
{
 mlx5_wq_destroy(&cq->wq_ctrl);
}

static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param)
{
 u32 out[MLX5_ST_SZ_DW(create_cq_out)];
 struct mlx5_core_dev *mdev = cq->mdev;
 struct mlx5_core_cq *mcq = &cq->mcq;

 void *in;
 void *cqc;
 int inlen;
 int eqn;
 int err;

 err = mlx5_comp_eqn_get(mdev, param->eq_ix, &eqn);
 if (err)
  return err;

 inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
  sizeof(u64) * cq->wq_ctrl.buf.npages;
 in = kvzalloc(inlen, GFP_KERNEL);
 if (!in)
  return -ENOMEM;

 cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);

 memcpy(cqc, param->cqc, sizeof(param->cqc));

 mlx5_fill_page_frag_array(&cq->wq_ctrl.buf,
      (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas));

 MLX5_SET(cqc, cqc, cq_period_mode, mlx5e_cq_period_mode(param->cq_period_mode));

 MLX5_SET(cqc,   cqc, c_eqn_or_apu_element, eqn);
 MLX5_SET(cqc,   cqc, uar_page,      cq->uar->index);
 MLX5_SET(cqc,   cqc, log_page_size, cq->wq_ctrl.buf.page_shift -
         MLX5_ADAPTER_PAGE_SHIFT);
 MLX5_SET64(cqc, cqc, dbr_addr,      cq->wq_ctrl.db.dma);

 err = mlx5_core_create_cq(mdev, mcq, in, inlen, out, sizeof(out));

 kvfree(in);

 if (err)
  return err;

 mlx5e_cq_arm(cq);

 return 0;
}

static void mlx5e_destroy_cq(struct mlx5e_cq *cq)
{
 mlx5_core_destroy_cq(cq->mdev, &cq->mcq);
}

int mlx5e_open_cq(struct mlx5_core_dev *mdev, struct dim_cq_moder moder,
    struct mlx5e_cq_param *param, struct mlx5e_create_cq_param *ccp,
    struct mlx5e_cq *cq)
{
 int err;

 err = mlx5e_alloc_cq(mdev, param, ccp, cq);
 if (err)
  return err;

 err = mlx5e_create_cq(cq, param);
 if (err)
  goto err_free_cq;

 if (MLX5_CAP_GEN(mdev, cq_moderation) &&
     MLX5_CAP_GEN(mdev, cq_period_mode_modify))
  mlx5e_modify_cq_moderation(mdev, &cq->mcq, moder.usec, moder.pkts,
        mlx5e_cq_period_mode(moder.cq_period_mode));
 return 0;

err_free_cq:
 mlx5e_free_cq(cq);

 return err;
}

void mlx5e_close_cq(struct mlx5e_cq *cq)
{
 mlx5e_destroy_cq(cq);
 mlx5e_free_cq(cq);
}

int mlx5e_modify_cq_period_mode(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
    u8 cq_period_mode)
{
 u32 in[MLX5_ST_SZ_DW(modify_cq_in)] = {};
 void *cqc;

 MLX5_SET(modify_cq_in, in, cqn, cq->cqn);
 cqc = MLX5_ADDR_OF(modify_cq_in, in, cq_context);
 MLX5_SET(cqc, cqc, cq_period_mode, mlx5e_cq_period_mode(cq_period_mode));
 MLX5_SET(modify_cq_in, in,
   modify_field_select_resize_field_select.modify_field_select.modify_field_select,
   MLX5_CQ_MODIFY_PERIOD_MODE);

 return mlx5_core_modify_cq(dev, cq, in, sizeof(in));
}

int mlx5e_modify_cq_moderation(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
          u16 cq_period, u16 cq_max_count, u8 cq_period_mode)
{
 u32 in[MLX5_ST_SZ_DW(modify_cq_in)] = {};
 void *cqc;

 MLX5_SET(modify_cq_in, in, cqn, cq->cqn);
 cqc = MLX5_ADDR_OF(modify_cq_in, in, cq_context);
 MLX5_SET(cqc, cqc, cq_period, cq_period);
 MLX5_SET(cqc, cqc, cq_max_count, cq_max_count);
 MLX5_SET(cqc, cqc, cq_period_mode, cq_period_mode);
 MLX5_SET(modify_cq_in, in,
   modify_field_select_resize_field_select.modify_field_select.modify_field_select,
   MLX5_CQ_MODIFY_PERIOD | MLX5_CQ_MODIFY_COUNT | MLX5_CQ_MODIFY_PERIOD_MODE);

 return mlx5_core_modify_cq(dev, cq, in, sizeof(in));
}

static int mlx5e_open_tx_cqs(struct mlx5e_channel *c,
        struct mlx5e_params *params,
        struct mlx5e_create_cq_param *ccp,
        struct mlx5e_channel_param *cparam)
{
 int err;
 int tc;

 for (tc = 0; tc < c->num_tc; tc++) {
  err = mlx5e_open_cq(c->mdev, params->tx_cq_moderation, &cparam->txq_sq.cqp,
        ccp, &c->sq[tc].cq);
  if (err)
   goto err_close_tx_cqs;
 }

 return 0;

err_close_tx_cqs:
 for (tc--; tc >= 0; tc--)
  mlx5e_close_cq(&c->sq[tc].cq);

 return err;
}

static void mlx5e_close_tx_cqs(struct mlx5e_channel *c)
{
 int tc;

 for (tc = 0; tc < c->num_tc; tc++)
  mlx5e_close_cq(&c->sq[tc].cq);
}

static int mlx5e_mqprio_txq_to_tc(struct netdev_tc_txq *tc_to_txq, unsigned int txq)
{
 int tc;

 for (tc = 0; tc < TC_MAX_QUEUE; tc++)
  if (txq - tc_to_txq[tc].offset < tc_to_txq[tc].count)
   return tc;

 WARN(1, "Unexpected TCs configuration. No match found for txq %u", txq);
 return -ENOENT;
}

static int mlx5e_txq_get_qos_node_hw_id(struct mlx5e_params *params, int txq_ix,
     u32 *hw_id)
{
 int tc;

 if (params->mqprio.mode != TC_MQPRIO_MODE_CHANNEL) {
  *hw_id = 0;
  return 0;
 }

 tc = mlx5e_mqprio_txq_to_tc(params->mqprio.tc_to_txq, txq_ix);
 if (tc < 0)
  return tc;

 if (tc >= params->mqprio.num_tc) {
  WARN(1, "Unexpected TCs configuration. tc %d is out of range of %u",
       tc, params->mqprio.num_tc);
  return -EINVAL;
 }

 *hw_id = params->mqprio.channel.hw_id[tc];
 return 0;
}

static int mlx5e_open_sqs(struct mlx5e_channel *c,
     struct mlx5e_params *params,
     struct mlx5e_channel_param *cparam)
{
 int err, tc;

 for (tc = 0; tc < mlx5e_get_dcb_num_tc(params); tc++) {
  int txq_ix = c->ix + tc * params->num_channels;
  u32 qos_queue_group_id;
  u32 tisn;

  tisn = mlx5e_profile_get_tisn(c->mdev, c->priv, c->priv->profile,
           c->lag_port, tc);
  err = mlx5e_txq_get_qos_node_hw_id(params, txq_ix, &qos_queue_group_id);
  if (err)
   goto err_close_sqs;

  err = mlx5e_open_txqsq(c, tisn, txq_ix,
           params, &cparam->txq_sq, &c->sq[tc], tc,
           qos_queue_group_id,
           &c->priv->channel_stats[c->ix]->sq[tc]);
  if (err)
   goto err_close_sqs;
 }

 return 0;

err_close_sqs:
 for (tc--; tc >= 0; tc--)
  mlx5e_close_txqsq(&c->sq[tc]);

 return err;
}

static void mlx5e_close_sqs(struct mlx5e_channel *c)
{
 int tc;

 for (tc = 0; tc < c->num_tc; tc++)
  mlx5e_close_txqsq(&c->sq[tc]);
}

static int mlx5e_set_sq_maxrate(struct net_device *dev,
    struct mlx5e_txqsq *sq, u32 rate)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5e_modify_sq_param msp = {0};
 struct mlx5_rate_limit rl = {0};
 u16 rl_index = 0;
 int err;

 if (rate == sq->rate_limit)
  /* nothing to do */
  return 0;

 if (sq->rate_limit) {
  rl.rate = sq->rate_limit;
  /* remove current rl index to free space to next ones */
  mlx5_rl_remove_rate(mdev, &rl);
 }

 sq->rate_limit = 0;

 if (rate) {
  rl.rate = rate;
  err = mlx5_rl_add_rate(mdev, &rl_index, &rl);
  if (err) {
   netdev_err(dev, "Failed configuring rate %u: %d\n",
       rate, err);
   return err;
  }
 }

 msp.curr_state = MLX5_SQC_STATE_RDY;
 msp.next_state = MLX5_SQC_STATE_RDY;
 msp.rl_index   = rl_index;
 msp.rl_update  = true;
 err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
 if (err) {
  netdev_err(dev, "Failed configuring rate %u: %d\n",
      rate, err);
  /* remove the rate from the table */
  if (rate)
   mlx5_rl_remove_rate(mdev, &rl);
  return err;
 }

 sq->rate_limit = rate;
 return 0;
}

static int mlx5e_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5e_txqsq *sq = priv->txq2sq[index];
 int err = 0;

 if (!mlx5_rl_is_supported(mdev)) {
  netdev_err(dev, "Rate limiting is not supported on this device\n");
  return -EINVAL;
 }

 /* rate is given in Mb/sec, HW config is in Kb/sec */
 rate = rate << 10;

 /* Check whether rate in valid range, 0 is always valid */
 if (rate && !mlx5_rl_is_in_range(mdev, rate)) {
  netdev_err(dev, "TX rate %u, is not in range\n", rate);
  return -ERANGE;
 }

 mutex_lock(&priv->state_lock);
 if (test_bit(MLX5E_STATE_OPENED, &priv->state))
  err = mlx5e_set_sq_maxrate(dev, sq, rate);
 if (!err)
  priv->tx_rates[index] = rate;
 mutex_unlock(&priv->state_lock);

 return err;
}

static int mlx5e_open_rxq_rq(struct mlx5e_channel *c, struct mlx5e_params *params,
        struct mlx5e_rq_param *rq_params)
{
 u16 q_counter = c->priv->q_counter[c->sd_ix];
 int err;

 err = mlx5e_init_rxq_rq(c, params, rq_params->xdp_frag_size, &c->rq);
 if (err)
  return err;

 return mlx5e_open_rq(params, rq_params, NULL, cpu_to_node(c->cpu), q_counter, &c->rq);
}

static int mlx5e_open_queues(struct mlx5e_channel *c,
        struct mlx5e_params *params,
        struct mlx5e_channel_param *cparam)
{
 const struct net_device_ops *netdev_ops = c->netdev->netdev_ops;
 struct dim_cq_moder icocq_moder = {0, 0};
 struct mlx5e_create_cq_param ccp;
 int err;

 mlx5e_build_create_cq_param(&ccp, c);

 err = mlx5e_open_cq(c->mdev, icocq_moder, &cparam->async_icosq.cqp, &ccp,
       &c->async_icosq.cq);
 if (err)
  return err;

 err = mlx5e_open_cq(c->mdev, icocq_moder, &cparam->icosq.cqp, &ccp,
       &c->icosq.cq);
 if (err)
  goto err_close_async_icosq_cq;

 err = mlx5e_open_tx_cqs(c, params, &ccp, cparam);
 if (err)
  goto err_close_icosq_cq;

 if (netdev_ops->ndo_xdp_xmit) {
  c->xdpsq = mlx5e_open_xdpredirect_sq(c, params, cparam, &ccp);
  if (IS_ERR(c->xdpsq)) {
   err = PTR_ERR(c->xdpsq);
   goto err_close_tx_cqs;
  }
 }

 err = mlx5e_open_cq(c->mdev, params->rx_cq_moderation, &cparam->rq.cqp, &ccp,
       &c->rq.cq);
 if (err)
  goto err_close_xdpredirect_sq;

 err = c->xdp ? mlx5e_open_cq(c->mdev, params->tx_cq_moderation, &cparam->xdp_sq.cqp,
         &ccp, &c->rq_xdpsq.cq) : 0;
 if (err)
  goto err_close_rx_cq;

 spin_lock_init(&c->async_icosq_lock);

 err = mlx5e_open_icosq(c, params, &cparam->async_icosq, &c->async_icosq,
          mlx5e_async_icosq_err_cqe_work);
 if (err)
  goto err_close_rq_xdpsq_cq;

 mutex_init(&c->icosq_recovery_lock);

 err = mlx5e_open_icosq(c, params, &cparam->icosq, &c->icosq,
          mlx5e_icosq_err_cqe_work);
 if (err)
  goto err_close_async_icosq;

 err = mlx5e_open_sqs(c, params, cparam);
 if (err)
  goto err_close_icosq;

 err = mlx5e_open_rxq_rq(c, params, &cparam->rq);
 if (err)
  goto err_close_sqs;

 if (c->xdp) {
  err = mlx5e_open_xdpsq(c, params, &cparam->xdp_sq, NULL,
           &c->rq_xdpsq, false);
  if (err)
   goto err_close_rq;
 }

 return 0;

err_close_rq:
 mlx5e_close_rq(&c->rq);

err_close_sqs:
 mlx5e_close_sqs(c);

err_close_icosq:
 mlx5e_close_icosq(&c->icosq);

err_close_async_icosq:
 mlx5e_close_icosq(&c->async_icosq);

err_close_rq_xdpsq_cq:
 if (c->xdp)
  mlx5e_close_cq(&c->rq_xdpsq.cq);

err_close_rx_cq:
 mlx5e_close_cq(&c->rq.cq);

err_close_xdpredirect_sq:
 if (c->xdpsq)
  mlx5e_close_xdpredirect_sq(c->xdpsq);

err_close_tx_cqs:
 mlx5e_close_tx_cqs(c);

err_close_icosq_cq:
 mlx5e_close_cq(&c->icosq.cq);

err_close_async_icosq_cq:
 mlx5e_close_cq(&c->async_icosq.cq);

 return err;
}

static void mlx5e_close_queues(struct mlx5e_channel *c)
{
 if (c->xdp)
  mlx5e_close_xdpsq(&c->rq_xdpsq);
 /* The same ICOSQ is used for UMRs for both RQ and XSKRQ. */
 cancel_work_sync(&c->icosq.recover_work);
 mlx5e_close_rq(&c->rq);
 mlx5e_close_sqs(c);
 mlx5e_close_icosq(&c->icosq);
 mutex_destroy(&c->icosq_recovery_lock);
 mlx5e_close_icosq(&c->async_icosq);
 if (c->xdp)
  mlx5e_close_cq(&c->rq_xdpsq.cq);
 mlx5e_close_cq(&c->rq.cq);
 if (c->xdpsq)
  mlx5e_close_xdpredirect_sq(c->xdpsq);
 mlx5e_close_tx_cqs(c);
 mlx5e_close_cq(&c->icosq.cq);
 mlx5e_close_cq(&c->async_icosq.cq);
}

static u8 mlx5e_enumerate_lag_port(struct mlx5_core_dev *mdev, int ix)
{
 u16 port_aff_bias = mlx5_core_is_pf(mdev) ? 0 : MLX5_CAP_GEN(mdev, vhca_id);

 return (ix + port_aff_bias) % mlx5e_get_num_lag_ports(mdev);
}

static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu)
{
 if (ix > priv->stats_nch)  {
  netdev_warn(priv->netdev, "Unexpected channel stats index %d > %d\n", ix,
       priv->stats_nch);
  return -EINVAL;
 }

 if (priv->channel_stats[ix])
  return 0;

 /* Asymmetric dynamic memory allocation.
 * Freed in mlx5e_priv_arrays_free, not on channel closure.
 */

 netdev_dbg(priv->netdev, "Creating channel stats %d\n", ix);
 priv->channel_stats[ix] = kvzalloc_node(sizeof(**priv->channel_stats),
      GFP_KERNEL, cpu_to_node(cpu));
 if (!priv->channel_stats[ix])
  return -ENOMEM;
 priv->stats_nch++;

 return 0;
}

void mlx5e_trigger_napi_icosq(struct mlx5e_channel *c)
{
 spin_lock_bh(&c->async_icosq_lock);
 mlx5e_trigger_irq(&c->async_icosq);
 spin_unlock_bh(&c->async_icosq_lock);
}

void mlx5e_trigger_napi_sched(struct napi_struct *napi)
{
 local_bh_disable();
 napi_schedule(napi);
 local_bh_enable();
}

static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
         struct mlx5e_params *params,
         struct xsk_buff_pool *xsk_pool,
         struct mlx5e_channel **cp)
{
 struct net_device *netdev = priv->netdev;
 struct mlx5e_channel_param *cparam;
 struct mlx5_core_dev *mdev;
 struct mlx5e_xsk_param xsk;
 struct mlx5e_channel *c;
 unsigned int irq;
 int vec_ix;
 int cpu;
 int err;

 mdev = mlx5_sd_ch_ix_get_dev(priv->mdev, ix);
 vec_ix = mlx5_sd_ch_ix_get_vec_ix(mdev, ix);
 cpu = mlx5_comp_vector_get_cpu(mdev, vec_ix);

 err = mlx5_comp_irqn_get(mdev, vec_ix, &irq);
 if (err)
  return err;

 err = mlx5e_channel_stats_alloc(priv, ix, cpu);
 if (err)
  return err;

 c = kvzalloc_node(sizeof(*c), GFP_KERNEL, cpu_to_node(cpu));
 cparam = kvzalloc(sizeof(*cparam), GFP_KERNEL);
 if (!c || !cparam) {
  err = -ENOMEM;
  goto err_free;
 }

 err = mlx5e_build_channel_param(mdev, params, cparam);
 if (err)
  goto err_free;

 c->priv     = priv;
 c->mdev     = mdev;
 c->tstamp   = &priv->tstamp;
 c->ix       = ix;
 c->vec_ix   = vec_ix;
 c->sd_ix    = mlx5_sd_ch_ix_get_dev_ix(mdev, ix);
 c->cpu      = cpu;
 c->pdev     = mlx5_core_dma_dev(mdev);
 c->netdev   = priv->netdev;
 c->mkey_be  = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey);
 c->num_tc   = mlx5e_get_dcb_num_tc(params);
 c->xdp      = !!params->xdp_prog;
 c->stats    = &priv->channel_stats[ix]->ch;
 c->aff_mask = irq_get_effective_affinity_mask(irq);
 c->lag_port = mlx5e_enumerate_lag_port(mdev, ix);

 netif_napi_add_config_locked(netdev, &c->napi, mlx5e_napi_poll, ix);
 netif_napi_set_irq_locked(&c->napi, irq);

 err = mlx5e_open_queues(c, params, cparam);
 if (unlikely(err))
  goto err_napi_del;

 if (xsk_pool) {
  mlx5e_build_xsk_param(xsk_pool, &xsk);
  err = mlx5e_open_xsk(priv, params, &xsk, xsk_pool, c);
  if (unlikely(err))
   goto err_close_queues;
 }

 *cp = c;

 kvfree(cparam);
 return 0;

err_close_queues:
 mlx5e_close_queues(c);

err_napi_del:
 netif_napi_del_locked(&c->napi);

err_free:
 kvfree(cparam);
 kvfree(c);

 return err;
}

static void mlx5e_activate_channel(struct mlx5e_channel *c)
{
 int tc;

 napi_enable_locked(&c->napi);

 for (tc = 0; tc < c->num_tc; tc++)
  mlx5e_activate_txqsq(&c->sq[tc]);
 mlx5e_activate_icosq(&c->icosq);
 mlx5e_activate_icosq(&c->async_icosq);

 if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
  mlx5e_activate_xsk(c);
 else
  mlx5e_activate_rq(&c->rq);

 netif_queue_set_napi(c->netdev, c->ix, NETDEV_QUEUE_TYPE_RX, &c->napi);
}

static void mlx5e_deactivate_channel(struct mlx5e_channel *c)
{
 int tc;

 netif_queue_set_napi(c->netdev, c->ix, NETDEV_QUEUE_TYPE_RX, NULL);

 if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
  mlx5e_deactivate_xsk(c);
 else
  mlx5e_deactivate_rq(&c->rq);

 mlx5e_deactivate_icosq(&c->async_icosq);
 mlx5e_deactivate_icosq(&c->icosq);
 for (tc = 0; tc < c->num_tc; tc++)
  mlx5e_deactivate_txqsq(&c->sq[tc]);
 mlx5e_qos_deactivate_queues(c);

 napi_disable_locked(&c->napi);
}

static void mlx5e_close_channel(struct mlx5e_channel *c)
{
 if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
  mlx5e_close_xsk(c);
 mlx5e_close_queues(c);
 mlx5e_qos_close_queues(c);
 netif_napi_del_locked(&c->napi);

 kvfree(c);
}

int mlx5e_open_channels(struct mlx5e_priv *priv,
   struct mlx5e_channels *chs)
{
 int err = -ENOMEM;
 int i;

 chs->num = chs->params.num_channels;

 chs->c = kcalloc(chs->num, sizeof(struct mlx5e_channel *), GFP_KERNEL);
 if (!chs->c)
  goto err_out;

 for (i = 0; i < chs->num; i++) {
  struct xsk_buff_pool *xsk_pool = NULL;

  if (chs->params.xdp_prog)
   xsk_pool = mlx5e_xsk_get_pool(&chs->params, chs->params.xsk, i);

  err = mlx5e_open_channel(priv, i, &chs->params, xsk_pool, &chs->c[i]);
  if (err)
   goto err_close_channels;
 }

 if (MLX5E_GET_PFLAG(&chs->params, MLX5E_PFLAG_TX_PORT_TS) || chs->params.ptp_rx) {
  err = mlx5e_ptp_open(priv, &chs->params, chs->c[0]->lag_port, &chs->ptp);
  if (err)
   goto err_close_channels;
 }

 if (priv->htb) {
  err = mlx5e_qos_open_queues(priv, chs);
  if (err)
   goto err_close_ptp;
 }

 mlx5e_health_channels_update(priv);
 return 0;

err_close_ptp:
 if (chs->ptp)
  mlx5e_ptp_close(chs->ptp);

err_close_channels:
 for (i--; i >= 0; i--)
  mlx5e_close_channel(chs->c[i]);

 kfree(chs->c);
err_out:
 chs->num = 0;
 return err;
}

static void mlx5e_activate_channels(struct mlx5e_priv *priv, struct mlx5e_channels *chs)
{
 int i;

 for (i = 0; i < chs->num; i++)
  mlx5e_activate_channel(chs->c[i]);

 if (priv->htb)
  mlx5e_qos_activate_queues(priv);

 for (i = 0; i < chs->num; i++)
  mlx5e_trigger_napi_icosq(chs->c[i]);

 if (chs->ptp)
  mlx5e_ptp_activate_channel(chs->ptp);
}

static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs)
{
 int err = 0;
 int i;

 for (i = 0; i < chs->num; i++) {
  int timeout = err ? 0 : MLX5E_RQ_WQES_TIMEOUT;
  struct mlx5e_channel *c = chs->c[i];

  if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
   continue;

  err |= mlx5e_wait_for_min_rx_wqes(&c->rq, timeout);

  /* Don't wait on the XSK RQ, because the newer xdpsock sample
 * doesn't provide any Fill Ring entries at the setup stage.
 */

 }

 return err ? -ETIMEDOUT : 0;
}

static void mlx5e_deactivate_channels(struct mlx5e_channels *chs)
{
 int i;

 if (chs->ptp)
  mlx5e_ptp_deactivate_channel(chs->ptp);

 for (i = 0; i < chs->num; i++)
  mlx5e_deactivate_channel(chs->c[i]);
}

void mlx5e_close_channels(struct mlx5e_channels *chs)
{
 int i;

 ASSERT_RTNL();
 if (chs->ptp) {
  mlx5e_ptp_close(chs->ptp);
  chs->ptp = NULL;
 }
 for (i = 0; i < chs->num; i++)
  mlx5e_close_channel(chs->c[i]);

 kfree(chs->c);
 chs->num = 0;
}

static int mlx5e_modify_tirs_packet_merge(struct mlx5e_priv *priv)
{
 struct mlx5e_rx_res *res = priv->rx_res;

 return mlx5e_rx_res_packet_merge_set_param(res, &priv->channels.params.packet_merge);
}

static MLX5E_DEFINE_PREACTIVATE_WRAPPER_CTX(mlx5e_modify_tirs_packet_merge);

static int mlx5e_set_mtu(struct mlx5_core_dev *mdev,
    struct mlx5e_params *params, u16 mtu)
{
 u16 hw_mtu = MLX5E_SW2HW_MTU(params, mtu);
 int err;

 err = mlx5_set_port_mtu(mdev, hw_mtu, 1);
 if (err)
  return err;

 /* Update vport context MTU */
 mlx5_modify_nic_vport_mtu(mdev, hw_mtu);
 return 0;
}

static void mlx5e_query_mtu(struct mlx5_core_dev *mdev,
       struct mlx5e_params *params, u16 *mtu)
{
 u16 hw_mtu = 0;
 int err;

 err = mlx5_query_nic_vport_mtu(mdev, &hw_mtu);
 if (err || !hw_mtu) /* fallback to port oper mtu */
  mlx5_query_port_oper_mtu(mdev, &hw_mtu, 1);

 *mtu = MLX5E_HW2SW_MTU(params, hw_mtu);
}

int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv)
{
 struct mlx5e_params *params = &priv->channels.params;
 struct net_device *netdev = priv->netdev;
 struct mlx5_core_dev *mdev = priv->mdev;
 u16 mtu;
 int err;

 err = mlx5e_set_mtu(mdev, params, params->sw_mtu);
 if (err)
  return err;

 mlx5e_query_mtu(mdev, params, &mtu);
 if (mtu != params->sw_mtu)
  netdev_warn(netdev, "%s: VPort MTU %d is different than netdev mtu %d\n",
       __func__, mtu, params->sw_mtu);

 params->sw_mtu = mtu;
 return 0;
}

MLX5E_DEFINE_PREACTIVATE_WRAPPER_CTX(mlx5e_set_dev_port_mtu);

void mlx5e_set_netdev_mtu_boundaries(struct mlx5e_priv *priv)
{
 struct mlx5e_params *params = &priv->channels.params;
 struct net_device *netdev   = priv->netdev;
 struct mlx5_core_dev *mdev  = priv->mdev;
 u16 max_mtu;

 /* MTU range: 68 - hw-specific max */
 netdev->min_mtu = ETH_MIN_MTU;

 mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
 netdev->max_mtu = min_t(unsigned int, MLX5E_HW2SW_MTU(params, max_mtu),
    ETH_MAX_MTU);
}

static int mlx5e_netdev_set_tcs(struct net_device *netdev, u16 nch, u8 ntc,
    struct netdev_tc_txq *tc_to_txq)
{
 int tc, err;

 netdev_reset_tc(netdev);

 if (ntc == 1)
  return 0;

 err = netdev_set_num_tc(netdev, ntc);
 if (err) {
  netdev_WARN(netdev, "netdev_set_num_tc failed (%d), ntc = %d\n", err, ntc);
  return err;
 }

 for (tc = 0; tc < ntc; tc++) {
  u16 count, offset;

  count = tc_to_txq[tc].count;
  offset = tc_to_txq[tc].offset;
  netdev_set_tc_queue(netdev, tc, count, offset);
 }

 return 0;
}

int mlx5e_update_tx_netdev_queues(struct mlx5e_priv *priv)
{
 int nch, ntc, num_txqs, err;
 int qos_queues = 0;

 if (priv->htb)
  qos_queues = mlx5e_htb_cur_leaf_nodes(priv->htb);

 nch = priv->channels.params.num_channels;
 ntc = mlx5e_get_dcb_num_tc(&priv->channels.params);
 num_txqs = nch * ntc + qos_queues;
 if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS))
  num_txqs += ntc;

 netdev_dbg(priv->netdev, "Setting num_txqs %d\n", num_txqs);
 err = netif_set_real_num_tx_queues(priv->netdev, num_txqs);
 if (err)
  netdev_warn(priv->netdev, "netif_set_real_num_tx_queues failed, %d\n", err);

 return err;
}

static void mlx5e_set_default_xps_cpumasks(struct mlx5e_priv *priv,
        struct mlx5e_params *params)
{
 int ix;

 for (ix = 0; ix < params->num_channels; ix++) {
  int num_comp_vectors, irq, vec_ix;
  struct mlx5_core_dev *mdev;

  mdev = mlx5_sd_ch_ix_get_dev(priv->mdev, ix);
  num_comp_vectors = mlx5_comp_vectors_max(mdev);
  cpumask_clear(priv->scratchpad.cpumask);
  vec_ix = mlx5_sd_ch_ix_get_vec_ix(mdev, ix);

  for (irq = vec_ix; irq < num_comp_vectors; irq += params->num_channels) {
   int cpu = mlx5_comp_vector_get_cpu(mdev, irq);

   cpumask_set_cpu(cpu, priv->scratchpad.cpumask);
  }

  netif_set_xps_queue(priv->netdev, priv->scratchpad.cpumask, ix);
 }
}

static int mlx5e_update_tc_and_tx_queues(struct mlx5e_priv *priv)
{
 struct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE], *tc_to_txq;
 struct net_device *netdev = priv->netdev;
 int old_num_txqs, old_ntc;
 int nch, ntc;
 int err;
 int i;

 old_num_txqs = netdev->real_num_tx_queues;
 old_ntc = netdev->num_tc ? : 1;
 for (i = 0; i < ARRAY_SIZE(old_tc_to_txq); i++)
  old_tc_to_txq[i] = netdev->tc_to_txq[i];

 nch = priv->channels.params.num_channels;
 ntc = priv->channels.params.mqprio.num_tc;
 tc_to_txq = priv->channels.params.mqprio.tc_to_txq;

 err = mlx5e_netdev_set_tcs(netdev, nch, ntc, tc_to_txq);
 if (err)
  goto err_out;
 err = mlx5e_update_tx_netdev_queues(priv);
 if (err)
  goto err_tcs;
 mlx5e_set_default_xps_cpumasks(priv, &priv->channels.params);

 return 0;

err_tcs:
 WARN_ON_ONCE(mlx5e_netdev_set_tcs(netdev, old_num_txqs / old_ntc, old_ntc,
       old_tc_to_txq));
err_out:
 return err;
}

MLX5E_DEFINE_PREACTIVATE_WRAPPER_CTX(mlx5e_update_tc_and_tx_queues);

static int mlx5e_num_channels_changed(struct mlx5e_priv *priv)
{
 u16 count = priv->channels.params.num_channels;
 struct net_device *netdev = priv->netdev;
 int old_num_rxqs;
 int err;

 old_num_rxqs = netdev->real_num_rx_queues;
 err = netif_set_real_num_rx_queues(netdev, count);
 if (err) {
  netdev_warn(netdev, "%s: netif_set_real_num_rx_queues failed, %d\n",
       __func__, err);
  return err;
 }
 err = mlx5e_update_tc_and_tx_queues(priv);
 if (err) {
  /* mlx5e_update_tc_and_tx_queues can fail if channels or TCs number increases.
 * Since channel number changed, it increased. That means, the call to
 * netif_set_real_num_rx_queues below should not fail, because it
 * decreases the number of RX queues.
 */

  WARN_ON_ONCE(netif_set_real_num_rx_queues(netdev, old_num_rxqs));
  return err;
 }

 /* This function may be called on attach, before priv->rx_res is created. */
 if (priv->rx_res) {
  mlx5e_rx_res_rss_update_num_channels(priv->rx_res, count);

  if (!netif_is_rxfh_configured(priv->netdev))
   mlx5e_rx_res_rss_set_indir_uniform(priv->rx_res, count);
 }

 return 0;
}

MLX5E_DEFINE_PREACTIVATE_WRAPPER_CTX(mlx5e_num_channels_changed);

static void mlx5e_build_txq_maps(struct mlx5e_priv *priv)
{
 int i, ch, tc, num_tc;

 ch = priv->channels.num;
 num_tc = mlx5e_get_dcb_num_tc(&priv->channels.params);

 for (i = 0; i < ch; i++) {
  for (tc = 0; tc < num_tc; tc++) {
   struct mlx5e_channel *c = priv->channels.c[i];
   struct mlx5e_txqsq *sq = &c->sq[tc];

   priv->txq2sq[sq->txq_ix] = sq;
   priv->txq2sq_stats[sq->txq_ix] = sq->stats;
  }
 }

 if (!priv->channels.ptp)
  goto out;

 if (!test_bit(MLX5E_PTP_STATE_TX, priv->channels.ptp->state))
  goto out;

 for (tc = 0; tc < num_tc; tc++) {
  struct mlx5e_ptp *c = priv->channels.ptp;
  struct mlx5e_txqsq *sq = &c->ptpsq[tc].txqsq;

  priv->txq2sq[sq->txq_ix] = sq;
  priv->txq2sq_stats[sq->txq_ix] = sq->stats;
 }

out:
 /* Make the change to txq2sq visible before the queue is started.
 * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE,
 * which pairs with this barrier.
 */

 smp_wmb();
}

void mlx5e_activate_priv_channels(struct mlx5e_priv *priv)
{
 mlx5e_build_txq_maps(priv);
 mlx5e_activate_channels(priv, &priv->channels);
 mlx5e_xdp_tx_enable(priv);

 /* dev_watchdog() wants all TX queues to be started when the carrier is
 * OK, including the ones in range real_num_tx_queues..num_tx_queues-1.
 * Make it happy to avoid TX timeout false alarms.
 */

 netif_tx_start_all_queues(priv->netdev);

 if (mlx5e_is_vport_rep(priv))
  mlx5e_rep_activate_channels(priv);

 set_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state);

 mlx5e_wait_channels_min_rx_wqes(&priv->channels);

 if (priv->rx_res)
  mlx5e_rx_res_channels_activate(priv->rx_res, &priv->channels);
}

static void mlx5e_cancel_tx_timeout_work(struct mlx5e_priv *priv)
{
 WARN_ON_ONCE(test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state));
 if (current_work() != &priv->tx_timeout_work)
  cancel_work_sync(&priv->tx_timeout_work);
}

void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv)
{
 if (priv->rx_res)
  mlx5e_rx_res_channels_deactivate(priv->rx_res);

 clear_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state);
 mlx5e_cancel_tx_timeout_work(priv);

 if (mlx5e_is_vport_rep(priv))
  mlx5e_rep_deactivate_channels(priv);

 /* The results of ndo_select_queue are unreliable, while netdev config
 * is being changed (real_num_tx_queues, num_tc). Stop all queues to
 * prevent ndo_start_xmit from being called, so that it can assume that
 * the selected queue is always valid.
 */

 netif_tx_disable(priv->netdev);

 mlx5e_xdp_tx_disable(priv);
 mlx5e_deactivate_channels(&priv->channels);
}

static int mlx5e_switch_priv_params(struct mlx5e_priv *priv,
        struct mlx5e_params *new_params,
        mlx5e_fp_preactivate preactivate,
        void *context)
{
 struct mlx5e_params old_params;

 old_params = priv->channels.params;
 priv->channels.params = *new_params;

 if (preactivate) {
  int err;

  err = preactivate(priv, context);
  if (err) {
   priv->channels.params = old_params;
   return err;
  }
 }

 return 0;
}

static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
          struct mlx5e_channels *new_chs,
          mlx5e_fp_preactivate preactivate,
          void *context)
{
 struct net_device *netdev = priv->netdev;
 struct mlx5e_channels old_chs;
 int carrier_ok;
 int err = 0;

 carrier_ok = netif_carrier_ok(netdev);
 netif_carrier_off(netdev);

 mlx5e_deactivate_priv_channels(priv);

 old_chs = priv->channels;
 priv->channels = *new_chs;

 /* New channels are ready to roll, call the preactivate hook if needed
 * to modify HW settings or update kernel parameters.
 */

 if (preactivate) {
  err = preactivate(priv, context);
  if (err) {
   priv->channels = old_chs;
   goto out;
  }
 }

 mlx5e_close_channels(&old_chs);
 priv->profile->update_rx(priv);

 mlx5e_selq_apply(&priv->selq);
out:
 mlx5e_activate_priv_channels(priv);

 /* return carrier back if needed */
 if (carrier_ok)
  netif_carrier_on(netdev);

 return err;
}

int mlx5e_safe_switch_params(struct mlx5e_priv *priv,
        struct mlx5e_params *params,
        mlx5e_fp_preactivate preactivate,
        void *context, bool reset)
{
 struct mlx5e_channels *new_chs;
 int err;

 reset &= test_bit(MLX5E_STATE_OPENED, &priv->state);
 if (!reset)
  return mlx5e_switch_priv_params(priv, params, preactivate, context);

 new_chs = kzalloc(sizeof(*new_chs), GFP_KERNEL);
 if (!new_chs)
  return -ENOMEM;
 new_chs->params = *params;

 mlx5e_selq_prepare_params(&priv->selq, &new_chs->params);

 err = mlx5e_open_channels(priv, new_chs);
 if (err)
  goto err_cancel_selq;

 err = mlx5e_switch_priv_channels(priv, new_chs, preactivate, context);
 if (err)
  goto err_close;

 kfree(new_chs);
 return 0;

err_close:
 mlx5e_close_channels(new_chs);

err_cancel_selq:
 mlx5e_selq_cancel(&priv->selq);
 kfree(new_chs);
 return err;
}

int mlx5e_safe_reopen_channels(struct mlx5e_priv *priv)
{
 return mlx5e_safe_switch_params(priv, &priv->channels.params, NULL, NULL, true);
}

void mlx5e_timestamp_init(struct mlx5e_priv *priv)
{
 priv->tstamp.tx_type   = HWTSTAMP_TX_OFF;
 priv->tstamp.rx_filter = HWTSTAMP_FILTER_NONE;
}

static void mlx5e_modify_admin_state(struct mlx5_core_dev *mdev,
         enum mlx5_port_status state)
{
 struct mlx5_eswitch *esw = mdev->priv.eswitch;
 int vport_admin_state;

 mlx5_set_port_admin_status(mdev, state);

 if (mlx5_eswitch_mode(mdev) == MLX5_ESWITCH_OFFLOADS ||
     !MLX5_CAP_GEN(mdev, uplink_follow))
  return;

 if (state == MLX5_PORT_UP)
  vport_admin_state = MLX5_VPORT_ADMIN_STATE_AUTO;
 else
  vport_admin_state = MLX5_VPORT_ADMIN_STATE_DOWN;

 mlx5_eswitch_set_vport_state(esw, MLX5_VPORT_UPLINK, vport_admin_state);
}

int mlx5e_open_locked(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

 mlx5e_selq_prepare_params(&priv->selq, &priv->channels.params);

 set_bit(MLX5E_STATE_OPENED, &priv->state);

 err = mlx5e_open_channels(priv, &priv->channels);
 if (err)
  goto err_clear_state_opened_flag;

 err = priv->profile->update_rx(priv);
 if (err)
  goto err_close_channels;

 mlx5e_selq_apply(&priv->selq);
 mlx5e_activate_priv_channels(priv);
 mlx5e_apply_traps(priv, true);
 if (priv->profile->update_carrier)
  priv->profile->update_carrier(priv);

 mlx5e_queue_update_stats(priv);
 return 0;

err_close_channels:
 mlx5e_close_channels(&priv->channels);
err_clear_state_opened_flag:
 clear_bit(MLX5E_STATE_OPENED, &priv->state);
 mlx5e_selq_cancel(&priv->selq);
 return err;
}

int mlx5e_open(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

 mutex_lock(&priv->state_lock);
 err = mlx5e_open_locked(netdev);
 if (!err)
  mlx5e_modify_admin_state(priv->mdev, MLX5_PORT_UP);
 mutex_unlock(&priv->state_lock);

 return err;
}

int mlx5e_close_locked(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);

 /* May already be CLOSED in case a previous configuration operation
 * (e.g RX/TX queue size change) that involves close&open failed.
 */

 if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
  return 0;

 mlx5e_apply_traps(priv, false);
 clear_bit(MLX5E_STATE_OPENED, &priv->state);

 netif_carrier_off(priv->netdev);
 mlx5e_deactivate_priv_channels(priv);
 mlx5e_close_channels(&priv->channels);

 return 0;
}

int mlx5e_close(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

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

 mutex_lock(&priv->state_lock);
 mlx5e_modify_admin_state(priv->mdev, MLX5_PORT_DOWN);
 err = mlx5e_close_locked(netdev);
 mutex_unlock(&priv->state_lock);

 return err;
}

static void mlx5e_free_drop_rq(struct mlx5e_rq *rq)
{
 mlx5_wq_destroy(&rq->wq_ctrl);
}

static int mlx5e_alloc_drop_rq(struct mlx5_core_dev *mdev,
          struct mlx5e_rq *rq,
          struct mlx5e_rq_param *param)
{
 void *rqc = param->rqc;
 void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq);
 int err;

 param->wq.db_numa_node = param->wq.buf_numa_node;

 err = mlx5_wq_cyc_create(mdev, ¶m->wq, rqc_wq, &rq->wqe.wq,
     &rq->wq_ctrl);
 if (err)
  return err;

 /* Mark as unused given "Drop-RQ" packets never reach XDP */
 xdp_rxq_info_unused(&rq->xdp_rxq);

 rq->mdev = mdev;

 return 0;
}

static int mlx5e_alloc_drop_cq(struct mlx5e_priv *priv,
          struct mlx5e_cq *cq,
          struct mlx5e_cq_param *param)
{
 struct mlx5_core_dev *mdev = priv->mdev;

 param->wq.buf_numa_node = dev_to_node(mlx5_core_dma_dev(mdev));
 param->wq.db_numa_node  = dev_to_node(mlx5_core_dma_dev(mdev));

 return mlx5e_alloc_cq_common(priv->mdev, priv->netdev, priv->wq,
         mdev->priv.bfreg.up, param, cq);
}

int mlx5e_open_drop_rq(struct mlx5e_priv *priv,
         struct mlx5e_rq *drop_rq)
{
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5e_cq_param cq_param = {};
 struct mlx5e_rq_param rq_param = {};
 struct mlx5e_cq *cq = &drop_rq->cq;
 int err;

 mlx5e_build_drop_rq_param(mdev, &rq_param);

 err = mlx5e_alloc_drop_cq(priv, cq, &cq_param);
 if (err)
  return err;

 err = mlx5e_create_cq(cq, &cq_param);
 if (err)
  goto err_free_cq;

 err = mlx5e_alloc_drop_rq(mdev, drop_rq, &rq_param);
 if (err)
  goto err_destroy_cq;

 err = mlx5e_create_rq(drop_rq, &rq_param, priv->drop_rq_q_counter);
 if (err)
  goto err_free_rq;

 err = mlx5e_modify_rq_state(drop_rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY);
 if (err)
  mlx5_core_warn(priv->mdev, "modify_rq_state failed, rx_if_down_packets won't be counted %d\n", err);

 return 0;

err_free_rq:
 mlx5e_free_drop_rq(drop_rq);

err_destroy_cq:
 mlx5e_destroy_cq(cq);

err_free_cq:
 mlx5e_free_cq(cq);

 return err;
}

void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq)
{
 mlx5e_destroy_rq(drop_rq);
 mlx5e_free_drop_rq(drop_rq);
 mlx5e_destroy_cq(&drop_rq->cq);
 mlx5e_free_cq(&drop_rq->cq);
}

static void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv)
{
 if (priv->mqprio_rl) {
  mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
  mlx5e_mqprio_rl_free(priv->mqprio_rl);
  priv->mqprio_rl = NULL;
 }
 mlx5e_accel_cleanup_tx(priv);
}

static int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd)
{
 int err;
 int i;

 for (i = 0; i < chs->num; i++) {
  err = mlx5e_modify_rq_vsd(&chs->c[i]->rq, vsd);
  if (err)
   return err;
 }
 if (chs->ptp && test_bit(MLX5E_PTP_STATE_RX, chs->ptp->state))
  return mlx5e_modify_rq_vsd(&chs->ptp->rq, vsd);

 return 0;
}

static void mlx5e_mqprio_build_default_tc_to_txq(struct netdev_tc_txq *tc_to_txq,
       int ntc, int nch)
{
 int tc;

 memset(tc_to_txq, 0, sizeof(*tc_to_txq) * TC_MAX_QUEUE);

 /* Map netdev TCs to offset 0.
 * We have our own UP to TXQ mapping for DCB mode of QoS
 */

 for (tc = 0; tc < ntc; tc++) {
  tc_to_txq[tc] = (struct netdev_tc_txq) {
   .count = nch,
   .offset = 0,
  };
 }
}

static void mlx5e_mqprio_build_tc_to_txq(struct netdev_tc_txq *tc_to_txq,
      struct tc_mqprio_qopt *qopt)
{
 int tc;

 for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
  tc_to_txq[tc] = (struct netdev_tc_txq) {
   .count = qopt->count[tc],
   .offset = qopt->offset[tc],
  };
 }
}

static void mlx5e_params_mqprio_dcb_set(struct mlx5e_params *params, u8 num_tc)
{
 params->mqprio.mode = TC_MQPRIO_MODE_DCB;
 params->mqprio.num_tc = num_tc;
 mlx5e_mqprio_build_default_tc_to_txq(params->mqprio.tc_to_txq, num_tc,
          params->num_channels);
}

static void mlx5e_mqprio_rl_update_params(struct mlx5e_params *params,
       struct mlx5e_mqprio_rl *rl)
{
 int tc;

 for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
  u32 hw_id = 0;

  if (rl)
   mlx5e_mqprio_rl_get_node_hw_id(rl, tc, &hw_id);
  params->mqprio.channel.hw_id[tc] = hw_id;
 }
}

static void mlx5e_params_mqprio_channel_set(struct mlx5e_params *params,
         struct tc_mqprio_qopt_offload *mqprio,
         struct mlx5e_mqprio_rl *rl)
{
 int tc;

 params->mqprio.mode = TC_MQPRIO_MODE_CHANNEL;
 params->mqprio.num_tc = mqprio->qopt.num_tc;

 for (tc = 0; tc < TC_MAX_QUEUE; tc++)
  params->mqprio.channel.max_rate[tc] = mqprio->max_rate[tc];

 mlx5e_mqprio_rl_update_params(params, rl);
 mlx5e_mqprio_build_tc_to_txq(params->mqprio.tc_to_txq, &mqprio->qopt);
}

static void mlx5e_params_mqprio_reset(struct mlx5e_params *params)
{
 mlx5e_params_mqprio_dcb_set(params, 1);
}

static int mlx5e_setup_tc_mqprio_dcb(struct mlx5e_priv *priv,
         struct tc_mqprio_qopt *mqprio)
{
 struct mlx5e_params new_params;
 u8 tc = mqprio->num_tc;
 int err;

 mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;

 if (tc && tc != MLX5_MAX_NUM_TC)
  return -EINVAL;

 new_params = priv->channels.params;
 mlx5e_params_mqprio_dcb_set(&new_params, tc ? tc : 1);

 err = mlx5e_safe_switch_params(priv, &new_params,
           mlx5e_update_tc_and_tx_queues_ctx, NULL, true);

 if (!err && priv->mqprio_rl) {
  mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
  mlx5e_mqprio_rl_free(priv->mqprio_rl);
  priv->mqprio_rl = NULL;
 }

 priv->max_opened_tc = max_t(u8, priv->max_opened_tc,
        mlx5e_get_dcb_num_tc(&priv->channels.params));
 return err;
}

static int mlx5e_mqprio_channel_validate(struct mlx5e_priv *priv,
      struct tc_mqprio_qopt_offload *mqprio)
{
 struct net_device *netdev = priv->netdev;
 struct mlx5e_ptp *ptp_channel;
 int agg_count = 0;
 int i;

 ptp_channel = priv->channels.ptp;
 if (ptp_channel && test_bit(MLX5E_PTP_STATE_TX, ptp_channel->state)) {
  netdev_err(netdev,
      "Cannot activate MQPRIO mode channel since it conflicts with TX port TS\n");
  return -EINVAL;
 }

 if (mqprio->qopt.offset[0] != 0 || mqprio->qopt.num_tc < 1 ||
     mqprio->qopt.num_tc > MLX5E_MAX_NUM_MQPRIO_CH_TC)
  return -EINVAL;

 for (i = 0; i < mqprio->qopt.num_tc; i++) {
  if (!mqprio->qopt.count[i]) {
   netdev_err(netdev, "Zero size for queue-group (%d) is not supported\n", i);
   return -EINVAL;
  }
  if (mqprio->min_rate[i]) {
   netdev_err(netdev, "Min tx rate is not supported\n");
   return -EINVAL;
  }

  if (mqprio->max_rate[i]) {
   int err;

   err = mlx5e_qos_bytes_rate_check(priv->mdev, mqprio->max_rate[i]);
   if (err)
    return err;
  }

  if (mqprio->qopt.offset[i] != agg_count) {
   netdev_err(netdev, "Discontinuous queues config is not supported\n");
   return -EINVAL;
  }
  agg_count += mqprio->qopt.count[i];
 }

 if (priv->channels.params.num_channels != agg_count) {
  netdev_err(netdev, "Num of queues (%d) does not match available (%d)\n",
      agg_count, priv->channels.params.num_channels);
  return -EINVAL;
 }

 return 0;
}

static bool mlx5e_mqprio_rate_limit(u8 num_tc, u64 max_rate[])
{
 int tc;

 for (tc = 0; tc < num_tc; tc++)
  if (max_rate[tc])
   return true;
 return false;
}

static struct mlx5e_mqprio_rl *mlx5e_mqprio_rl_create(struct mlx5_core_dev *mdev,
            u8 num_tc, u64 max_rate[])
{
 struct mlx5e_mqprio_rl *rl;
 int err;

 if (!mlx5e_mqprio_rate_limit(num_tc, max_rate))
  return NULL;

 rl = mlx5e_mqprio_rl_alloc();
 if (!rl)
  return ERR_PTR(-ENOMEM);

 err = mlx5e_mqprio_rl_init(rl, mdev, num_tc, max_rate);
 if (err) {
  mlx5e_mqprio_rl_free(rl);
  return ERR_PTR(err);
 }

 return rl;
}

static int mlx5e_setup_tc_mqprio_channel(struct mlx5e_priv *priv,
      struct tc_mqprio_qopt_offload *mqprio)
{
 struct mlx5e_params new_params;
 struct mlx5e_mqprio_rl *rl;
 int err;

 err = mlx5e_mqprio_channel_validate(priv, mqprio);
 if (err)
  return err;

 rl = mlx5e_mqprio_rl_create(priv->mdev, mqprio->qopt.num_tc, mqprio->max_rate);
 if (IS_ERR(rl))
  return PTR_ERR(rl);

 new_params = priv->channels.params;
 mlx5e_params_mqprio_channel_set(&new_params, mqprio, rl);

 err = mlx5e_safe_switch_params(priv, &new_params,
           mlx5e_update_tc_and_tx_queues_ctx, NULL, true);
 if (err) {
  if (rl) {
   mlx5e_mqprio_rl_cleanup(rl);
   mlx5e_mqprio_rl_free(rl);
  }
  return err;
 }

 if (priv->mqprio_rl) {
  mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
  mlx5e_mqprio_rl_free(priv->mqprio_rl);
 }
 priv->mqprio_rl = rl;

 return 0;
}

static int mlx5e_setup_tc_mqprio(struct mlx5e_priv *priv,
     struct tc_mqprio_qopt_offload *mqprio)
{
 /* MQPRIO is another toplevel qdisc that can't be attached
 * simultaneously with the offloaded HTB.
 */

 if (mlx5e_selq_is_htb_enabled(&priv->selq)) {
  NL_SET_ERR_MSG_MOD(mqprio->extack,
       "MQPRIO cannot be configured when HTB offload is enabled.");
  return -EOPNOTSUPP;
 }

 switch (mqprio->mode) {
 case TC_MQPRIO_MODE_DCB:
  return mlx5e_setup_tc_mqprio_dcb(priv, &mqprio->qopt);
 case TC_MQPRIO_MODE_CHANNEL:
  return mlx5e_setup_tc_mqprio_channel(priv, mqprio);
 default:
  return -EOPNOTSUPP;
 }
}

static LIST_HEAD(mlx5e_block_cb_list);

static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
     void *type_data)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 bool tc_unbind = false;
 int err;

 if (type == TC_SETUP_BLOCK &&
     ((struct flow_block_offload *)type_data)->command == FLOW_BLOCK_UNBIND)
  tc_unbind = true;

 if (!netif_device_present(dev) && !tc_unbind)
  return -ENODEV;

 switch (type) {
 case TC_SETUP_BLOCK: {
  struct flow_block_offload *f = type_data;

  f->unlocked_driver_cb = true;
  return flow_block_cb_setup_simple(type_data,
        &mlx5e_block_cb_list,
        mlx5e_setup_tc_block_cb,
        priv, priv, true);
 }
 case TC_SETUP_QDISC_MQPRIO:
  mutex_lock(&priv->state_lock);
  err = mlx5e_setup_tc_mqprio(priv, type_data);
  mutex_unlock(&priv->state_lock);
  return err;
 case TC_SETUP_QDISC_HTB:
  mutex_lock(&priv->state_lock);
  err = mlx5e_htb_setup_tc(priv, type_data);
  mutex_unlock(&priv->state_lock);
  return err;
 default:
  return -EOPNOTSUPP;
 }
}

void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
{
 int i;

 for (i = 0; i < priv->stats_nch; i++) {
  struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
  struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq;
  struct mlx5e_rq_stats *rq_stats = &channel_stats->rq;
  int j;

  s->rx_packets   += rq_stats->packets + xskrq_stats->packets;
  s->rx_bytes     += rq_stats->bytes + xskrq_stats->bytes;
  s->multicast    += rq_stats->mcast_packets + xskrq_stats->mcast_packets;

  for (j = 0; j < priv->max_opened_tc; j++) {
   struct mlx5e_sq_stats *sq_stats = &channel_stats->sq[j];

   s->tx_packets    += sq_stats->packets;
   s->tx_bytes      += sq_stats->bytes;
   s->tx_dropped    += sq_stats->dropped;
  }
 }
 if (priv->tx_ptp_opened) {
  for (i = 0; i < priv->max_opened_tc; i++) {
   struct mlx5e_sq_stats *sq_stats = &priv->ptp_stats.sq[i];

   s->tx_packets    += sq_stats->packets;
   s->tx_bytes      += sq_stats->bytes;
   s->tx_dropped    += sq_stats->dropped;
  }
 }
 if (priv->rx_ptp_opened) {
  struct mlx5e_rq_stats *rq_stats = &priv->ptp_stats.rq;

  s->rx_packets   += rq_stats->packets;
  s->rx_bytes     += rq_stats->bytes;
  s->multicast    += rq_stats->mcast_packets;
 }
}

void
mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_pport_stats *pstats = &priv->stats.pport;

 if (!netif_device_present(dev))
  return;

 /* In switchdev mode, monitor counters doesn't monitor
 * rx/tx stats of 802_3. The update stats mechanism
 * should keep the 802_3 layout counters updated
 */

 if (!mlx5e_monitor_counter_supported(priv) ||
     mlx5e_is_uplink_rep(priv)) {
  /* update HW stats in background for next time */
  mlx5e_queue_update_stats(priv);
 }

 if (mlx5e_is_uplink_rep(priv)) {
  struct mlx5e_vport_stats *vstats = &priv->stats.vport;

  stats->rx_packets = PPORT_802_3_GET(pstats, a_frames_received_ok);
  stats->rx_bytes   = PPORT_802_3_GET(pstats, a_octets_received_ok);
  stats->tx_packets = PPORT_802_3_GET(pstats, a_frames_transmitted_ok);
  stats->tx_bytes   = PPORT_802_3_GET(pstats, a_octets_transmitted_ok);

  /* vport multicast also counts packets that are dropped due to steering
 * or rx out of buffer
 */

  stats->multicast = VPORT_COUNTER_GET(vstats, received_eth_multicast.packets);
 } else {
  mlx5e_fold_sw_stats64(priv, stats);
 }

 stats->rx_missed_errors = priv->stats.qcnt.rx_out_of_buffer;
 stats->rx_dropped = PPORT_2863_GET(pstats, if_in_discards);

 stats->rx_length_errors =
  PPORT_802_3_GET(pstats, a_in_range_length_errors) +
  PPORT_802_3_GET(pstats, a_out_of_range_length_field) +
  PPORT_802_3_GET(pstats, a_frame_too_long_errors) +
  VNIC_ENV_GET(&priv->stats.vnic, eth_wqe_too_small);
 stats->rx_crc_errors =
  PPORT_802_3_GET(pstats, a_frame_check_sequence_errors);
 stats->rx_frame_errors = PPORT_802_3_GET(pstats, a_alignment_errors);
 stats->tx_aborted_errors = PPORT_2863_GET(pstats, if_out_discards);
 stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
      stats->rx_frame_errors;
 stats->tx_errors = stats->tx_aborted_errors + stats->tx_carrier_errors;
}

static void mlx5e_nic_set_rx_mode(struct mlx5e_priv *priv)
{
 if (mlx5e_is_uplink_rep(priv))
  return/* no rx mode for uplink rep */

 queue_work(priv->wq, &priv->set_rx_mode_work);
}

static void mlx5e_set_rx_mode(struct net_device *dev)
{
 struct mlx5e_priv *priv = netdev_priv(dev);

 mlx5e_nic_set_rx_mode(priv);
}

static int mlx5e_set_mac(struct net_device *netdev, void *addr)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct sockaddr *saddr = addr;

 if (!is_valid_ether_addr(saddr->sa_data))
  return -EADDRNOTAVAIL;

 netif_addr_lock_bh(netdev);
 eth_hw_addr_set(netdev, saddr->sa_data);
 netif_addr_unlock_bh(netdev);

 mlx5e_nic_set_rx_mode(priv);

 return 0;
}

#define MLX5E_SET_FEATURE(features, feature, enable) \
 do {      \
  if (enable)    \
   *features |= feature;  \
  else     \
   *features &= ~feature;  \
 } while (0)

typedef int (*mlx5e_feature_handler)(struct net_device *netdev, bool enable);

static int set_feature_lro(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5e_params *cur_params;
 struct mlx5e_params new_params;
 bool reset = true;
 int err = 0;

 mutex_lock(&priv->state_lock);

 cur_params = &priv->channels.params;
 new_params = *cur_params;

 if (enable)
  new_params.packet_merge.type = MLX5E_PACKET_MERGE_LRO;
 else if (new_params.packet_merge.type == MLX5E_PACKET_MERGE_LRO)
  new_params.packet_merge.type = MLX5E_PACKET_MERGE_NONE;
 else
  goto out;

 if (!(cur_params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO &&
       new_params.packet_merge.type == MLX5E_PACKET_MERGE_LRO)) {
  if (cur_params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) {
   if (mlx5e_rx_mpwqe_is_linear_skb(mdev, cur_params, NULL) ==
       mlx5e_rx_mpwqe_is_linear_skb(mdev, &new_params, NULL))
    reset = false;
  }
 }

 err = mlx5e_safe_switch_params(priv, &new_params,
           mlx5e_modify_tirs_packet_merge_ctx, NULL, reset);
out:
 mutex_unlock(&priv->state_lock);
 return err;
}

static int set_feature_hw_gro(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_params new_params;
 bool reset = true;
 int err = 0;

 mutex_lock(&priv->state_lock);
 new_params = priv->channels.params;

 if (enable) {
  new_params.packet_merge.type = MLX5E_PACKET_MERGE_SHAMPO;
 } else if (new_params.packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO) {
  new_params.packet_merge.type = MLX5E_PACKET_MERGE_NONE;
 } else {
  goto out;
 }

 err = mlx5e_safe_switch_params(priv, &new_params, NULL, NULL, reset);
out:
 mutex_unlock(&priv->state_lock);
 return err;
}

static int set_feature_cvlan_filter(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);

 if (enable)
  mlx5e_enable_cvlan_filter(priv->fs,
       !!(priv->netdev->flags & IFF_PROMISC));
 else
  mlx5e_disable_cvlan_filter(priv->fs,
        !!(priv->netdev->flags & IFF_PROMISC));

 return 0;
}

static int set_feature_hw_tc(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err = 0;

#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
 int tc_flag = mlx5e_is_uplink_rep(priv) ? MLX5_TC_FLAG(ESW_OFFLOAD) :
        MLX5_TC_FLAG(NIC_OFFLOAD);
 if (!enable && mlx5e_tc_num_filters(priv, tc_flag)) {
  netdev_err(netdev,
      "Active offloaded tc filters, can't turn hw_tc_offload off\n");
  return -EINVAL;
 }
#endif

 mutex_lock(&priv->state_lock);
 if (!enable && mlx5e_selq_is_htb_enabled(&priv->selq)) {
  netdev_err(netdev, "Active HTB offload, can't turn hw_tc_offload off\n");
  err = -EINVAL;
 }
 mutex_unlock(&priv->state_lock);

 return err;
}

static int set_feature_rx_all(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_set_port_fcs(mdev, !enable);
}

static struct dim_cq_moder mlx5e_get_def_rx_moderation(u8 cq_period_mode)
{
 return (struct dim_cq_moder) {
  .cq_period_mode = cq_period_mode,
  .pkts = MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_PKTS,
  .usec = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
    MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC_FROM_CQE :
    MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC,
 };
}

bool mlx5e_reset_rx_moderation(struct dim_cq_moder *cq_moder, u8 cq_period_mode,
          bool dim_enabled)
{
 bool reset_needed = cq_moder->cq_period_mode != cq_period_mode;

 if (dim_enabled)
  *cq_moder = net_dim_get_def_rx_moderation(cq_period_mode);
 else
  *cq_moder = mlx5e_get_def_rx_moderation(cq_period_mode);

 return reset_needed;
}

bool mlx5e_reset_rx_channels_moderation(struct mlx5e_channels *chs, u8 cq_period_mode,
     bool dim_enabled, bool keep_dim_state)
{
 bool reset = false;
 int i;

 for (i = 0; i < chs->num; i++) {
  if (keep_dim_state)
   dim_enabled = !!chs->c[i]->rq.dim;

  reset |= mlx5e_reset_rx_moderation(&chs->c[i]->rx_cq_moder,
         cq_period_mode, dim_enabled);
 }

 return reset;
}

static int mlx5e_set_rx_port_ts(struct mlx5_core_dev *mdev, bool enable)
{
 u32 in[MLX5_ST_SZ_DW(pcmr_reg)] = {};
 bool supported, curr_state;
 int err;

 if (!MLX5_CAP_GEN(mdev, ports_check))
  return 0;

 err = mlx5_query_ports_check(mdev, in, sizeof(in));
 if (err)
  return err;

 supported = MLX5_GET(pcmr_reg, in, rx_ts_over_crc_cap);
 curr_state = MLX5_GET(pcmr_reg, in, rx_ts_over_crc);

 if (!supported || enable == curr_state)
  return 0;

 MLX5_SET(pcmr_reg, in, local_port, 1);
 MLX5_SET(pcmr_reg, in, rx_ts_over_crc, enable);

 return mlx5_set_ports_check(mdev, in, sizeof(in));
}

static int mlx5e_set_rx_port_ts_wrap(struct mlx5e_priv *priv, void *ctx)
{
 struct mlx5_core_dev *mdev = priv->mdev;
 bool enable = *(bool *)ctx;

 return mlx5e_set_rx_port_ts(mdev, enable);
}

static int set_feature_rx_fcs(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_channels *chs = &priv->channels;
 struct mlx5e_params new_params;
 int err;
 bool rx_ts_over_crc = !enable;

 mutex_lock(&priv->state_lock);

 new_params = chs->params;
 new_params.scatter_fcs_en = enable;
 err = mlx5e_safe_switch_params(priv, &new_params, mlx5e_set_rx_port_ts_wrap,
           &rx_ts_over_crc, true);
 mutex_unlock(&priv->state_lock);
 return err;
}

static int set_feature_rx_vlan(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err = 0;

 mutex_lock(&priv->state_lock);

 mlx5e_fs_set_vlan_strip_disable(priv->fs, !enable);
 priv->channels.params.vlan_strip_disable = !enable;

 if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
  goto unlock;

 err = mlx5e_modify_channels_vsd(&priv->channels, !enable);
 if (err) {
  mlx5e_fs_set_vlan_strip_disable(priv->fs, enable);
  priv->channels.params.vlan_strip_disable = enable;
 }
unlock:
 mutex_unlock(&priv->state_lock);

 return err;
}

int mlx5e_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_flow_steering *fs = priv->fs;

 if (mlx5e_is_uplink_rep(priv))
  return 0; /* no vlan table for uplink rep */

 return mlx5e_fs_vlan_rx_add_vid(fs, dev, proto, vid);
}

int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_flow_steering *fs = priv->fs;

 if (mlx5e_is_uplink_rep(priv))
  return 0; /* no vlan table for uplink rep */

 return mlx5e_fs_vlan_rx_kill_vid(fs, dev, proto, vid);
}

#ifdef CONFIG_MLX5_EN_ARFS
static int set_feature_arfs(struct net_device *netdev, bool enable)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

 if (enable)
  err = mlx5e_arfs_enable(priv->fs);
 else
  err = mlx5e_arfs_disable(priv->fs);

 return err;
}
#endif

static int mlx5e_handle_feature(struct net_device *netdev,
    netdev_features_t *features,
    netdev_features_t feature,
    mlx5e_feature_handler feature_handler)
{
 netdev_features_t changes = *features ^ netdev->features;
 bool enable = !!(*features & feature);
 int err;

 if (!(changes & feature))
  return 0;

 err = feature_handler(netdev, enable);
 if (err) {
  MLX5E_SET_FEATURE(features, feature, !enable);
  netdev_err(netdev, "%s feature %pNF failed, err %d\n",
      enable ? "Enable" : "Disable", &feature, err);
  return err;
 }

 return 0;
}

void mlx5e_set_xdp_feature(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_params *params = &priv->channels.params;
 xdp_features_t val;

 if (!netdev->netdev_ops->ndo_bpf ||
     params->packet_merge.type != MLX5E_PACKET_MERGE_NONE) {
  xdp_set_features_flag_locked(netdev, 0);
  return;
 }

 val = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
       NETDEV_XDP_ACT_XSK_ZEROCOPY |
       NETDEV_XDP_ACT_RX_SG |
       NETDEV_XDP_ACT_NDO_XMIT |
       NETDEV_XDP_ACT_NDO_XMIT_SG;
 xdp_set_features_flag_locked(netdev, val);
}

int mlx5e_set_features(struct net_device *netdev, netdev_features_t features)
{
 netdev_features_t oper_features = features;
 int err = 0;

#define MLX5E_HANDLE_FEATURE(feature, handler) \
 mlx5e_handle_feature(netdev, &oper_features, feature, handler)

 if (features & (NETIF_F_GRO_HW | NETIF_F_LRO)) {
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs);
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro);
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_GRO_HW, set_feature_hw_gro);
 } else {
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro);
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_GRO_HW, set_feature_hw_gro);
  err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs);
 }
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER,
        set_feature_cvlan_filter);
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_hw_tc);
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all);
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan);
#ifdef CONFIG_MLX5_EN_ARFS
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_NTUPLE, set_feature_arfs);
#endif
 err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TLS_RX, mlx5e_ktls_set_feature_rx);

 if (err) {
  netdev->features = oper_features;
  return -EINVAL;
 }

 /* update XDP supported features */
 mlx5e_set_xdp_feature(netdev);

 return 0;
}

static netdev_features_t mlx5e_fix_uplink_rep_features(struct net_device *netdev,
             netdev_features_t features)
{
 features &= ~NETIF_F_HW_TLS_RX;
 if (netdev->features & NETIF_F_HW_TLS_RX)
  netdev_warn(netdev, "Disabling hw_tls_rx, not supported in switchdev mode\n");

 features &= ~NETIF_F_HW_TLS_TX;
 if (netdev->features & NETIF_F_HW_TLS_TX)
  netdev_warn(netdev, "Disabling hw_tls_tx, not supported in switchdev mode\n");

 features &= ~NETIF_F_NTUPLE;
 if (netdev->features & NETIF_F_NTUPLE)
  netdev_warn(netdev, "Disabling ntuple, not supported in switchdev mode\n");

 features &= ~NETIF_F_GRO_HW;
 if (netdev->features & NETIF_F_GRO_HW)
  netdev_warn(netdev, "Disabling HW_GRO, not supported in switchdev mode\n");

 features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
 if (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
  netdev_warn(netdev, "Disabling HW_VLAN CTAG FILTERING, not supported in switchdev mode\n");

 features &= ~NETIF_F_HW_MACSEC;
 if (netdev->features & NETIF_F_HW_MACSEC)
  netdev_warn(netdev, "Disabling HW MACsec offload, not supported in switchdev mode\n");

 return features;
}

static netdev_features_t mlx5e_fix_features(struct net_device *netdev,
         netdev_features_t features)
{
 struct netdev_config *cfg = netdev->cfg_pending;
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_vlan_table *vlan;
 struct mlx5e_params *params;

 if (!netif_device_present(netdev))
  return features;

 vlan = mlx5e_fs_get_vlan(priv->fs);
 mutex_lock(&priv->state_lock);
 params = &priv->channels.params;
 if (!vlan ||
     !bitmap_empty(mlx5e_vlan_get_active_svlans(vlan), VLAN_N_VID)) {
  /* HW strips the outer C-tag header, this is a problem
 * for S-tag traffic.
 */

  features &= ~NETIF_F_HW_VLAN_CTAG_RX;
  if (!params->vlan_strip_disable)
   netdev_warn(netdev, "Dropping C-tag vlan stripping offload due to S-tag vlan\n");
 }

 if (!MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ)) {
  if (features & NETIF_F_LRO) {
   netdev_warn(netdev, "Disabling LRO, not supported in legacy RQ\n");
   features &= ~NETIF_F_LRO;
  }
  if (features & NETIF_F_GRO_HW) {
   netdev_warn(netdev, "Disabling HW-GRO, not supported in legacy RQ\n");
   features &= ~NETIF_F_GRO_HW;
  }
 }

 if (params->xdp_prog) {
  if (features & NETIF_F_LRO) {
   netdev_warn(netdev, "LRO is incompatible with XDP\n");
   features &= ~NETIF_F_LRO;
  }
  if (features & NETIF_F_GRO_HW) {
   netdev_warn(netdev, "HW GRO is incompatible with XDP\n");
   features &= ~NETIF_F_GRO_HW;
  }
 }

 if (priv->xsk.refcnt) {
  if (features & NETIF_F_LRO) {
   netdev_warn(netdev, "LRO is incompatible with AF_XDP (%u XSKs are active)\n",
        priv->xsk.refcnt);
   features &= ~NETIF_F_LRO;
  }
  if (features & NETIF_F_GRO_HW) {
   netdev_warn(netdev, "HW GRO is incompatible with AF_XDP (%u XSKs are active)\n",
        priv->xsk.refcnt);
   features &= ~NETIF_F_GRO_HW;
  }
 }

 if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)) {
  features &= ~NETIF_F_RXHASH;
  if (netdev->features & NETIF_F_RXHASH)
   netdev_warn(netdev, "Disabling rxhash, not supported when CQE compress is active\n");

  if (features & NETIF_F_GRO_HW) {
   netdev_warn(netdev, "Disabling HW-GRO, not supported when CQE compress is active\n");
   features &= ~NETIF_F_GRO_HW;
  }
 }

 /* The header-data split ring param requires HW GRO to stay enabled. */
 if (cfg && cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED &&
     !(features & NETIF_F_GRO_HW)) {
  netdev_warn(netdev, "Keeping HW-GRO enabled, TCP header-data split depends on it\n");
  features |= NETIF_F_GRO_HW;
 }

 if (mlx5e_is_uplink_rep(priv)) {
  features = mlx5e_fix_uplink_rep_features(netdev, features);
  netdev->netns_immutable = true;
 } else {
  netdev->netns_immutable = false;
 }

 mutex_unlock(&priv->state_lock);

 return features;
}

static bool mlx5e_xsk_validate_mtu(struct net_device *netdev,
       struct mlx5e_channels *chs,
       struct mlx5e_params *new_params,
       struct mlx5_core_dev *mdev)
{
 u16 ix;

 for (ix = 0; ix < chs->params.num_channels; ix++) {
  struct xsk_buff_pool *xsk_pool =
   mlx5e_xsk_get_pool(&chs->params, chs->params.xsk, ix);
  struct mlx5e_xsk_param xsk;
  int max_xdp_mtu;

  if (!xsk_pool)
   continue;

  mlx5e_build_xsk_param(xsk_pool, &xsk);
  max_xdp_mtu = mlx5e_xdp_max_mtu(new_params, &xsk);

  /* Validate XSK params and XDP MTU in advance */
  if (!mlx5e_validate_xsk_param(new_params, &xsk, mdev) ||
      new_params->sw_mtu > max_xdp_mtu) {
   u32 hr = mlx5e_get_linear_rq_headroom(new_params, &xsk);
   int max_mtu_frame, max_mtu_page, max_mtu;

   /* Two criteria must be met:
 * 1. HW MTU + all headrooms <= XSK frame size.
 * 2. Size of SKBs allocated on XDP_PASS <= PAGE_SIZE.
 */

   max_mtu_frame = MLX5E_HW2SW_MTU(new_params, xsk.chunk_size - hr);
   max_mtu_page = MLX5E_HW2SW_MTU(new_params, SKB_MAX_HEAD(0));
   max_mtu = min3(max_mtu_frame, max_mtu_page, max_xdp_mtu);

   netdev_err(netdev, "MTU %d is too big for an XSK running on channel %u or its redirection XDP program. Try MTU <= %d\n",
       new_params->sw_mtu, ix, max_mtu);
   return false;
  }
 }

 return true;
}

static bool mlx5e_params_validate_xdp(struct net_device *netdev,
          struct mlx5_core_dev *mdev,
          struct mlx5e_params *params)
{
 bool is_linear;

 /* No XSK params: AF_XDP can't be enabled yet at the point of setting
 * the XDP program.
 */

 is_linear = params->rq_wq_type == MLX5_WQ_TYPE_CYCLIC ?
  mlx5e_rx_is_linear_skb(mdev, params, NULL) :
  mlx5e_rx_mpwqe_is_linear_skb(mdev, params, NULL);

 if (!is_linear) {
  if (!params->xdp_prog->aux->xdp_has_frags) {
   netdev_warn(netdev, "MTU(%d) > %d, too big for an XDP program not aware of multi buffer\n",
        params->sw_mtu,
        mlx5e_xdp_max_mtu(params, NULL));
   return false;
  }
  if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ &&
      !mlx5e_verify_params_rx_mpwqe_strides(mdev, params, NULL)) {
   netdev_warn(netdev, "XDP is not allowed with striding RQ and MTU(%d) > %d\n",
        params->sw_mtu,
        mlx5e_xdp_max_mtu(params, NULL));
   return false;
  }
 }

 return true;
}

int mlx5e_change_mtu(struct net_device *netdev, int new_mtu,
       mlx5e_fp_preactivate preactivate)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_params new_params;
 struct mlx5e_params *params;
 bool reset = true;
 int err = 0;

 mutex_lock(&priv->state_lock);

 params = &priv->channels.params;

 new_params = *params;
 new_params.sw_mtu = new_mtu;
 err = mlx5e_validate_params(priv->mdev, &new_params);
 if (err)
  goto out;

 if (new_params.xdp_prog && !mlx5e_params_validate_xdp(netdev, priv->mdev,
             &new_params)) {
  err = -EINVAL;
  goto out;
 }

 if (priv->xsk.refcnt &&
     !mlx5e_xsk_validate_mtu(netdev, &priv->channels,
        &new_params, priv->mdev)) {
  err = -EINVAL;
  goto out;
 }

 if (params->packet_merge.type == MLX5E_PACKET_MERGE_LRO)
  reset = false;

 if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ &&
     params->packet_merge.type != MLX5E_PACKET_MERGE_SHAMPO) {
  bool is_linear_old = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev, params, NULL);
  bool is_linear_new = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev,
          &new_params, NULL);
  u8 sz_old = mlx5e_mpwqe_get_log_rq_size(priv->mdev, params, NULL);
  u8 sz_new = mlx5e_mpwqe_get_log_rq_size(priv->mdev, &new_params, NULL);

  /* Always reset in linear mode - hw_mtu is used in data path.
 * Check that the mode was non-linear and didn't change.
 * If XSK is active, XSK RQs are linear.
 * Reset if the RQ size changed, even if it's non-linear.
 */

  if (!is_linear_old && !is_linear_new && !priv->xsk.refcnt &&
      sz_old == sz_new)
   reset = false;
 }

 err = mlx5e_safe_switch_params(priv, &new_params, preactivate, NULL, reset);

out:
 WRITE_ONCE(netdev->mtu, params->sw_mtu);
 mutex_unlock(&priv->state_lock);

 if (!err)
  netdev_update_features(netdev);

 return err;
}

static int mlx5e_change_nic_mtu(struct net_device *netdev, int new_mtu)
{
 return mlx5e_change_mtu(netdev, new_mtu, mlx5e_set_dev_port_mtu_ctx);
}

int mlx5e_ptp_rx_manage_fs_ctx(struct mlx5e_priv *priv, void *ctx)
{
 bool set  = *(bool *)ctx;

 return mlx5e_ptp_rx_manage_fs(priv, set);
}

static int mlx5e_hwstamp_config_no_ptp_rx(struct mlx5e_priv *priv, bool rx_filter)
{
 bool rx_cqe_compress_def = priv->channels.params.rx_cqe_compress_def;
 int err;

 if (!rx_filter)
  /* Reset CQE compression to Admin default */
  return mlx5e_modify_rx_cqe_compression_locked(priv, rx_cqe_compress_def, false);

 if (!MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS))
  return 0;

 /* Disable CQE compression */
 netdev_warn(priv->netdev, "Disabling RX cqe compression\n");
 err = mlx5e_modify_rx_cqe_compression_locked(priv, falsetrue);
 if (err)
  netdev_err(priv->netdev, "Failed disabling cqe compression err=%d\n", err);

 return err;
}

static int mlx5e_hwstamp_config_ptp_rx(struct mlx5e_priv *priv, bool ptp_rx)
{
 struct mlx5e_params new_params;

 if (ptp_rx == priv->channels.params.ptp_rx)
  return 0;

 new_params = priv->channels.params;
 new_params.ptp_rx = ptp_rx;
 return mlx5e_safe_switch_params(priv, &new_params, mlx5e_ptp_rx_manage_fs_ctx,
     &new_params.ptp_rx, true);
}

int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr)
{
 struct hwtstamp_config config;
 bool rx_cqe_compress_def;
 bool ptp_rx;
 int err;

 if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz) ||
     (mlx5_clock_get_ptp_index(priv->mdev) == -1))
  return -EOPNOTSUPP;

 if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
  return -EFAULT;

 /* TX HW timestamp */
 switch (config.tx_type) {
 case HWTSTAMP_TX_OFF:
 case HWTSTAMP_TX_ON:
  break;
 default:
  return -ERANGE;
 }

 mutex_lock(&priv->state_lock);
 rx_cqe_compress_def = priv->channels.params.rx_cqe_compress_def;

 /* RX HW timestamp */
 switch (config.rx_filter) {
 case HWTSTAMP_FILTER_NONE:
  ptp_rx = false;
  break;
 case HWTSTAMP_FILTER_ALL:
 case HWTSTAMP_FILTER_SOME:
 case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
 case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
 case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
 case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
 case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
 case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
 case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
 case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
 case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
 case HWTSTAMP_FILTER_PTP_V2_EVENT:
 case HWTSTAMP_FILTER_PTP_V2_SYNC:
 case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
 case HWTSTAMP_FILTER_NTP_ALL:
  config.rx_filter = HWTSTAMP_FILTER_ALL;
  /* ptp_rx is set if both HW TS is set and CQE
 * compression is set
 */

  ptp_rx = rx_cqe_compress_def;
  break;
 default:
  err = -ERANGE;
  goto err_unlock;
 }

 if (!mlx5e_profile_feature_cap(priv->profile, PTP_RX))
  err = mlx5e_hwstamp_config_no_ptp_rx(priv,
           config.rx_filter != HWTSTAMP_FILTER_NONE);
 else
  err = mlx5e_hwstamp_config_ptp_rx(priv, ptp_rx);
 if (err)
  goto err_unlock;

 memcpy(&priv->tstamp, &config, sizeof(config));
 mutex_unlock(&priv->state_lock);

 /* might need to fix some features */
 netdev_update_features(priv->netdev);

 return copy_to_user(ifr->ifr_data, &config,
       sizeof(config)) ? -EFAULT : 0;
err_unlock:
 mutex_unlock(&priv->state_lock);
 return err;
}

int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr)
{
 struct hwtstamp_config *cfg = &priv->tstamp;

 if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
  return -EOPNOTSUPP;

 return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0;
}

static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
 struct mlx5e_priv *priv = netdev_priv(dev);

 switch (cmd) {
 case SIOCSHWTSTAMP:
  return mlx5e_hwstamp_set(priv, ifr);
 case SIOCGHWTSTAMP:
  return mlx5e_hwstamp_get(priv, ifr);
 default:
  return -EOPNOTSUPP;
 }
}

#ifdef CONFIG_MLX5_ESWITCH
int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_eswitch_set_vport_mac(mdev->priv.eswitch, vf + 1, mac);
}

static int mlx5e_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos,
        __be16 vlan_proto)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 if (vlan_proto != htons(ETH_P_8021Q))
  return -EPROTONOSUPPORT;

 return mlx5_eswitch_set_vport_vlan(mdev->priv.eswitch, vf + 1,
        vlan, qos);
}

static int mlx5e_set_vf_spoofchk(struct net_device *dev, int vf, bool setting)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_eswitch_set_vport_spoofchk(mdev->priv.eswitch, vf + 1, setting);
}

static int mlx5e_set_vf_trust(struct net_device *dev, int vf, bool setting)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_eswitch_set_vport_trust(mdev->priv.eswitch, vf + 1, setting);
}

int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate,
        int max_tx_rate)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_eswitch_set_vport_rate(mdev->priv.eswitch, vf + 1,
        max_tx_rate, min_tx_rate);
}

static int mlx5_vport_link2ifla(u8 esw_link)
{
 switch (esw_link) {
 case MLX5_VPORT_ADMIN_STATE_DOWN:
  return IFLA_VF_LINK_STATE_DISABLE;
 case MLX5_VPORT_ADMIN_STATE_UP:
  return IFLA_VF_LINK_STATE_ENABLE;
 }
 return IFLA_VF_LINK_STATE_AUTO;
}

static int mlx5_ifla_link2vport(u8 ifla_link)
{
 switch (ifla_link) {
 case IFLA_VF_LINK_STATE_DISABLE:
  return MLX5_VPORT_ADMIN_STATE_DOWN;
 case IFLA_VF_LINK_STATE_ENABLE:
  return MLX5_VPORT_ADMIN_STATE_UP;
 }
 return MLX5_VPORT_ADMIN_STATE_AUTO;
}

static int mlx5e_set_vf_link_state(struct net_device *dev, int vf,
       int link_state)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 if (mlx5e_is_uplink_rep(priv))
  return -EOPNOTSUPP;

 return mlx5_eswitch_set_vport_state(mdev->priv.eswitch, vf + 1,
         mlx5_ifla_link2vport(link_state));
}

int mlx5e_get_vf_config(struct net_device *dev,
   int vf, struct ifla_vf_info *ivi)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;
 int err;

 if (!netif_device_present(dev))
  return -EOPNOTSUPP;

 err = mlx5_eswitch_get_vport_config(mdev->priv.eswitch, vf + 1, ivi);
 if (err)
  return err;
 ivi->linkstate = mlx5_vport_link2ifla(ivi->linkstate);
 return 0;
}

int mlx5e_get_vf_stats(struct net_device *dev,
         int vf, struct ifla_vf_stats *vf_stats)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;

 return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1,
         vf_stats);
}

static bool
mlx5e_has_offload_stats(const struct net_device *dev, int attr_id)
{
 struct mlx5e_priv *priv = netdev_priv(dev);

 if (!netif_device_present(dev))
  return false;

 if (!mlx5e_is_uplink_rep(priv))
  return false;

 return mlx5e_rep_has_offload_stats(dev, attr_id);
}

static int
mlx5e_get_offload_stats(int attr_id, const struct net_device *dev,
   void *sp)
{
 struct mlx5e_priv *priv = netdev_priv(dev);

 if (!mlx5e_is_uplink_rep(priv))
  return -EOPNOTSUPP;

 return mlx5e_rep_get_offload_stats(attr_id, dev, sp);
}
#endif

static bool mlx5e_tunnel_proto_supported_tx(struct mlx5_core_dev *mdev, u8 proto_type)
{
 switch (proto_type) {
 case IPPROTO_GRE:
  return MLX5_CAP_ETH(mdev, tunnel_stateless_gre);
 case IPPROTO_IPIP:
 case IPPROTO_IPV6:
  return (MLX5_CAP_ETH(mdev, tunnel_stateless_ip_over_ip) ||
   MLX5_CAP_ETH(mdev, tunnel_stateless_ip_over_ip_tx));
 default:
  return false;
 }
}

static bool mlx5e_gre_tunnel_inner_proto_offload_supported(struct mlx5_core_dev *mdev,
          struct sk_buff *skb)
{
 switch (skb->inner_protocol) {
 case htons(ETH_P_IP):
 case htons(ETH_P_IPV6):
 case htons(ETH_P_TEB):
  return true;
 case htons(ETH_P_MPLS_UC):
 case htons(ETH_P_MPLS_MC):
  return MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_gre);
 }
 return false;
}

static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv,
           struct sk_buff *skb,
           netdev_features_t features)
{
 unsigned int offset = 0;
 struct udphdr *udph;
 u8 proto;
 u16 port;

 switch (vlan_get_protocol(skb)) {
 case htons(ETH_P_IP):
  proto = ip_hdr(skb)->protocol;
  break;
 case htons(ETH_P_IPV6):
  proto = ipv6_find_hdr(skb, &offset, -1, NULL, NULL);
  break;
 default:
  goto out;
 }

 switch (proto) {
 case IPPROTO_GRE:
  if (mlx5e_gre_tunnel_inner_proto_offload_supported(priv->mdev, skb))
   return features;
  break;
 case IPPROTO_IPIP:
 case IPPROTO_IPV6:
  if (mlx5e_tunnel_proto_supported_tx(priv->mdev, IPPROTO_IPIP))
   return features;
  break;
 case IPPROTO_UDP:
  udph = udp_hdr(skb);
  port = be16_to_cpu(udph->dest);

  /* Verify if UDP port is being offloaded by HW */
  if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, port))
   return vxlan_features_check(skb, features);

#if IS_ENABLED(CONFIG_GENEVE)
  /* Support Geneve offload for default UDP port */
  if (port == GENEVE_UDP_PORT && mlx5_geneve_tx_allowed(priv->mdev))
   return features;
#endif
  break;
#ifdef CONFIG_MLX5_EN_IPSEC
 case IPPROTO_ESP:
  return mlx5e_ipsec_feature_check(skb, features);
#endif
 }

out:
 /* Disable CSUM and GSO if skb cannot be offloaded by HW */
 return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
}

netdev_features_t mlx5e_features_check(struct sk_buff *skb,
           struct net_device *netdev,
           netdev_features_t features)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);

 features = vlan_features_check(skb, features);

 /* Validate if the tunneled packet is being offloaded by HW */
 if (skb->encapsulation &&
     (features & NETIF_F_CSUM_MASK || features & NETIF_F_GSO_MASK))
  return mlx5e_tunnel_features_check(priv, skb, features);

 return features;
}

static void mlx5e_tx_timeout_work(struct work_struct *work)
{
 struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
            tx_timeout_work);
 struct net_device *netdev = priv->netdev;
 int i;

 /* Recovering the TX queues implies re-enabling NAPI, which requires
 * the netdev instance lock.
 * However, channel closing flows have to wait for this work to finish
 * while holding the same lock. So either get the lock or find that
 * channels are being closed for other reason and this work is not
 * relevant anymore.
 */

 while (!netdev_trylock(netdev)) {
  if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
   return;
  msleep(20);
 }

 for (i = 0; i < netdev->real_num_tx_queues; i++) {
  struct netdev_queue *dev_queue =
   netdev_get_tx_queue(netdev, i);
  struct mlx5e_txqsq *sq = priv->txq2sq[i];

  if (!netif_xmit_stopped(dev_queue))
   continue;

  if (mlx5e_reporter_tx_timeout(sq))
  /* break if tried to reopened channels */
   break;
 }

 netdev_unlock(netdev);
}

static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
{
 struct mlx5e_priv *priv = netdev_priv(dev);

 netdev_err(dev, "TX timeout detected\n");
 queue_work(priv->wq, &priv->tx_timeout_work);
}

static int mlx5e_xdp_allowed(struct net_device *netdev, struct mlx5_core_dev *mdev,
        struct mlx5e_params *params)
{
 if (params->packet_merge.type != MLX5E_PACKET_MERGE_NONE) {
  netdev_warn(netdev, "can't set XDP while HW-GRO/LRO is on, disable them first\n");
  return -EINVAL;
 }

 if (!mlx5e_params_validate_xdp(netdev, mdev, params))
  return -EINVAL;

 return 0;
}

static void mlx5e_rq_replace_xdp_prog(struct mlx5e_rq *rq, struct bpf_prog *prog)
{
 struct bpf_prog *old_prog;

 old_prog = rcu_replace_pointer(rq->xdp_prog, prog,
           lockdep_is_held(&rq->priv->state_lock));
 if (old_prog)
  bpf_prog_put(old_prog);
}

static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_params new_params;
 struct bpf_prog *old_prog;
 int err = 0;
 bool reset;
 int i;

 mutex_lock(&priv->state_lock);

 new_params = priv->channels.params;
 new_params.xdp_prog = prog;

 if (prog) {
  err = mlx5e_xdp_allowed(netdev, priv->mdev, &new_params);
  if (err)
   goto unlock;
 }

 /* no need for full reset when exchanging programs */
 reset = (!priv->channels.params.xdp_prog || !prog);

 old_prog = priv->channels.params.xdp_prog;

 err = mlx5e_safe_switch_params(priv, &new_params, NULL, NULL, reset);
 if (err)
  goto unlock;

 if (old_prog)
  bpf_prog_put(old_prog);

 if (!test_bit(MLX5E_STATE_OPENED, &priv->state) || reset)
  goto unlock;

 /* exchanging programs w/o reset, we update ref counts on behalf
 * of the channels RQs here.
 */

 bpf_prog_add(prog, priv->channels.num);
 for (i = 0; i < priv->channels.num; i++) {
  struct mlx5e_channel *c = priv->channels.c[i];

  mlx5e_rq_replace_xdp_prog(&c->rq, prog);
  if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) {
   bpf_prog_inc(prog);
   mlx5e_rq_replace_xdp_prog(&c->xskrq, prog);
  }
 }

unlock:
 mutex_unlock(&priv->state_lock);

 /* Need to fix some features. */
 if (!err)
  netdev_update_features(netdev);

 return err;
}

static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp)
{
 switch (xdp->command) {
 case XDP_SETUP_PROG:
  return mlx5e_xdp_set(dev, xdp->prog);
 case XDP_SETUP_XSK_POOL:
  return mlx5e_xsk_setup_pool(dev, xdp->xsk.pool,
         xdp->xsk.queue_id);
 default:
  return -EINVAL;
 }
}

#ifdef CONFIG_MLX5_ESWITCH
static int mlx5e_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
    struct net_device *dev, u32 filter_mask,
    int nlflags)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;
 u8 mode, setting;

 if (mlx5_eswitch_get_vepa(mdev->priv.eswitch, &setting))
  return -EOPNOTSUPP;
 mode = setting ? BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB;
 return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
           mode,
           0, 0, nlflags, filter_mask, NULL);
}

static int mlx5e_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
    u16 flags, struct netlink_ext_ack *extack)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5_core_dev *mdev = priv->mdev;
 struct nlattr *attr, *br_spec;
 u16 mode = BRIDGE_MODE_UNDEF;
 u8 setting;
 int rem;

 br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
 if (!br_spec)
  return -EINVAL;

 nla_for_each_nested_type(attr, IFLA_BRIDGE_MODE, br_spec, rem) {
  mode = nla_get_u16(attr);
  if (mode > BRIDGE_MODE_VEPA)
   return -EINVAL;

  break;
 }

 if (mode == BRIDGE_MODE_UNDEF)
  return -EINVAL;

 setting = (mode == BRIDGE_MODE_VEPA) ?  1 : 0;
 return mlx5_eswitch_set_vepa(mdev->priv.eswitch, setting);
}
#endif

const struct net_device_ops mlx5e_netdev_ops = {
 .ndo_open                = mlx5e_open,
 .ndo_stop                = mlx5e_close,
 .ndo_start_xmit          = mlx5e_xmit,
 .ndo_setup_tc            = mlx5e_setup_tc,
 .ndo_select_queue        = mlx5e_select_queue,
 .ndo_get_stats64         = mlx5e_get_stats,
 .ndo_set_rx_mode         = mlx5e_set_rx_mode,
 .ndo_set_mac_address     = mlx5e_set_mac,
 .ndo_vlan_rx_add_vid     = mlx5e_vlan_rx_add_vid,
 .ndo_vlan_rx_kill_vid    = mlx5e_vlan_rx_kill_vid,
 .ndo_set_features        = mlx5e_set_features,
 .ndo_fix_features        = mlx5e_fix_features,
 .ndo_change_mtu          = mlx5e_change_nic_mtu,
 .ndo_eth_ioctl            = mlx5e_ioctl,
 .ndo_set_tx_maxrate      = mlx5e_set_tx_maxrate,
 .ndo_features_check      = mlx5e_features_check,
 .ndo_tx_timeout          = mlx5e_tx_timeout,
 .ndo_bpf   = mlx5e_xdp,
 .ndo_xdp_xmit            = mlx5e_xdp_xmit,
 .ndo_xsk_wakeup          = mlx5e_xsk_wakeup,
#ifdef CONFIG_MLX5_EN_ARFS
 .ndo_rx_flow_steer  = mlx5e_rx_flow_steer,
#endif
#ifdef CONFIG_MLX5_ESWITCH
 .ndo_bridge_setlink      = mlx5e_bridge_setlink,
 .ndo_bridge_getlink      = mlx5e_bridge_getlink,

 /* SRIOV E-Switch NDOs */
 .ndo_set_vf_mac          = mlx5e_set_vf_mac,
 .ndo_set_vf_vlan         = mlx5e_set_vf_vlan,
 .ndo_set_vf_spoofchk     = mlx5e_set_vf_spoofchk,
 .ndo_set_vf_trust        = mlx5e_set_vf_trust,
 .ndo_set_vf_rate         = mlx5e_set_vf_rate,
 .ndo_get_vf_config       = mlx5e_get_vf_config,
 .ndo_set_vf_link_state   = mlx5e_set_vf_link_state,
 .ndo_get_vf_stats        = mlx5e_get_vf_stats,
 .ndo_has_offload_stats   = mlx5e_has_offload_stats,
 .ndo_get_offload_stats   = mlx5e_get_offload_stats,
#endif
};

void mlx5e_build_nic_params(struct mlx5e_priv *priv, struct mlx5e_xsk *xsk, u16 mtu)
{
 struct mlx5e_params *params = &priv->channels.params;
 struct mlx5_core_dev *mdev = priv->mdev;

 params->sw_mtu = mtu;
 params->hard_mtu = MLX5E_ETH_HARD_MTU;
 params->num_channels = min_t(unsigned int, MLX5E_MAX_NUM_CHANNELS / 2,
         priv->max_nch);
 mlx5e_params_mqprio_reset(params);

 /* SQ */
 params->log_sq_size = is_kdump_kernel() ?
  MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE :
  MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
 MLX5E_SET_PFLAG(params, MLX5E_PFLAG_SKB_TX_MPWQE, mlx5e_tx_mpwqe_supported(mdev));

 /* XDP SQ */
 MLX5E_SET_PFLAG(params, MLX5E_PFLAG_XDP_TX_MPWQE, mlx5e_tx_mpwqe_supported(mdev));

 /* set CQE compression */
 params->rx_cqe_compress_def = false;
 if (MLX5_CAP_GEN(mdev, cqe_compression) &&
     MLX5_CAP_GEN(mdev, vport_group_manager))
  params->rx_cqe_compress_def = slow_pci_heuristic(mdev);

 MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS, params->rx_cqe_compress_def);
 MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_NO_CSUM_COMPLETE, false);

 /* RQ */
 mlx5e_build_rq_params(mdev, params);

 params->terminate_lkey_be = mlx5_core_get_terminate_scatter_list_mkey(mdev);

 params->packet_merge.timeout = mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT);

 /* CQ moderation params */
 params->rx_dim_enabled = MLX5_CAP_GEN(mdev, cq_moderation) &&
     MLX5_CAP_GEN(mdev, cq_period_mode_modify);
 params->tx_dim_enabled = MLX5_CAP_GEN(mdev, cq_moderation) &&
     MLX5_CAP_GEN(mdev, cq_period_mode_modify);
 params->rx_moder_use_cqe_mode = !!MLX5_CAP_GEN(mdev, cq_period_start_from_cqe);
 params->tx_moder_use_cqe_mode = false;
 mlx5e_reset_rx_moderation(¶ms->rx_cq_moderation, params->rx_moder_use_cqe_mode,
      params->rx_dim_enabled);
 mlx5e_reset_tx_moderation(¶ms->tx_cq_moderation, params->tx_moder_use_cqe_mode,
      params->tx_dim_enabled);

 /* TX inline */
 mlx5_query_min_inline(mdev, ¶ms->tx_min_inline_mode);

 /* AF_XDP */
 params->xsk = xsk;

 /* Do not update netdev->features directly in here
 * on mlx5e_attach_netdev() we will call mlx5e_update_features()
 * To update netdev->features please modify mlx5e_fix_features()
 */

}

static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 u8 addr[ETH_ALEN];

 mlx5_query_mac_address(priv->mdev, addr);
 if (is_zero_ether_addr(addr) &&
     !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) {
  eth_hw_addr_random(netdev);
  mlx5_core_info(priv->mdev, "Assigned random MAC address %pM\n", netdev->dev_addr);
  return;
 }

 eth_hw_addr_set(netdev, addr);
}

static int mlx5e_vxlan_set_port(struct net_device *netdev, unsigned int table,
    unsigned int entry, struct udp_tunnel_info *ti)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);

 return mlx5_vxlan_add_port(priv->mdev->vxlan, ntohs(ti->port));
}

static int mlx5e_vxlan_unset_port(struct net_device *netdev, unsigned int table,
      unsigned int entry, struct udp_tunnel_info *ti)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);

 return mlx5_vxlan_del_port(priv->mdev->vxlan, ntohs(ti->port));
}

void mlx5e_vxlan_set_netdev_info(struct mlx5e_priv *priv)
{
 if (!mlx5_vxlan_allowed(priv->mdev->vxlan))
  return;

 priv->nic_info.set_port = mlx5e_vxlan_set_port;
 priv->nic_info.unset_port = mlx5e_vxlan_unset_port;
 priv->nic_info.flags = UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN;
 priv->nic_info.tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN;
 /* Don't count the space hard-coded to the IANA port */
 priv->nic_info.tables[0].n_entries =
  mlx5_vxlan_max_udp_ports(priv->mdev) - 1;

 priv->netdev->udp_tunnel_nic_info = &priv->nic_info;
}

static bool mlx5e_tunnel_any_tx_proto_supported(struct mlx5_core_dev *mdev)
{
 int tt;

 for (tt = 0; tt < MLX5_NUM_TUNNEL_TT; tt++) {
  if (mlx5e_tunnel_proto_supported_tx(mdev, mlx5_get_proto_by_tunnel_type(tt)))
   return true;
 }
 return (mlx5_vxlan_allowed(mdev->vxlan) || mlx5_geneve_tx_allowed(mdev));
}

static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i,
         struct netdev_queue_stats_rx *stats)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_channel_stats *channel_stats;
 struct mlx5e_rq_stats *xskrq_stats;
 struct mlx5e_rq_stats *rq_stats;

 if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch)
  return;

 channel_stats = priv->channel_stats[i];
 xskrq_stats = &channel_stats->xskrq;
 rq_stats = &channel_stats->rq;

 stats->packets = rq_stats->packets + xskrq_stats->packets;
 stats->bytes = rq_stats->bytes + xskrq_stats->bytes;
 stats->alloc_fail = rq_stats->buff_alloc_err +
       xskrq_stats->buff_alloc_err;
}

static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i,
         struct netdev_queue_stats_tx *stats)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_sq_stats *sq_stats;

 if (!priv->stats_nch)
  return;

 /* no special case needed for ptp htb etc since txq2sq_stats is kept up
 * to date for active sq_stats, otherwise get_base_stats takes care of
 * inactive sqs.
 */

 sq_stats = priv->txq2sq_stats[i];
 stats->packets = sq_stats->packets;
 stats->bytes = sq_stats->bytes;
}

static void mlx5e_get_base_stats(struct net_device *dev,
     struct netdev_queue_stats_rx *rx,
     struct netdev_queue_stats_tx *tx)
{
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_ptp *ptp_channel;
 int i, tc;

 if (!mlx5e_is_uplink_rep(priv)) {
  rx->packets = 0;
  rx->bytes = 0;
  rx->alloc_fail = 0;

  for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) {
   struct netdev_queue_stats_rx rx_i = {0};

   mlx5e_get_queue_stats_rx(dev, i, &rx_i);

   rx->packets += rx_i.packets;
   rx->bytes += rx_i.bytes;
   rx->alloc_fail += rx_i.alloc_fail;
  }

  /* always report PTP RX stats from base as there is no
 * corresponding channel to report them under in
 * mlx5e_get_queue_stats_rx.
 */

  if (priv->rx_ptp_opened) {
   struct mlx5e_rq_stats *rq_stats = &priv->ptp_stats.rq;

   rx->packets += rq_stats->packets;
   rx->bytes += rq_stats->bytes;
  }
 }

 tx->packets = 0;
 tx->bytes = 0;

 for (i = 0; i < priv->stats_nch; i++) {
  struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];

  /* handle two cases:
 *
 *  1. channels which are active. In this case,
 *     report only deactivated TCs on these channels.
 *
 *  2. channels which were deactivated
 *     (i > priv->channels.params.num_channels)
 *     must have all of their TCs [0 .. priv->max_opened_tc)
 *     examined because deactivated channels will not be in the
 *     range of [0..real_num_tx_queues) and will not have their
 *     stats reported by mlx5e_get_queue_stats_tx.
 */

  if (i < priv->channels.params.num_channels)
   tc = mlx5e_get_dcb_num_tc(&priv->channels.params);
  else
   tc = 0;

  for (; tc < priv->max_opened_tc; tc++) {
   struct mlx5e_sq_stats *sq_stats = &channel_stats->sq[tc];

   tx->packets += sq_stats->packets;
   tx->bytes += sq_stats->bytes;
  }
 }

 /* if PTP TX was opened at some point and has since either:
 *    -  been shutdown and set to NULL, or
 *    -  simply disabled (bit unset)
 *
 * report stats directly from the ptp_stats structures as these queues
 * are now unavailable and there is no txq index to retrieve these
 * stats via calls to mlx5e_get_queue_stats_tx.
 */

 ptp_channel = priv->channels.ptp;
 if (priv->tx_ptp_opened && (!ptp_channel || !test_bit(MLX5E_PTP_STATE_TX, ptp_channel->state))) {
  for (tc = 0; tc < priv->max_opened_tc; tc++) {
   struct mlx5e_sq_stats *sq_stats = &priv->ptp_stats.sq[tc];

   tx->packets += sq_stats->packets;
   tx->bytes   += sq_stats->bytes;
  }
 }
}

static const struct netdev_stat_ops mlx5e_stat_ops = {
 .get_queue_stats_rx  = mlx5e_get_queue_stats_rx,
 .get_queue_stats_tx  = mlx5e_get_queue_stats_tx,
 .get_base_stats      = mlx5e_get_base_stats,
};

struct mlx5_qmgmt_data {
 struct mlx5e_channel *c;
 struct mlx5e_channel_param cparam;
};

static int mlx5e_queue_mem_alloc(struct net_device *dev, void *newq,
     int queue_index)
{
 struct mlx5_qmgmt_data *new = (struct mlx5_qmgmt_data *)newq;
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_channels *chs = &priv->channels;
 struct mlx5e_params params = chs->params;
 struct mlx5_core_dev *mdev;
 int err;

 mutex_lock(&priv->state_lock);
 if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
  err = -ENODEV;
  goto unlock;
 }

 if (queue_index >= chs->num) {
  err = -ERANGE;
  goto unlock;
 }

 if (MLX5E_GET_PFLAG(&chs->params, MLX5E_PFLAG_TX_PORT_TS) ||
     chs->params.ptp_rx   ||
     chs->params.xdp_prog ||
     priv->htb) {
  netdev_err(priv->netdev,
      "Cloning channels with Port/rx PTP, XDP or HTB is not supported\n");
  err = -EOPNOTSUPP;
  goto unlock;
 }

 mdev = mlx5_sd_ch_ix_get_dev(priv->mdev, queue_index);
 err = mlx5e_build_channel_param(mdev, ¶ms, &new->cparam);
 if (err)
  goto unlock;

 err = mlx5e_open_channel(priv, queue_index, ¶ms, NULL, &new->c);
unlock:
 mutex_unlock(&priv->state_lock);
 return err;
}

static void mlx5e_queue_mem_free(struct net_device *dev, void *mem)
{
 struct mlx5_qmgmt_data *data = (struct mlx5_qmgmt_data *)mem;

 /* not supposed to happen since mlx5e_queue_start never fails
 * but this is how this should be implemented just in case
 */

 if (data->c)
  mlx5e_close_channel(data->c);
}

static int mlx5e_queue_stop(struct net_device *dev, void *oldq, int queue_index)
{
 /* In mlx5 a txq cannot be simply stopped in isolation, only restarted.
 * mlx5e_queue_start does not fail, we stop the old queue there.
 * TODO: Improve this.
 */

 return 0;
}

static int mlx5e_queue_start(struct net_device *dev, void *newq,
        int queue_index)
{
 struct mlx5_qmgmt_data *new = (struct mlx5_qmgmt_data *)newq;
 struct mlx5e_priv *priv = netdev_priv(dev);
 struct mlx5e_channel *old;

 mutex_lock(&priv->state_lock);

 /* stop and close the old */
 old = priv->channels.c[queue_index];
 mlx5e_deactivate_priv_channels(priv);
 /* close old before activating new, to avoid napi conflict */
 mlx5e_close_channel(old);

 /* start the new */
 priv->channels.c[queue_index] = new->c;
 mlx5e_activate_priv_channels(priv);
 mutex_unlock(&priv->state_lock);
 return 0;
}

static const struct netdev_queue_mgmt_ops mlx5e_queue_mgmt_ops = {
 .ndo_queue_mem_size = sizeof(struct mlx5_qmgmt_data),
 .ndo_queue_mem_alloc = mlx5e_queue_mem_alloc,
 .ndo_queue_mem_free = mlx5e_queue_mem_free,
 .ndo_queue_start = mlx5e_queue_start,
 .ndo_queue_stop  = mlx5e_queue_stop,
};

static void mlx5e_build_nic_netdev(struct net_device *netdev)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5_core_dev *mdev = priv->mdev;
 bool fcs_supported;
 bool fcs_enabled;

 SET_NETDEV_DEV(netdev, mdev->device);

 netdev->netdev_ops = &mlx5e_netdev_ops;
 netdev->queue_mgmt_ops = &mlx5e_queue_mgmt_ops;
 netdev->xdp_metadata_ops = &mlx5e_xdp_metadata_ops;
 netdev->xsk_tx_metadata_ops = &mlx5e_xsk_tx_metadata_ops;
 netdev->request_ops_lock = true;
 netdev_lockdep_set_classes(netdev);

 mlx5e_dcbnl_build_netdev(netdev);

 netdev->watchdog_timeo    = 15 * HZ;

 netdev->stat_ops   = &mlx5e_stat_ops;
 netdev->ethtool_ops   = &mlx5e_ethtool_ops;

 netdev->vlan_features    |= NETIF_F_SG;
 netdev->vlan_features    |= NETIF_F_HW_CSUM;
 netdev->vlan_features    |= NETIF_F_HW_MACSEC;
 netdev->vlan_features    |= NETIF_F_GRO;
 netdev->vlan_features    |= NETIF_F_TSO;
 netdev->vlan_features    |= NETIF_F_TSO6;
 netdev->vlan_features    |= NETIF_F_RXCSUM;
 netdev->vlan_features    |= NETIF_F_RXHASH;
 netdev->vlan_features    |= NETIF_F_GSO_PARTIAL;

 netdev->mpls_features    |= NETIF_F_SG;
 netdev->mpls_features    |= NETIF_F_HW_CSUM;
 netdev->mpls_features    |= NETIF_F_TSO;
 netdev->mpls_features    |= NETIF_F_TSO6;

 netdev->hw_enc_features  |= NETIF_F_HW_VLAN_CTAG_TX;
 netdev->hw_enc_features  |= NETIF_F_HW_VLAN_CTAG_RX;

 /* Tunneled LRO is not supported in the driver, and the same RQs are
 * shared between inner and outer TIRs, so the driver can't disable LRO
 * for inner TIRs while having it enabled for outer TIRs. Due to this,
 * block LRO altogether if the firmware declares tunneled LRO support.
 */

 if (!!MLX5_CAP_ETH(mdev, lro_cap) &&
     !MLX5_CAP_ETH(mdev, tunnel_lro_vxlan) &&
     !MLX5_CAP_ETH(mdev, tunnel_lro_gre) &&
     mlx5e_check_fragmented_striding_rq_cap(mdev, PAGE_SHIFT,
         MLX5E_MPWRQ_UMR_MODE_ALIGNED))
  netdev->vlan_features    |= NETIF_F_LRO;

 if (mlx5e_hw_gro_supported(mdev) &&
     mlx5e_check_fragmented_striding_rq_cap(mdev, PAGE_SHIFT,
         MLX5E_MPWRQ_UMR_MODE_ALIGNED))
  netdev->vlan_features |= NETIF_F_GRO_HW;

 netdev->hw_features       = netdev->vlan_features;
 netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_TX;
 netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_RX;
 netdev->hw_features      |= NETIF_F_HW_VLAN_CTAG_FILTER;
 netdev->hw_features      |= NETIF_F_HW_VLAN_STAG_TX;

 if (mlx5e_tunnel_any_tx_proto_supported(mdev)) {
  netdev->hw_enc_features |= NETIF_F_HW_CSUM;
  netdev->hw_enc_features |= NETIF_F_TSO;
  netdev->hw_enc_features |= NETIF_F_TSO6;
  netdev->hw_enc_features |= NETIF_F_GSO_PARTIAL;
 }

 if (mlx5_vxlan_allowed(mdev->vxlan) || mlx5_geneve_tx_allowed(mdev)) {
  netdev->hw_features     |= NETIF_F_GSO_UDP_TUNNEL |
        NETIF_F_GSO_UDP_TUNNEL_CSUM;
  netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL |
        NETIF_F_GSO_UDP_TUNNEL_CSUM;
  netdev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM;
  netdev->vlan_features |= NETIF_F_GSO_UDP_TUNNEL |
      NETIF_F_GSO_UDP_TUNNEL_CSUM;
 }

 if (mlx5e_tunnel_proto_supported_tx(mdev, IPPROTO_GRE)) {
  netdev->hw_features     |= NETIF_F_GSO_GRE |
        NETIF_F_GSO_GRE_CSUM;
  netdev->hw_enc_features |= NETIF_F_GSO_GRE |
        NETIF_F_GSO_GRE_CSUM;
  netdev->gso_partial_features |= NETIF_F_GSO_GRE |
      NETIF_F_GSO_GRE_CSUM;
 }

 if (mlx5e_tunnel_proto_supported_tx(mdev, IPPROTO_IPIP)) {
  netdev->hw_features |= NETIF_F_GSO_IPXIP4 |
           NETIF_F_GSO_IPXIP6;
  netdev->hw_enc_features |= NETIF_F_GSO_IPXIP4 |
        NETIF_F_GSO_IPXIP6;
  netdev->gso_partial_features |= NETIF_F_GSO_IPXIP4 |
      NETIF_F_GSO_IPXIP6;
 }

 netdev->gso_partial_features             |= NETIF_F_GSO_UDP_L4;
 netdev->hw_features                      |= NETIF_F_GSO_UDP_L4;

 mlx5_query_port_fcs(mdev, &fcs_supported, &fcs_enabled);

 if (fcs_supported)
  netdev->hw_features |= NETIF_F_RXALL;

 if (MLX5_CAP_ETH(mdev, scatter_fcs))
  netdev->hw_features |= NETIF_F_RXFCS;

 if (mlx5_qos_is_supported(mdev))
  netdev->hw_features |= NETIF_F_HW_TC;

 netdev->features          = netdev->hw_features;

 /* Defaults */
 if (fcs_enabled)
  netdev->features  &= ~NETIF_F_RXALL;
 netdev->features  &= ~NETIF_F_LRO;
 netdev->features  &= ~NETIF_F_GRO_HW;
 netdev->features  &= ~NETIF_F_RXFCS;

#define FT_CAP(f) MLX5_CAP_FLOWTABLE(mdev, flow_table_properties_nic_receive.f)
 if (FT_CAP(flow_modify_en) &&
     FT_CAP(modify_root) &&
     FT_CAP(identified_miss_table_mode) &&
     FT_CAP(flow_table_modify)) {
#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
  netdev->hw_features      |= NETIF_F_HW_TC;
#endif
#if IS_ENABLED(CONFIG_MLX5_EN_ARFS)
  netdev->hw_features  |= NETIF_F_NTUPLE;
#elif IS_ENABLED(CONFIG_MLX5_EN_RXNFC)
  netdev->features  |= NETIF_F_NTUPLE;
#endif
 }

 netdev->features         |= NETIF_F_HIGHDMA;
 netdev->features         |= NETIF_F_HW_VLAN_STAG_FILTER;

 netdev->priv_flags       |= IFF_UNICAST_FLT;

 netdev->netmem_tx = true;

 netif_set_tso_max_size(netdev, GSO_MAX_SIZE);
 mlx5e_set_xdp_feature(netdev);
 mlx5e_set_netdev_dev_addr(netdev);
 mlx5e_macsec_build_netdev(priv);
 mlx5e_ipsec_build_netdev(priv);
 mlx5e_ktls_build_netdev(priv);
}

void mlx5e_create_q_counters(struct mlx5e_priv *priv)
{
 u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {};
 u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {};
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5_core_dev *pos;
 int err, i;

 MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);

 mlx5_sd_for_each_dev(i, mdev, pos) {
  err = mlx5_cmd_exec_inout(pos, alloc_q_counter, in, out);
  if (!err)
   priv->q_counter[i] =
    MLX5_GET(alloc_q_counter_out, out, counter_set_id);
 }

 err = mlx5_cmd_exec_inout(mdev, alloc_q_counter, in, out);
 if (!err)
  priv->drop_rq_q_counter =
   MLX5_GET(alloc_q_counter_out, out, counter_set_id);
}

void mlx5e_destroy_q_counters(struct mlx5e_priv *priv)
{
 u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)] = {};
 struct mlx5_core_dev *pos;
 int i;

 MLX5_SET(dealloc_q_counter_in, in, opcode,
   MLX5_CMD_OP_DEALLOC_Q_COUNTER);
 mlx5_sd_for_each_dev(i, priv->mdev, pos) {
  if (priv->q_counter[i]) {
   MLX5_SET(dealloc_q_counter_in, in, counter_set_id,
     priv->q_counter[i]);
   mlx5_cmd_exec_in(pos, dealloc_q_counter, in);
  }
 }

 if (priv->drop_rq_q_counter) {
  MLX5_SET(dealloc_q_counter_in, in, counter_set_id,
    priv->drop_rq_q_counter);
  mlx5_cmd_exec_in(priv->mdev, dealloc_q_counter, in);
 }
}

static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
     struct net_device *netdev)
{
 const bool take_rtnl = netdev->reg_state == NETREG_REGISTERED;
 struct mlx5e_priv *priv = netdev_priv(netdev);
 struct mlx5e_flow_steering *fs;
 int err;

 mlx5e_build_nic_params(priv, &priv->xsk, netdev->mtu);
 mlx5e_vxlan_set_netdev_info(priv);

 mlx5e_timestamp_init(priv);

 priv->dfs_root = debugfs_create_dir("nic",
         mlx5_debugfs_get_dev_root(mdev));

 fs = mlx5e_fs_init(priv->profile, mdev,
      !test_bit(MLX5E_STATE_DESTROYING, &priv->state),
      priv->dfs_root);
 if (!fs) {
  err = -ENOMEM;
  mlx5_core_err(mdev, "FS initialization failed, %d\n", err);
  debugfs_remove_recursive(priv->dfs_root);
  return err;
 }
 priv->fs = fs;

 err = mlx5e_ktls_init(priv);
 if (err)
  mlx5_core_err(mdev, "TLS initialization failed, %d\n", err);

 mlx5e_health_create_reporters(priv);

 /* If netdev is already registered (e.g. move from uplink to nic profile),
 * RTNL lock must be held before triggering netdev notifiers.
 */

 if (take_rtnl)
  rtnl_lock();

 /* update XDP supported features */
 mlx5e_set_xdp_feature(netdev);

 if (take_rtnl)
  rtnl_unlock();

 return 0;
}

static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
{
 mlx5e_health_destroy_reporters(priv);
 mlx5e_ktls_cleanup(priv);
 mlx5e_fs_cleanup(priv->fs);
 debugfs_remove_recursive(priv->dfs_root);
 priv->fs = NULL;
}

static int mlx5e_init_nic_rx(struct mlx5e_priv *priv)
{
 struct mlx5_core_dev *mdev = priv->mdev;
 enum mlx5e_rx_res_features features;
 int err;

 mlx5e_create_q_counters(priv);

 err = mlx5e_open_drop_rq(priv, &priv->drop_rq);
 if (err) {
  mlx5_core_err(mdev, "open drop rq failed, %d\n", err);
  goto err_destroy_q_counters;
 }

 features = MLX5E_RX_RES_FEATURE_PTP;
 if (mlx5_tunnel_inner_ft_supported(mdev))
  features |= MLX5E_RX_RES_FEATURE_INNER_FT;
 if (mlx5_get_sd(priv->mdev))
  features |= MLX5E_RX_RES_FEATURE_MULTI_VHCA;

 priv->rx_res = mlx5e_rx_res_create(priv->mdev, features, priv->max_nch, priv->drop_rq.rqn,
        &priv->channels.params.packet_merge,
        priv->channels.params.num_channels);
 if (IS_ERR(priv->rx_res)) {
  err = PTR_ERR(priv->rx_res);
  priv->rx_res = NULL;
  mlx5_core_err(mdev, "create rx resources failed, %d\n", err);
  goto err_close_drop_rq;
 }

 err = mlx5e_create_flow_steering(priv->fs, priv->rx_res, priv->profile,
      priv->netdev);
 if (err) {
  mlx5_core_warn(mdev, "create flow steering failed, %d\n", err);
  goto err_destroy_rx_res;
 }

 err = mlx5e_tc_nic_init(priv);
 if (err)
  goto err_destroy_flow_steering;

 err = mlx5e_accel_init_rx(priv);
 if (err)
  goto err_tc_nic_cleanup;

#ifdef CONFIG_MLX5_EN_ARFS
 priv->netdev->rx_cpu_rmap =  mlx5_eq_table_get_rmap(priv->mdev);
#endif

 return 0;

err_tc_nic_cleanup:
 mlx5e_tc_nic_cleanup(priv);
err_destroy_flow_steering:
 mlx5e_destroy_flow_steering(priv->fs, mlx5e_fs_has_arfs(priv->netdev),
        priv->profile);
err_destroy_rx_res:
 mlx5e_rx_res_destroy(priv->rx_res);
 priv->rx_res = NULL;
err_close_drop_rq:
 mlx5e_close_drop_rq(&priv->drop_rq);
err_destroy_q_counters:
 mlx5e_destroy_q_counters(priv);
 return err;
}

static void mlx5e_cleanup_nic_rx(struct mlx5e_priv *priv)
{
 mlx5e_accel_cleanup_rx(priv);
 mlx5e_tc_nic_cleanup(priv);
 mlx5e_destroy_flow_steering(priv->fs, mlx5e_fs_has_arfs(priv->netdev),
        priv->profile);
 mlx5e_rx_res_destroy(priv->rx_res);
 priv->rx_res = NULL;
 mlx5e_close_drop_rq(&priv->drop_rq);
 mlx5e_destroy_q_counters(priv);
}

static void mlx5e_set_mqprio_rl(struct mlx5e_priv *priv)
{
 struct mlx5e_params *params;
 struct mlx5e_mqprio_rl *rl;

 params = &priv->channels.params;
 if (params->mqprio.mode != TC_MQPRIO_MODE_CHANNEL)
  return;

 rl = mlx5e_mqprio_rl_create(priv->mdev, params->mqprio.num_tc,
        params->mqprio.channel.max_rate);
 if (IS_ERR(rl))
  rl = NULL;
 priv->mqprio_rl = rl;
 mlx5e_mqprio_rl_update_params(params, rl);
}

static int mlx5e_init_nic_tx(struct mlx5e_priv *priv)
{
 int err;

 err = mlx5e_accel_init_tx(priv);
 if (err)
  return err;

 mlx5e_set_mqprio_rl(priv);
 mlx5e_dcbnl_initialize(priv);
 return 0;
}

static void mlx5e_nic_enable(struct mlx5e_priv *priv)
{
 struct net_device *netdev = priv->netdev;
 struct mlx5_core_dev *mdev = priv->mdev;
 int err;

 mlx5e_fs_init_l2_addr(priv->fs, netdev);
 mlx5e_ipsec_init(priv);

 err = mlx5e_macsec_init(priv);
 if (err)
  mlx5_core_err(mdev, "MACsec initialization failed, %d\n", err);

 /* Marking the link as currently not needed by the Driver */
 if (!netif_running(netdev))
  mlx5e_modify_admin_state(mdev, MLX5_PORT_DOWN);

 mlx5e_set_netdev_mtu_boundaries(priv);
 mlx5e_set_dev_port_mtu(priv);

 mlx5_lag_add_netdev(mdev, netdev);

 mlx5e_enable_async_events(priv);
 mlx5e_enable_blocking_events(priv);
 if (mlx5e_monitor_counter_supported(priv))
  mlx5e_monitor_counter_init(priv);

 mlx5e_pcie_cong_event_init(priv);
 mlx5e_hv_vhca_stats_create(priv);
 if (netdev->reg_state != NETREG_REGISTERED)
  return;
 mlx5e_dcbnl_init_app(priv);

 mlx5e_nic_set_rx_mode(priv);

 rtnl_lock();
 netdev_lock(netdev);
 if (netif_running(netdev))
  mlx5e_open(netdev);
 udp_tunnel_nic_reset_ntf(priv->netdev);
 netdev_unlock(netdev);
 netif_device_attach(netdev);
 rtnl_unlock();
}

static void mlx5e_nic_disable(struct mlx5e_priv *priv)
{
 struct mlx5_core_dev *mdev = priv->mdev;

 if (priv->netdev->reg_state == NETREG_REGISTERED)
  mlx5e_dcbnl_delete_app(priv);

 rtnl_lock();
 netdev_lock(priv->netdev);
 if (netif_running(priv->netdev))
  mlx5e_close(priv->netdev);
 netif_device_detach(priv->netdev);
 if (priv->en_trap) {
  mlx5e_deactivate_trap(priv);
  mlx5e_close_trap(priv->en_trap);
  priv->en_trap = NULL;
 }
 netdev_unlock(priv->netdev);
 rtnl_unlock();

 mlx5e_nic_set_rx_mode(priv);

 mlx5e_pcie_cong_event_cleanup(priv);
 mlx5e_hv_vhca_stats_destroy(priv);
 if (mlx5e_monitor_counter_supported(priv))
  mlx5e_monitor_counter_cleanup(priv);

 mlx5e_ipsec_disable_events(priv);
 mlx5e_disable_blocking_events(priv);
 mlx5e_disable_async_events(priv);
 mlx5_lag_remove_netdev(mdev, priv->netdev);
 mlx5_vxlan_reset_to_default(mdev->vxlan);
 mlx5e_macsec_cleanup(priv);
 mlx5e_ipsec_cleanup(priv);
}

static int mlx5e_update_nic_rx(struct mlx5e_priv *priv)
{
 return mlx5e_refresh_tirs(priv, falsefalse);
}

static const struct mlx5e_profile mlx5e_nic_profile = {
 .init     = mlx5e_nic_init,
 .cleanup    = mlx5e_nic_cleanup,
 .init_rx    = mlx5e_init_nic_rx,
 .cleanup_rx    = mlx5e_cleanup_nic_rx,
 .init_tx    = mlx5e_init_nic_tx,
 .cleanup_tx    = mlx5e_cleanup_nic_tx,
 .enable     = mlx5e_nic_enable,
 .disable    = mlx5e_nic_disable,
 .update_rx    = mlx5e_update_nic_rx,
 .update_stats    = mlx5e_stats_update_ndo_stats,
 .update_carrier    = mlx5e_update_carrier,
 .rx_handlers       = &mlx5e_rx_handlers_nic,
 .max_tc     = MLX5_MAX_NUM_TC,
 .stats_grps    = mlx5e_nic_stats_grps,
 .stats_grps_num    = mlx5e_nic_stats_grps_num,
 .features          = BIT(MLX5E_PROFILE_FEATURE_PTP_RX) |
  BIT(MLX5E_PROFILE_FEATURE_PTP_TX) |
  BIT(MLX5E_PROFILE_FEATURE_QOS_HTB) |
  BIT(MLX5E_PROFILE_FEATURE_FS_VLAN) |
  BIT(MLX5E_PROFILE_FEATURE_FS_TC),
};

static int mlx5e_profile_max_num_channels(struct mlx5_core_dev *mdev,
       const struct mlx5e_profile *profile)
{
 int nch;

 nch = mlx5e_get_max_num_channels(mdev);

 if (profile->max_nch_limit)
  nch = min_t(int, nch, profile->max_nch_limit(mdev));
 return nch;
}

static unsigned int
mlx5e_calc_max_nch(struct mlx5_core_dev *mdev, struct net_device *netdev,
     const struct mlx5e_profile *profile)

{
 unsigned int max_nch, tmp;

 /* core resources */
 max_nch = mlx5e_profile_max_num_channels(mdev, profile);

 /* netdev rx queues */
 max_nch = min_t(unsigned int, max_nch, netdev->num_rx_queues);

 /* netdev tx queues */
 tmp = netdev->num_tx_queues;
 if (mlx5_qos_is_supported(mdev))
  tmp -= mlx5e_qos_max_leaf_nodes(mdev);
 if (MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn))
  tmp -= profile->max_tc;
 tmp = tmp / profile->max_tc;
 max_nch = min_t(unsigned int, max_nch, tmp);

 return max_nch;
}

int mlx5e_get_pf_num_tirs(struct mlx5_core_dev *mdev)
{
 /* Indirect TIRS: 2 sets of TTCs (inner + outer steering)
 * and 1 set of direct TIRS
 */

 return 2 * MLX5E_NUM_INDIR_TIRS
  + mlx5e_profile_max_num_channels(mdev, &mlx5e_nic_profile);
}

void mlx5e_set_rx_mode_work(struct work_struct *work)
{
 struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
            set_rx_mode_work);

 return mlx5e_fs_set_rx_mode_work(priv->fs, priv->netdev);
}

/* mlx5e generic netdev management API (move to en_common.c) */
int mlx5e_priv_init(struct mlx5e_priv *priv,
      const struct mlx5e_profile *profile,
      struct net_device *netdev,
      struct mlx5_core_dev *mdev)
{
 int nch, num_txqs, node;
 int err;

 num_txqs = netdev->num_tx_queues;
 nch = mlx5e_calc_max_nch(mdev, netdev, profile);
 node = dev_to_node(mlx5_core_dma_dev(mdev));

 /* priv init */
 priv->mdev        = mdev;
 priv->netdev      = netdev;
 priv->max_nch     = nch;
 priv->max_opened_tc = 1;

 if (!alloc_cpumask_var(&priv->scratchpad.cpumask, GFP_KERNEL))
  return -ENOMEM;

 mutex_init(&priv->state_lock);

 err = mlx5e_selq_init(&priv->selq, &priv->state_lock);
 if (err)
  goto err_free_cpumask;

 INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work);
 INIT_WORK(&priv->set_rx_mode_work, mlx5e_set_rx_mode_work);
 INIT_WORK(&priv->tx_timeout_work, mlx5e_tx_timeout_work);
 INIT_WORK(&priv->update_stats_work, mlx5e_update_stats_work);

 priv->wq = create_singlethread_workqueue("mlx5e");
 if (!priv->wq)
  goto err_free_selq;

 priv->txq2sq = kcalloc_node(num_txqs, sizeof(*priv->txq2sq), GFP_KERNEL, node);
 if (!priv->txq2sq)
  goto err_destroy_workqueue;

 priv->txq2sq_stats = kcalloc_node(num_txqs, sizeof(*priv->txq2sq_stats), GFP_KERNEL, node);
 if (!priv->txq2sq_stats)
  goto err_free_txq2sq;

 priv->tx_rates = kcalloc_node(num_txqs, sizeof(*priv->tx_rates), GFP_KERNEL, node);
 if (!priv->tx_rates)
  goto err_free_txq2sq_stats;

 priv->channel_stats =
  kcalloc_node(nch, sizeof(*priv->channel_stats), GFP_KERNEL, node);
 if (!priv->channel_stats)
  goto err_free_tx_rates;

 return 0;

err_free_tx_rates:
 kfree(priv->tx_rates);
err_free_txq2sq_stats:
 kfree(priv->txq2sq_stats);
err_free_txq2sq:
 kfree(priv->txq2sq);
err_destroy_workqueue:
 destroy_workqueue(priv->wq);
err_free_selq:
 mlx5e_selq_cleanup(&priv->selq);
err_free_cpumask:
 free_cpumask_var(priv->scratchpad.cpumask);
 return -ENOMEM;
}

void mlx5e_priv_cleanup(struct mlx5e_priv *priv)
{
 int i;

 /* bail if change profile failed and also rollback failed */
 if (!priv->mdev)
  return;

 for (i = 0; i < priv->stats_nch; i++)
  kvfree(priv->channel_stats[i]);
 kfree(priv->channel_stats);
 kfree(priv->tx_rates);
 kfree(priv->txq2sq_stats);
 kfree(priv->txq2sq);
 destroy_workqueue(priv->wq);
 mlx5e_selq_cleanup(&priv->selq);
 free_cpumask_var(priv->scratchpad.cpumask);

 for (i = 0; i < priv->htb_max_qos_sqs; i++)
  kfree(priv->htb_qos_sq_stats[i]);
 kvfree(priv->htb_qos_sq_stats);

 if (priv->mqprio_rl) {
  mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
  mlx5e_mqprio_rl_free(priv->mqprio_rl);
 }

 memset(priv, 0, sizeof(*priv));
}

static unsigned int mlx5e_get_max_num_txqs(struct mlx5_core_dev *mdev,
        const struct mlx5e_profile *profile)
{
 unsigned int nch, ptp_txqs, qos_txqs;

 nch = mlx5e_profile_max_num_channels(mdev, profile);

 ptp_txqs = MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn) &&
  mlx5e_profile_feature_cap(profile, PTP_TX) ?
  profile->max_tc : 0;

 qos_txqs = mlx5_qos_is_supported(mdev) &&
  mlx5e_profile_feature_cap(profile, QOS_HTB) ?
  mlx5e_qos_max_leaf_nodes(mdev) : 0;

 return nch * profile->max_tc + ptp_txqs + qos_txqs;
}

static unsigned int mlx5e_get_max_num_rxqs(struct mlx5_core_dev *mdev,
        const struct mlx5e_profile *profile)
{
 return mlx5e_profile_max_num_channels(mdev, profile);
}

struct net_device *
mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile)
{
 struct net_device *netdev;
 unsigned int txqs, rxqs;
 int err;

 txqs = mlx5e_get_max_num_txqs(mdev, profile);
 rxqs = mlx5e_get_max_num_rxqs(mdev, profile);

 netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv), txqs, rxqs);
 if (!netdev) {
  mlx5_core_err(mdev, "alloc_etherdev_mqs() failed\n");
  return NULL;
 }

 err = mlx5e_priv_init(netdev_priv(netdev), profile, netdev, mdev);
 if (err) {
  mlx5_core_err(mdev, "mlx5e_priv_init failed, err=%d\n", err);
  goto err_free_netdev;
 }

 netif_carrier_off(netdev);
 netif_tx_disable(netdev);
 dev_net_set(netdev, mlx5_core_net(mdev));

 return netdev;

err_free_netdev:
 free_netdev(netdev);

 return NULL;
}

static void mlx5e_update_features(struct net_device *netdev)
{
 if (netdev->reg_state != NETREG_REGISTERED)
  return/* features will be updated on netdev registration */

 rtnl_lock();
 netdev_lock(netdev);
 netdev_update_features(netdev);
 netdev_unlock(netdev);
 rtnl_unlock();
}

static void mlx5e_reset_channels(struct net_device *netdev)
{
 netdev_reset_tc(netdev);
}

int mlx5e_attach_netdev(struct mlx5e_priv *priv)
{
 const bool need_lock = priv->netdev->reg_state == NETREG_REGISTERED;
 const struct mlx5e_profile *profile = priv->profile;
 int max_nch;
 int err;

 clear_bit(MLX5E_STATE_DESTROYING, &priv->state);
 if (priv->fs)
  mlx5e_fs_set_state_destroy(priv->fs,
        !test_bit(MLX5E_STATE_DESTROYING, &priv->state));

 /* Validate the max_wqe_size_sq capability. */
 if (WARN_ON_ONCE(mlx5e_get_max_sq_wqebbs(priv->mdev) < MLX5E_MAX_TX_WQEBBS)) {
  mlx5_core_warn(priv->mdev, "MLX5E: Max SQ WQEBBs firmware capability: %u, needed %u\n",
          mlx5e_get_max_sq_wqebbs(priv->mdev), (unsigned int)MLX5E_MAX_TX_WQEBBS);
  return -EIO;
 }

 /* max number of channels may have changed */
 max_nch = mlx5e_calc_max_nch(priv->mdev, priv->netdev, profile);
 if (priv->channels.params.num_channels > max_nch) {
  mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n", max_nch);
  /* Reducing the number of channels - RXFH has to be reset, and
 * mlx5e_num_channels_changed below will build the RQT.
 */

  priv->netdev->priv_flags &= ~IFF_RXFH_CONFIGURED;
  priv->channels.params.num_channels = max_nch;
  if (priv->channels.params.mqprio.mode == TC_MQPRIO_MODE_CHANNEL) {
   mlx5_core_warn(priv->mdev, "MLX5E: Disabling MQPRIO channel mode\n");
   mlx5e_params_mqprio_reset(&priv->channels.params);
  }
 }
 if (max_nch != priv->max_nch) {
  mlx5_core_warn(priv->mdev,
          "MLX5E: Updating max number of channels from %u to %u\n",
          priv->max_nch, max_nch);
  priv->max_nch = max_nch;
 }

 /* 1. Set the real number of queues in the kernel the first time.
 * 2. Set our default XPS cpumask.
 * 3. Build the RQT.
 *
 * Locking is required by netif_set_real_num_*_queues in case the
 * netdev has been registered by this point (if this function was called
 * in the reload or resume flow).
 */

 if (need_lock) {
  rtnl_lock();
  netdev_lock(priv->netdev);
 }
 err = mlx5e_num_channels_changed(priv);
 if (need_lock) {
  netdev_unlock(priv->netdev);
  rtnl_unlock();
 }
 if (err)
  goto out;

 err = profile->init_tx(priv);
 if (err)
  goto out;

 err = profile->init_rx(priv);
 if (err)
  goto err_cleanup_tx;

 if (profile->enable)
  profile->enable(priv);

 mlx5e_update_features(priv->netdev);

 return 0;

err_cleanup_tx:
 profile->cleanup_tx(priv);

out:
 mlx5e_reset_channels(priv->netdev);
 set_bit(MLX5E_STATE_DESTROYING, &priv->state);
 if (priv->fs)
  mlx5e_fs_set_state_destroy(priv->fs,
        !test_bit(MLX5E_STATE_DESTROYING, &priv->state));
 cancel_work_sync(&priv->update_stats_work);
 return err;
}

void mlx5e_detach_netdev(struct mlx5e_priv *priv)
{
 const struct mlx5e_profile *profile = priv->profile;

 set_bit(MLX5E_STATE_DESTROYING, &priv->state);
 if (priv->fs)
  mlx5e_fs_set_state_destroy(priv->fs,
        !test_bit(MLX5E_STATE_DESTROYING, &priv->state));

 if (profile->disable)
  profile->disable(priv);
 flush_workqueue(priv->wq);

 profile->cleanup_rx(priv);
 profile->cleanup_tx(priv);
 mlx5e_reset_channels(priv->netdev);
 cancel_work_sync(&priv->update_stats_work);
}

static int
mlx5e_netdev_init_profile(struct net_device *netdev, struct mlx5_core_dev *mdev,
     const struct mlx5e_profile *new_profile, void *new_ppriv)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

 err = mlx5e_priv_init(priv, new_profile, netdev, mdev);
 if (err) {
  mlx5_core_err(mdev, "mlx5e_priv_init failed, err=%d\n", err);
  return err;
 }
 netif_carrier_off(netdev);
 priv->profile = new_profile;
 priv->ppriv = new_ppriv;
 err = new_profile->init(priv->mdev, priv->netdev);
 if (err)
  goto priv_cleanup;

 return 0;

priv_cleanup:
 mlx5e_priv_cleanup(priv);
 return err;
}

static int
mlx5e_netdev_attach_profile(struct net_device *netdev, struct mlx5_core_dev *mdev,
       const struct mlx5e_profile *new_profile, void *new_ppriv)
{
 struct mlx5e_priv *priv = netdev_priv(netdev);
 int err;

 err = mlx5e_netdev_init_profile(netdev, mdev, new_profile, new_ppriv);
 if (err)
  return err;

 err = mlx5e_attach_netdev(priv);
 if (err)
  goto profile_cleanup;
 return err;

profile_cleanup:
 new_profile->cleanup(priv);
 mlx5e_priv_cleanup(priv);
 return err;
}

int mlx5e_netdev_change_profile(struct mlx5e_priv *priv,
    const struct mlx5e_profile *new_profile, void *new_ppriv)
{
 const struct mlx5e_profile *orig_profile = priv->profile;
 struct net_device *netdev = priv->netdev;
 struct mlx5_core_dev *mdev = priv->mdev;
 void *orig_ppriv = priv->ppriv;
 int err, rollback_err;

 /* cleanup old profile */
 mlx5e_detach_netdev(priv);
 priv->profile->cleanup(priv);
 mlx5e_priv_cleanup(priv);

 if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
  mlx5e_netdev_init_profile(netdev, mdev, new_profile, new_ppriv);
  set_bit(MLX5E_STATE_DESTROYING, &priv->state);
  return -EIO;
 }

 err = mlx5e_netdev_attach_profile(netdev, mdev, new_profile, new_ppriv);
 if (err) { /* roll back to original profile */
  netdev_warn(netdev, "%s: new profile init failed, %d\n", __func__, err);
  goto rollback;
 }

 return 0;

rollback:
 rollback_err = mlx5e_netdev_attach_profile(netdev, mdev, orig_profile, orig_ppriv);
 if (rollback_err)
  netdev_err(netdev, "%s: failed to rollback to orig profile, %d\n",
      __func__, rollback_err);
 return err;
}

void mlx5e_netdev_attach_nic_profile(struct mlx5e_priv *priv)
{
 mlx5e_netdev_change_profile(priv, &mlx5e_nic_profile, NULL);
}

void mlx5e_destroy_netdev(struct mlx5e_priv *priv)
{
 struct net_device *netdev = priv->netdev;

 mlx5e_priv_cleanup(priv);
 free_netdev(netdev);
}

static int _mlx5e_resume(struct auxiliary_device *adev)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5e_dev *mlx5e_dev = auxiliary_get_drvdata(adev);
 struct mlx5e_priv *priv = mlx5e_dev->priv;
 struct net_device *netdev = priv->netdev;
 struct mlx5_core_dev *mdev = edev->mdev;
 struct mlx5_core_dev *pos, *to;
 int err, i;

 if (netif_device_present(netdev))
  return 0;

 mlx5_sd_for_each_dev(i, mdev, pos) {
  err = mlx5e_create_mdev_resources(pos, true);
  if (err)
   goto err_destroy_mdev_res;
 }

 err = mlx5e_attach_netdev(priv);
 if (err)
  goto err_destroy_mdev_res;

 return 0;

err_destroy_mdev_res:
 to = pos;
 mlx5_sd_for_each_dev_to(i, mdev, to, pos)
  mlx5e_destroy_mdev_resources(pos);
 return err;
}

static int mlx5e_resume(struct auxiliary_device *adev)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5_core_dev *mdev = edev->mdev;
 struct auxiliary_device *actual_adev;
 int err;

 err = mlx5_sd_init(mdev);
 if (err)
  return err;

 actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
 if (actual_adev)
  return _mlx5e_resume(actual_adev);
 return 0;
}

static int _mlx5e_suspend(struct auxiliary_device *adev, bool pre_netdev_reg)
{
 struct mlx5e_dev *mlx5e_dev = auxiliary_get_drvdata(adev);
 struct mlx5e_priv *priv = mlx5e_dev->priv;
 struct net_device *netdev = priv->netdev;
 struct mlx5_core_dev *mdev = priv->mdev;
 struct mlx5_core_dev *pos;
 int i;

 if (!pre_netdev_reg && !netif_device_present(netdev)) {
  if (test_bit(MLX5E_STATE_DESTROYING, &priv->state))
   mlx5_sd_for_each_dev(i, mdev, pos)
    mlx5e_destroy_mdev_resources(pos);
  return -ENODEV;
 }

 mlx5e_detach_netdev(priv);
 mlx5_sd_for_each_dev(i, mdev, pos)
  mlx5e_destroy_mdev_resources(pos);

 return 0;
}

static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5_core_dev *mdev = edev->mdev;
 struct auxiliary_device *actual_adev;
 int err = 0;

 actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
 if (actual_adev)
  err = _mlx5e_suspend(actual_adev, false);

 mlx5_sd_cleanup(mdev);
 return err;
}

static int _mlx5e_probe(struct auxiliary_device *adev)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 const struct mlx5e_profile *profile = &mlx5e_nic_profile;
 struct mlx5_core_dev *mdev = edev->mdev;
 struct mlx5e_dev *mlx5e_dev;
 struct net_device *netdev;
 struct mlx5e_priv *priv;
 int err;

 mlx5e_dev = mlx5e_create_devlink(&adev->dev, mdev);
 if (IS_ERR(mlx5e_dev))
  return PTR_ERR(mlx5e_dev);
 auxiliary_set_drvdata(adev, mlx5e_dev);

 err = mlx5e_devlink_port_register(mlx5e_dev, mdev);
 if (err) {
  mlx5_core_err(mdev, "mlx5e_devlink_port_register failed, %d\n", err);
  goto err_devlink_unregister;
 }

 netdev = mlx5e_create_netdev(mdev, profile);
 if (!netdev) {
  mlx5_core_err(mdev, "mlx5e_create_netdev failed\n");
  err = -ENOMEM;
  goto err_devlink_port_unregister;
 }
 SET_NETDEV_DEVLINK_PORT(netdev, &mlx5e_dev->dl_port);

 mlx5e_build_nic_netdev(netdev);

 priv = netdev_priv(netdev);
 mlx5e_dev->priv = priv;

 priv->profile = profile;
 priv->ppriv = NULL;

 err = profile->init(mdev, netdev);
 if (err) {
  mlx5_core_err(mdev, "mlx5e_nic_profile init failed, %d\n", err);
  goto err_destroy_netdev;
 }

 err = _mlx5e_resume(adev);
 if (err) {
  mlx5_core_err(mdev, "_mlx5e_resume failed, %d\n", err);
  goto err_profile_cleanup;
 }

 err = register_netdev(netdev);
 if (err) {
  mlx5_core_err(mdev, "register_netdev failed, %d\n", err);
  goto err_resume;
 }

 mlx5e_dcbnl_init_app(priv);
 mlx5_core_uplink_netdev_set(mdev, netdev);
 mlx5e_params_print_info(mdev, &priv->channels.params);
 return 0;

err_resume:
 _mlx5e_suspend(adev, true);
err_profile_cleanup:
 profile->cleanup(priv);
err_destroy_netdev:
 mlx5e_destroy_netdev(priv);
err_devlink_port_unregister:
 mlx5e_devlink_port_unregister(mlx5e_dev);
err_devlink_unregister:
 mlx5e_destroy_devlink(mlx5e_dev);
 return err;
}

static int mlx5e_probe(struct auxiliary_device *adev,
         const struct auxiliary_device_id *id)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5_core_dev *mdev = edev->mdev;
 struct auxiliary_device *actual_adev;
 int err;

 err = mlx5_sd_init(mdev);
 if (err)
  return err;

 actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
 if (actual_adev)
  return _mlx5e_probe(actual_adev);
 return 0;
}

static void _mlx5e_remove(struct auxiliary_device *adev)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5e_dev *mlx5e_dev = auxiliary_get_drvdata(adev);
 struct mlx5e_priv *priv = mlx5e_dev->priv;
 struct mlx5_core_dev *mdev = edev->mdev;

 mlx5_core_uplink_netdev_set(mdev, NULL);
 mlx5e_dcbnl_delete_app(priv);
 /* When unload driver, the netdev is in registered state
 * if it's from legacy mode. If from switchdev mode, it
 * is already unregistered before changing to NIC profile.
 */

 if (priv->netdev->reg_state == NETREG_REGISTERED) {
  unregister_netdev(priv->netdev);
  _mlx5e_suspend(adev, false);
 } else {
  struct mlx5_core_dev *pos;
  int i;

  if (test_bit(MLX5E_STATE_DESTROYING, &priv->state))
   mlx5_sd_for_each_dev(i, mdev, pos)
    mlx5e_destroy_mdev_resources(pos);
  else
   _mlx5e_suspend(adev, true);
 }
 /* Avoid cleanup if profile rollback failed. */
 if (priv->profile)
  priv->profile->cleanup(priv);
 mlx5e_destroy_netdev(priv);
 mlx5e_devlink_port_unregister(mlx5e_dev);
 mlx5e_destroy_devlink(mlx5e_dev);
}

static void mlx5e_remove(struct auxiliary_device *adev)
{
 struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
 struct mlx5_core_dev *mdev = edev->mdev;
 struct auxiliary_device *actual_adev;

 actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
 if (actual_adev)
  _mlx5e_remove(actual_adev);

 mlx5_sd_cleanup(mdev);
}

static const struct auxiliary_device_id mlx5e_id_table[] = {
 { .name = MLX5_ADEV_NAME ".eth", },
 {},
};

MODULE_DEVICE_TABLE(auxiliary, mlx5e_id_table);

static struct auxiliary_driver mlx5e_driver = {
 .name = "eth",
 .probe = mlx5e_probe,
 .remove = mlx5e_remove,
 .suspend = mlx5e_suspend,
 .resume = mlx5e_resume,
 .id_table = mlx5e_id_table,
};

int mlx5e_init(void)
{
 int ret;

 mlx5e_build_ptys2ethtool_map();
 ret = auxiliary_driver_register(&mlx5e_driver);
 if (ret)
  return ret;

 ret = mlx5e_rep_init();
 if (ret)
  auxiliary_driver_unregister(&mlx5e_driver);
 return ret;
}

void mlx5e_cleanup(void)
{
 mlx5e_rep_cleanup();
 auxiliary_driver_unregister(&mlx5e_driver);
}

Messung V0.5 in Prozent
C=94 H=90 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.113Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-25) ¤

*Eine klare Vorstellung vom Zielzustand






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.