From 8b1db02b771f0de1600766f94884817caccabfd0 Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Thu, 19 Mar 2026 17:16:42 +0000 Subject: [PATCH 1/4] i40e: prepare for XDP metadata ops support Prepare 'struct i40e_xdp_buff' that contains an xdp_buff and a pointer to i40e_rx_desc in order to pass the RX descriptor to the XDP kfuncs. Also in ZC path, use XSK_CHECK_PRIV_TYPE() to ensure i40e_xdp_buff doesn't exceed the offset of cb in xdp_buff_xsk. No functional changes. Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 5 ++++- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 7 ++++++- drivers/net/ethernet/intel/i40e/i40e_xsk.c | 12 ++++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0cd0e5597c90..4b11ae9ed8d5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3622,7 +3622,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) } skip: - xdp_init_buff(&ring->xdp, xdp_frame_sz, &ring->xdp_rxq); + xdp_init_buff(&ring->xdp_ctx.xdp, xdp_frame_sz, &ring->xdp_rxq); rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len, BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index ef5e657816f0..7f68adb5109b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2438,10 +2438,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, unsigned int *rx_cleaned) { unsigned int total_rx_bytes = 0, total_rx_packets = 0; + struct i40e_xdp_buff *xdp_ctx = &rx_ring->xdp_ctx; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); u16 clean_threshold = rx_ring->count / 2; unsigned int offset = rx_ring->rx_offset; - struct xdp_buff *xdp = &rx_ring->xdp; + struct xdp_buff *xdp = &xdp_ctx->xdp; unsigned int xdp_xmit = 0; struct bpf_prog *xdp_prog; bool failure = false; @@ -2530,6 +2531,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, if (neop) continue; + xdp_ctx->desc = rx_desc; + xdp_res = i40e_run_xdp(rx_ring, xdp, xdp_prog); if (xdp_res) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 1e5fd63d47f4..bb741ff3e5f2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -283,6 +283,11 @@ struct i40e_rx_buffer { __u32 page_count; }; +struct i40e_xdp_buff { + struct xdp_buff xdp; + const union i40e_rx_desc *desc; +}; + struct i40e_queue_stats { u64 packets; u64 bytes; @@ -345,7 +350,7 @@ struct i40e_ring { * and to resume packet building for this ring in the next call to * i40e_clean_rx_ring_irq(). */ - struct xdp_buff xdp; + struct i40e_xdp_buff xdp_ctx; /* Next descriptor to be processed; next_to_clean is updated only on * processing EOP descriptor diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c index 9f47388eaba5..51a05ce4c7ce 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c @@ -246,6 +246,8 @@ bool i40e_alloc_rx_buffers_zc(struct i40e_ring *rx_ring, u16 count) u32 nb_buffs, i; dma_addr_t dma; + XSK_CHECK_PRIV_TYPE(struct i40e_xdp_buff); + rx_desc = I40E_RX_DESC(rx_ring, ntu); xdp = i40e_rx_bi(rx_ring, ntu); @@ -396,6 +398,14 @@ static void i40e_handle_xdp_result_zc(struct i40e_ring *rx_ring, WARN_ON_ONCE(1); } +static struct i40e_xdp_buff *xsk_buff_to_i40e_ctx(struct xdp_buff *xdp) +{ + /* xdp_buff pointer used by ZC code path is allocated as xdp_buff_xsk. + * i40e_xdp_buff private fields overlap with xdp_buff_xsk->cb. + */ + return (struct i40e_xdp_buff *)xdp; +} + /** * i40e_clean_rx_irq_zc - Consumes Rx packets from the hardware ring * @rx_ring: Rx ring @@ -472,6 +482,8 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget) if (i40e_is_non_eop(rx_ring, rx_desc)) continue; + xsk_buff_to_i40e_ctx(first)->desc = rx_desc; + xdp_res = i40e_run_xdp_zc(rx_ring, first, xdp_prog); i40e_handle_xdp_result_zc(rx_ring, first, rx_desc, &rx_packets, &rx_bytes, xdp_res, &failure); From cff6f1378df7d7fcb408a8ca2259e98e7cdf7fca Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Thu, 19 Mar 2026 17:16:43 +0000 Subject: [PATCH 2/4] i40e: add support for bpf_xdp_metadata_rx_hash() Introduce i40e_xdp_rx_hash() which takes the same approach as i40e_rx_hash() to extract the hash from RX descriptors. Tested with X710 adapter using xdp_hw_metadata, and verified hash consistency between bpf_xdp_metadata_rx_hash() and skb->hash. Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 30 +++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_type.h | 5 ++++ 2 files changed, 35 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4b11ae9ed8d5..e487d697a634 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13582,6 +13583,34 @@ static int i40e_xdp(struct net_device *dev, } } +static int i40e_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash, + enum xdp_rss_hash_type *rss_type) +{ + const struct i40e_xdp_buff *ctx = (const void *)_ctx; + const union i40e_rx_desc *desc = ctx->desc; + struct libeth_rx_pt rx_ptype; + u8 raw_rx_ptype; + u64 status; + + status = le64_to_cpu(desc->wb.qword1.status_error_len); + raw_rx_ptype = FIELD_GET(I40E_RXD_QW1_PTYPE_MASK, status); + rx_ptype = libie_rx_pt_parse(raw_rx_ptype); + + if (!libeth_rx_pt_has_hash(ctx->xdp.rxq->dev, rx_ptype) || + FIELD_GET(I40E_RX_DESC_STATUS_FLTSTAT_MASK, status) != + I40E_RX_DESC_FLTSTAT_RSS_HASH) + return -ENODATA; + + *hash = le32_to_cpu(desc->wb.qword0.hi_dword.rss); + *rss_type = rx_ptype.hash_type; + + return 0; +} + +static const struct xdp_metadata_ops i40e_xdp_metadata_ops = { + .xmo_rx_hash = i40e_xdp_rx_hash, +}; + static const struct net_device_ops i40e_netdev_ops = { .ndo_open = i40e_open, .ndo_stop = i40e_close, @@ -13784,6 +13813,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) i40e_vsi_config_netdev_tc(vsi, vsi->tc_config.enabled_tc); netdev->netdev_ops = &i40e_netdev_ops; + netdev->xdp_metadata_ops = &i40e_xdp_metadata_ops; netdev->watchdog_timeo = 5 * HZ; i40e_set_ethtool_ops(netdev); diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index ed8bbdb586da..16a65c6e5153 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -4,6 +4,7 @@ #ifndef _I40E_TYPE_H_ #define _I40E_TYPE_H_ +#include #include #include "i40e_adminq.h" #include "i40e_hmc.h" @@ -699,6 +700,10 @@ enum i40e_rx_desc_status_bits { I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */ }; +#define I40E_RX_DESC_STATUS_FLTSTAT_MASK \ + GENMASK_ULL(I40E_RX_DESC_STATUS_FLTSTAT_SHIFT + 1, \ + I40E_RX_DESC_STATUS_FLTSTAT_SHIFT) + #define I40E_RXD_QW1_STATUS_SHIFT 0 #define I40E_RXD_QW1_STATUS_MASK ((BIT(I40E_RX_DESC_STATUS_LAST) - 1) \ << I40E_RXD_QW1_STATUS_SHIFT) From bbcaa1fe8749e21e8936058be8c5b43ceced0a38 Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Thu, 19 Mar 2026 17:16:44 +0000 Subject: [PATCH 3/4] i40e: add support for bpf_xdp_metadata_rx_vlan_tag() Introduce i40e_xdp_rx_vlan_tag() which takes the same approach as i40e_process_skb_fields() to extract the VLAN tag from the RX descriptor. Tested with X710 adapter using xdp_hw_metadata, and confirmed that VLAN tags match between bpf_xdp_metadata_rx_vlan_tag() and skb->vlan_proto/vlan_tci. Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e487d697a634..719f45b5cb40 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -13607,8 +13607,27 @@ static int i40e_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash, return 0; } +static int i40e_xdp_rx_vlan_tag(const struct xdp_md *_ctx, __be16 *vlan_proto, + u16 *vlan_tci) +{ + const struct i40e_xdp_buff *ctx = (const void *)_ctx; + const union i40e_rx_desc *desc = ctx->desc; + u64 status; + + status = le64_to_cpu(desc->wb.qword1.status_error_len); + + if (!(status & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT))) + return -ENODATA; + + *vlan_proto = cpu_to_be16(ETH_P_8021Q); + *vlan_tci = le16_to_cpu(desc->wb.qword0.lo_dword.l2tag1); + + return 0; +} + static const struct xdp_metadata_ops i40e_xdp_metadata_ops = { .xmo_rx_hash = i40e_xdp_rx_hash, + .xmo_rx_vlan_tag = i40e_xdp_rx_vlan_tag, }; static const struct net_device_ops i40e_netdev_ops = { From 907bd786209ed8c735db94889386aa48d2409d6a Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 14 May 2026 12:37:33 +1200 Subject: [PATCH 4/4] i40e: Avoid repeating RX filter warning When the i40e runs out of space for RX filters the driver switches to promiscuous mode and warns that it has done so. In scenarios with a large number of these filters this can generate a lot of warnings. For example: $ dmesg -c > /dev/null $ ip link add dev br0 type bridge vlan_filtering 1 vlan_default_pvid 1 $ ip link set dev eth7 master br0 $ bridge vlan add vid 1 dev eth7 pvid untagged self $ bridge vlan add vid 2-4094 dev eth7 tagged $ dmesg [ 25.601705] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF [ 25.601833] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF [ 25.601961] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF [ 25.602088] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF [ 25.602216] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF [ 25.602344] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF ... Use test_and_set_bit() so that the warning is only issued when the driver enables promiscuous mode and not on the addition of subsequent RX filters. Signed-off-by: Chris Packham Reviewed-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 719f45b5cb40..abbc71e815ae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2412,10 +2412,12 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, if (fcnt != num_add) { if (vsi->type == I40E_VSI_MAIN) { - set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); - dev_warn(&vsi->back->pdev->dev, - "Error %s adding RX filters on %s, promiscuous mode forced on\n", - libie_aq_str(aq_status), vsi_name); + if (!test_and_set_bit(__I40E_VSI_OVERFLOW_PROMISC, + vsi->state)) { + dev_warn(&vsi->back->pdev->dev, + "Error %s adding RX filters on %s, promiscuous mode forced on\n", + libie_aq_str(aq_status), vsi_name); + } } else if (vsi->type == I40E_VSI_SRIOV || vsi->type == I40E_VSI_VMDQ1 || vsi->type == I40E_VSI_VMDQ2) { @@ -2465,10 +2467,11 @@ i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name, } if (aq_ret) { - set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); - dev_warn(&vsi->back->pdev->dev, - "Error %s, forcing overflow promiscuous on %s\n", - libie_aq_str(hw->aq.asq_last_status), vsi_name); + if (!test_and_set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state)) { + dev_warn(&vsi->back->pdev->dev, + "Error %s, forcing overflow promiscuous on %s\n", + libie_aq_str(hw->aq.asq_last_status), vsi_name); + } } return aq_ret;