[PATCH net 1/2] net: pass the XDP action to generic_xdp_tx() for tracing
From: Vladimir Oltean
Date: Fri Oct 30 2026 - 07:53:05 EST
When generic_xdp_tx() fails to transmit an skb, it reports the drop
through trace_xdp_exception() with the action hardcoded to XDP_TX.
That was right when generic XDP was added, and XDP_TX was the only
caller. Since then, two XDP_REDIRECT paths have started to transmit
through generic_xdp_tx() as well:
- xdp_do_generic_redirect(), for a redirect to an ifindex
- dev_map_generic_redirect(), for DEVMAP and DEVMAP_HASH redirects,
including broadcast
Drops on those paths are reported as XDP_TX, an action which the
program never returned. Tools which count xdp:xdp_exception events per
action, such as the samples/bpf programs built on xdp_sample, therefore
attribute generic XDP redirect drops to the wrong action. Drivers with
native XDP already report XDP_TX and XDP_REDIRECT drops separately.
Add an "act" argument to generic_xdp_tx(), and have each caller pass
the action that led to the transmission: XDP_TX from do_xdp_generic(),
XDP_REDIRECT from the two redirect paths. generic_xdp_tx() is not
exported, so these are all of its users.
Fixes: 6103aa96ec07 ("net: implement XDP_REDIRECT for xdp generic")
Fixes: 6d5fc1957989 ("xdp: Fix handling of devmap in generic XDP")
Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
---
include/linux/netdevice.h | 3 ++-
kernel/bpf/devmap.c | 2 +-
net/core/dev.c | 7 ++++---
net/core/filter.c | 2 +-
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c0ae442c4344..b988afe797c3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4355,7 +4355,8 @@ static inline void dev_consume_skb_any(struct sk_buff *skb)
u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
const struct bpf_prog *xdp_prog);
-void generic_xdp_tx(struct sk_buff *skb, const struct bpf_prog *xdp_prog);
+void generic_xdp_tx(struct sk_buff *skb, const struct bpf_prog *xdp_prog,
+ u32 act);
int do_xdp_generic(const struct bpf_prog *xdp_prog, struct sk_buff **pskb);
int netif_rx(struct sk_buff *skb);
int __netif_rx(struct sk_buff *skb);
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index 1b8f0d7f8455..c73e8797c0fc 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -731,7 +731,7 @@ int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb,
return 0;
skb->dev = dst->dev;
- generic_xdp_tx(skb, xdp_prog);
+ generic_xdp_tx(skb, xdp_prog, XDP_REDIRECT);
return 0;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 3ac700d37e59..88860185f583 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5851,7 +5851,8 @@ static u32 netif_receive_generic_xdp(struct sk_buff **pskb,
* and DDOS attacks will be more effective. In-driver-XDP use dedicated TX
* queues, so they do not have this starvation issue.
*/
-void generic_xdp_tx(struct sk_buff *skb, const struct bpf_prog *xdp_prog)
+void generic_xdp_tx(struct sk_buff *skb, const struct bpf_prog *xdp_prog,
+ u32 act)
{
struct net_device *dev = skb->dev;
struct netdev_queue *txq;
@@ -5868,7 +5869,7 @@ void generic_xdp_tx(struct sk_buff *skb, const struct bpf_prog *xdp_prog)
}
HARD_TX_UNLOCK(dev, txq);
if (free_skb) {
- trace_xdp_exception(dev, xdp_prog, XDP_TX);
+ trace_xdp_exception(dev, xdp_prog, act);
dev_core_stats_tx_dropped_inc(dev);
kfree_skb(skb);
}
@@ -5896,7 +5897,7 @@ int do_xdp_generic(const struct bpf_prog *xdp_prog, struct sk_buff **pskb)
goto out_redir;
break;
case XDP_TX:
- generic_xdp_tx(*pskb, xdp_prog);
+ generic_xdp_tx(*pskb, xdp_prog, XDP_TX);
break;
}
bpf_net_ctx_clear(bpf_net_ctx);
diff --git a/net/core/filter.c b/net/core/filter.c
index 09c60a58c82a..7c6f8bcc6c29 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -4821,7 +4821,7 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb,
skb->dev = fwd;
_trace_xdp_redirect(dev, xdp_prog, ri->tgt_index);
- generic_xdp_tx(skb, xdp_prog);
+ generic_xdp_tx(skb, xdp_prog, XDP_REDIRECT);
return 0;
}
--
2.43.0