Skip to content

Commit 9d2b68c

Browse files
vladimirolteandavem330
authored andcommitted
net: enetc: add support for XDP_REDIRECT
The driver implementation of the XDP_REDIRECT action reuses parts from XDP_TX, most notably the enetc_xdp_tx function which transmits an array of TX software BDs. Only this time, the buffers don't have DMA mappings, we need to create them. When a BPF program reaches the XDP_REDIRECT verdict for a frame, we can employ the same buffer reuse strategy as for the normal processing path and for XDP_PASS: we can flip to the other page half and seed that to the RX ring. Note that scatter/gather support is there, but disabled due to lack of multi-buffer support in XDP (which is added by this series): https://patchwork.kernel.org/project/netdevbpf/cover/cover.1616179034.git.lorenzo@kernel.org/ Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent d6a2829 commit 9d2b68c

4 files changed

Lines changed: 218 additions & 12 deletions

File tree

drivers/net/ethernet/freescale/enetc/enetc.c

Lines changed: 201 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@
88
#include <linux/vmalloc.h>
99
#include <net/pkt_sched.h>
1010

11+
static struct sk_buff *enetc_tx_swbd_get_skb(struct enetc_tx_swbd *tx_swbd)
12+
{
13+
if (tx_swbd->is_xdp_tx || tx_swbd->is_xdp_redirect)
14+
return NULL;
15+
16+
return tx_swbd->skb;
17+
}
18+
19+
static struct xdp_frame *
20+
enetc_tx_swbd_get_xdp_frame(struct enetc_tx_swbd *tx_swbd)
21+
{
22+
if (tx_swbd->is_xdp_redirect)
23+
return tx_swbd->xdp_frame;
24+
25+
return NULL;
26+
}
27+
1128
static void enetc_unmap_tx_buff(struct enetc_bdr *tx_ring,
1229
struct enetc_tx_swbd *tx_swbd)
1330
{
@@ -25,14 +42,20 @@ static void enetc_unmap_tx_buff(struct enetc_bdr *tx_ring,
2542
tx_swbd->dma = 0;
2643
}
2744

28-
static void enetc_free_tx_skb(struct enetc_bdr *tx_ring,
29-
struct enetc_tx_swbd *tx_swbd)
45+
static void enetc_free_tx_frame(struct enetc_bdr *tx_ring,
46+
struct enetc_tx_swbd *tx_swbd)
3047
{
48+
struct xdp_frame *xdp_frame = enetc_tx_swbd_get_xdp_frame(tx_swbd);
49+
struct sk_buff *skb = enetc_tx_swbd_get_skb(tx_swbd);
50+
3151
if (tx_swbd->dma)
3252
enetc_unmap_tx_buff(tx_ring, tx_swbd);
3353

34-
if (tx_swbd->skb) {
35-
dev_kfree_skb_any(tx_swbd->skb);
54+
if (xdp_frame) {
55+
xdp_return_frame(tx_swbd->xdp_frame);
56+
tx_swbd->xdp_frame = NULL;
57+
} else if (skb) {
58+
dev_kfree_skb_any(skb);
3659
tx_swbd->skb = NULL;
3760
}
3861
}
@@ -183,7 +206,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb,
183206

184207
do {
185208
tx_swbd = &tx_ring->tx_swbd[i];
186-
enetc_free_tx_skb(tx_ring, tx_swbd);
209+
enetc_free_tx_frame(tx_ring, tx_swbd);
187210
if (i == 0)
188211
i = tx_ring->bd_count;
189212
i--;
@@ -381,6 +404,9 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
381404
do_tstamp = false;
382405

383406
while (bds_to_clean && tx_frm_cnt < ENETC_DEFAULT_TX_WORK) {
407+
struct xdp_frame *xdp_frame = enetc_tx_swbd_get_xdp_frame(tx_swbd);
408+
struct sk_buff *skb = enetc_tx_swbd_get_skb(tx_swbd);
409+
384410
if (unlikely(tx_swbd->check_wb)) {
385411
struct enetc_ndev_priv *priv = netdev_priv(ndev);
386412
union enetc_tx_bd *txbd;
@@ -400,12 +426,15 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
400426
else if (likely(tx_swbd->dma))
401427
enetc_unmap_tx_buff(tx_ring, tx_swbd);
402428

403-
if (tx_swbd->skb) {
429+
if (xdp_frame) {
430+
xdp_return_frame(xdp_frame);
431+
tx_swbd->xdp_frame = NULL;
432+
} else if (skb) {
404433
if (unlikely(do_tstamp)) {
405-
enetc_tstamp_tx(tx_swbd->skb, tstamp);
434+
enetc_tstamp_tx(skb, tstamp);
406435
do_tstamp = false;
407436
}
408-
napi_consume_skb(tx_swbd->skb, napi_budget);
437+
napi_consume_skb(skb, napi_budget);
409438
tx_swbd->skb = NULL;
410439
}
411440

@@ -827,6 +856,109 @@ static bool enetc_xdp_tx(struct enetc_bdr *tx_ring,
827856
return true;
828857
}
829858

859+
static int enetc_xdp_frame_to_xdp_tx_swbd(struct enetc_bdr *tx_ring,
860+
struct enetc_tx_swbd *xdp_tx_arr,
861+
struct xdp_frame *xdp_frame)
862+
{
863+
struct enetc_tx_swbd *xdp_tx_swbd = &xdp_tx_arr[0];
864+
struct skb_shared_info *shinfo;
865+
void *data = xdp_frame->data;
866+
int len = xdp_frame->len;
867+
skb_frag_t *frag;
868+
dma_addr_t dma;
869+
unsigned int f;
870+
int n = 0;
871+
872+
dma = dma_map_single(tx_ring->dev, data, len, DMA_TO_DEVICE);
873+
if (unlikely(dma_mapping_error(tx_ring->dev, dma))) {
874+
netdev_err(tx_ring->ndev, "DMA map error\n");
875+
return -1;
876+
}
877+
878+
xdp_tx_swbd->dma = dma;
879+
xdp_tx_swbd->dir = DMA_TO_DEVICE;
880+
xdp_tx_swbd->len = len;
881+
xdp_tx_swbd->is_xdp_redirect = true;
882+
xdp_tx_swbd->is_eof = false;
883+
xdp_tx_swbd->xdp_frame = NULL;
884+
885+
n++;
886+
xdp_tx_swbd = &xdp_tx_arr[n];
887+
888+
shinfo = xdp_get_shared_info_from_frame(xdp_frame);
889+
890+
for (f = 0, frag = &shinfo->frags[0]; f < shinfo->nr_frags;
891+
f++, frag++) {
892+
data = skb_frag_address(frag);
893+
len = skb_frag_size(frag);
894+
895+
dma = dma_map_single(tx_ring->dev, data, len, DMA_TO_DEVICE);
896+
if (unlikely(dma_mapping_error(tx_ring->dev, dma))) {
897+
/* Undo the DMA mapping for all fragments */
898+
while (n-- >= 0)
899+
enetc_unmap_tx_buff(tx_ring, &xdp_tx_arr[n]);
900+
901+
netdev_err(tx_ring->ndev, "DMA map error\n");
902+
return -1;
903+
}
904+
905+
xdp_tx_swbd->dma = dma;
906+
xdp_tx_swbd->dir = DMA_TO_DEVICE;
907+
xdp_tx_swbd->len = len;
908+
xdp_tx_swbd->is_xdp_redirect = true;
909+
xdp_tx_swbd->is_eof = false;
910+
xdp_tx_swbd->xdp_frame = NULL;
911+
912+
n++;
913+
xdp_tx_swbd = &xdp_tx_arr[n];
914+
}
915+
916+
xdp_tx_arr[n - 1].is_eof = true;
917+
xdp_tx_arr[n - 1].xdp_frame = xdp_frame;
918+
919+
return n;
920+
}
921+
922+
int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
923+
struct xdp_frame **frames, u32 flags)
924+
{
925+
struct enetc_tx_swbd xdp_redirect_arr[ENETC_MAX_SKB_FRAGS] = {0};
926+
struct enetc_ndev_priv *priv = netdev_priv(ndev);
927+
struct enetc_bdr *tx_ring;
928+
int xdp_tx_bd_cnt, i, k;
929+
int xdp_tx_frm_cnt = 0;
930+
931+
tx_ring = priv->tx_ring[smp_processor_id()];
932+
933+
prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use));
934+
935+
for (k = 0; k < num_frames; k++) {
936+
xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring,
937+
xdp_redirect_arr,
938+
frames[k]);
939+
if (unlikely(xdp_tx_bd_cnt < 0))
940+
break;
941+
942+
if (unlikely(!enetc_xdp_tx(tx_ring, xdp_redirect_arr,
943+
xdp_tx_bd_cnt))) {
944+
for (i = 0; i < xdp_tx_bd_cnt; i++)
945+
enetc_unmap_tx_buff(tx_ring,
946+
&xdp_redirect_arr[i]);
947+
tx_ring->stats.xdp_tx_drops++;
948+
break;
949+
}
950+
951+
xdp_tx_frm_cnt++;
952+
}
953+
954+
if (unlikely((flags & XDP_XMIT_FLUSH) || k != xdp_tx_frm_cnt))
955+
enetc_update_tx_ring_tail(tx_ring);
956+
957+
tx_ring->stats.xdp_tx += xdp_tx_frm_cnt;
958+
959+
return xdp_tx_frm_cnt;
960+
}
961+
830962
static void enetc_map_rx_buff_to_xdp(struct enetc_bdr *rx_ring, int i,
831963
struct xdp_buff *xdp_buff, u16 size)
832964
{
@@ -948,14 +1080,31 @@ static void enetc_xdp_drop(struct enetc_bdr *rx_ring, int rx_ring_first,
9481080
rx_ring->stats.xdp_drops++;
9491081
}
9501082

1083+
static void enetc_xdp_free(struct enetc_bdr *rx_ring, int rx_ring_first,
1084+
int rx_ring_last)
1085+
{
1086+
while (rx_ring_first != rx_ring_last) {
1087+
struct enetc_rx_swbd *rx_swbd = &rx_ring->rx_swbd[rx_ring_first];
1088+
1089+
if (rx_swbd->page) {
1090+
dma_unmap_page(rx_ring->dev, rx_swbd->dma, PAGE_SIZE,
1091+
rx_swbd->dir);
1092+
__free_page(rx_swbd->page);
1093+
rx_swbd->page = NULL;
1094+
}
1095+
enetc_bdr_idx_inc(rx_ring, &rx_ring_first);
1096+
}
1097+
rx_ring->stats.xdp_redirect_failures++;
1098+
}
1099+
9511100
static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
9521101
struct napi_struct *napi, int work_limit,
9531102
struct bpf_prog *prog)
9541103
{
1104+
int xdp_tx_bd_cnt, xdp_tx_frm_cnt = 0, xdp_redirect_frm_cnt = 0;
9551105
struct enetc_tx_swbd xdp_tx_arr[ENETC_MAX_SKB_FRAGS] = {0};
9561106
struct enetc_ndev_priv *priv = netdev_priv(rx_ring->ndev);
9571107
struct enetc_bdr *tx_ring = priv->tx_ring[rx_ring->index];
958-
int xdp_tx_bd_cnt, xdp_tx_frm_cnt = 0;
9591108
int rx_frm_cnt = 0, rx_byte_cnt = 0;
9601109
int cleaned_cnt, i;
9611110
u32 xdp_act;
@@ -969,6 +1118,7 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
9691118
int orig_i, orig_cleaned_cnt;
9701119
struct xdp_buff xdp_buff;
9711120
struct sk_buff *skb;
1121+
int tmp_orig_i, err;
9721122
u32 bd_status;
9731123

9741124
rxbd = enetc_rxbd(rx_ring, i);
@@ -1026,6 +1176,43 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
10261176
rx_ring->xdp.xdp_tx_in_flight += xdp_tx_bd_cnt;
10271177
xdp_tx_frm_cnt++;
10281178
}
1179+
break;
1180+
case XDP_REDIRECT:
1181+
/* xdp_return_frame does not support S/G in the sense
1182+
* that it leaks the fragments (__xdp_return should not
1183+
* call page_frag_free only for the initial buffer).
1184+
* Until XDP_REDIRECT gains support for S/G let's keep
1185+
* the code structure in place, but dead. We drop the
1186+
* S/G frames ourselves to avoid memory leaks which
1187+
* would otherwise leave the kernel OOM.
1188+
*/
1189+
if (unlikely(cleaned_cnt - orig_cleaned_cnt != 1)) {
1190+
enetc_xdp_drop(rx_ring, orig_i, i);
1191+
rx_ring->stats.xdp_redirect_sg++;
1192+
break;
1193+
}
1194+
1195+
tmp_orig_i = orig_i;
1196+
1197+
while (orig_i != i) {
1198+
enetc_put_rx_buff(rx_ring,
1199+
&rx_ring->rx_swbd[orig_i]);
1200+
enetc_bdr_idx_inc(rx_ring, &orig_i);
1201+
}
1202+
1203+
err = xdp_do_redirect(rx_ring->ndev, &xdp_buff, prog);
1204+
if (unlikely(err)) {
1205+
enetc_xdp_free(rx_ring, tmp_orig_i, i);
1206+
} else {
1207+
xdp_redirect_frm_cnt++;
1208+
rx_ring->stats.xdp_redirect++;
1209+
}
1210+
1211+
if (unlikely(xdp_redirect_frm_cnt > ENETC_DEFAULT_TX_WORK)) {
1212+
xdp_do_flush_map();
1213+
xdp_redirect_frm_cnt = 0;
1214+
}
1215+
10291216
break;
10301217
default:
10311218
bpf_warn_invalid_xdp_action(xdp_act);
@@ -1039,6 +1226,9 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
10391226
rx_ring->stats.packets += rx_frm_cnt;
10401227
rx_ring->stats.bytes += rx_byte_cnt;
10411228

1229+
if (xdp_redirect_frm_cnt)
1230+
xdp_do_flush_map();
1231+
10421232
if (xdp_tx_frm_cnt)
10431233
enetc_update_tx_ring_tail(tx_ring);
10441234

@@ -1173,7 +1363,7 @@ static void enetc_free_txbdr(struct enetc_bdr *txr)
11731363
int size, i;
11741364

11751365
for (i = 0; i < txr->bd_count; i++)
1176-
enetc_free_tx_skb(txr, &txr->tx_swbd[i]);
1366+
enetc_free_tx_frame(txr, &txr->tx_swbd[i]);
11771367

11781368
size = txr->bd_count * sizeof(union enetc_tx_bd);
11791369

@@ -1290,7 +1480,7 @@ static void enetc_free_tx_ring(struct enetc_bdr *tx_ring)
12901480
for (i = 0; i < tx_ring->bd_count; i++) {
12911481
struct enetc_tx_swbd *tx_swbd = &tx_ring->tx_swbd[i];
12921482

1293-
enetc_free_tx_skb(tx_ring, tx_swbd);
1483+
enetc_free_tx_frame(tx_ring, tx_swbd);
12941484
}
12951485

12961486
tx_ring->next_to_clean = 0;

drivers/net/ethernet/freescale/enetc/enetc.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
(ETH_FCS_LEN + ETH_HLEN + VLAN_HLEN))
2020

2121
struct enetc_tx_swbd {
22-
struct sk_buff *skb;
22+
union {
23+
struct sk_buff *skb;
24+
struct xdp_frame *xdp_frame;
25+
};
2326
dma_addr_t dma;
2427
struct page *page; /* valid only if is_xdp_tx */
2528
u16 page_offset; /* valid only if is_xdp_tx */
@@ -30,6 +33,7 @@ struct enetc_tx_swbd {
3033
u8 do_tstamp:1;
3134
u8 is_eof:1;
3235
u8 is_xdp_tx:1;
36+
u8 is_xdp_redirect:1;
3337
};
3438

3539
#define ENETC_RX_MAXFRM_SIZE ENETC_MAC_MAXFRM_SIZE
@@ -61,6 +65,9 @@ struct enetc_ring_stats {
6165
unsigned int xdp_drops;
6266
unsigned int xdp_tx;
6367
unsigned int xdp_tx_drops;
68+
unsigned int xdp_redirect;
69+
unsigned int xdp_redirect_failures;
70+
unsigned int xdp_redirect_sg;
6471
unsigned int recycles;
6572
unsigned int recycle_failures;
6673
};
@@ -354,6 +361,8 @@ int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd);
354361
int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
355362
void *type_data);
356363
int enetc_setup_bpf(struct net_device *dev, struct netdev_bpf *xdp);
364+
int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
365+
struct xdp_frame **frames, u32 flags);
357366

358367
/* ethtool */
359368
void enetc_set_ethtool_ops(struct net_device *ndev);

drivers/net/ethernet/freescale/enetc/enetc_ethtool.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ static const char rx_ring_stats[][ETH_GSTRING_LEN] = {
195195
"Rx ring %2d XDP drops",
196196
"Rx ring %2d recycles",
197197
"Rx ring %2d recycle failures",
198+
"Rx ring %2d redirects",
199+
"Rx ring %2d redirect failures",
200+
"Rx ring %2d redirect S/G",
198201
};
199202

200203
static const char tx_ring_stats[][ETH_GSTRING_LEN] = {
@@ -284,6 +287,9 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
284287
data[o++] = priv->rx_ring[i]->stats.xdp_drops;
285288
data[o++] = priv->rx_ring[i]->stats.recycles;
286289
data[o++] = priv->rx_ring[i]->stats.recycle_failures;
290+
data[o++] = priv->rx_ring[i]->stats.xdp_redirect;
291+
data[o++] = priv->rx_ring[i]->stats.xdp_redirect_failures;
292+
data[o++] = priv->rx_ring[i]->stats.xdp_redirect_sg;
287293
}
288294

289295
if (!enetc_si_is_pf(priv->si))

drivers/net/ethernet/freescale/enetc/enetc_pf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ static const struct net_device_ops enetc_ndev_ops = {
708708
.ndo_do_ioctl = enetc_ioctl,
709709
.ndo_setup_tc = enetc_setup_tc,
710710
.ndo_bpf = enetc_setup_bpf,
711+
.ndo_xdp_xmit = enetc_xdp_xmit,
711712
};
712713

713714
static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,

0 commit comments

Comments
 (0)