Merge branch 'eth-fbnic-add-xdp-support-for-fbnic'

Mohsin Bashir says:

====================
eth: fbnic: Add XDP support for fbnic

This patch series introduces basic XDP support for fbnic. To enable this,
it also includes preparatory changes such as making the HDS threshold
configurable via ethtool, updating headroom for fbnic, tracking
frag state in shinfo, and prefetching the first cacheline of data.

V3: https://lore.kernel.org/netdev/20250812220150.161848-1-mohsin.bashr@gmail.com/
V2: https://lore.kernel.org/netdev/20250811211338.857992-1-mohsin.bashr@gmail.com/
V1: https://lore.kernel.org/netdev/20250723145926.4120434-1-mohsin.bashr@gmail.com/
====================

Link: https://patch.msgid.link/20250813221319.3367670-1-mohsin.bashr@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2025-08-19 10:51:20 +02:00
6 changed files with 580 additions and 86 deletions
@@ -160,3 +160,14 @@ behavior and potential performance bottlenecks.
credit exhaustion
- ``pcie_ob_rd_no_np_cred``: Read requests dropped due to non-posted
credit exhaustion
XDP Length Error:
~~~~~~~~~~~~~~~~~
For XDP programs without frags support, fbnic tries to make sure that MTU fits
into a single buffer. If an oversized frame is received and gets fragmented,
it is dropped and the following netlink counters are updated
- ``rx-length``: number of frames dropped due to lack of fragmentation
support in the attached XDP program
- ``rx-errors``: total number of packets with errors received on the interface
@@ -2,6 +2,7 @@
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <net/ipv6.h>
@@ -111,6 +112,20 @@ static const struct fbnic_stat fbnic_gstrings_hw_q_stats[] = {
FBNIC_HW_RXB_DEQUEUE_STATS_LEN * FBNIC_RXB_DEQUEUE_INDICES + \
FBNIC_HW_Q_STATS_LEN * FBNIC_MAX_QUEUES)
#define FBNIC_QUEUE_STAT(name, stat) \
FBNIC_STAT_FIELDS(fbnic_ring, name, stat)
static const struct fbnic_stat fbnic_gstrings_xdp_stats[] = {
FBNIC_QUEUE_STAT("xdp_tx_queue_%u_packets", stats.packets),
FBNIC_QUEUE_STAT("xdp_tx_queue_%u_bytes", stats.bytes),
FBNIC_QUEUE_STAT("xdp_tx_queue_%u_dropped", stats.dropped),
};
#define FBNIC_XDP_STATS_LEN ARRAY_SIZE(fbnic_gstrings_xdp_stats)
#define FBNIC_STATS_LEN \
(FBNIC_HW_STATS_LEN + FBNIC_XDP_STATS_LEN * FBNIC_MAX_XDPQS)
static void
fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
@@ -160,6 +175,7 @@ static void fbnic_clone_swap_cfg(struct fbnic_net *orig,
swap(clone->num_rx_queues, orig->num_rx_queues);
swap(clone->num_tx_queues, orig->num_tx_queues);
swap(clone->num_napi, orig->num_napi);
swap(clone->hds_thresh, orig->hds_thresh);
}
static void fbnic_aggregate_vector_counters(struct fbnic_net *fbn,
@@ -277,15 +293,21 @@ fbnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
ring->rx_mini_pending = fbn->hpq_size;
ring->rx_jumbo_pending = fbn->ppq_size;
ring->tx_pending = fbn->txq_size;
kernel_ring->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED;
kernel_ring->hds_thresh_max = FBNIC_HDS_THRESH_MAX;
kernel_ring->hds_thresh = fbn->hds_thresh;
}
static void fbnic_set_rings(struct fbnic_net *fbn,
struct ethtool_ringparam *ring)
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ring)
{
fbn->rcq_size = ring->rx_pending;
fbn->hpq_size = ring->rx_mini_pending;
fbn->ppq_size = ring->rx_jumbo_pending;
fbn->txq_size = ring->tx_pending;
fbn->hds_thresh = kernel_ring->hds_thresh;
}
static int
@@ -316,8 +338,24 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
return -EINVAL;
}
if (kernel_ring->tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED) {
NL_SET_ERR_MSG_MOD(extack, "Cannot disable TCP data split");
return -EINVAL;
}
/* If an XDP program is attached, we should check for potential frame
* splitting. If the new HDS threshold can cause splitting, we should
* only allow if the attached XDP program can handle frags.
*/
if (fbnic_check_split_frames(fbn->xdp_prog, netdev->mtu,
kernel_ring->hds_thresh)) {
NL_SET_ERR_MSG_MOD(extack,
"Use higher HDS threshold or multi-buf capable program");
return -EINVAL;
}
if (!netif_running(netdev)) {
fbnic_set_rings(fbn, ring);
fbnic_set_rings(fbn, ring, kernel_ring);
return 0;
}
@@ -325,7 +363,7 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
if (!clone)
return -ENOMEM;
fbnic_set_rings(clone, ring);
fbnic_set_rings(clone, ring, kernel_ring);
err = fbnic_alloc_napi_vectors(clone);
if (err)
@@ -398,6 +436,16 @@ static void fbnic_get_rxb_dequeue_strings(u8 **data, unsigned int idx)
ethtool_sprintf(data, stat->string, idx);
}
static void fbnic_get_xdp_queue_strings(u8 **data, unsigned int idx)
{
const struct fbnic_stat *stat;
int i;
stat = fbnic_gstrings_xdp_stats;
for (i = 0; i < FBNIC_XDP_STATS_LEN; i++, stat++)
ethtool_sprintf(data, stat->string, idx);
}
static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data)
{
const struct fbnic_stat *stat;
@@ -423,6 +471,9 @@ static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data)
for (i = 0; i < FBNIC_HW_Q_STATS_LEN; i++, stat++)
ethtool_sprintf(&data, stat->string, idx);
}
for (i = 0; i < FBNIC_MAX_XDPQS; i++)
fbnic_get_xdp_queue_strings(&data, i);
break;
}
}
@@ -440,6 +491,24 @@ static void fbnic_report_hw_stats(const struct fbnic_stat *stat,
}
}
static void fbnic_get_xdp_queue_stats(struct fbnic_ring *ring, u64 **data)
{
const struct fbnic_stat *stat;
int i;
if (!ring) {
*data += FBNIC_XDP_STATS_LEN;
return;
}
stat = fbnic_gstrings_xdp_stats;
for (i = 0; i < FBNIC_XDP_STATS_LEN; i++, stat++, (*data)++) {
u8 *p = (u8 *)ring + stat->offset;
**data = *(u64 *)p;
}
}
static void fbnic_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
@@ -487,13 +556,16 @@ static void fbnic_get_ethtool_stats(struct net_device *dev,
FBNIC_HW_Q_STATS_LEN, &data);
}
spin_unlock(&fbd->hw_stats_lock);
for (i = 0; i < FBNIC_MAX_XDPQS; i++)
fbnic_get_xdp_queue_stats(fbn->tx[i + FBNIC_MAX_TXQS], &data);
}
static int fbnic_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return FBNIC_HW_STATS_LEN;
return FBNIC_STATS_LEN;
default:
return -EOPNOTSUPP;
}
@@ -1678,6 +1750,8 @@ fbnic_get_rmon_stats(struct net_device *netdev,
static const struct ethtool_ops fbnic_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_RX_MAX_FRAMES,
.supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
ETHTOOL_RING_USE_HDS_THRS,
.rxfh_max_num_contexts = FBNIC_RPC_RSS_TBL_COUNT,
.get_drvinfo = fbnic_get_drvinfo,
.get_regs_len = fbnic_get_regs_len,
+74 -1
View File
@@ -407,11 +407,12 @@ static void fbnic_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats64)
{
u64 rx_bytes, rx_packets, rx_dropped = 0, rx_errors = 0;
u64 rx_over = 0, rx_missed = 0, rx_length = 0;
u64 tx_bytes, tx_packets, tx_dropped = 0;
struct fbnic_net *fbn = netdev_priv(dev);
struct fbnic_dev *fbd = fbn->fbd;
struct fbnic_queue_stats *stats;
u64 rx_over = 0, rx_missed = 0;
unsigned int start, i;
fbnic_get_hw_stats(fbd);
@@ -489,6 +490,7 @@ static void fbnic_get_stats64(struct net_device *dev,
stats64->rx_missed_errors = rx_missed;
for (i = 0; i < fbn->num_rx_queues; i++) {
struct fbnic_ring *xdpr = fbn->tx[FBNIC_MAX_TXQS + i];
struct fbnic_ring *rxr = fbn->rx[i];
if (!rxr)
@@ -500,14 +502,66 @@ static void fbnic_get_stats64(struct net_device *dev,
rx_bytes = stats->bytes;
rx_packets = stats->packets;
rx_dropped = stats->dropped;
rx_length = stats->rx.length_errors;
} while (u64_stats_fetch_retry(&stats->syncp, start));
stats64->rx_bytes += rx_bytes;
stats64->rx_packets += rx_packets;
stats64->rx_dropped += rx_dropped;
stats64->rx_errors += rx_length;
stats64->rx_length_errors += rx_length;
if (!xdpr)
continue;
stats = &xdpr->stats;
do {
start = u64_stats_fetch_begin(&stats->syncp);
tx_bytes = stats->bytes;
tx_packets = stats->packets;
tx_dropped = stats->dropped;
} while (u64_stats_fetch_retry(&stats->syncp, start));
stats64->tx_bytes += tx_bytes;
stats64->tx_packets += tx_packets;
stats64->tx_dropped += tx_dropped;
}
}
bool fbnic_check_split_frames(struct bpf_prog *prog, unsigned int mtu,
u32 hds_thresh)
{
if (!prog)
return false;
if (prog->aux->xdp_has_frags)
return false;
return mtu + ETH_HLEN > hds_thresh;
}
static int fbnic_bpf(struct net_device *netdev, struct netdev_bpf *bpf)
{
struct bpf_prog *prog = bpf->prog, *prev_prog;
struct fbnic_net *fbn = netdev_priv(netdev);
if (bpf->command != XDP_SETUP_PROG)
return -EINVAL;
if (fbnic_check_split_frames(prog, netdev->mtu,
fbn->hds_thresh)) {
NL_SET_ERR_MSG_MOD(bpf->extack,
"MTU too high, or HDS threshold is too low for single buffer XDP");
return -EOPNOTSUPP;
}
prev_prog = xchg(&fbn->xdp_prog, prog);
if (prev_prog)
bpf_prog_put(prev_prog);
return 0;
}
static const struct net_device_ops fbnic_netdev_ops = {
.ndo_open = fbnic_open,
.ndo_stop = fbnic_stop,
@@ -517,6 +571,7 @@ static const struct net_device_ops fbnic_netdev_ops = {
.ndo_set_mac_address = fbnic_set_mac,
.ndo_set_rx_mode = fbnic_set_rx_mode,
.ndo_get_stats64 = fbnic_get_stats64,
.ndo_bpf = fbnic_bpf,
.ndo_hwtstamp_get = fbnic_hwtstamp_get,
.ndo_hwtstamp_set = fbnic_hwtstamp_set,
};
@@ -568,6 +623,7 @@ static void fbnic_get_queue_stats_tx(struct net_device *dev, int idx,
struct fbnic_ring *txr = fbn->tx[idx];
struct fbnic_queue_stats *stats;
u64 stop, wake, csum, lso;
struct fbnic_ring *xdpr;
unsigned int start;
u64 bytes, packets;
@@ -591,6 +647,19 @@ static void fbnic_get_queue_stats_tx(struct net_device *dev, int idx,
tx->hw_gso_wire_packets = lso;
tx->stop = stop;
tx->wake = wake;
xdpr = fbn->tx[FBNIC_MAX_TXQS + idx];
if (xdpr) {
stats = &xdpr->stats;
do {
start = u64_stats_fetch_begin(&stats->syncp);
bytes = stats->bytes;
packets = stats->packets;
} while (u64_stats_fetch_retry(&stats->syncp, start));
tx->bytes += bytes;
tx->packets += packets;
}
}
static void fbnic_get_base_stats(struct net_device *dev,
@@ -695,6 +764,10 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
fbn->rx_usecs = FBNIC_RX_USECS_DEFAULT;
fbn->rx_max_frames = FBNIC_RX_FRAMES_DEFAULT;
/* Initialize the hds_thresh */
netdev->cfg->hds_thresh = FBNIC_HDS_THRESH_DEFAULT;
fbn->hds_thresh = FBNIC_HDS_THRESH_DEFAULT;
default_queues = netif_get_num_default_rss_queues();
if (default_queues > fbd->max_num_queues)
default_queues = fbd->max_num_queues;
@@ -18,7 +18,9 @@
#define FBNIC_TUN_GSO_FEATURES NETIF_F_GSO_IPXIP6
struct fbnic_net {
struct fbnic_ring *tx[FBNIC_MAX_TXQS];
struct bpf_prog *xdp_prog;
struct fbnic_ring *tx[FBNIC_MAX_TXQS + FBNIC_MAX_XDPQS];
struct fbnic_ring *rx[FBNIC_MAX_RXQS];
struct fbnic_napi_vector *napi[FBNIC_MAX_NAPI_VECTORS];
@@ -31,6 +33,8 @@ struct fbnic_net {
u32 ppq_size;
u32 rcq_size;
u32 hds_thresh;
u16 rx_usecs;
u16 tx_usecs;
@@ -102,4 +106,7 @@ int fbnic_phylink_ethtool_ksettings_get(struct net_device *netdev,
int fbnic_phylink_get_fecparam(struct net_device *netdev,
struct ethtool_fecparam *fecparam);
int fbnic_phylink_init(struct net_device *netdev);
bool fbnic_check_split_frames(struct bpf_prog *prog,
unsigned int mtu, u32 hds_threshold);
#endif /* _FBNIC_NETDEV_H_ */
+391 -75
View File
@@ -2,17 +2,27 @@
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/bitfield.h>
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <net/netdev_queues.h>
#include <net/page_pool/helpers.h>
#include <net/tcp.h>
#include <net/xdp.h>
#include "fbnic.h"
#include "fbnic_csr.h"
#include "fbnic_netdev.h"
#include "fbnic_txrx.h"
enum {
FBNIC_XDP_PASS = 0,
FBNIC_XDP_CONSUME,
FBNIC_XDP_TX,
FBNIC_XDP_LEN_ERR,
};
enum {
FBNIC_XMIT_CB_TS = 0x01,
};
@@ -606,6 +616,54 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget,
}
}
static void fbnic_clean_twq1(struct fbnic_napi_vector *nv, bool pp_allow_direct,
struct fbnic_ring *ring, bool discard,
unsigned int hw_head)
{
u64 total_bytes = 0, total_packets = 0;
unsigned int head = ring->head;
while (hw_head != head) {
struct page *page;
u64 twd;
if (unlikely(!(ring->desc[head] & FBNIC_TWD_TYPE(AL))))
goto next_desc;
twd = le64_to_cpu(ring->desc[head]);
page = ring->tx_buf[head];
/* TYPE_AL is 2, TYPE_LAST_AL is 3. So this trick gives
* us one increment per packet, with no branches.
*/
total_packets += FIELD_GET(FBNIC_TWD_TYPE_MASK, twd) -
FBNIC_TWD_TYPE_AL;
total_bytes += FIELD_GET(FBNIC_TWD_LEN_MASK, twd);
page_pool_put_page(nv->page_pool, page, -1, pp_allow_direct);
next_desc:
head++;
head &= ring->size_mask;
}
if (!total_bytes)
return;
ring->head = head;
if (discard) {
u64_stats_update_begin(&ring->stats.syncp);
ring->stats.dropped += total_packets;
u64_stats_update_end(&ring->stats.syncp);
return;
}
u64_stats_update_begin(&ring->stats.syncp);
ring->stats.bytes += total_bytes;
ring->stats.packets += total_packets;
u64_stats_update_end(&ring->stats.syncp);
}
static void fbnic_clean_tsq(struct fbnic_napi_vector *nv,
struct fbnic_ring *ring,
u64 tcd, int *ts_head, int *head0)
@@ -689,12 +747,21 @@ static void fbnic_page_pool_drain(struct fbnic_ring *ring, unsigned int idx,
}
static void fbnic_clean_twq(struct fbnic_napi_vector *nv, int napi_budget,
struct fbnic_q_triad *qt, s32 ts_head, s32 head0)
struct fbnic_q_triad *qt, s32 ts_head, s32 head0,
s32 head1)
{
if (head0 >= 0)
fbnic_clean_twq0(nv, napi_budget, &qt->sub0, false, head0);
else if (ts_head >= 0)
fbnic_clean_twq0(nv, napi_budget, &qt->sub0, false, ts_head);
if (head1 >= 0) {
qt->cmpl.deferred_head = -1;
if (napi_budget)
fbnic_clean_twq1(nv, true, &qt->sub1, false, head1);
else
qt->cmpl.deferred_head = head1;
}
}
static void
@@ -702,6 +769,7 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
int napi_budget)
{
struct fbnic_ring *cmpl = &qt->cmpl;
s32 head1 = cmpl->deferred_head;
s32 head0 = -1, ts_head = -1;
__le64 *raw_tcd, done;
u32 head = cmpl->head;
@@ -719,7 +787,10 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
switch (FIELD_GET(FBNIC_TCD_TYPE_MASK, tcd)) {
case FBNIC_TCD_TYPE_0:
if (!(tcd & FBNIC_TCD_TWQ1))
if (tcd & FBNIC_TCD_TWQ1)
head1 = FIELD_GET(FBNIC_TCD_TYPE0_HEAD1_MASK,
tcd);
else
head0 = FIELD_GET(FBNIC_TCD_TYPE0_HEAD0_MASK,
tcd);
/* Currently all err status bits are related to
@@ -752,7 +823,7 @@ fbnic_clean_tcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt,
}
/* Unmap and free processed buffers */
fbnic_clean_twq(nv, napi_budget, qt, ts_head, head0);
fbnic_clean_twq(nv, napi_budget, qt, ts_head, head0, head1);
}
static void fbnic_clean_bdq(struct fbnic_napi_vector *nv, int napi_budget,
@@ -877,7 +948,7 @@ static void fbnic_pkt_prepare(struct fbnic_napi_vector *nv, u64 rcd,
headroom = hdr_pg_off - hdr_pg_start + FBNIC_RX_PAD;
frame_sz = hdr_pg_end - hdr_pg_start;
xdp_init_buff(&pkt->buff, frame_sz, NULL);
xdp_init_buff(&pkt->buff, frame_sz, &qt->xdp_rxq);
hdr_pg_start += (FBNIC_RCD_AL_BUFF_FRAG_MASK & rcd) *
FBNIC_BD_FRAG_SIZE;
@@ -888,13 +959,12 @@ static void fbnic_pkt_prepare(struct fbnic_napi_vector *nv, u64 rcd,
/* Build frame around buffer */
hdr_start = page_address(page) + hdr_pg_start;
net_prefetch(pkt->buff.data);
xdp_prepare_buff(&pkt->buff, hdr_start, headroom,
len - FBNIC_RX_PAD, true);
pkt->data_truesize = 0;
pkt->data_len = 0;
pkt->nr_frags = 0;
pkt->hwtstamp = 0;
pkt->add_frag_failed = false;
}
static void fbnic_add_rx_frag(struct fbnic_napi_vector *nv, u64 rcd,
@@ -905,8 +975,8 @@ static void fbnic_add_rx_frag(struct fbnic_napi_vector *nv, u64 rcd,
unsigned int pg_off = FIELD_GET(FBNIC_RCD_AL_BUFF_OFF_MASK, rcd);
unsigned int len = FIELD_GET(FBNIC_RCD_AL_BUFF_LEN_MASK, rcd);
struct page *page = fbnic_page_pool_get(&qt->sub1, pg_idx);
struct skb_shared_info *shinfo;
unsigned int truesize;
bool added;
truesize = FIELD_GET(FBNIC_RCD_AL_PAGE_FIN, rcd) ?
FBNIC_BD_FRAG_SIZE - pg_off : ALIGN(len, 128);
@@ -918,34 +988,34 @@ static void fbnic_add_rx_frag(struct fbnic_napi_vector *nv, u64 rcd,
dma_sync_single_range_for_cpu(nv->dev, page_pool_get_dma_addr(page),
pg_off, truesize, DMA_BIDIRECTIONAL);
/* Add page to xdp shared info */
shinfo = xdp_get_shared_info_from_buff(&pkt->buff);
/* We use gso_segs to store truesize */
pkt->data_truesize += truesize;
__skb_fill_page_desc_noacc(shinfo, pkt->nr_frags++, page, pg_off, len);
/* Store data_len in gso_size */
pkt->data_len += len;
added = xdp_buff_add_frag(&pkt->buff, page_to_netmem(page), pg_off, len,
truesize);
if (unlikely(!added)) {
pkt->add_frag_failed = true;
netdev_err_once(nv->napi.dev,
"Failed to add fragment to xdp_buff\n");
}
}
static void fbnic_put_pkt_buff(struct fbnic_napi_vector *nv,
struct fbnic_pkt_buff *pkt, int budget)
{
struct skb_shared_info *shinfo;
struct page *page;
int nr_frags;
if (!pkt->buff.data_hard_start)
return;
shinfo = xdp_get_shared_info_from_buff(&pkt->buff);
nr_frags = pkt->nr_frags;
if (xdp_buff_has_frags(&pkt->buff)) {
struct skb_shared_info *shinfo;
int nr_frags;
while (nr_frags--) {
page = skb_frag_page(&shinfo->frags[nr_frags]);
page_pool_put_full_page(nv->page_pool, page, !!budget);
shinfo = xdp_get_shared_info_from_buff(&pkt->buff);
nr_frags = shinfo->nr_frags;
while (nr_frags--) {
page = skb_frag_page(&shinfo->frags[nr_frags]);
page_pool_put_full_page(nv->page_pool, page, !!budget);
}
}
page = virt_to_page(pkt->buff.data_hard_start);
@@ -955,43 +1025,12 @@ static void fbnic_put_pkt_buff(struct fbnic_napi_vector *nv,
static struct sk_buff *fbnic_build_skb(struct fbnic_napi_vector *nv,
struct fbnic_pkt_buff *pkt)
{
unsigned int nr_frags = pkt->nr_frags;
struct skb_shared_info *shinfo;
unsigned int truesize;
struct sk_buff *skb;
truesize = xdp_data_hard_end(&pkt->buff) + FBNIC_RX_TROOM -
pkt->buff.data_hard_start;
/* Build frame around buffer */
skb = napi_build_skb(pkt->buff.data_hard_start, truesize);
if (unlikely(!skb))
skb = xdp_build_skb_from_buff(&pkt->buff);
if (!skb)
return NULL;
/* Push data pointer to start of data, put tail to end of data */
skb_reserve(skb, pkt->buff.data - pkt->buff.data_hard_start);
__skb_put(skb, pkt->buff.data_end - pkt->buff.data);
/* Add tracking for metadata at the start of the frame */
skb_metadata_set(skb, pkt->buff.data - pkt->buff.data_meta);
/* Add Rx frags */
if (nr_frags) {
/* Verify that shared info didn't move */
shinfo = xdp_get_shared_info_from_buff(&pkt->buff);
WARN_ON(skb_shinfo(skb) != shinfo);
skb->truesize += pkt->data_truesize;
skb->data_len += pkt->data_len;
shinfo->nr_frags = nr_frags;
skb->len += pkt->data_len;
}
skb_mark_for_recycle(skb);
/* Set MAC header specific fields */
skb->protocol = eth_type_trans(skb, nv->napi.dev);
/* Add timestamp if present */
if (pkt->hwtstamp)
skb_hwtstamps(skb)->hwtstamp = pkt->hwtstamp;
@@ -999,6 +1038,119 @@ static struct sk_buff *fbnic_build_skb(struct fbnic_napi_vector *nv,
return skb;
}
static long fbnic_pkt_tx(struct fbnic_napi_vector *nv,
struct fbnic_pkt_buff *pkt)
{
struct fbnic_ring *ring = &nv->qt[0].sub1;
int size, offset, nsegs = 1, data_len = 0;
unsigned int tail = ring->tail;
struct skb_shared_info *shinfo;
skb_frag_t *frag = NULL;
struct page *page;
dma_addr_t dma;
__le64 *twd;
if (unlikely(xdp_buff_has_frags(&pkt->buff))) {
shinfo = xdp_get_shared_info_from_buff(&pkt->buff);
nsegs += shinfo->nr_frags;
data_len = shinfo->xdp_frags_size;
frag = &shinfo->frags[0];
}
if (fbnic_desc_unused(ring) < nsegs) {
u64_stats_update_begin(&ring->stats.syncp);
ring->stats.dropped++;
u64_stats_update_end(&ring->stats.syncp);
return -FBNIC_XDP_CONSUME;
}
page = virt_to_page(pkt->buff.data_hard_start);
offset = offset_in_page(pkt->buff.data);
dma = page_pool_get_dma_addr(page);
size = pkt->buff.data_end - pkt->buff.data;
while (nsegs--) {
dma_sync_single_range_for_device(nv->dev, dma, offset, size,
DMA_BIDIRECTIONAL);
dma += offset;
ring->tx_buf[tail] = page;
twd = &ring->desc[tail];
*twd = cpu_to_le64(FIELD_PREP(FBNIC_TWD_ADDR_MASK, dma) |
FIELD_PREP(FBNIC_TWD_LEN_MASK, size) |
FIELD_PREP(FBNIC_TWD_TYPE_MASK,
FBNIC_TWD_TYPE_AL));
tail++;
tail &= ring->size_mask;
if (!data_len)
break;
offset = skb_frag_off(frag);
page = skb_frag_page(frag);
dma = page_pool_get_dma_addr(page);
size = skb_frag_size(frag);
data_len -= size;
frag++;
}
*twd |= FBNIC_TWD_TYPE(LAST_AL);
ring->tail = tail;
return -FBNIC_XDP_TX;
}
static void fbnic_pkt_commit_tail(struct fbnic_napi_vector *nv,
unsigned int pkt_tail)
{
struct fbnic_ring *ring = &nv->qt[0].sub1;
/* Force DMA writes to flush before writing to tail */
dma_wmb();
writel(pkt_tail, ring->doorbell);
}
static struct sk_buff *fbnic_run_xdp(struct fbnic_napi_vector *nv,
struct fbnic_pkt_buff *pkt)
{
struct fbnic_net *fbn = netdev_priv(nv->napi.dev);
struct bpf_prog *xdp_prog;
int act;
xdp_prog = READ_ONCE(fbn->xdp_prog);
if (!xdp_prog)
goto xdp_pass;
/* Should never happen, config paths enforce HDS threshold > MTU */
if (xdp_buff_has_frags(&pkt->buff) && !xdp_prog->aux->xdp_has_frags)
return ERR_PTR(-FBNIC_XDP_LEN_ERR);
act = bpf_prog_run_xdp(xdp_prog, &pkt->buff);
switch (act) {
case XDP_PASS:
xdp_pass:
return fbnic_build_skb(nv, pkt);
case XDP_TX:
return ERR_PTR(fbnic_pkt_tx(nv, pkt));
default:
bpf_warn_invalid_xdp_action(nv->napi.dev, xdp_prog, act);
fallthrough;
case XDP_ABORTED:
trace_xdp_exception(nv->napi.dev, xdp_prog, act);
fallthrough;
case XDP_DROP:
break;
}
return ERR_PTR(-FBNIC_XDP_CONSUME);
}
static enum pkt_hash_types fbnic_skb_hash_type(u64 rcd)
{
return (FBNIC_RCD_META_L4_TYPE_MASK & rcd) ? PKT_HASH_TYPE_L4 :
@@ -1050,10 +1202,10 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv,
struct fbnic_q_triad *qt, int budget)
{
unsigned int packets = 0, bytes = 0, dropped = 0, alloc_failed = 0;
u64 csum_complete = 0, csum_none = 0;
u64 csum_complete = 0, csum_none = 0, length_errors = 0;
s32 head0 = -1, head1 = -1, pkt_tail = -1;
struct fbnic_ring *rcq = &qt->cmpl;
struct fbnic_pkt_buff *pkt;
s32 head0 = -1, head1 = -1;
__le64 *raw_rcd, done;
u32 head = rcq->head;
@@ -1094,8 +1246,10 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv,
/* We currently ignore the action table index */
break;
case FBNIC_RCD_TYPE_META:
if (likely(!fbnic_rcd_metadata_err(rcd)))
skb = fbnic_build_skb(nv, pkt);
if (unlikely(pkt->add_frag_failed))
skb = NULL;
else if (likely(!fbnic_rcd_metadata_err(rcd)))
skb = fbnic_run_xdp(nv, pkt);
/* Populate skb and invalidate XDP */
if (!IS_ERR_OR_NULL(skb)) {
@@ -1107,10 +1261,15 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv,
bytes += skb->len;
napi_gro_receive(&nv->napi, skb);
} else if (skb == ERR_PTR(-FBNIC_XDP_TX)) {
pkt_tail = nv->qt[0].sub1.tail;
bytes += xdp_get_buff_len(&pkt->buff);
} else {
if (!skb) {
alloc_failed++;
dropped++;
} else if (skb == ERR_PTR(-FBNIC_XDP_LEN_ERR)) {
length_errors++;
} else {
dropped++;
}
@@ -1140,8 +1299,12 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv,
rcq->stats.rx.alloc_failed += alloc_failed;
rcq->stats.rx.csum_complete += csum_complete;
rcq->stats.rx.csum_none += csum_none;
rcq->stats.rx.length_errors += length_errors;
u64_stats_update_end(&rcq->stats.syncp);
if (pkt_tail >= 0)
fbnic_pkt_commit_tail(nv, pkt_tail);
/* Unmap and free processed buffers */
if (head0 >= 0)
fbnic_clean_bdq(nv, budget, &qt->sub0, head0);
@@ -1220,8 +1383,9 @@ void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn,
fbn->rx_stats.rx.alloc_failed += stats->rx.alloc_failed;
fbn->rx_stats.rx.csum_complete += stats->rx.csum_complete;
fbn->rx_stats.rx.csum_none += stats->rx.csum_none;
fbn->rx_stats.rx.length_errors += stats->rx.length_errors;
/* Remember to add new stats here */
BUILD_BUG_ON(sizeof(fbn->rx_stats.rx) / 8 != 3);
BUILD_BUG_ON(sizeof(fbn->rx_stats.rx) / 8 != 4);
}
void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
@@ -1243,6 +1407,22 @@ void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
BUILD_BUG_ON(sizeof(fbn->tx_stats.twq) / 8 != 6);
}
static void fbnic_aggregate_ring_xdp_counters(struct fbnic_net *fbn,
struct fbnic_ring *xdpr)
{
struct fbnic_queue_stats *stats = &xdpr->stats;
if (!(xdpr->flags & FBNIC_RING_F_STATS))
return;
/* Capture stats from queues before dissasociating them */
fbn->rx_stats.bytes += stats->bytes;
fbn->rx_stats.packets += stats->packets;
fbn->rx_stats.dropped += stats->dropped;
fbn->tx_stats.bytes += stats->bytes;
fbn->tx_stats.packets += stats->packets;
}
static void fbnic_remove_tx_ring(struct fbnic_net *fbn,
struct fbnic_ring *txr)
{
@@ -1256,6 +1436,19 @@ static void fbnic_remove_tx_ring(struct fbnic_net *fbn,
fbn->tx[txr->q_idx] = NULL;
}
static void fbnic_remove_xdp_ring(struct fbnic_net *fbn,
struct fbnic_ring *xdpr)
{
if (!(xdpr->flags & FBNIC_RING_F_STATS))
return;
fbnic_aggregate_ring_xdp_counters(fbn, xdpr);
/* Remove pointer to the Tx ring */
WARN_ON(fbn->tx[xdpr->q_idx] && fbn->tx[xdpr->q_idx] != xdpr);
fbn->tx[xdpr->q_idx] = NULL;
}
static void fbnic_remove_rx_ring(struct fbnic_net *fbn,
struct fbnic_ring *rxr)
{
@@ -1277,10 +1470,12 @@ static void fbnic_free_napi_vector(struct fbnic_net *fbn,
for (i = 0; i < nv->txt_count; i++) {
fbnic_remove_tx_ring(fbn, &nv->qt[i].sub0);
fbnic_remove_xdp_ring(fbn, &nv->qt[i].sub1);
fbnic_remove_tx_ring(fbn, &nv->qt[i].cmpl);
}
for (j = 0; j < nv->rxt_count; j++, i++) {
xdp_rxq_info_unreg(&nv->qt[i].xdp_rxq);
fbnic_remove_rx_ring(fbn, &nv->qt[i].sub0);
fbnic_remove_rx_ring(fbn, &nv->qt[i].sub1);
fbnic_remove_rx_ring(fbn, &nv->qt[i].cmpl);
@@ -1350,6 +1545,7 @@ static void fbnic_ring_init(struct fbnic_ring *ring, u32 __iomem *doorbell,
ring->doorbell = doorbell;
ring->q_idx = q_idx;
ring->flags = flags;
ring->deferred_head = -1;
}
static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
@@ -1359,11 +1555,18 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
{
int txt_count = txq_count, rxt_count = rxq_count;
u32 __iomem *uc_addr = fbd->uc_addr0;
int xdp_count = 0, qt_count, err;
struct fbnic_napi_vector *nv;
struct fbnic_q_triad *qt;
int qt_count, err;
u32 __iomem *db;
/* We need to reserve at least one Tx Queue Triad for an XDP ring */
if (rxq_count) {
xdp_count = 1;
if (!txt_count)
txt_count = 1;
}
qt_count = txt_count + rxq_count;
if (!qt_count)
return -EINVAL;
@@ -1412,12 +1615,13 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
qt = nv->qt;
while (txt_count) {
u8 flags = FBNIC_RING_F_CTX | FBNIC_RING_F_STATS;
/* Configure Tx queue */
db = &uc_addr[FBNIC_QUEUE(txq_idx) + FBNIC_QUEUE_TWQ0_TAIL];
/* Assign Tx queue to netdev if applicable */
if (txq_count > 0) {
u8 flags = FBNIC_RING_F_CTX | FBNIC_RING_F_STATS;
fbnic_ring_init(&qt->sub0, db, txq_idx, flags);
fbn->tx[txq_idx] = &qt->sub0;
@@ -1427,6 +1631,28 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
FBNIC_RING_F_DISABLED);
}
/* Configure XDP queue */
db = &uc_addr[FBNIC_QUEUE(txq_idx) + FBNIC_QUEUE_TWQ1_TAIL];
/* Assign XDP queue to netdev if applicable
*
* The setup for this is in itself a bit different.
* 1. We only need one XDP Tx queue per NAPI vector.
* 2. We associate it to the first Rx queue index.
* 3. The hardware side is associated based on the Tx Queue.
* 4. The netdev queue is offset by FBNIC_MAX_TXQs.
*/
if (xdp_count > 0) {
unsigned int xdp_idx = FBNIC_MAX_TXQS + rxq_idx;
fbnic_ring_init(&qt->sub1, db, xdp_idx, flags);
fbn->tx[xdp_idx] = &qt->sub1;
xdp_count--;
} else {
fbnic_ring_init(&qt->sub1, db, 0,
FBNIC_RING_F_DISABLED);
}
/* Configure Tx completion queue */
db = &uc_addr[FBNIC_QUEUE(txq_idx) + FBNIC_QUEUE_TCQ_HEAD];
fbnic_ring_init(&qt->cmpl, db, 0, 0);
@@ -1453,6 +1679,11 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
fbnic_ring_init(&qt->cmpl, db, rxq_idx, FBNIC_RING_F_STATS);
fbn->rx[rxq_idx] = &qt->cmpl;
err = xdp_rxq_info_reg(&qt->xdp_rxq, fbn->netdev, rxq_idx,
nv->napi.napi_id);
if (err)
goto free_ring_cur_qt;
/* Update Rx queue index */
rxt_count--;
rxq_idx += v_count;
@@ -1463,6 +1694,26 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn,
return 0;
while (rxt_count < nv->rxt_count) {
qt--;
xdp_rxq_info_unreg(&qt->xdp_rxq);
free_ring_cur_qt:
fbnic_remove_rx_ring(fbn, &qt->sub0);
fbnic_remove_rx_ring(fbn, &qt->sub1);
fbnic_remove_rx_ring(fbn, &qt->cmpl);
rxt_count++;
}
while (txt_count < nv->txt_count) {
qt--;
fbnic_remove_tx_ring(fbn, &qt->sub0);
fbnic_remove_xdp_ring(fbn, &qt->sub1);
fbnic_remove_tx_ring(fbn, &qt->cmpl);
txt_count++;
}
fbnic_napi_free_irq(fbd, nv);
pp_destroy:
page_pool_destroy(nv->page_pool);
napi_del:
@@ -1692,6 +1943,10 @@ static int fbnic_alloc_tx_qt_resources(struct fbnic_net *fbn,
if (err)
return err;
err = fbnic_alloc_tx_ring_resources(fbn, &qt->sub1);
if (err)
goto free_sub0;
err = fbnic_alloc_tx_ring_resources(fbn, &qt->cmpl);
if (err)
goto free_sub1;
@@ -1699,6 +1954,8 @@ static int fbnic_alloc_tx_qt_resources(struct fbnic_net *fbn,
return 0;
free_sub1:
fbnic_free_ring_resources(dev, &qt->sub1);
free_sub0:
fbnic_free_ring_resources(dev, &qt->sub0);
return err;
}
@@ -1739,8 +1996,10 @@ static void fbnic_free_nv_resources(struct fbnic_net *fbn,
for (i = 0; i < nv->txt_count; i++)
fbnic_free_qt_resources(fbn, &nv->qt[i]);
for (j = 0; j < nv->rxt_count; j++, i++)
for (j = 0; j < nv->rxt_count; j++, i++) {
fbnic_free_qt_resources(fbn, &nv->qt[i]);
xdp_rxq_info_unreg_mem_model(&nv->qt[i].xdp_rxq);
}
}
static int fbnic_alloc_nv_resources(struct fbnic_net *fbn,
@@ -1752,19 +2011,32 @@ static int fbnic_alloc_nv_resources(struct fbnic_net *fbn,
for (i = 0; i < nv->txt_count; i++) {
err = fbnic_alloc_tx_qt_resources(fbn, &nv->qt[i]);
if (err)
goto free_resources;
goto free_qt_resources;
}
/* Allocate Rx Resources */
for (j = 0; j < nv->rxt_count; j++, i++) {
/* Register XDP memory model for completion queue */
err = xdp_reg_mem_model(&nv->qt[i].xdp_rxq.mem,
MEM_TYPE_PAGE_POOL,
nv->page_pool);
if (err)
goto xdp_unreg_mem_model;
err = fbnic_alloc_rx_qt_resources(fbn, &nv->qt[i]);
if (err)
goto free_resources;
goto xdp_unreg_cur_model;
}
return 0;
free_resources:
xdp_unreg_mem_model:
while (j-- && i--) {
fbnic_free_qt_resources(fbn, &nv->qt[i]);
xdp_unreg_cur_model:
xdp_rxq_info_unreg_mem_model(&nv->qt[i].xdp_rxq);
}
free_qt_resources:
while (i--)
fbnic_free_qt_resources(fbn, &nv->qt[i]);
return err;
@@ -1871,6 +2143,15 @@ static void fbnic_disable_twq0(struct fbnic_ring *txr)
fbnic_ring_wr32(txr, FBNIC_QUEUE_TWQ0_CTL, twq_ctl);
}
static void fbnic_disable_twq1(struct fbnic_ring *txr)
{
u32 twq_ctl = fbnic_ring_rd32(txr, FBNIC_QUEUE_TWQ1_CTL);
twq_ctl &= ~FBNIC_QUEUE_TWQ_CTL_ENABLE;
fbnic_ring_wr32(txr, FBNIC_QUEUE_TWQ1_CTL, twq_ctl);
}
static void fbnic_disable_tcq(struct fbnic_ring *txr)
{
fbnic_ring_wr32(txr, FBNIC_QUEUE_TCQ_CTL, 0);
@@ -1916,6 +2197,7 @@ void fbnic_disable(struct fbnic_net *fbn)
struct fbnic_q_triad *qt = &nv->qt[t];
fbnic_disable_twq0(&qt->sub0);
fbnic_disable_twq1(&qt->sub1);
fbnic_disable_tcq(&qt->cmpl);
}
@@ -2030,6 +2312,8 @@ void fbnic_flush(struct fbnic_net *fbn)
/* Clean the work queues of unprocessed work */
fbnic_clean_twq0(nv, 0, &qt->sub0, true, qt->sub0.tail);
fbnic_clean_twq1(nv, false, &qt->sub1, true,
qt->sub1.tail);
/* Reset completion queue descriptor ring */
memset(qt->cmpl.desc, 0, qt->cmpl.size);
@@ -2056,7 +2340,7 @@ void fbnic_flush(struct fbnic_net *fbn)
memset(qt->cmpl.desc, 0, qt->cmpl.size);
fbnic_put_pkt_buff(nv, qt->cmpl.pkt, 0);
qt->cmpl.pkt->buff.data_hard_start = NULL;
memset(qt->cmpl.pkt, 0, sizeof(struct fbnic_pkt_buff));
}
}
}
@@ -2104,6 +2388,28 @@ static void fbnic_enable_twq0(struct fbnic_ring *twq)
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ0_CTL, FBNIC_QUEUE_TWQ_CTL_ENABLE);
}
static void fbnic_enable_twq1(struct fbnic_ring *twq)
{
u32 log_size = fls(twq->size_mask);
if (!twq->size_mask)
return;
/* Reset head/tail */
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ1_CTL, FBNIC_QUEUE_TWQ_CTL_RESET);
twq->tail = 0;
twq->head = 0;
/* Store descriptor ring address and size */
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ1_BAL, lower_32_bits(twq->dma));
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ1_BAH, upper_32_bits(twq->dma));
/* Write lower 4 bits of log size as 64K ring size is 0 */
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ1_SIZE, log_size & 0xf);
fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ1_CTL, FBNIC_QUEUE_TWQ_CTL_ENABLE);
}
static void fbnic_enable_tcq(struct fbnic_napi_vector *nv,
struct fbnic_ring *tcq)
{
@@ -2232,13 +2538,22 @@ static void fbnic_enable_rcq(struct fbnic_napi_vector *nv,
{
struct fbnic_net *fbn = netdev_priv(nv->napi.dev);
u32 log_size = fls(rcq->size_mask);
u32 rcq_ctl;
u32 hds_thresh = fbn->hds_thresh;
u32 rcq_ctl = 0;
fbnic_config_drop_mode_rcq(nv, rcq);
rcq_ctl = FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PADLEN_MASK, FBNIC_RX_PAD) |
FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_MAX_HDR_MASK,
FBNIC_RX_MAX_HDR) |
/* Force lower bound on MAX_HEADER_BYTES. Below this, all frames should
* be split at L4. It would also result in the frames being split at
* L2/L3 depending on the frame size.
*/
if (fbn->hds_thresh < FBNIC_HDR_BYTES_MIN) {
rcq_ctl = FBNIC_QUEUE_RDE_CTL0_EN_HDR_SPLIT;
hds_thresh = FBNIC_HDR_BYTES_MIN;
}
rcq_ctl |= FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PADLEN_MASK, FBNIC_RX_PAD) |
FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_MAX_HDR_MASK, hds_thresh) |
FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PAYLD_OFF_MASK,
FBNIC_RX_PAYLD_OFFSET) |
FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PAYLD_PG_CL_MASK,
@@ -2280,6 +2595,7 @@ void fbnic_enable(struct fbnic_net *fbn)
struct fbnic_q_triad *qt = &nv->qt[t];
fbnic_enable_twq0(&qt->sub0);
fbnic_enable_twq1(&qt->sub1);
fbnic_enable_tcq(nv, &qt->cmpl);
}
+18 -5
View File
@@ -35,6 +35,7 @@ struct fbnic_net;
#define FBNIC_MAX_TXQS 128u
#define FBNIC_MAX_RXQS 128u
#define FBNIC_MAX_XDPQS 128u
/* These apply to TWQs, TCQ, RCQ */
#define FBNIC_QUEUE_SIZE_MIN 16u
@@ -50,10 +51,10 @@ struct fbnic_net;
#define FBNIC_RX_TROOM \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
#define FBNIC_RX_HROOM_PAD 128
#define FBNIC_RX_HROOM \
(ALIGN(FBNIC_RX_TROOM + NET_SKB_PAD, 128) - FBNIC_RX_TROOM)
(ALIGN(FBNIC_RX_TROOM + FBNIC_RX_HROOM_PAD, 128) - FBNIC_RX_TROOM)
#define FBNIC_RX_PAD 0
#define FBNIC_RX_MAX_HDR (1536 - FBNIC_RX_PAD)
#define FBNIC_RX_PAYLD_OFFSET 0
#define FBNIC_RX_PAYLD_PG_CL 0
@@ -61,12 +62,16 @@ struct fbnic_net;
#define FBNIC_RING_F_CTX BIT(1)
#define FBNIC_RING_F_STATS BIT(2) /* Ring's stats may be used */
#define FBNIC_HDS_THRESH_MAX \
(4096 - FBNIC_RX_HROOM - FBNIC_RX_TROOM - FBNIC_RX_PAD)
#define FBNIC_HDS_THRESH_DEFAULT \
(1536 - FBNIC_RX_PAD)
#define FBNIC_HDR_BYTES_MIN 128
struct fbnic_pkt_buff {
struct xdp_buff buff;
ktime_t hwtstamp;
u32 data_truesize;
u16 data_len;
u16 nr_frags;
bool add_frag_failed;
};
struct fbnic_queue_stats {
@@ -85,6 +90,7 @@ struct fbnic_queue_stats {
u64 alloc_failed;
u64 csum_complete;
u64 csum_none;
u64 length_errors;
} rx;
};
u64 dropped;
@@ -115,6 +121,12 @@ struct fbnic_ring {
u32 head, tail; /* Head/Tail of ring */
/* Deferred_head is used to cache the head for TWQ1 if an attempt
* is made to clean TWQ1 with zero napi_budget. We do not use it for
* any other ring.
*/
s32 deferred_head;
struct fbnic_queue_stats stats;
/* Slow path fields follow */
@@ -124,6 +136,7 @@ struct fbnic_ring {
struct fbnic_q_triad {
struct fbnic_ring sub0, sub1, cmpl;
struct xdp_rxq_info xdp_rxq;
};
struct fbnic_napi_vector {