[Intel-wired-lan] [PATCH net-next v6 9/9] igc: Add support for XDP_REDIRECT action

Dvora Fuxbrumer dvorax.fuxbrumer at linux.intel.com
Tue Mar 2 08:22:58 UTC 2021


On 10/02/2021 23:58, Vedang Patel wrote:
> From: Andre Guedes <andre.guedes at intel.com>
> 
> This patch adds support for the XDP_REDIRECT action which enables XDP
> programs to redirect packets arriving at I225 NIC. It also implements
> the ndo_xdp_xmit ops, enabling the igc driver to transmit packets
> forwarded to it by xdp programs running on other interfaces.
> 
> The patch tweaks the driver's page counting and recycling scheme as
> described in the following two commits and implemented by other Intel
> drivers in order to properly support XDP_REDIRECT action:
>    - 8ce29c679a6e i40e: tweak page counting for XDP_REDIRECT
>    - 75aab4e10ae6 i40e: avoid premature Rx buffer reuse
> 
> This patch has been tested with the sample apps "xdp_redirect_cpu" and
> "xdp_redirect_map" located in samples/bpf/.
> 
> Signed-off-by: Andre Guedes <andre.guedes at intel.com>
> Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski at intel.com>
> Signed-off-by: Vedang Patel <vedang.patel at intel.com>
> ---
>   drivers/net/ethernet/intel/igc/igc_main.c | 84 ++++++++++++++++++++---
>   1 file changed, 73 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> index 90dae25dd5bb..2d7be4f2d48f 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -26,6 +26,7 @@
>   #define IGC_XDP_PASS		0
>   #define IGC_XDP_CONSUMED	BIT(0)
>   #define IGC_XDP_TX		BIT(1)
> +#define IGC_XDP_REDIRECT	BIT(2)
>   
>   static int debug = -1;
>   
> @@ -1506,11 +1507,18 @@ static void igc_process_skb_fields(struct igc_ring *rx_ring,
>   }
>   
>   static struct igc_rx_buffer *igc_get_rx_buffer(struct igc_ring *rx_ring,
> -					       const unsigned int size)
> +					       const unsigned int size,
> +					       int *rx_buffer_pgcnt)
>   {
>   	struct igc_rx_buffer *rx_buffer;
>   
>   	rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];
> +	*rx_buffer_pgcnt =
> +#if (PAGE_SIZE < 8192)
> +		page_count(rx_buffer->page);
> +#else
> +		0;
> +#endif
>   	prefetchw(rx_buffer->page);
>   
>   	/* we are reusing so sync this buffer for CPU use */
> @@ -1678,7 +1686,8 @@ static void igc_reuse_rx_page(struct igc_ring *rx_ring,
>   	new_buff->pagecnt_bias	= old_buff->pagecnt_bias;
>   }
>   
> -static bool igc_can_reuse_rx_page(struct igc_rx_buffer *rx_buffer)
> +static bool igc_can_reuse_rx_page(struct igc_rx_buffer *rx_buffer,
> +				  int rx_buffer_pgcnt)
>   {
>   	unsigned int pagecnt_bias = rx_buffer->pagecnt_bias;
>   	struct page *page = rx_buffer->page;
> @@ -1689,7 +1698,7 @@ static bool igc_can_reuse_rx_page(struct igc_rx_buffer *rx_buffer)
>   
>   #if (PAGE_SIZE < 8192)
>   	/* if we are only owner of page we can reuse it */
> -	if (unlikely((page_ref_count(page) - pagecnt_bias) > 1))
> +	if (unlikely((rx_buffer_pgcnt - pagecnt_bias) > 1))
>   		return false;
>   #else
>   #define IGC_LAST_OFFSET \
> @@ -1703,8 +1712,8 @@ static bool igc_can_reuse_rx_page(struct igc_rx_buffer *rx_buffer)
>   	 * the pagecnt_bias and page count so that we fully restock the
>   	 * number of references the driver holds.
>   	 */
> -	if (unlikely(!pagecnt_bias)) {
> -		page_ref_add(page, USHRT_MAX);
> +	if (unlikely(pagecnt_bias == 1)) {
> +		page_ref_add(page, USHRT_MAX - 1);
>   		rx_buffer->pagecnt_bias = USHRT_MAX;
>   	}
>   
> @@ -1777,9 +1786,10 @@ static bool igc_cleanup_headers(struct igc_ring *rx_ring,
>   }
>   
>   static void igc_put_rx_buffer(struct igc_ring *rx_ring,
> -			      struct igc_rx_buffer *rx_buffer)
> +			      struct igc_rx_buffer *rx_buffer,
> +			      int rx_buffer_pgcnt)
>   {
> -	if (igc_can_reuse_rx_page(rx_buffer)) {
> +	if (igc_can_reuse_rx_page(rx_buffer, rx_buffer_pgcnt)) {
>   		/* hand second half of page back to the ring */
>   		igc_reuse_rx_page(rx_ring, rx_buffer);
>   	} else {
> @@ -1845,7 +1855,8 @@ static bool igc_alloc_mapped_page(struct igc_ring *rx_ring,
>   	bi->dma = dma;
>   	bi->page = page;
>   	bi->page_offset = igc_rx_offset(rx_ring);
> -	bi->pagecnt_bias = 1;
> +	page_ref_add(page, USHRT_MAX - 1);
> +	bi->pagecnt_bias = USHRT_MAX;
>   
>   	return true;
>   }
> @@ -2041,6 +2052,12 @@ static struct sk_buff *igc_xdp_run_prog(struct igc_adapter *adapter,
>   		else
>   			res = IGC_XDP_TX;
>   		break;
> +	case XDP_REDIRECT:
> +		if (xdp_do_redirect(adapter->netdev, xdp, prog) < 0)
> +			res = IGC_XDP_CONSUMED;
> +		else
> +			res = IGC_XDP_REDIRECT;
> +		break;
>   	default:
>   		bpf_warn_invalid_xdp_action(act);
>   		fallthrough;
> @@ -2082,6 +2099,9 @@ static void igc_finalize_xdp(struct igc_adapter *adapter, int status)
>   		igc_flush_tx_descriptors(ring);
>   		__netif_tx_unlock(nq);
>   	}
> +
> +	if (status & IGC_XDP_REDIRECT)
> +		xdp_do_flush();
>   }
>   
>   static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
> @@ -2091,7 +2111,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
>   	struct igc_ring *rx_ring = q_vector->rx.ring;
>   	struct sk_buff *skb = rx_ring->skb;
>   	u16 cleaned_count = igc_desc_unused(rx_ring);
> -	int xdp_status = 0;
> +	int xdp_status = 0, rx_buffer_pgcnt;
>   
>   	while (likely(total_packets < budget)) {
>   		union igc_adv_rx_desc *rx_desc;
> @@ -2119,7 +2139,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
>   		 */
>   		dma_rmb();
>   
> -		rx_buffer = igc_get_rx_buffer(rx_ring, size);
> +		rx_buffer = igc_get_rx_buffer(rx_ring, size, &rx_buffer_pgcnt);
>   		truesize = igc_get_rx_frame_truesize(rx_ring, size);
>   
>   		pktbuf = page_address(rx_buffer->page) + rx_buffer->page_offset;
> @@ -2150,6 +2170,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
>   				rx_buffer->pagecnt_bias++;
>   				break;
>   			case IGC_XDP_TX:
> +			case IGC_XDP_REDIRECT:
>   				igc_rx_buffer_flip(rx_buffer, truesize);
>   				xdp_status |= xdp_res;
>   				break;
> @@ -2172,7 +2193,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
>   			break;
>   		}
>   
> -		igc_put_rx_buffer(rx_ring, rx_buffer);
> +		igc_put_rx_buffer(rx_ring, rx_buffer, rx_buffer_pgcnt);
>   		cleaned_count++;
>   
>   		/* fetch next buffer in frame if non-eop */
> @@ -5114,6 +5135,46 @@ static int igc_bpf(struct net_device *dev, struct netdev_bpf *bpf)
>   	}
>   }
>   
> +static int igc_xdp_xmit(struct net_device *dev, int num_frames,
> +			struct xdp_frame **frames, u32 flags)
> +{
> +	struct igc_adapter *adapter = netdev_priv(dev);
> +	int cpu = smp_processor_id();
> +	struct netdev_queue *nq;
> +	struct igc_ring *ring;
> +	int i, drops;
> +
> +	if (unlikely(test_bit(__IGC_DOWN, &adapter->state)))
> +		return -ENETDOWN;
> +
> +	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
> +		return -EINVAL;
> +
> +	ring = igc_xdp_get_tx_ring(adapter, cpu);
> +	nq = txring_txq(ring);
> +
> +	__netif_tx_lock(nq, cpu);
> +
> +	drops = 0;
> +	for (i = 0; i < num_frames; i++) {
> +		int err;
> +		struct xdp_frame *xdpf = frames[i];
> +
> +		err = igc_xdp_init_tx_descriptor(ring, xdpf);
> +		if (err) {
> +			xdp_return_frame_rx_napi(xdpf);
> +			drops++;
> +		}
> +	}
> +
> +	if (flags & XDP_XMIT_FLUSH)
> +		igc_flush_tx_descriptors(ring);
> +
> +	__netif_tx_unlock(nq);
> +
> +	return num_frames - drops;
> +}
> +
>   static const struct net_device_ops igc_netdev_ops = {
>   	.ndo_open		= igc_open,
>   	.ndo_stop		= igc_close,
> @@ -5128,6 +5189,7 @@ static const struct net_device_ops igc_netdev_ops = {
>   	.ndo_do_ioctl		= igc_ioctl,
>   	.ndo_setup_tc		= igc_setup_tc,
>   	.ndo_bpf		= igc_bpf,
> +	.ndo_xdp_xmit		= igc_xdp_xmit,
>   };
>   
>   /* PCIe configuration access */
> 
Tested-by: Dvora Fuxbrumer <dvorax.fuxbrumer at linux.intel.com>


More information about the Intel-wired-lan mailing list