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  

Quelle  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,
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.31 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.