162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020, Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/if_vlan.h>
562306a36Sopenharmony_ci#include <net/xdp_sock_drv.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "igc.h"
862306a36Sopenharmony_ci#include "igc_xdp.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ciint igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog,
1162306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	struct net_device *dev = adapter->netdev;
1462306a36Sopenharmony_ci	bool if_running = netif_running(dev);
1562306a36Sopenharmony_ci	struct bpf_prog *old_prog;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	if (dev->mtu > ETH_DATA_LEN) {
1862306a36Sopenharmony_ci		/* For now, the driver doesn't support XDP functionality with
1962306a36Sopenharmony_ci		 * jumbo frames so we return error.
2062306a36Sopenharmony_ci		 */
2162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Jumbo frames not supported");
2262306a36Sopenharmony_ci		return -EOPNOTSUPP;
2362306a36Sopenharmony_ci	}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (if_running)
2662306a36Sopenharmony_ci		igc_close(dev);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	old_prog = xchg(&adapter->xdp_prog, prog);
2962306a36Sopenharmony_ci	if (old_prog)
3062306a36Sopenharmony_ci		bpf_prog_put(old_prog);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (prog)
3362306a36Sopenharmony_ci		xdp_features_set_redirect_target(dev, true);
3462306a36Sopenharmony_ci	else
3562306a36Sopenharmony_ci		xdp_features_clear_redirect_target(dev);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (if_running)
3862306a36Sopenharmony_ci		igc_open(dev);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int igc_xdp_enable_pool(struct igc_adapter *adapter,
4462306a36Sopenharmony_ci			       struct xsk_buff_pool *pool, u16 queue_id)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct net_device *ndev = adapter->netdev;
4762306a36Sopenharmony_ci	struct device *dev = &adapter->pdev->dev;
4862306a36Sopenharmony_ci	struct igc_ring *rx_ring, *tx_ring;
4962306a36Sopenharmony_ci	struct napi_struct *napi;
5062306a36Sopenharmony_ci	bool needs_reset;
5162306a36Sopenharmony_ci	u32 frame_size;
5262306a36Sopenharmony_ci	int err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (queue_id >= adapter->num_rx_queues ||
5562306a36Sopenharmony_ci	    queue_id >= adapter->num_tx_queues)
5662306a36Sopenharmony_ci		return -EINVAL;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	frame_size = xsk_pool_get_rx_frame_size(pool);
5962306a36Sopenharmony_ci	if (frame_size < ETH_FRAME_LEN + VLAN_HLEN * 2) {
6062306a36Sopenharmony_ci		/* When XDP is enabled, the driver doesn't support frames that
6162306a36Sopenharmony_ci		 * span over multiple buffers. To avoid that, we check if xsk
6262306a36Sopenharmony_ci		 * frame size is big enough to fit the max ethernet frame size
6362306a36Sopenharmony_ci		 * + vlan double tagging.
6462306a36Sopenharmony_ci		 */
6562306a36Sopenharmony_ci		return -EOPNOTSUPP;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	err = xsk_pool_dma_map(pool, dev, IGC_RX_DMA_ATTR);
6962306a36Sopenharmony_ci	if (err) {
7062306a36Sopenharmony_ci		netdev_err(ndev, "Failed to map xsk pool\n");
7162306a36Sopenharmony_ci		return err;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	needs_reset = netif_running(adapter->netdev) && igc_xdp_is_enabled(adapter);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	rx_ring = adapter->rx_ring[queue_id];
7762306a36Sopenharmony_ci	tx_ring = adapter->tx_ring[queue_id];
7862306a36Sopenharmony_ci	/* Rx and Tx rings share the same napi context. */
7962306a36Sopenharmony_ci	napi = &rx_ring->q_vector->napi;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (needs_reset) {
8262306a36Sopenharmony_ci		igc_disable_rx_ring(rx_ring);
8362306a36Sopenharmony_ci		igc_disable_tx_ring(tx_ring);
8462306a36Sopenharmony_ci		napi_disable(napi);
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	set_bit(IGC_RING_FLAG_AF_XDP_ZC, &rx_ring->flags);
8862306a36Sopenharmony_ci	set_bit(IGC_RING_FLAG_AF_XDP_ZC, &tx_ring->flags);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (needs_reset) {
9162306a36Sopenharmony_ci		napi_enable(napi);
9262306a36Sopenharmony_ci		igc_enable_rx_ring(rx_ring);
9362306a36Sopenharmony_ci		igc_enable_tx_ring(tx_ring);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		err = igc_xsk_wakeup(ndev, queue_id, XDP_WAKEUP_RX);
9662306a36Sopenharmony_ci		if (err) {
9762306a36Sopenharmony_ci			xsk_pool_dma_unmap(pool, IGC_RX_DMA_ATTR);
9862306a36Sopenharmony_ci			return err;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int igc_xdp_disable_pool(struct igc_adapter *adapter, u16 queue_id)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct igc_ring *rx_ring, *tx_ring;
10862306a36Sopenharmony_ci	struct xsk_buff_pool *pool;
10962306a36Sopenharmony_ci	struct napi_struct *napi;
11062306a36Sopenharmony_ci	bool needs_reset;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (queue_id >= adapter->num_rx_queues ||
11362306a36Sopenharmony_ci	    queue_id >= adapter->num_tx_queues)
11462306a36Sopenharmony_ci		return -EINVAL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	pool = xsk_get_pool_from_qid(adapter->netdev, queue_id);
11762306a36Sopenharmony_ci	if (!pool)
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	needs_reset = netif_running(adapter->netdev) && igc_xdp_is_enabled(adapter);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	rx_ring = adapter->rx_ring[queue_id];
12362306a36Sopenharmony_ci	tx_ring = adapter->tx_ring[queue_id];
12462306a36Sopenharmony_ci	/* Rx and Tx rings share the same napi context. */
12562306a36Sopenharmony_ci	napi = &rx_ring->q_vector->napi;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (needs_reset) {
12862306a36Sopenharmony_ci		igc_disable_rx_ring(rx_ring);
12962306a36Sopenharmony_ci		igc_disable_tx_ring(tx_ring);
13062306a36Sopenharmony_ci		napi_disable(napi);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	xsk_pool_dma_unmap(pool, IGC_RX_DMA_ATTR);
13462306a36Sopenharmony_ci	clear_bit(IGC_RING_FLAG_AF_XDP_ZC, &rx_ring->flags);
13562306a36Sopenharmony_ci	clear_bit(IGC_RING_FLAG_AF_XDP_ZC, &tx_ring->flags);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (needs_reset) {
13862306a36Sopenharmony_ci		napi_enable(napi);
13962306a36Sopenharmony_ci		igc_enable_rx_ring(rx_ring);
14062306a36Sopenharmony_ci		igc_enable_tx_ring(tx_ring);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint igc_xdp_setup_pool(struct igc_adapter *adapter, struct xsk_buff_pool *pool,
14762306a36Sopenharmony_ci		       u16 queue_id)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	return pool ? igc_xdp_enable_pool(adapter, pool, queue_id) :
15062306a36Sopenharmony_ci		      igc_xdp_disable_pool(adapter, queue_id);
15162306a36Sopenharmony_ci}
152