Merge branch 'mlx5e-xdp'
Tariq Toukan says:
====================
mlx5e XDP support
This series adds XDP support in mlx5e driver.
This includes the use cases: XDP_DROP, XDP_PASS, and XDP_TX.
Single stream performance tests show 16.5 Mpps for XDP_DROP,
and 12.4 Mpps for XDP_TX, with nice scalability for multiple streams/rings.
This rate of XDP_DROP is lower than the 32 Mpps we got in previous
implementation, when Striding RQ was used.
We moved to non-Striding RQ, as some XDP_TX requirements (like headroom,
packet-per-page) cannot be satisfied with the current Striding RQ HW,
and we decided to fully support both DROP/TX.
Few directions are considered in order to enable the faster rate for XDP_DROP,
e.g a possibility for users to enable Striding RQ so they choose optimized
XDP_DROP on the price of partial XDP_TX functionality, or some HW changes.
Series generated against net-next commit:
cf714ac147
'ipvlan: Fix dependency issue'
Thanks,
Tariq
V2:
* patch 8:
- when XDP_TX fails, call mlx5e_page_release and drop the packet.
- update xdp_tx counter within mlx5e_xmit_xdp_frame.
(mlx5e_xmit_xdp_frame return value becomes obsolete, change it to void)
- drop the packet for unknown XDP return code.
* patch 9:
- use a boolean for xdp_doorbell in SQ struct, instead of dragging it
throughout the functions calls.
- handle doorbell and counters within mlx5e_xmit_xdp_frame.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b80b8d7a97
|
@ -65,6 +65,8 @@
|
|||
#define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW 0x3
|
||||
#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW 0x6
|
||||
|
||||
#define MLX5_RX_HEADROOM NET_SKB_PAD
|
||||
|
||||
#define MLX5_MPWRQ_LOG_STRIDE_SIZE 6 /* >= 6, HW restriction */
|
||||
#define MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS 8 /* >= 6, HW restriction */
|
||||
#define MLX5_MPWRQ_LOG_WQE_SZ 18
|
||||
|
@ -99,6 +101,18 @@
|
|||
#define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */
|
||||
#define MLX5E_SQ_BF_BUDGET 16
|
||||
|
||||
#define MLX5E_ICOSQ_MAX_WQEBBS \
|
||||
(DIV_ROUND_UP(sizeof(struct mlx5e_umr_wqe), MLX5_SEND_WQE_BB))
|
||||
|
||||
#define MLX5E_XDP_MIN_INLINE (ETH_HLEN + VLAN_HLEN)
|
||||
#define MLX5E_XDP_IHS_DS_COUNT \
|
||||
DIV_ROUND_UP(MLX5E_XDP_MIN_INLINE - 2, MLX5_SEND_WQE_DS)
|
||||
#define MLX5E_XDP_TX_DS_COUNT \
|
||||
(MLX5E_XDP_IHS_DS_COUNT + \
|
||||
(sizeof(struct mlx5e_tx_wqe) / MLX5_SEND_WQE_DS) + 1 /* SG DS */)
|
||||
#define MLX5E_XDP_TX_WQEBBS \
|
||||
DIV_ROUND_UP(MLX5E_XDP_TX_DS_COUNT, MLX5_SEND_WQEBB_NUM_DS)
|
||||
|
||||
#define MLX5E_NUM_MAIN_GROUPS 9
|
||||
|
||||
static inline u16 mlx5_min_rx_wqes(int wq_type, u32 wq_size)
|
||||
|
@ -302,10 +316,20 @@ struct mlx5e_page_cache {
|
|||
struct mlx5e_rq {
|
||||
/* data path */
|
||||
struct mlx5_wq_ll wq;
|
||||
u32 wqe_sz;
|
||||
struct sk_buff **skb;
|
||||
struct mlx5e_mpw_info *wqe_info;
|
||||
void *mtt_no_align;
|
||||
|
||||
union {
|
||||
struct mlx5e_dma_info *dma_info;
|
||||
struct {
|
||||
struct mlx5e_mpw_info *info;
|
||||
void *mtt_no_align;
|
||||
u32 mtt_offset;
|
||||
} mpwqe;
|
||||
};
|
||||
struct {
|
||||
u8 page_order;
|
||||
u32 wqe_sz; /* wqe data buffer size */
|
||||
u8 map_dir; /* dma map direction */
|
||||
} buff;
|
||||
__be32 mkey_be;
|
||||
|
||||
struct device *pdev;
|
||||
|
@ -321,9 +345,9 @@ struct mlx5e_rq {
|
|||
|
||||
unsigned long state;
|
||||
int ix;
|
||||
u32 mpwqe_mtt_offset;
|
||||
|
||||
struct mlx5e_rx_am am; /* Adaptive Moderation */
|
||||
struct bpf_prog *xdp_prog;
|
||||
|
||||
/* control */
|
||||
struct mlx5_wq_ctrl wq_ctrl;
|
||||
|
@ -370,11 +394,17 @@ enum {
|
|||
MLX5E_SQ_STATE_BF_ENABLE,
|
||||
};
|
||||
|
||||
struct mlx5e_ico_wqe_info {
|
||||
struct mlx5e_sq_wqe_info {
|
||||
u8 opcode;
|
||||
u8 num_wqebbs;
|
||||
};
|
||||
|
||||
enum mlx5e_sq_type {
|
||||
MLX5E_SQ_TXQ,
|
||||
MLX5E_SQ_ICO,
|
||||
MLX5E_SQ_XDP
|
||||
};
|
||||
|
||||
struct mlx5e_sq {
|
||||
/* data path */
|
||||
|
||||
|
@ -392,10 +422,20 @@ struct mlx5e_sq {
|
|||
|
||||
struct mlx5e_cq cq;
|
||||
|
||||
/* pointers to per packet info: write@xmit, read@completion */
|
||||
struct sk_buff **skb;
|
||||
struct mlx5e_sq_dma *dma_fifo;
|
||||
struct mlx5e_tx_wqe_info *wqe_info;
|
||||
/* pointers to per tx element info: write@xmit, read@completion */
|
||||
union {
|
||||
struct {
|
||||
struct sk_buff **skb;
|
||||
struct mlx5e_sq_dma *dma_fifo;
|
||||
struct mlx5e_tx_wqe_info *wqe_info;
|
||||
} txq;
|
||||
struct mlx5e_sq_wqe_info *ico_wqe;
|
||||
struct {
|
||||
struct mlx5e_sq_wqe_info *wqe_info;
|
||||
struct mlx5e_dma_info *di;
|
||||
bool doorbell;
|
||||
} xdp;
|
||||
} db;
|
||||
|
||||
/* read only */
|
||||
struct mlx5_wq_cyc wq;
|
||||
|
@ -417,8 +457,8 @@ struct mlx5e_sq {
|
|||
struct mlx5_uar uar;
|
||||
struct mlx5e_channel *channel;
|
||||
int tc;
|
||||
struct mlx5e_ico_wqe_info *ico_wqe_info;
|
||||
u32 rate_limit;
|
||||
u8 type;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
static inline bool mlx5e_sq_has_room_for(struct mlx5e_sq *sq, u16 n)
|
||||
|
@ -434,8 +474,10 @@ enum channel_flags {
|
|||
struct mlx5e_channel {
|
||||
/* data path */
|
||||
struct mlx5e_rq rq;
|
||||
struct mlx5e_sq xdp_sq;
|
||||
struct mlx5e_sq sq[MLX5E_MAX_NUM_TC];
|
||||
struct mlx5e_sq icosq; /* internal control operations */
|
||||
bool xdp;
|
||||
struct napi_struct napi;
|
||||
struct device *pdev;
|
||||
struct net_device *netdev;
|
||||
|
@ -617,6 +659,7 @@ struct mlx5e_priv {
|
|||
/* priv data path fields - start */
|
||||
struct mlx5e_sq **txq_to_sq_map;
|
||||
int channeltc_to_txq_map[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC];
|
||||
struct bpf_prog *xdp_prog;
|
||||
/* priv data path fields - end */
|
||||
|
||||
unsigned long state;
|
||||
|
@ -663,7 +706,7 @@ void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event);
|
|||
int mlx5e_napi_poll(struct napi_struct *napi, int budget);
|
||||
bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget);
|
||||
int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
|
||||
void mlx5e_free_tx_descs(struct mlx5e_sq *sq);
|
||||
void mlx5e_free_sq_descs(struct mlx5e_sq *sq);
|
||||
|
||||
void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
|
||||
bool recycle);
|
||||
|
@ -764,7 +807,7 @@ static inline void mlx5e_cq_arm(struct mlx5e_cq *cq)
|
|||
|
||||
static inline u32 mlx5e_get_wqe_mtt_offset(struct mlx5e_rq *rq, u16 wqe_ix)
|
||||
{
|
||||
return rq->mpwqe_mtt_offset +
|
||||
return rq->mpwqe.mtt_offset +
|
||||
wqe_ix * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <net/pkt_cls.h>
|
||||
#include <linux/mlx5/fs.h>
|
||||
#include <net/vxlan.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "en.h"
|
||||
#include "en_tc.h"
|
||||
#include "eswitch.h"
|
||||
|
@ -50,7 +51,7 @@ struct mlx5e_sq_param {
|
|||
struct mlx5_wq_param wq;
|
||||
u16 max_inline;
|
||||
u8 min_inline_mode;
|
||||
bool icosq;
|
||||
enum mlx5e_sq_type type;
|
||||
};
|
||||
|
||||
struct mlx5e_cq_param {
|
||||
|
@ -63,12 +64,55 @@ struct mlx5e_cq_param {
|
|||
struct mlx5e_channel_param {
|
||||
struct mlx5e_rq_param rq;
|
||||
struct mlx5e_sq_param sq;
|
||||
struct mlx5e_sq_param xdp_sq;
|
||||
struct mlx5e_sq_param icosq;
|
||||
struct mlx5e_cq_param rx_cq;
|
||||
struct mlx5e_cq_param tx_cq;
|
||||
struct mlx5e_cq_param icosq_cq;
|
||||
};
|
||||
|
||||
static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
|
||||
{
|
||||
return MLX5_CAP_GEN(mdev, striding_rq) &&
|
||||
MLX5_CAP_GEN(mdev, umr_ptr_rlky) &&
|
||||
MLX5_CAP_ETH(mdev, reg_umr_sq);
|
||||
}
|
||||
|
||||
static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type)
|
||||
{
|
||||
priv->params.rq_wq_type = rq_type;
|
||||
switch (priv->params.rq_wq_type) {
|
||||
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
|
||||
priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW;
|
||||
priv->params.mpwqe_log_stride_sz = priv->params.rx_cqe_compress ?
|
||||
MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS :
|
||||
MLX5_MPWRQ_LOG_STRIDE_SIZE;
|
||||
priv->params.mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ -
|
||||
priv->params.mpwqe_log_stride_sz;
|
||||
break;
|
||||
default: /* MLX5_WQ_TYPE_LINKED_LIST */
|
||||
priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE;
|
||||
}
|
||||
priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type,
|
||||
BIT(priv->params.log_rq_size));
|
||||
|
||||
mlx5_core_info(priv->mdev,
|
||||
"MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n",
|
||||
priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ,
|
||||
BIT(priv->params.log_rq_size),
|
||||
BIT(priv->params.mpwqe_log_stride_sz),
|
||||
priv->params.rx_cqe_compress_admin);
|
||||
}
|
||||
|
||||
static void mlx5e_set_rq_priv_params(struct mlx5e_priv *priv)
|
||||
{
|
||||
u8 rq_type = mlx5e_check_fragmented_striding_rq_cap(priv->mdev) &&
|
||||
!priv->xdp_prog ?
|
||||
MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ :
|
||||
MLX5_WQ_TYPE_LINKED_LIST;
|
||||
mlx5e_set_rq_type_params(priv, rq_type);
|
||||
}
|
||||
|
||||
static void mlx5e_update_carrier(struct mlx5e_priv *priv)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
|
@ -136,6 +180,9 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv)
|
|||
s->rx_csum_none += rq_stats->csum_none;
|
||||
s->rx_csum_complete += rq_stats->csum_complete;
|
||||
s->rx_csum_unnecessary_inner += rq_stats->csum_unnecessary_inner;
|
||||
s->rx_xdp_drop += rq_stats->xdp_drop;
|
||||
s->rx_xdp_tx += rq_stats->xdp_tx;
|
||||
s->rx_xdp_tx_full += rq_stats->xdp_tx_full;
|
||||
s->rx_wqe_err += rq_stats->wqe_err;
|
||||
s->rx_mpwqe_filler += rq_stats->mpwqe_filler;
|
||||
s->rx_buff_alloc_err += rq_stats->buff_alloc_err;
|
||||
|
@ -314,7 +361,7 @@ static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq, struct mlx5e_sq *sq,
|
|||
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
|
||||
struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->uctrl;
|
||||
struct mlx5_wqe_data_seg *dseg = &wqe->data;
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[ix];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
|
||||
u8 ds_cnt = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_DS);
|
||||
u32 umr_wqe_mtt_offset = mlx5e_get_wqe_mtt_offset(rq, ix);
|
||||
|
||||
|
@ -342,21 +389,21 @@ static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq,
|
|||
int mtt_alloc = mtt_sz + MLX5_UMR_ALIGN - 1;
|
||||
int i;
|
||||
|
||||
rq->wqe_info = kzalloc_node(wq_sz * sizeof(*rq->wqe_info),
|
||||
GFP_KERNEL, cpu_to_node(c->cpu));
|
||||
if (!rq->wqe_info)
|
||||
rq->mpwqe.info = kzalloc_node(wq_sz * sizeof(*rq->mpwqe.info),
|
||||
GFP_KERNEL, cpu_to_node(c->cpu));
|
||||
if (!rq->mpwqe.info)
|
||||
goto err_out;
|
||||
|
||||
/* We allocate more than mtt_sz as we will align the pointer */
|
||||
rq->mtt_no_align = kzalloc_node(mtt_alloc * wq_sz, GFP_KERNEL,
|
||||
rq->mpwqe.mtt_no_align = kzalloc_node(mtt_alloc * wq_sz, GFP_KERNEL,
|
||||
cpu_to_node(c->cpu));
|
||||
if (unlikely(!rq->mtt_no_align))
|
||||
if (unlikely(!rq->mpwqe.mtt_no_align))
|
||||
goto err_free_wqe_info;
|
||||
|
||||
for (i = 0; i < wq_sz; i++) {
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[i];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
|
||||
|
||||
wi->umr.mtt = PTR_ALIGN(rq->mtt_no_align + i * mtt_alloc,
|
||||
wi->umr.mtt = PTR_ALIGN(rq->mpwqe.mtt_no_align + i * mtt_alloc,
|
||||
MLX5_UMR_ALIGN);
|
||||
wi->umr.mtt_addr = dma_map_single(c->pdev, wi->umr.mtt, mtt_sz,
|
||||
PCI_DMA_TODEVICE);
|
||||
|
@ -370,14 +417,14 @@ static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq,
|
|||
|
||||
err_unmap_mtts:
|
||||
while (--i >= 0) {
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[i];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
|
||||
|
||||
dma_unmap_single(c->pdev, wi->umr.mtt_addr, mtt_sz,
|
||||
PCI_DMA_TODEVICE);
|
||||
}
|
||||
kfree(rq->mtt_no_align);
|
||||
kfree(rq->mpwqe.mtt_no_align);
|
||||
err_free_wqe_info:
|
||||
kfree(rq->wqe_info);
|
||||
kfree(rq->mpwqe.info);
|
||||
|
||||
err_out:
|
||||
return -ENOMEM;
|
||||
|
@ -390,13 +437,13 @@ static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < wq_sz; i++) {
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[i];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
|
||||
|
||||
dma_unmap_single(rq->pdev, wi->umr.mtt_addr, mtt_sz,
|
||||
PCI_DMA_TODEVICE);
|
||||
}
|
||||
kfree(rq->mtt_no_align);
|
||||
kfree(rq->wqe_info);
|
||||
kfree(rq->mpwqe.mtt_no_align);
|
||||
kfree(rq->mpwqe.info);
|
||||
}
|
||||
|
||||
static int mlx5e_create_rq(struct mlx5e_channel *c,
|
||||
|
@ -408,6 +455,8 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
|
|||
void *rqc = param->rqc;
|
||||
void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq);
|
||||
u32 byte_count;
|
||||
u32 frag_sz;
|
||||
int npages;
|
||||
int wq_sz;
|
||||
int err;
|
||||
int i;
|
||||
|
@ -430,6 +479,11 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
|
|||
rq->channel = c;
|
||||
rq->ix = c->ix;
|
||||
rq->priv = c->priv;
|
||||
rq->xdp_prog = priv->xdp_prog;
|
||||
|
||||
rq->buff.map_dir = DMA_FROM_DEVICE;
|
||||
if (rq->xdp_prog)
|
||||
rq->buff.map_dir = DMA_BIDIRECTIONAL;
|
||||
|
||||
switch (priv->params.rq_wq_type) {
|
||||
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
|
||||
|
@ -437,34 +491,45 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
|
|||
rq->alloc_wqe = mlx5e_alloc_rx_mpwqe;
|
||||
rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe;
|
||||
|
||||
rq->mpwqe_mtt_offset = c->ix *
|
||||
rq->mpwqe.mtt_offset = c->ix *
|
||||
MLX5E_REQUIRED_MTTS(1, BIT(priv->params.log_rq_size));
|
||||
|
||||
rq->mpwqe_stride_sz = BIT(priv->params.mpwqe_log_stride_sz);
|
||||
rq->mpwqe_num_strides = BIT(priv->params.mpwqe_log_num_strides);
|
||||
rq->wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides;
|
||||
byte_count = rq->wqe_sz;
|
||||
|
||||
rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides;
|
||||
byte_count = rq->buff.wqe_sz;
|
||||
rq->mkey_be = cpu_to_be32(c->priv->umr_mkey.key);
|
||||
err = mlx5e_rq_alloc_mpwqe_info(rq, c);
|
||||
if (err)
|
||||
goto err_rq_wq_destroy;
|
||||
break;
|
||||
default: /* MLX5_WQ_TYPE_LINKED_LIST */
|
||||
rq->skb = kzalloc_node(wq_sz * sizeof(*rq->skb), GFP_KERNEL,
|
||||
cpu_to_node(c->cpu));
|
||||
if (!rq->skb) {
|
||||
rq->dma_info = kzalloc_node(wq_sz * sizeof(*rq->dma_info),
|
||||
GFP_KERNEL, cpu_to_node(c->cpu));
|
||||
if (!rq->dma_info) {
|
||||
err = -ENOMEM;
|
||||
goto err_rq_wq_destroy;
|
||||
}
|
||||
|
||||
rq->handle_rx_cqe = mlx5e_handle_rx_cqe;
|
||||
rq->alloc_wqe = mlx5e_alloc_rx_wqe;
|
||||
rq->dealloc_wqe = mlx5e_dealloc_rx_wqe;
|
||||
|
||||
rq->wqe_sz = (priv->params.lro_en) ?
|
||||
rq->buff.wqe_sz = (priv->params.lro_en) ?
|
||||
priv->params.lro_wqe_sz :
|
||||
MLX5E_SW2HW_MTU(priv->netdev->mtu);
|
||||
rq->wqe_sz = SKB_DATA_ALIGN(rq->wqe_sz);
|
||||
byte_count = rq->wqe_sz;
|
||||
byte_count = rq->buff.wqe_sz;
|
||||
|
||||
/* calc the required page order */
|
||||
frag_sz = MLX5_RX_HEADROOM +
|
||||
byte_count /* packet data */ +
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
frag_sz = SKB_DATA_ALIGN(frag_sz);
|
||||
|
||||
npages = DIV_ROUND_UP(frag_sz, PAGE_SIZE);
|
||||
rq->buff.page_order = order_base_2(npages);
|
||||
|
||||
byte_count |= MLX5_HW_START_PADDING;
|
||||
rq->mkey_be = c->mkey_be;
|
||||
}
|
||||
|
@ -482,6 +547,9 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
|
|||
rq->page_cache.head = 0;
|
||||
rq->page_cache.tail = 0;
|
||||
|
||||
if (rq->xdp_prog)
|
||||
bpf_prog_add(rq->xdp_prog, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rq_wq_destroy:
|
||||
|
@ -494,12 +562,15 @@ static void mlx5e_destroy_rq(struct mlx5e_rq *rq)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (rq->xdp_prog)
|
||||
bpf_prog_put(rq->xdp_prog);
|
||||
|
||||
switch (rq->wq_type) {
|
||||
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
|
||||
mlx5e_rq_free_mpwqe_info(rq);
|
||||
break;
|
||||
default: /* MLX5_WQ_TYPE_LINKED_LIST */
|
||||
kfree(rq->skb);
|
||||
kfree(rq->dma_info);
|
||||
}
|
||||
|
||||
for (i = rq->page_cache.head; i != rq->page_cache.tail;
|
||||
|
@ -641,7 +712,7 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq)
|
|||
|
||||
/* UMR WQE (if in progress) is always at wq->head */
|
||||
if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state))
|
||||
mlx5e_free_rx_mpwqe(rq, &rq->wqe_info[wq->head]);
|
||||
mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]);
|
||||
|
||||
while (!mlx5_wq_ll_is_empty(wq)) {
|
||||
wqe_ix_be = *wq->tail_next;
|
||||
|
@ -676,8 +747,8 @@ static int mlx5e_open_rq(struct mlx5e_channel *c,
|
|||
if (param->am_enabled)
|
||||
set_bit(MLX5E_RQ_STATE_AM, &c->rq.state);
|
||||
|
||||
sq->ico_wqe_info[pi].opcode = MLX5_OPCODE_NOP;
|
||||
sq->ico_wqe_info[pi].num_wqebbs = 1;
|
||||
sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP;
|
||||
sq->db.ico_wqe[pi].num_wqebbs = 1;
|
||||
mlx5e_send_nop(sq, true); /* trigger mlx5e_post_rx_wqes() */
|
||||
|
||||
return 0;
|
||||
|
@ -701,26 +772,65 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq)
|
|||
mlx5e_destroy_rq(rq);
|
||||
}
|
||||
|
||||
static void mlx5e_free_sq_db(struct mlx5e_sq *sq)
|
||||
static void mlx5e_free_sq_xdp_db(struct mlx5e_sq *sq)
|
||||
{
|
||||
kfree(sq->wqe_info);
|
||||
kfree(sq->dma_fifo);
|
||||
kfree(sq->skb);
|
||||
kfree(sq->db.xdp.di);
|
||||
kfree(sq->db.xdp.wqe_info);
|
||||
}
|
||||
|
||||
static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa)
|
||||
static int mlx5e_alloc_sq_xdp_db(struct mlx5e_sq *sq, int numa)
|
||||
{
|
||||
int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
|
||||
|
||||
sq->db.xdp.di = kzalloc_node(sizeof(*sq->db.xdp.di) * wq_sz,
|
||||
GFP_KERNEL, numa);
|
||||
sq->db.xdp.wqe_info = kzalloc_node(sizeof(*sq->db.xdp.wqe_info) * wq_sz,
|
||||
GFP_KERNEL, numa);
|
||||
if (!sq->db.xdp.di || !sq->db.xdp.wqe_info) {
|
||||
mlx5e_free_sq_xdp_db(sq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5e_free_sq_ico_db(struct mlx5e_sq *sq)
|
||||
{
|
||||
kfree(sq->db.ico_wqe);
|
||||
}
|
||||
|
||||
static int mlx5e_alloc_sq_ico_db(struct mlx5e_sq *sq, int numa)
|
||||
{
|
||||
u8 wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
|
||||
|
||||
sq->db.ico_wqe = kzalloc_node(sizeof(*sq->db.ico_wqe) * wq_sz,
|
||||
GFP_KERNEL, numa);
|
||||
if (!sq->db.ico_wqe)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5e_free_sq_txq_db(struct mlx5e_sq *sq)
|
||||
{
|
||||
kfree(sq->db.txq.wqe_info);
|
||||
kfree(sq->db.txq.dma_fifo);
|
||||
kfree(sq->db.txq.skb);
|
||||
}
|
||||
|
||||
static int mlx5e_alloc_sq_txq_db(struct mlx5e_sq *sq, int numa)
|
||||
{
|
||||
int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
|
||||
int df_sz = wq_sz * MLX5_SEND_WQEBB_NUM_DS;
|
||||
|
||||
sq->skb = kzalloc_node(wq_sz * sizeof(*sq->skb), GFP_KERNEL, numa);
|
||||
sq->dma_fifo = kzalloc_node(df_sz * sizeof(*sq->dma_fifo), GFP_KERNEL,
|
||||
numa);
|
||||
sq->wqe_info = kzalloc_node(wq_sz * sizeof(*sq->wqe_info), GFP_KERNEL,
|
||||
numa);
|
||||
|
||||
if (!sq->skb || !sq->dma_fifo || !sq->wqe_info) {
|
||||
mlx5e_free_sq_db(sq);
|
||||
sq->db.txq.skb = kzalloc_node(wq_sz * sizeof(*sq->db.txq.skb),
|
||||
GFP_KERNEL, numa);
|
||||
sq->db.txq.dma_fifo = kzalloc_node(df_sz * sizeof(*sq->db.txq.dma_fifo),
|
||||
GFP_KERNEL, numa);
|
||||
sq->db.txq.wqe_info = kzalloc_node(wq_sz * sizeof(*sq->db.txq.wqe_info),
|
||||
GFP_KERNEL, numa);
|
||||
if (!sq->db.txq.skb || !sq->db.txq.dma_fifo || !sq->db.txq.wqe_info) {
|
||||
mlx5e_free_sq_txq_db(sq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -729,6 +839,46 @@ static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5e_free_sq_db(struct mlx5e_sq *sq)
|
||||
{
|
||||
switch (sq->type) {
|
||||
case MLX5E_SQ_TXQ:
|
||||
mlx5e_free_sq_txq_db(sq);
|
||||
break;
|
||||
case MLX5E_SQ_ICO:
|
||||
mlx5e_free_sq_ico_db(sq);
|
||||
break;
|
||||
case MLX5E_SQ_XDP:
|
||||
mlx5e_free_sq_xdp_db(sq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa)
|
||||
{
|
||||
switch (sq->type) {
|
||||
case MLX5E_SQ_TXQ:
|
||||
return mlx5e_alloc_sq_txq_db(sq, numa);
|
||||
case MLX5E_SQ_ICO:
|
||||
return mlx5e_alloc_sq_ico_db(sq, numa);
|
||||
case MLX5E_SQ_XDP:
|
||||
return mlx5e_alloc_sq_xdp_db(sq, numa);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx5e_sq_get_max_wqebbs(u8 sq_type)
|
||||
{
|
||||
switch (sq_type) {
|
||||
case MLX5E_SQ_ICO:
|
||||
return MLX5E_ICOSQ_MAX_WQEBBS;
|
||||
case MLX5E_SQ_XDP:
|
||||
return MLX5E_XDP_TX_WQEBBS;
|
||||
}
|
||||
return MLX5_SEND_WQE_MAX_WQEBBS;
|
||||
}
|
||||
|
||||
static int mlx5e_create_sq(struct mlx5e_channel *c,
|
||||
int tc,
|
||||
struct mlx5e_sq_param *param,
|
||||
|
@ -741,6 +891,13 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
|
|||
void *sqc_wq = MLX5_ADDR_OF(sqc, sqc, wq);
|
||||
int err;
|
||||
|
||||
sq->type = param->type;
|
||||
sq->pdev = c->pdev;
|
||||
sq->tstamp = &priv->tstamp;
|
||||
sq->mkey_be = c->mkey_be;
|
||||
sq->channel = c;
|
||||
sq->tc = tc;
|
||||
|
||||
err = mlx5_alloc_map_uar(mdev, &sq->uar, !!MLX5_CAP_GEN(mdev, bf));
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -769,18 +926,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
|
|||
if (err)
|
||||
goto err_sq_wq_destroy;
|
||||
|
||||
if (param->icosq) {
|
||||
u8 wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
|
||||
|
||||
sq->ico_wqe_info = kzalloc_node(sizeof(*sq->ico_wqe_info) *
|
||||
wq_sz,
|
||||
GFP_KERNEL,
|
||||
cpu_to_node(c->cpu));
|
||||
if (!sq->ico_wqe_info) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_sq_db;
|
||||
}
|
||||
} else {
|
||||
if (sq->type == MLX5E_SQ_TXQ) {
|
||||
int txq_ix;
|
||||
|
||||
txq_ix = c->ix + tc * priv->params.num_channels;
|
||||
|
@ -788,19 +934,11 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
|
|||
priv->txq_to_sq_map[txq_ix] = sq;
|
||||
}
|
||||
|
||||
sq->pdev = c->pdev;
|
||||
sq->tstamp = &priv->tstamp;
|
||||
sq->mkey_be = c->mkey_be;
|
||||
sq->channel = c;
|
||||
sq->tc = tc;
|
||||
sq->edge = (sq->wq.sz_m1 + 1) - MLX5_SEND_WQE_MAX_WQEBBS;
|
||||
sq->edge = (sq->wq.sz_m1 + 1) - mlx5e_sq_get_max_wqebbs(sq->type);
|
||||
sq->bf_budget = MLX5E_SQ_BF_BUDGET;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_sq_db:
|
||||
mlx5e_free_sq_db(sq);
|
||||
|
||||
err_sq_wq_destroy:
|
||||
mlx5_wq_destroy(&sq->wq_ctrl);
|
||||
|
||||
|
@ -815,7 +953,6 @@ static void mlx5e_destroy_sq(struct mlx5e_sq *sq)
|
|||
struct mlx5e_channel *c = sq->channel;
|
||||
struct mlx5e_priv *priv = c->priv;
|
||||
|
||||
kfree(sq->ico_wqe_info);
|
||||
mlx5e_free_sq_db(sq);
|
||||
mlx5_wq_destroy(&sq->wq_ctrl);
|
||||
mlx5_unmap_free_uar(priv->mdev, &sq->uar);
|
||||
|
@ -844,11 +981,12 @@ static int mlx5e_enable_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param)
|
|||
|
||||
memcpy(sqc, param->sqc, sizeof(param->sqc));
|
||||
|
||||
MLX5_SET(sqc, sqc, tis_num_0, param->icosq ? 0 : priv->tisn[sq->tc]);
|
||||
MLX5_SET(sqc, sqc, tis_num_0, param->type == MLX5E_SQ_ICO ?
|
||||
0 : priv->tisn[sq->tc]);
|
||||
MLX5_SET(sqc, sqc, cqn, sq->cq.mcq.cqn);
|
||||
MLX5_SET(sqc, sqc, min_wqe_inline_mode, sq->min_inline_mode);
|
||||
MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST);
|
||||
MLX5_SET(sqc, sqc, tis_lst_sz, param->icosq ? 0 : 1);
|
||||
MLX5_SET(sqc, sqc, tis_lst_sz, param->type == MLX5E_SQ_ICO ? 0 : 1);
|
||||
MLX5_SET(sqc, sqc, flush_in_error_en, 1);
|
||||
|
||||
MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC);
|
||||
|
@ -963,12 +1101,14 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq)
|
|||
netif_tx_disable_queue(sq->txq);
|
||||
|
||||
/* last doorbell out, godspeed .. */
|
||||
if (mlx5e_sq_has_room_for(sq, 1))
|
||||
if (mlx5e_sq_has_room_for(sq, 1)) {
|
||||
sq->db.txq.skb[(sq->pc & sq->wq.sz_m1)] = NULL;
|
||||
mlx5e_send_nop(sq, true);
|
||||
}
|
||||
}
|
||||
|
||||
mlx5e_disable_sq(sq);
|
||||
mlx5e_free_tx_descs(sq);
|
||||
mlx5e_free_sq_descs(sq);
|
||||
mlx5e_destroy_sq(sq);
|
||||
}
|
||||
|
||||
|
@ -1329,14 +1469,31 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
|
|||
}
|
||||
}
|
||||
|
||||
if (priv->xdp_prog) {
|
||||
/* XDP SQ CQ params are same as normal TXQ sq CQ params */
|
||||
err = mlx5e_open_cq(c, &cparam->tx_cq, &c->xdp_sq.cq,
|
||||
priv->params.tx_cq_moderation);
|
||||
if (err)
|
||||
goto err_close_sqs;
|
||||
|
||||
err = mlx5e_open_sq(c, 0, &cparam->xdp_sq, &c->xdp_sq);
|
||||
if (err) {
|
||||
mlx5e_close_cq(&c->xdp_sq.cq);
|
||||
goto err_close_sqs;
|
||||
}
|
||||
}
|
||||
|
||||
c->xdp = !!priv->xdp_prog;
|
||||
err = mlx5e_open_rq(c, &cparam->rq, &c->rq);
|
||||
if (err)
|
||||
goto err_close_sqs;
|
||||
goto err_close_xdp_sq;
|
||||
|
||||
netif_set_xps_queue(netdev, get_cpu_mask(c->cpu), ix);
|
||||
*cp = c;
|
||||
|
||||
return 0;
|
||||
err_close_xdp_sq:
|
||||
mlx5e_close_sq(&c->xdp_sq);
|
||||
|
||||
err_close_sqs:
|
||||
mlx5e_close_sqs(c);
|
||||
|
@ -1365,9 +1522,13 @@ err_napi_del:
|
|||
static void mlx5e_close_channel(struct mlx5e_channel *c)
|
||||
{
|
||||
mlx5e_close_rq(&c->rq);
|
||||
if (c->xdp)
|
||||
mlx5e_close_sq(&c->xdp_sq);
|
||||
mlx5e_close_sqs(c);
|
||||
mlx5e_close_sq(&c->icosq);
|
||||
napi_disable(&c->napi);
|
||||
if (c->xdp)
|
||||
mlx5e_close_cq(&c->xdp_sq.cq);
|
||||
mlx5e_close_cq(&c->rq.cq);
|
||||
mlx5e_close_tx_cqs(c);
|
||||
mlx5e_close_cq(&c->icosq.cq);
|
||||
|
@ -1441,6 +1602,7 @@ static void mlx5e_build_sq_param(struct mlx5e_priv *priv,
|
|||
|
||||
param->max_inline = priv->params.tx_max_inline;
|
||||
param->min_inline_mode = priv->params.tx_min_inline_mode;
|
||||
param->type = MLX5E_SQ_TXQ;
|
||||
}
|
||||
|
||||
static void mlx5e_build_common_cq_param(struct mlx5e_priv *priv,
|
||||
|
@ -1514,7 +1676,22 @@ static void mlx5e_build_icosq_param(struct mlx5e_priv *priv,
|
|||
MLX5_SET(wq, wq, log_wq_sz, log_wq_size);
|
||||
MLX5_SET(sqc, sqc, reg_umr, MLX5_CAP_ETH(priv->mdev, reg_umr_sq));
|
||||
|
||||
param->icosq = true;
|
||||
param->type = MLX5E_SQ_ICO;
|
||||
}
|
||||
|
||||
static void mlx5e_build_xdpsq_param(struct mlx5e_priv *priv,
|
||||
struct mlx5e_sq_param *param)
|
||||
{
|
||||
void *sqc = param->sqc;
|
||||
void *wq = MLX5_ADDR_OF(sqc, sqc, wq);
|
||||
|
||||
mlx5e_build_sq_param_common(priv, param);
|
||||
MLX5_SET(wq, wq, log_wq_sz, priv->params.log_sq_size);
|
||||
|
||||
param->max_inline = priv->params.tx_max_inline;
|
||||
/* FOR XDP SQs will support only L2 inline mode */
|
||||
param->min_inline_mode = MLX5_INLINE_MODE_NONE;
|
||||
param->type = MLX5E_SQ_XDP;
|
||||
}
|
||||
|
||||
static void mlx5e_build_channel_param(struct mlx5e_priv *priv, struct mlx5e_channel_param *cparam)
|
||||
|
@ -1523,6 +1700,7 @@ static void mlx5e_build_channel_param(struct mlx5e_priv *priv, struct mlx5e_chan
|
|||
|
||||
mlx5e_build_rq_param(priv, &cparam->rq);
|
||||
mlx5e_build_sq_param(priv, &cparam->sq);
|
||||
mlx5e_build_xdpsq_param(priv, &cparam->xdp_sq);
|
||||
mlx5e_build_icosq_param(priv, &cparam->icosq, icosq_log_wq_sz);
|
||||
mlx5e_build_rx_cq_param(priv, &cparam->rx_cq);
|
||||
mlx5e_build_tx_cq_param(priv, &cparam->tx_cq);
|
||||
|
@ -2901,6 +3079,92 @@ static void mlx5e_tx_timeout(struct net_device *dev)
|
|||
schedule_work(&priv->tx_timeout_work);
|
||||
}
|
||||
|
||||
static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||
struct bpf_prog *old_prog;
|
||||
int err = 0;
|
||||
bool reset, was_opened;
|
||||
int i;
|
||||
|
||||
mutex_lock(&priv->state_lock);
|
||||
|
||||
if ((netdev->features & NETIF_F_LRO) && prog) {
|
||||
netdev_warn(netdev, "can't set XDP while LRO is on, disable LRO first\n");
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
|
||||
/* no need for full reset when exchanging programs */
|
||||
reset = (!priv->xdp_prog || !prog);
|
||||
|
||||
if (was_opened && reset)
|
||||
mlx5e_close_locked(netdev);
|
||||
|
||||
/* exchange programs */
|
||||
old_prog = xchg(&priv->xdp_prog, prog);
|
||||
if (prog)
|
||||
bpf_prog_add(prog, 1);
|
||||
if (old_prog)
|
||||
bpf_prog_put(old_prog);
|
||||
|
||||
if (reset) /* change RQ type according to priv->xdp_prog */
|
||||
mlx5e_set_rq_priv_params(priv);
|
||||
|
||||
if (was_opened && reset)
|
||||
mlx5e_open_locked(netdev);
|
||||
|
||||
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->params.num_channels);
|
||||
for (i = 0; i < priv->params.num_channels; i++) {
|
||||
struct mlx5e_channel *c = priv->channel[i];
|
||||
|
||||
set_bit(MLX5E_RQ_STATE_FLUSH, &c->rq.state);
|
||||
napi_synchronize(&c->napi);
|
||||
/* prevent mlx5e_poll_rx_cq from accessing rq->xdp_prog */
|
||||
|
||||
old_prog = xchg(&c->rq.xdp_prog, prog);
|
||||
|
||||
clear_bit(MLX5E_RQ_STATE_FLUSH, &c->rq.state);
|
||||
/* napi_schedule in case we have missed anything */
|
||||
set_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags);
|
||||
napi_schedule(&c->napi);
|
||||
|
||||
if (old_prog)
|
||||
bpf_prog_put(old_prog);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&priv->state_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool mlx5e_xdp_attached(struct net_device *dev)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(dev);
|
||||
|
||||
return !!priv->xdp_prog;
|
||||
}
|
||||
|
||||
static int mlx5e_xdp(struct net_device *dev, struct netdev_xdp *xdp)
|
||||
{
|
||||
switch (xdp->command) {
|
||||
case XDP_SETUP_PROG:
|
||||
return mlx5e_xdp_set(dev, xdp->prog);
|
||||
case XDP_QUERY_PROG:
|
||||
xdp->prog_attached = mlx5e_xdp_attached(dev);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct net_device_ops mlx5e_netdev_ops_basic = {
|
||||
.ndo_open = mlx5e_open,
|
||||
.ndo_stop = mlx5e_close,
|
||||
|
@ -2920,6 +3184,7 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = {
|
|||
.ndo_rx_flow_steer = mlx5e_rx_flow_steer,
|
||||
#endif
|
||||
.ndo_tx_timeout = mlx5e_tx_timeout,
|
||||
.ndo_xdp = mlx5e_xdp,
|
||||
};
|
||||
|
||||
static const struct net_device_ops mlx5e_netdev_ops_sriov = {
|
||||
|
@ -2951,6 +3216,7 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = {
|
|||
.ndo_set_vf_link_state = mlx5e_set_vf_link_state,
|
||||
.ndo_get_vf_stats = mlx5e_get_vf_stats,
|
||||
.ndo_tx_timeout = mlx5e_tx_timeout,
|
||||
.ndo_xdp = mlx5e_xdp,
|
||||
};
|
||||
|
||||
static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
|
||||
|
@ -3025,13 +3291,6 @@ void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev,
|
|||
indirection_rqt[i] = i % num_channels;
|
||||
}
|
||||
|
||||
static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
|
||||
{
|
||||
return MLX5_CAP_GEN(mdev, striding_rq) &&
|
||||
MLX5_CAP_GEN(mdev, umr_ptr_rlky) &&
|
||||
MLX5_CAP_ETH(mdev, reg_umr_sq);
|
||||
}
|
||||
|
||||
static int mlx5e_get_pci_bw(struct mlx5_core_dev *mdev, u32 *pci_bw)
|
||||
{
|
||||
enum pcie_link_width width;
|
||||
|
@ -3111,11 +3370,13 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev,
|
|||
MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
|
||||
MLX5_CQ_PERIOD_MODE_START_FROM_EQE;
|
||||
|
||||
priv->params.log_sq_size =
|
||||
MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
|
||||
priv->params.rq_wq_type = mlx5e_check_fragmented_striding_rq_cap(mdev) ?
|
||||
MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ :
|
||||
MLX5_WQ_TYPE_LINKED_LIST;
|
||||
priv->mdev = mdev;
|
||||
priv->netdev = netdev;
|
||||
priv->params.num_channels = profile->max_nch(mdev);
|
||||
priv->profile = profile;
|
||||
priv->ppriv = ppriv;
|
||||
|
||||
priv->params.log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
|
||||
|
||||
/* set CQE compression */
|
||||
priv->params.rx_cqe_compress_admin = false;
|
||||
|
@ -3128,33 +3389,11 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev,
|
|||
priv->params.rx_cqe_compress_admin =
|
||||
cqe_compress_heuristic(link_speed, pci_bw);
|
||||
}
|
||||
|
||||
priv->params.rx_cqe_compress = priv->params.rx_cqe_compress_admin;
|
||||
|
||||
switch (priv->params.rq_wq_type) {
|
||||
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
|
||||
priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW;
|
||||
priv->params.mpwqe_log_stride_sz =
|
||||
priv->params.rx_cqe_compress ?
|
||||
MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS :
|
||||
MLX5_MPWRQ_LOG_STRIDE_SIZE;
|
||||
priv->params.mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ -
|
||||
priv->params.mpwqe_log_stride_sz;
|
||||
mlx5e_set_rq_priv_params(priv);
|
||||
if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
|
||||
priv->params.lro_en = true;
|
||||
break;
|
||||
default: /* MLX5_WQ_TYPE_LINKED_LIST */
|
||||
priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE;
|
||||
}
|
||||
|
||||
mlx5_core_info(mdev,
|
||||
"MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n",
|
||||
priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ,
|
||||
BIT(priv->params.log_rq_size),
|
||||
BIT(priv->params.mpwqe_log_stride_sz),
|
||||
priv->params.rx_cqe_compress_admin);
|
||||
|
||||
priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type,
|
||||
BIT(priv->params.log_rq_size));
|
||||
|
||||
priv->params.rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation);
|
||||
mlx5e_set_rx_cq_mode_params(&priv->params, cq_period_mode);
|
||||
|
@ -3174,19 +3413,16 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev,
|
|||
mlx5e_build_default_indir_rqt(mdev, priv->params.indirection_rqt,
|
||||
MLX5E_INDIR_RQT_SIZE, profile->max_nch(mdev));
|
||||
|
||||
priv->params.lro_wqe_sz =
|
||||
MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ;
|
||||
priv->params.lro_wqe_sz =
|
||||
MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ -
|
||||
/* Extra room needed for build_skb */
|
||||
MLX5_RX_HEADROOM -
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
|
||||
/* Initialize pflags */
|
||||
MLX5E_SET_PRIV_FLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER,
|
||||
priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE);
|
||||
|
||||
priv->mdev = mdev;
|
||||
priv->netdev = netdev;
|
||||
priv->params.num_channels = profile->max_nch(mdev);
|
||||
priv->profile = profile;
|
||||
priv->ppriv = ppriv;
|
||||
|
||||
#ifdef CONFIG_MLX5_CORE_EN_DCB
|
||||
mlx5e_ets_init(priv);
|
||||
#endif
|
||||
|
|
|
@ -179,50 +179,99 @@ unlock:
|
|||
mutex_unlock(&priv->state_lock);
|
||||
}
|
||||
|
||||
int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
dma_addr_t dma_addr;
|
||||
#define RQ_PAGE_SIZE(rq) ((1 << rq->buff.page_order) << PAGE_SHIFT)
|
||||
|
||||
skb = napi_alloc_skb(rq->cq.napi, rq->wqe_sz);
|
||||
if (unlikely(!skb))
|
||||
static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct mlx5e_page_cache *cache = &rq->page_cache;
|
||||
u32 tail_next = (cache->tail + 1) & (MLX5E_CACHE_SIZE - 1);
|
||||
|
||||
if (tail_next == cache->head) {
|
||||
rq->stats.cache_full++;
|
||||
return false;
|
||||
}
|
||||
|
||||
cache->page_cache[cache->tail] = *dma_info;
|
||||
cache->tail = tail_next;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool mlx5e_rx_cache_get(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct mlx5e_page_cache *cache = &rq->page_cache;
|
||||
|
||||
if (unlikely(cache->head == cache->tail)) {
|
||||
rq->stats.cache_empty++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page_ref_count(cache->page_cache[cache->head].page) != 1) {
|
||||
rq->stats.cache_busy++;
|
||||
return false;
|
||||
}
|
||||
|
||||
*dma_info = cache->page_cache[cache->head];
|
||||
cache->head = (cache->head + 1) & (MLX5E_CACHE_SIZE - 1);
|
||||
rq->stats.cache_reuse++;
|
||||
|
||||
dma_sync_single_for_device(rq->pdev, dma_info->addr,
|
||||
RQ_PAGE_SIZE(rq),
|
||||
DMA_FROM_DEVICE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int mlx5e_page_alloc_mapped(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (mlx5e_rx_cache_get(rq, dma_info))
|
||||
return 0;
|
||||
|
||||
page = dev_alloc_pages(rq->buff.page_order);
|
||||
if (unlikely(!page))
|
||||
return -ENOMEM;
|
||||
|
||||
dma_addr = dma_map_single(rq->pdev,
|
||||
/* hw start padding */
|
||||
skb->data,
|
||||
/* hw end padding */
|
||||
rq->wqe_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (unlikely(dma_mapping_error(rq->pdev, dma_addr)))
|
||||
goto err_free_skb;
|
||||
|
||||
*((dma_addr_t *)skb->cb) = dma_addr;
|
||||
wqe->data.addr = cpu_to_be64(dma_addr);
|
||||
|
||||
rq->skb[ix] = skb;
|
||||
dma_info->page = page;
|
||||
dma_info->addr = dma_map_page(rq->pdev, page, 0,
|
||||
RQ_PAGE_SIZE(rq), rq->buff.map_dir);
|
||||
if (unlikely(dma_mapping_error(rq->pdev, dma_info->addr))) {
|
||||
put_page(page);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_free_skb:
|
||||
dev_kfree_skb(skb);
|
||||
void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
|
||||
bool recycle)
|
||||
{
|
||||
if (likely(recycle) && mlx5e_rx_cache_put(rq, dma_info))
|
||||
return;
|
||||
|
||||
return -ENOMEM;
|
||||
dma_unmap_page(rq->pdev, dma_info->addr, RQ_PAGE_SIZE(rq),
|
||||
rq->buff.map_dir);
|
||||
put_page(dma_info->page);
|
||||
}
|
||||
|
||||
int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
|
||||
{
|
||||
struct mlx5e_dma_info *di = &rq->dma_info[ix];
|
||||
|
||||
if (unlikely(mlx5e_page_alloc_mapped(rq, di)))
|
||||
return -ENOMEM;
|
||||
|
||||
wqe->data.addr = cpu_to_be64(di->addr + MLX5_RX_HEADROOM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)
|
||||
{
|
||||
struct sk_buff *skb = rq->skb[ix];
|
||||
struct mlx5e_dma_info *di = &rq->dma_info[ix];
|
||||
|
||||
if (skb) {
|
||||
rq->skb[ix] = NULL;
|
||||
dma_unmap_single(rq->pdev,
|
||||
*((dma_addr_t *)skb->cb),
|
||||
rq->wqe_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
mlx5e_page_release(rq, di, true);
|
||||
}
|
||||
|
||||
static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq)
|
||||
|
@ -279,7 +328,7 @@ mlx5e_copy_skb_header_mpwqe(struct device *pdev,
|
|||
|
||||
static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix)
|
||||
{
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[ix];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
|
||||
struct mlx5e_sq *sq = &rq->channel->icosq;
|
||||
struct mlx5_wq_cyc *wq = &sq->wq;
|
||||
struct mlx5e_umr_wqe *wqe;
|
||||
|
@ -288,8 +337,8 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix)
|
|||
|
||||
/* fill sq edge with nops to avoid wqe wrap around */
|
||||
while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) {
|
||||
sq->ico_wqe_info[pi].opcode = MLX5_OPCODE_NOP;
|
||||
sq->ico_wqe_info[pi].num_wqebbs = 1;
|
||||
sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP;
|
||||
sq->db.ico_wqe[pi].num_wqebbs = 1;
|
||||
mlx5e_send_nop(sq, true);
|
||||
}
|
||||
|
||||
|
@ -299,90 +348,17 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix)
|
|||
cpu_to_be32((sq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) |
|
||||
MLX5_OPCODE_UMR);
|
||||
|
||||
sq->ico_wqe_info[pi].opcode = MLX5_OPCODE_UMR;
|
||||
sq->ico_wqe_info[pi].num_wqebbs = num_wqebbs;
|
||||
sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR;
|
||||
sq->db.ico_wqe[pi].num_wqebbs = num_wqebbs;
|
||||
sq->pc += num_wqebbs;
|
||||
mlx5e_tx_notify_hw(sq, &wqe->ctrl, 0);
|
||||
}
|
||||
|
||||
static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct mlx5e_page_cache *cache = &rq->page_cache;
|
||||
u32 tail_next = (cache->tail + 1) & (MLX5E_CACHE_SIZE - 1);
|
||||
|
||||
if (tail_next == cache->head) {
|
||||
rq->stats.cache_full++;
|
||||
return false;
|
||||
}
|
||||
|
||||
cache->page_cache[cache->tail] = *dma_info;
|
||||
cache->tail = tail_next;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool mlx5e_rx_cache_get(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct mlx5e_page_cache *cache = &rq->page_cache;
|
||||
|
||||
if (unlikely(cache->head == cache->tail)) {
|
||||
rq->stats.cache_empty++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page_ref_count(cache->page_cache[cache->head].page) != 1) {
|
||||
rq->stats.cache_busy++;
|
||||
return false;
|
||||
}
|
||||
|
||||
*dma_info = cache->page_cache[cache->head];
|
||||
cache->head = (cache->head + 1) & (MLX5E_CACHE_SIZE - 1);
|
||||
rq->stats.cache_reuse++;
|
||||
|
||||
dma_sync_single_for_device(rq->pdev, dma_info->addr, PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int mlx5e_page_alloc_mapped(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *dma_info)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (mlx5e_rx_cache_get(rq, dma_info))
|
||||
return 0;
|
||||
|
||||
page = dev_alloc_page();
|
||||
if (unlikely(!page))
|
||||
return -ENOMEM;
|
||||
|
||||
dma_info->page = page;
|
||||
dma_info->addr = dma_map_page(rq->pdev, page, 0, PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (unlikely(dma_mapping_error(rq->pdev, dma_info->addr))) {
|
||||
put_page(page);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
|
||||
bool recycle)
|
||||
{
|
||||
if (likely(recycle) && mlx5e_rx_cache_put(rq, dma_info))
|
||||
return;
|
||||
|
||||
dma_unmap_page(rq->pdev, dma_info->addr, PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
put_page(dma_info->page);
|
||||
}
|
||||
|
||||
static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq,
|
||||
struct mlx5e_rx_wqe *wqe,
|
||||
u16 ix)
|
||||
{
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[ix];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
|
||||
u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, ix) << PAGE_SHIFT;
|
||||
int pg_strides = mlx5e_mpwqe_strides_per_page(rq);
|
||||
int err;
|
||||
|
@ -436,7 +412,7 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq)
|
|||
clear_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state);
|
||||
|
||||
if (unlikely(test_bit(MLX5E_RQ_STATE_FLUSH, &rq->state))) {
|
||||
mlx5e_free_rx_mpwqe(rq, &rq->wqe_info[wq->head]);
|
||||
mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -448,7 +424,7 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq)
|
|||
mlx5_wq_ll_update_db_record(wq);
|
||||
}
|
||||
|
||||
int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
|
||||
int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -462,7 +438,7 @@ int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
|
|||
|
||||
void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
|
||||
{
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[ix];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
|
||||
|
||||
mlx5e_free_rx_mpwqe(rq, wi);
|
||||
}
|
||||
|
@ -656,33 +632,154 @@ static inline void mlx5e_complete_rx_cqe(struct mlx5e_rq *rq,
|
|||
napi_gro_receive(rq->cq.napi, skb);
|
||||
}
|
||||
|
||||
static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_sq *sq)
|
||||
{
|
||||
struct mlx5_wq_cyc *wq = &sq->wq;
|
||||
struct mlx5e_tx_wqe *wqe;
|
||||
u16 pi = (sq->pc - MLX5E_XDP_TX_WQEBBS) & wq->sz_m1; /* last pi */
|
||||
|
||||
wqe = mlx5_wq_cyc_get_wqe(wq, pi);
|
||||
|
||||
wqe->ctrl.fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
|
||||
mlx5e_tx_notify_hw(sq, &wqe->ctrl, 0);
|
||||
}
|
||||
|
||||
static inline void mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq,
|
||||
struct mlx5e_dma_info *di,
|
||||
unsigned int data_offset,
|
||||
int len)
|
||||
{
|
||||
struct mlx5e_sq *sq = &rq->channel->xdp_sq;
|
||||
struct mlx5_wq_cyc *wq = &sq->wq;
|
||||
u16 pi = sq->pc & wq->sz_m1;
|
||||
struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi);
|
||||
struct mlx5e_sq_wqe_info *wi = &sq->db.xdp.wqe_info[pi];
|
||||
|
||||
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
|
||||
struct mlx5_wqe_eth_seg *eseg = &wqe->eth;
|
||||
struct mlx5_wqe_data_seg *dseg;
|
||||
|
||||
dma_addr_t dma_addr = di->addr + data_offset + MLX5E_XDP_MIN_INLINE;
|
||||
unsigned int dma_len = len - MLX5E_XDP_MIN_INLINE;
|
||||
void *data = page_address(di->page) + data_offset;
|
||||
|
||||
if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_XDP_TX_WQEBBS))) {
|
||||
if (sq->db.xdp.doorbell) {
|
||||
/* SQ is full, ring doorbell */
|
||||
mlx5e_xmit_xdp_doorbell(sq);
|
||||
sq->db.xdp.doorbell = false;
|
||||
}
|
||||
rq->stats.xdp_tx_full++;
|
||||
mlx5e_page_release(rq, di, true);
|
||||
return;
|
||||
}
|
||||
|
||||
dma_sync_single_for_device(sq->pdev, dma_addr, dma_len,
|
||||
PCI_DMA_TODEVICE);
|
||||
|
||||
memset(wqe, 0, sizeof(*wqe));
|
||||
|
||||
/* copy the inline part */
|
||||
memcpy(eseg->inline_hdr_start, data, MLX5E_XDP_MIN_INLINE);
|
||||
eseg->inline_hdr_sz = cpu_to_be16(MLX5E_XDP_MIN_INLINE);
|
||||
|
||||
dseg = (struct mlx5_wqe_data_seg *)cseg + (MLX5E_XDP_TX_DS_COUNT - 1);
|
||||
|
||||
/* write the dma part */
|
||||
dseg->addr = cpu_to_be64(dma_addr);
|
||||
dseg->byte_count = cpu_to_be32(dma_len);
|
||||
dseg->lkey = sq->mkey_be;
|
||||
|
||||
cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_SEND);
|
||||
cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | MLX5E_XDP_TX_DS_COUNT);
|
||||
|
||||
sq->db.xdp.di[pi] = *di;
|
||||
wi->opcode = MLX5_OPCODE_SEND;
|
||||
wi->num_wqebbs = MLX5E_XDP_TX_WQEBBS;
|
||||
sq->pc += MLX5E_XDP_TX_WQEBBS;
|
||||
|
||||
sq->db.xdp.doorbell = true;
|
||||
rq->stats.xdp_tx++;
|
||||
}
|
||||
|
||||
/* returns true if packet was consumed by xdp */
|
||||
static inline bool mlx5e_xdp_handle(struct mlx5e_rq *rq,
|
||||
const struct bpf_prog *prog,
|
||||
struct mlx5e_dma_info *di,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct xdp_buff xdp;
|
||||
u32 act;
|
||||
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
xdp.data = data;
|
||||
xdp.data_end = xdp.data + len;
|
||||
act = bpf_prog_run_xdp(prog, &xdp);
|
||||
switch (act) {
|
||||
case XDP_PASS:
|
||||
return false;
|
||||
case XDP_TX:
|
||||
mlx5e_xmit_xdp_frame(rq, di, MLX5_RX_HEADROOM, len);
|
||||
return true;
|
||||
default:
|
||||
bpf_warn_invalid_xdp_action(act);
|
||||
case XDP_ABORTED:
|
||||
case XDP_DROP:
|
||||
rq->stats.xdp_drop++;
|
||||
mlx5e_page_release(rq, di, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
|
||||
{
|
||||
struct bpf_prog *xdp_prog = READ_ONCE(rq->xdp_prog);
|
||||
struct mlx5e_dma_info *di;
|
||||
struct mlx5e_rx_wqe *wqe;
|
||||
struct sk_buff *skb;
|
||||
__be16 wqe_counter_be;
|
||||
struct sk_buff *skb;
|
||||
u16 wqe_counter;
|
||||
void *va, *data;
|
||||
u32 cqe_bcnt;
|
||||
|
||||
wqe_counter_be = cqe->wqe_counter;
|
||||
wqe_counter = be16_to_cpu(wqe_counter_be);
|
||||
wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_counter);
|
||||
skb = rq->skb[wqe_counter];
|
||||
prefetch(skb->data);
|
||||
rq->skb[wqe_counter] = NULL;
|
||||
di = &rq->dma_info[wqe_counter];
|
||||
va = page_address(di->page);
|
||||
data = va + MLX5_RX_HEADROOM;
|
||||
|
||||
dma_unmap_single(rq->pdev,
|
||||
*((dma_addr_t *)skb->cb),
|
||||
rq->wqe_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_range_for_cpu(rq->pdev,
|
||||
di->addr,
|
||||
MLX5_RX_HEADROOM,
|
||||
rq->buff.wqe_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
prefetch(data);
|
||||
cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
|
||||
|
||||
if (unlikely((cqe->op_own >> 4) != MLX5_CQE_RESP_SEND)) {
|
||||
rq->stats.wqe_err++;
|
||||
dev_kfree_skb(skb);
|
||||
mlx5e_page_release(rq, di, true);
|
||||
goto wq_ll_pop;
|
||||
}
|
||||
|
||||
cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
|
||||
if (mlx5e_xdp_handle(rq, xdp_prog, di, data, cqe_bcnt))
|
||||
goto wq_ll_pop; /* page/packet was consumed by XDP */
|
||||
|
||||
skb = build_skb(va, RQ_PAGE_SIZE(rq));
|
||||
if (unlikely(!skb)) {
|
||||
rq->stats.buff_alloc_err++;
|
||||
mlx5e_page_release(rq, di, true);
|
||||
goto wq_ll_pop;
|
||||
}
|
||||
|
||||
/* queue up for recycling ..*/
|
||||
page_ref_inc(di->page);
|
||||
mlx5e_page_release(rq, di, true);
|
||||
|
||||
skb_reserve(skb, MLX5_RX_HEADROOM);
|
||||
skb_put(skb, cqe_bcnt);
|
||||
|
||||
mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
|
||||
|
@ -734,7 +831,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
|
|||
{
|
||||
u16 cstrides = mpwrq_get_cqe_consumed_strides(cqe);
|
||||
u16 wqe_id = be16_to_cpu(cqe->wqe_id);
|
||||
struct mlx5e_mpw_info *wi = &rq->wqe_info[wqe_id];
|
||||
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[wqe_id];
|
||||
struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_id);
|
||||
struct sk_buff *skb;
|
||||
u16 cqe_bcnt;
|
||||
|
@ -776,6 +873,7 @@ mpwrq_cqe_out:
|
|||
int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
|
||||
{
|
||||
struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq);
|
||||
struct mlx5e_sq *xdp_sq = &rq->channel->xdp_sq;
|
||||
int work_done = 0;
|
||||
|
||||
if (unlikely(test_bit(MLX5E_RQ_STATE_FLUSH, &rq->state)))
|
||||
|
@ -802,6 +900,11 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
|
|||
rq->handle_rx_cqe(rq, cqe);
|
||||
}
|
||||
|
||||
if (xdp_sq->db.xdp.doorbell) {
|
||||
mlx5e_xmit_xdp_doorbell(xdp_sq);
|
||||
xdp_sq->db.xdp.doorbell = false;
|
||||
}
|
||||
|
||||
mlx5_cqwq_update_db_record(&cq->wq);
|
||||
|
||||
/* ensure cq space is freed before enabling more cqes */
|
||||
|
|
|
@ -65,6 +65,9 @@ struct mlx5e_sw_stats {
|
|||
u64 rx_csum_none;
|
||||
u64 rx_csum_complete;
|
||||
u64 rx_csum_unnecessary_inner;
|
||||
u64 rx_xdp_drop;
|
||||
u64 rx_xdp_tx;
|
||||
u64 rx_xdp_tx_full;
|
||||
u64 tx_csum_partial;
|
||||
u64 tx_csum_partial_inner;
|
||||
u64 tx_queue_stopped;
|
||||
|
@ -100,6 +103,9 @@ static const struct counter_desc sw_stats_desc[] = {
|
|||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_none) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_complete) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_unnecessary_inner) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_xdp_drop) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_xdp_tx) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_xdp_tx_full) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_partial) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_partial_inner) },
|
||||
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_stopped) },
|
||||
|
@ -278,6 +284,9 @@ struct mlx5e_rq_stats {
|
|||
u64 csum_none;
|
||||
u64 lro_packets;
|
||||
u64 lro_bytes;
|
||||
u64 xdp_drop;
|
||||
u64 xdp_tx;
|
||||
u64 xdp_tx_full;
|
||||
u64 wqe_err;
|
||||
u64 mpwqe_filler;
|
||||
u64 buff_alloc_err;
|
||||
|
@ -295,6 +304,9 @@ static const struct counter_desc rq_stats_desc[] = {
|
|||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_complete) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_unnecessary_inner) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_none) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, xdp_drop) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, xdp_tx) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, xdp_tx_full) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, lro_packets) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, lro_bytes) },
|
||||
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, wqe_err) },
|
||||
|
|
|
@ -52,7 +52,6 @@ void mlx5e_send_nop(struct mlx5e_sq *sq, bool notify_hw)
|
|||
cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_NOP);
|
||||
cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | 0x01);
|
||||
|
||||
sq->skb[pi] = NULL;
|
||||
sq->pc++;
|
||||
sq->stats.nop++;
|
||||
|
||||
|
@ -82,15 +81,17 @@ static inline void mlx5e_dma_push(struct mlx5e_sq *sq,
|
|||
u32 size,
|
||||
enum mlx5e_dma_map_type map_type)
|
||||
{
|
||||
sq->dma_fifo[sq->dma_fifo_pc & sq->dma_fifo_mask].addr = addr;
|
||||
sq->dma_fifo[sq->dma_fifo_pc & sq->dma_fifo_mask].size = size;
|
||||
sq->dma_fifo[sq->dma_fifo_pc & sq->dma_fifo_mask].type = map_type;
|
||||
u32 i = sq->dma_fifo_pc & sq->dma_fifo_mask;
|
||||
|
||||
sq->db.txq.dma_fifo[i].addr = addr;
|
||||
sq->db.txq.dma_fifo[i].size = size;
|
||||
sq->db.txq.dma_fifo[i].type = map_type;
|
||||
sq->dma_fifo_pc++;
|
||||
}
|
||||
|
||||
static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_sq *sq, u32 i)
|
||||
{
|
||||
return &sq->dma_fifo[i & sq->dma_fifo_mask];
|
||||
return &sq->db.txq.dma_fifo[i & sq->dma_fifo_mask];
|
||||
}
|
||||
|
||||
static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, u8 num_dma)
|
||||
|
@ -221,7 +222,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
|
|||
|
||||
u16 pi = sq->pc & wq->sz_m1;
|
||||
struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi);
|
||||
struct mlx5e_tx_wqe_info *wi = &sq->wqe_info[pi];
|
||||
struct mlx5e_tx_wqe_info *wi = &sq->db.txq.wqe_info[pi];
|
||||
|
||||
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
|
||||
struct mlx5_wqe_eth_seg *eseg = &wqe->eth;
|
||||
|
@ -341,7 +342,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
|
|||
cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode);
|
||||
cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt);
|
||||
|
||||
sq->skb[pi] = skb;
|
||||
sq->db.txq.skb[pi] = skb;
|
||||
|
||||
wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
|
||||
sq->pc += wi->num_wqebbs;
|
||||
|
@ -368,8 +369,10 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
/* fill sq edge with nops to avoid wqe wrap around */
|
||||
while ((sq->pc & wq->sz_m1) > sq->edge)
|
||||
while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) {
|
||||
sq->db.txq.skb[pi] = NULL;
|
||||
mlx5e_send_nop(sq, false);
|
||||
}
|
||||
|
||||
if (bf)
|
||||
sq->bf_budget--;
|
||||
|
@ -442,8 +445,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
|
|||
last_wqe = (sqcc == wqe_counter);
|
||||
|
||||
ci = sqcc & sq->wq.sz_m1;
|
||||
skb = sq->skb[ci];
|
||||
wi = &sq->wqe_info[ci];
|
||||
skb = sq->db.txq.skb[ci];
|
||||
wi = &sq->db.txq.wqe_info[ci];
|
||||
|
||||
if (unlikely(!skb)) { /* nop */
|
||||
sqcc++;
|
||||
|
@ -492,7 +495,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
|
|||
return (i == MLX5E_TX_CQ_POLL_BUDGET);
|
||||
}
|
||||
|
||||
void mlx5e_free_tx_descs(struct mlx5e_sq *sq)
|
||||
static void mlx5e_free_txq_sq_descs(struct mlx5e_sq *sq)
|
||||
{
|
||||
struct mlx5e_tx_wqe_info *wi;
|
||||
struct sk_buff *skb;
|
||||
|
@ -501,8 +504,8 @@ void mlx5e_free_tx_descs(struct mlx5e_sq *sq)
|
|||
|
||||
while (sq->cc != sq->pc) {
|
||||
ci = sq->cc & sq->wq.sz_m1;
|
||||
skb = sq->skb[ci];
|
||||
wi = &sq->wqe_info[ci];
|
||||
skb = sq->db.txq.skb[ci];
|
||||
wi = &sq->db.txq.wqe_info[ci];
|
||||
|
||||
if (!skb) { /* nop */
|
||||
sq->cc++;
|
||||
|
@ -520,3 +523,37 @@ void mlx5e_free_tx_descs(struct mlx5e_sq *sq)
|
|||
sq->cc += wi->num_wqebbs;
|
||||
}
|
||||
}
|
||||
|
||||
static void mlx5e_free_xdp_sq_descs(struct mlx5e_sq *sq)
|
||||
{
|
||||
struct mlx5e_sq_wqe_info *wi;
|
||||
struct mlx5e_dma_info *di;
|
||||
u16 ci;
|
||||
|
||||
while (sq->cc != sq->pc) {
|
||||
ci = sq->cc & sq->wq.sz_m1;
|
||||
di = &sq->db.xdp.di[ci];
|
||||
wi = &sq->db.xdp.wqe_info[ci];
|
||||
|
||||
if (wi->opcode == MLX5_OPCODE_NOP) {
|
||||
sq->cc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
sq->cc += wi->num_wqebbs;
|
||||
|
||||
mlx5e_page_release(&sq->channel->rq, di, false);
|
||||
}
|
||||
}
|
||||
|
||||
void mlx5e_free_sq_descs(struct mlx5e_sq *sq)
|
||||
{
|
||||
switch (sq->type) {
|
||||
case MLX5E_SQ_TXQ:
|
||||
mlx5e_free_txq_sq_descs(sq);
|
||||
break;
|
||||
case MLX5E_SQ_XDP:
|
||||
mlx5e_free_xdp_sq_descs(sq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
|
|||
|
||||
do {
|
||||
u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1;
|
||||
struct mlx5e_ico_wqe_info *icowi = &sq->ico_wqe_info[ci];
|
||||
struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci];
|
||||
|
||||
mlx5_cqwq_pop(&cq->wq);
|
||||
sqcc += icowi->num_wqebbs;
|
||||
|
@ -105,6 +105,66 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
|
|||
sq->cc = sqcc;
|
||||
}
|
||||
|
||||
static inline bool mlx5e_poll_xdp_tx_cq(struct mlx5e_cq *cq)
|
||||
{
|
||||
struct mlx5e_sq *sq;
|
||||
u16 sqcc;
|
||||
int i;
|
||||
|
||||
sq = container_of(cq, struct mlx5e_sq, cq);
|
||||
|
||||
if (unlikely(test_bit(MLX5E_SQ_STATE_FLUSH, &sq->state)))
|
||||
return false;
|
||||
|
||||
/* sq->cc must be updated only after mlx5_cqwq_update_db_record(),
|
||||
* otherwise a cq overrun may occur
|
||||
*/
|
||||
sqcc = sq->cc;
|
||||
|
||||
for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) {
|
||||
struct mlx5_cqe64 *cqe;
|
||||
u16 wqe_counter;
|
||||
bool last_wqe;
|
||||
|
||||
cqe = mlx5e_get_cqe(cq);
|
||||
if (!cqe)
|
||||
break;
|
||||
|
||||
mlx5_cqwq_pop(&cq->wq);
|
||||
|
||||
wqe_counter = be16_to_cpu(cqe->wqe_counter);
|
||||
|
||||
do {
|
||||
struct mlx5e_sq_wqe_info *wi;
|
||||
struct mlx5e_dma_info *di;
|
||||
u16 ci;
|
||||
|
||||
last_wqe = (sqcc == wqe_counter);
|
||||
|
||||
ci = sqcc & sq->wq.sz_m1;
|
||||
di = &sq->db.xdp.di[ci];
|
||||
wi = &sq->db.xdp.wqe_info[ci];
|
||||
|
||||
if (unlikely(wi->opcode == MLX5_OPCODE_NOP)) {
|
||||
sqcc++;
|
||||
continue;
|
||||
}
|
||||
|
||||
sqcc += wi->num_wqebbs;
|
||||
/* Recycle RX page */
|
||||
mlx5e_page_release(&sq->channel->rq, di, true);
|
||||
} while (!last_wqe);
|
||||
}
|
||||
|
||||
mlx5_cqwq_update_db_record(&cq->wq);
|
||||
|
||||
/* ensure cq space is freed before enabling more cqes */
|
||||
wmb();
|
||||
|
||||
sq->cc = sqcc;
|
||||
return (i == MLX5E_TX_CQ_POLL_BUDGET);
|
||||
}
|
||||
|
||||
int mlx5e_napi_poll(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel,
|
||||
|
@ -121,6 +181,9 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
|
|||
work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget);
|
||||
busy |= work_done == budget;
|
||||
|
||||
if (c->xdp)
|
||||
busy |= mlx5e_poll_xdp_tx_cq(&c->xdp_sq.cq);
|
||||
|
||||
mlx5e_poll_ico_cq(&c->icosq.cq);
|
||||
|
||||
busy |= mlx5e_post_rx_wqes(&c->rq);
|
||||
|
|
Loading…
Reference in New Issue