Skip to contentSkip to conversation

xdp: allow disable GRO per packet by XDP

RFC In review Yan Zhai 6 participants 34 messages Jun 20, 2024 – Jul 3, 2024

Subsystems (12)
lore
Share & tools
lore

Yan Zhai

author
#0 lore raw

cover xdp: allow disable GRO per packet by XDP

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

A natural approach to make this more flexible is to use XDP to control GRO behavior. But current semantic gap between XDP buffer/frame and socket buffer requires some new primitives.

This change set proposes a control bit gro_disabled on skbs to determine if GRO should work on an skb or not. To manipulate this bit, we introduce a new XDP kfunc bpf_xdp_disable_gro as well as generic helpers xdp_frame/buff_fixup_skb_offloading.

The expected working flow is that:
* XDP program examines packets and can call bpf_xdp_disable_gro to disable GRO on a packet
* Device drivers need to call xdp_buff_fixup_skb_offloading (or equivalent version for xdp_frame), to check if skb->gro_disabled needs to be set.
* The kernel will elide GRO later if this bit is used.

Initially we only modified a few drivers for demonstration purpose. The driver side changes is optional and also incremental depending on vendors' agenda. Any suggestions are welcome!

Jesper Dangaard Brouer (1):
mlx5: move xdp_buff scope one level up

Yan Zhai (8):
skb: introduce gro_disabled bit
xdp: add XDP_FLAGS_GRO_DISABLED flag
xdp: implement bpf_xdp_disable_gro kfunc
bnxt: apply XDP offloading fixup when building skb
ice: apply XDP offloading fixup when building skb
veth: apply XDP offloading fixup when building skb
mlx5: apply XDP offloading fixup when building skb
bpf: selftests: test disabling GRO by XDP

 drivers/net/ethernet/broadcom/bnxt/bnxt.c     |   4 +
 drivers/net/ethernet/intel/ice/ice_txrx.c     |   2 +
 drivers/net/ethernet/intel/ice/ice_xsk.c      |   6 +-
 drivers/net/ethernet/mellanox/mlx5/core/en.h  |   6 +-
 .../ethernet/mellanox/mlx5/core/en/xsk/rx.c   |  10 +-
 .../ethernet/mellanox/mlx5/core/en/xsk/rx.h   |   6 +-
 .../net/ethernet/mellanox/mlx5/core/en_rx.c   | 117 ++++++++++-------
 drivers/net/veth.c                            |   4 +
 include/linux/netdevice.h                     |   9 +-
 include/linux/skbuff.h                        |  10 ++
 include/net/xdp.h                             |  38 +++++-
 include/net/xdp_sock_drv.h                    |   2 +-
 net/Kconfig                                   |  10 ++
 net/core/gro.c                                |   2 +-
 net/core/gro_cells.c                          |   2 +-
 net/core/skbuff.c                             |   4 +
 net/core/xdp.c                                |  27 +++-
 tools/testing/selftests/bpf/config            |   1 +
 .../selftests/bpf/prog_tests/xdp_offloading.c | 122 ++++++++++++++++++
 .../selftests/bpf/progs/xdp_offloading.c      |  50 +++++++
 20 files changed, 369 insertions(+), 63 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_offloading.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_offloading.c
Signature · 1 line
2.30.2
author
#1 lore raw

RFC 1/9 skb: introduce gro_disabled bit

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +7 -2
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
 	((dev)->devlink_port = (port));				\
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-	if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+	if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
 		return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+	return skb->gro_disabled;
+#else
 	return false;
+#endif
 }
 
 #define	NETDEV_ALIGN		32
include/linux/skbuff.h +10 -0
index f4cda3fbdb75..48b10ece95b5 100644
@@ -1008,6 +1008,9 @@ struct sk_buff {
 #if IS_ENABLED(CONFIG_IP_SCTP)
 	__u8			csum_not_inet:1;
 #endif
+#ifdef CONFIG_SKB_GRO_CONTROL
+	__u8			gro_disabled:1;
+#endif
 
 #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
 	__u16			tc_index;	/* traffic control index */
@@ -1215,6 +1218,13 @@ static inline bool skb_wifi_acked_valid(const struct sk_buff *skb)
 #endif
 }
 
+static inline void skb_disable_gro(struct sk_buff *skb)
+{
+#ifdef CONFIG_SKB_GRO_CONTROL
+	skb->gro_disabled = 1;
+#endif
+}
+
 /**
  * skb_unref - decrement the skb's reference count
  * @skb: buffer
net/Kconfig +10 -0
index 9fe65fa26e48..47d1ee92df15 100644
@@ -289,6 +289,16 @@ config MAX_SKB_FRAGS
 	  and in drivers using build_skb().
 	  If unsure, say 17.
 
+config SKB_GRO_CONTROL
+	bool "allow disable GRO on per-packet basis"
+	default y
+	help
+	  By default GRO can only be enabled or disabled per network device.
+	  This can be cumbersome for certain scenarios.
+	  Toggling this option will allow disabling GRO for selected packets,
+	  e.g. by XDP programs, so that it is more flexibile.
+	  Extra overhead should be minimal.
+
 config RPS
 	bool "Receive packet steering"
 	depends on SMP && SYSFS
net/core/gro.c +1 -1
index b3b43de1a650..46232a0d1983 100644
@@ -476,7 +476,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
 	enum gro_result ret;
 	int same_flow;
 
-	if (netif_elide_gro(skb->dev))
+	if (netif_elide_gro(skb))
 		goto normal;
 
 	gro_list_prepare(&gro_list->list, skb);
net/core/gro_cells.c +1 -1
index ff8e5b64bf6b..1bf15783300f 100644
@@ -20,7 +20,7 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto drop;
 
-	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
+	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(skb)) {
 		res = netif_rx(skb);
 		goto unlock;
 	}
net/core/skbuff.c +4 -0
index 2315c088e91d..82bd297921c1 100644
@@ -6030,6 +6030,10 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 	ipvs_reset(skb);
 	skb->mark = 0;
 	skb_clear_tstamp(skb);
+#ifdef CONFIG_SKB_GRO_CONTROL
+	/* hand back GRO control to next netns */
+	skb->gro_disabled = 0;
+#endif
 }
 EXPORT_SYMBOL_GPL(skb_scrub_packet);
 
Signature · 1 line
2.30.2
#2 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:10 -0700

Quoted 8 lines

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

Quoted 7 lines

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two.

Implement it in the kernel then? :D

Quoted 75 lines

On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +7 -2
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
 	((dev)->devlink_port = (port));				\
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-	if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+	if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
 		return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+	return skb->gro_disabled;
+#else
 	return false;
+#endif
 }
 
 #define	NETDEV_ALIGN		32
include/linux/skbuff.h +10 -0
index f4cda3fbdb75..48b10ece95b5 100644
@@ -1008,6 +1008,9 @@ struct sk_buff {
 #if IS_ENABLED(CONFIG_IP_SCTP)
 	__u8			csum_not_inet:1;
 #endif
+#ifdef CONFIG_SKB_GRO_CONTROL
+	__u8			gro_disabled:1;
+#endif
 
 #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
 	__u16			tc_index;	/* traffic control index */
@@ -1215,6 +1218,13 @@ static inline bool skb_wifi_acked_valid(const struct sk_buff *skb)
 #endif
 }
 
+static inline void skb_disable_gro(struct sk_buff *skb)
+{
+#ifdef CONFIG_SKB_GRO_CONTROL
+	skb->gro_disabled = 1;
+#endif
+}
+
 /**
  * skb_unref - decrement the skb's reference count
  * @skb: buffer
net/Kconfig +0 -0
index 9fe65fa26e48..47d1ee92df15 100644
@@ -289,6 +289,16 @@ config MAX_SKB_FRAGS
 	  and in drivers using build_skb().
 	  If unsure, say 17.

net/Kconfig config MAX_SKB_FRAGS · line 300

Quoted 10 lines
net/Kconfig +9 -0
 
+config SKB_GRO_CONTROL
+	bool "allow disable GRO on per-packet basis"
+	default y
+	help
+	  By default GRO can only be enabled or disabled per network device.
+	  This can be cumbersome for certain scenarios.
+	  Toggling this option will allow disabling GRO for selected packets,
+	  e.g. by XDP programs, so that it is more flexibile.
+	  Extra overhead should be minimal.

I don't think we need a Kconfig option for that. Can't it be unconditional? Is there any real eye-visible overhead?

Quoted 35 lines
net/Kconfig +1 -0
+
 config RPS
 	bool "Receive packet steering"
 	depends on SMP && SYSFS
net/core/gro.c +1 -1
index b3b43de1a650..46232a0d1983 100644
@@ -476,7 +476,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
 	enum gro_result ret;
 	int same_flow;
 
-	if (netif_elide_gro(skb->dev))
+	if (netif_elide_gro(skb))
 		goto normal;
 
 	gro_list_prepare(&gro_list->list, skb);
net/core/gro_cells.c +1 -1
index ff8e5b64bf6b..1bf15783300f 100644
@@ -20,7 +20,7 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto drop;
 
-	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
+	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(skb)) {
 		res = netif_rx(skb);
 		goto unlock;
 	}
net/core/skbuff.c +0 -0
index 2315c088e91d..82bd297921c1 100644

net/core/skbuff.c void skb_scrub_packet(struct sk_buff *skb, bool xnet) · line 6038

Quoted 9 lines
net/core/skbuff.c +4 -0
@@ -6030,6 +6030,10 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 	ipvs_reset(skb);
 	skb->mark = 0;
 	skb_clear_tstamp(skb);
+#ifdef CONFIG_SKB_GRO_CONTROL
+	/* hand back GRO control to next netns */
+	skb->gro_disabled = 0;
+#endif
 }
 EXPORT_SYMBOL_GPL(skb_scrub_packet);

Thanks,
Olek

author
#3 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 20 lines · Alexander Lobakin

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:10 -0700

Quoted 8 lines

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

Quoted 7 lines

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two.

Quoted 2 lines

Implement it in the kernel then? :D

It would be a big commitment that I dare not make :) Out of curiosity, is it something that devices can handle today?

Quoted 76 lines
Quoted 76 lines

On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +7 -2
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
      ((dev)->devlink_port = (port));                         \
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif
 }
 
 #define      NETDEV_ALIGN            32
include/linux/skbuff.h +10 -0
index f4cda3fbdb75..48b10ece95b5 100644
@@ -1008,6 +1008,9 @@ struct sk_buff {
 #if IS_ENABLED(CONFIG_IP_SCTP)
      __u8                    csum_not_inet:1;
 #endif
+#ifdef CONFIG_SKB_GRO_CONTROL
+     __u8                    gro_disabled:1;
+#endif
 
 #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
      __u16                   tc_index;       /* traffic control index */
@@ -1215,6 +1218,13 @@ static inline bool skb_wifi_acked_valid(const struct sk_buff *skb)
 #endif
 }
 
+static inline void skb_disable_gro(struct sk_buff *skb)
+{
+#ifdef CONFIG_SKB_GRO_CONTROL
+     skb->gro_disabled = 1;
+#endif
+}
+
 /**
  * skb_unref - decrement the skb's reference count
  * @skb: buffer
net/Kconfig +0 -0
index 9fe65fa26e48..47d1ee92df15 100644
@@ -289,6 +289,16 @@ config MAX_SKB_FRAGS
        and in drivers using build_skb().
        If unsure, say 17.
 
Quoted 12 lines

net/Kconfig config MAX_SKB_FRAGS · line 300

Quoted 9 lines
net/Kconfig +9 -0
+config SKB_GRO_CONTROL
+     bool "allow disable GRO on per-packet basis"
+     default y
+     help
+       By default GRO can only be enabled or disabled per network device.
+       This can be cumbersome for certain scenarios.
+       Toggling this option will allow disabling GRO for selected packets,
+       e.g. by XDP programs, so that it is more flexibile.
+       Extra overhead should be minimal.

I don't think we need a Kconfig option for that. Can't it be unconditional? Is there any real eye-visible overhead?

Normally if it is a single branch I would not worry about it. But I know I am touching a hot potato here so I just want to be cautious :)

best
Yan

Quoted 3 lines

Thanks,
Olek

maintainer
#4 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

In reply to Yan Zhai
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
 	((dev)->devlink_port = (port));				\
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
 {
-	if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+	if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
 		return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+	return skb->gro_disabled;
+#else
 	return false;
+#endif

This will generate OoO if the gro_disabled is flipped in the middle of a stream.

Assuming the above is fine for your use case (I think it's _not_ in general), you could get the same result without an additional costly bit in sk_buff.

Let xdp_frame_fixup_skb_offloading() return a bool - e.g. 'true' when gro should be avoided - and let the NIC driver call netif_receive_skb() instead of the gro rx hook for such packet.

All in all the approach implemented in this series does not look worthy to me.

Thanks,

Paolo

author
#5 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 58 lines · Paolo Abeni
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
      ((dev)->devlink_port = (port));                         \
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

This will generate OoO if the gro_disabled is flipped in the middle of a stream.

Quoted 3 lines

Assuming the above is fine for your use case (I think it's _not_ in general), you could get the same result without an additional costly bit in sk_buff.

Calling it per-packet control seems inaccurate here, the motivation is to give users the ability to control per-flow behaviors. OoO is indeed a consequence if users don't do it correctly.

Quoted 5 lines

Let xdp_frame_fixup_skb_offloading() return a bool - e.g. 'true' when gro should be avoided - and let the NIC driver call netif_receive_skb() instead of the gro rx hook for such packet.

For rx on a single device, directly calling netif_receive_skb is reasonable. For tunnel receivers it is kinda inconsistent IMHO. For example, we terminate GRE tunnels in a netns, and it is necessary to disable GRO on both the entering veth device and also the GRE tunnel to shutdown GRO. That's why I'd hope to use a bit of skb, to be consistent within the same netns. Let me add a bit more context to clarify why we think this is necessary in another thread.

best,
Yan

Quoted 7 lines

All in all the approach implemented in this series does not look worthy to me.

Thanks,

Paolo

maintainer
#6 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

In reply to Yan Zhai
Quoted 8 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

Quoted 10 lines

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Could you please add more details WRT this last statement? I'm unsure if I understand your problem. My guess is as follow:

Your device receive some traffic, GRO and forward it, and the multiple encapsulation can happen on such forwarded traffic (since I can't find almost none of the above your message is mainly a wild guess).

Assuming I guessed correctly, I think you could solve the problem with no kernel changes: redirect the to-be-tunneled traffic to some virtual device and all TX offload on top of it and let the encap happen there.

Cheers,

Paolo

author
#7 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 24 lines · Paolo Abeni
Quoted 8 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

Quoted 10 lines

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Could you please add more details WRT this last statement? I'm unsure if I understand your problem. My guess is as follow:

Quoted 8 lines

Your device receive some traffic, GRO and forward it, and the multiple encapsulation can happen on such forwarded traffic (since I can't find almost none of the above your message is mainly a wild guess).

Assuming I guessed correctly, I think you could solve the problem with no kernel changes: redirect the to-be-tunneled traffic to some virtual device and all TX offload on top of it and let the encap happen there.

Let's say we have a netns to implement network functions like DoS/IDS/Load balancing for IP traffic. The netns has a single veth entrance/exit, and a bunch of ip tunnels, GRE/XFRM, to receive and tunnel traffic from customer's private sites. Some of such traffic could be encapsulated to reach services outside of the netns (but on the same server), for example, customers may also want to use our CDN/Caching functionality. The complication here is that we might have to further tunnel traffic to another data center, because the routing is asymmetric so we can receive client traffic from US but the response may come back to our EU data center, and in order to do layer4/layer7 service, we have to make sure those land on the same server.

It is true that a device like a veth pair or even netkit could allow the kernel segment GRO packets for us. But this does not sound actually right in terms of design: if we know already some packet path should not be GRO-ed, can we enforce this rather than having to aggregate it then chop it down soon after? For our specific case though, it also becomes a headache for analytics and customer rules that rely on ingress device name, we probably need to pair each tunnel with such a virtual device. There could be hundreds of ipsec tunnels, and that seems to be a substantial overhead for both data path and control plane management.

To make this a bit more general, what I'd like to introduce here is:
when we know GRO is either problematic or simply not useful (like to some UDP traffic), can we have more control toggle to skip it?

thanks
Yan

Quoted 4 lines

Cheers,

Paolo

#8 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

In reply to Yan Zhai
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
 include/linux/netdevice.h |  9 +++++++--
 include/linux/skbuff.h    | 10 ++++++++++
 net/Kconfig               | 10 ++++++++++
 net/core/gro.c            |  2 +-
 net/core/gro_cells.c      |  2 +-
 net/core/skbuff.c         |  4 ++++
 6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
 	((dev)->devlink_port = (port));				\
 })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
 {
-	if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+	if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
 		return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+	return skb->gro_disabled;
+#else
 	return false;
+#endif

Yet more branches in the hot path.

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 97 lines
include/linux/netdevice.h +0 -0
 }
 
 #define	NETDEV_ALIGN		32
include/linux/skbuff.h +10 -0
index f4cda3fbdb75..48b10ece95b5 100644
@@ -1008,6 +1008,9 @@ struct sk_buff {
 #if IS_ENABLED(CONFIG_IP_SCTP)
 	__u8			csum_not_inet:1;
 #endif
+#ifdef CONFIG_SKB_GRO_CONTROL
+	__u8			gro_disabled:1;
+#endif
 
 #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
 	__u16			tc_index;	/* traffic control index */
@@ -1215,6 +1218,13 @@ static inline bool skb_wifi_acked_valid(const struct sk_buff *skb)
 #endif
 }
 
+static inline void skb_disable_gro(struct sk_buff *skb)
+{
+#ifdef CONFIG_SKB_GRO_CONTROL
+	skb->gro_disabled = 1;
+#endif
+}
+
 /**
  * skb_unref - decrement the skb's reference count
  * @skb: buffer
net/Kconfig +10 -0
index 9fe65fa26e48..47d1ee92df15 100644
@@ -289,6 +289,16 @@ config MAX_SKB_FRAGS
 	  and in drivers using build_skb().
 	  If unsure, say 17.
 
+config SKB_GRO_CONTROL
+	bool "allow disable GRO on per-packet basis"
+	default y
+	help
+	  By default GRO can only be enabled or disabled per network device.
+	  This can be cumbersome for certain scenarios.
+	  Toggling this option will allow disabling GRO for selected packets,
+	  e.g. by XDP programs, so that it is more flexibile.
+	  Extra overhead should be minimal.
+
 config RPS
 	bool "Receive packet steering"
 	depends on SMP && SYSFS
net/core/gro.c +1 -1
index b3b43de1a650..46232a0d1983 100644
@@ -476,7 +476,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
 	enum gro_result ret;
 	int same_flow;
 
-	if (netif_elide_gro(skb->dev))
+	if (netif_elide_gro(skb))
 		goto normal;
 
 	gro_list_prepare(&gro_list->list, skb);
net/core/gro_cells.c +1 -1
index ff8e5b64bf6b..1bf15783300f 100644
@@ -20,7 +20,7 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto drop;
 
-	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
+	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(skb)) {
 		res = netif_rx(skb);
 		goto unlock;
 	}
net/core/skbuff.c +4 -0
index 2315c088e91d..82bd297921c1 100644
@@ -6030,6 +6030,10 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 	ipvs_reset(skb);
 	skb->mark = 0;
 	skb_clear_tstamp(skb);
+#ifdef CONFIG_SKB_GRO_CONTROL
+	/* hand back GRO control to next netns */
+	skb->gro_disabled = 0;
+#endif
 }
 EXPORT_SYMBOL_GPL(skb_scrub_packet);
 
Signature · 1 line
2.30.2
maintainer
#9 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
  include/linux/netdevice.h |  9 +++++++--
  include/linux/skbuff.h    | 10 ++++++++++
  net/Kconfig               | 10 ++++++++++
  net/core/gro.c            |  2 +-
  net/core/gro_cells.c      |  2 +-
  net/core/skbuff.c         |  4 ++++
  6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
  	((dev)->devlink_port = (port));				\
  })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
  {
-	if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+	if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
  		return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+	return skb->gro_disabled;
+#else
  	return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

author
#10 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 66 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
  include/linux/netdevice.h |  9 +++++++--
  include/linux/skbuff.h    | 10 ++++++++++
  net/Kconfig               | 10 ++++++++++
  net/core/gro.c            |  2 +-
  net/core/gro_cells.c      |  2 +-
  net/core/skbuff.c         |  4 ++++
  6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
     ((dev)->devlink_port = (port));                         \
  })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
  {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
             return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
     return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

To extend the discussion a bit, putting GRO aside, I think some common hook before GRO would be still valuable moving forward: it is a limited window where the driver code has both access to XDP context and skb. Today we do not have a good way to transfer HW offloading info to skbs if XDP redirect-to-cpu or if XDP encap-and-tx for load balancing purposes. The XDP metadata infrastructure already allows XDP to read this information with driver supports, so to complete that, a place to use it (which I introduced as xdp_buff/frame_fixup_skb_offloading in a later patch) would be beneficial to pass on things like the flow hash, vlan information, etc.

best
Yan

maintainer
#11 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 73 lines · Yan Zhai
Quoted 65 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
   include/linux/netdevice.h |  9 +++++++--
   include/linux/skbuff.h    | 10 ++++++++++
   net/Kconfig               | 10 ++++++++++
   net/core/gro.c            |  2 +-
   net/core/gro_cells.c      |  2 +-
   net/core/skbuff.c         |  4 ++++
   6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
      ((dev)->devlink_port = (port));                         \
   })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
   {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Quoted 9 lines

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

Oh wait, one thing that just came to mind.. have you tried u64 per-CPU counter map in XDP? For packets which should not be GRO-aggregated you add count++ into the meta data area, and this forces GRO to not aggregate since meta data that needs to be transported to tc BPF layer mismatches (and therefore the contract/intent is that tc BPF needs to see the different meta data passed to it).

Thanks,
Daniel

author
#12 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 85 lines · Daniel Borkmann
Quoted 73 lines · Yan Zhai
Quoted 65 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
   include/linux/netdevice.h |  9 +++++++--
   include/linux/skbuff.h    | 10 ++++++++++
   net/Kconfig               | 10 ++++++++++
   net/core/gro.c            |  2 +-
   net/core/gro_cells.c      |  2 +-
   net/core/skbuff.c         |  4 ++++
   6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
      ((dev)->devlink_port = (port));                         \
   })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
   {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Quoted 9 lines

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

Quoted 7 lines

Oh wait, one thing that just came to mind.. have you tried u64 per-CPU counter map in XDP? For packets which should not be GRO-aggregated you add count++ into the meta data area, and this forces GRO to not aggregate since meta data that needs to be transported to tc BPF layer mismatches (and therefore the contract/intent is that tc BPF needs to see the different meta data passed to it).

Very very sorry to resendx2 :( Not sure why my laptop also decided to switch on html... I removed CCs from the message hopefully it reduces some noises...

We did this before accidentally (we put a timestamp for debugging purposes in metadata) and this actually caused about 20% of OoO for TCP in production: all PSH packets are reordered. GRO does not fire the packet to the upper layer when a diff in metadata is found for a non-PSH packet, instead it is queued as a “new flow” on the GRO list and waits for flushing. When a PSH packet arrives, its semantic is to flush this packet immediately and thus precedes earlier packets of the same flow.

The artifact of this behavior can also cause latency increase and hash degradation since the mismatch does not result in flushing, it results in extra queuing on the same hash list, until the list is flushed. It’s another reason we want to disable GRO when we know metadata can be set differently for tracing purposes (I didn’t mention this though because it seems distracting).

Thanks
Yan

Quoted 2 lines

Thanks,
Daniel

#13 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 94 lines · Yan Zhai
Quoted 85 lines · Daniel Borkmann
Quoted 73 lines · Yan Zhai
Quoted 65 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
   include/linux/netdevice.h |  9 +++++++--
   include/linux/skbuff.h    | 10 ++++++++++
   net/Kconfig               | 10 ++++++++++
   net/core/gro.c            |  2 +-
   net/core/gro_cells.c      |  2 +-
   net/core/skbuff.c         |  4 ++++
   6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
      ((dev)->devlink_port = (port));                         \
   })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
   {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Quoted 9 lines

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

Quoted 7 lines

Oh wait, one thing that just came to mind.. have you tried u64 per-CPU counter map in XDP? For packets which should not be GRO-aggregated you add count++ into the meta data area, and this forces GRO to not aggregate since meta data that needs to be transported to tc BPF layer mismatches (and therefore the contract/intent is that tc BPF needs to see the different meta data passed to it).

Quoted 8 lines

We did this before accidentally (we put a timestamp for debugging purposes in metadata) and this actually caused about 20% of OoO for TCP in production: all PSH packets are reordered. GRO does not fire the packet to the upper layer when a diff in metadata is found for a non-PSH packet, instead it is queued as a “new flow” on the GRO list and waits for flushing. When a PSH packet arrives, its semantic is to flush this packet immediately and thus precedes earlier packets of the same flow.

Is that a bug in XDP metadata handling for GRO?

Mismatching metadata should not be taken as separate flows, but as a flush condition.

maintainer
#14 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 102 lines · Willem de Bruijn
Quoted 92 lines · Yan Zhai
Quoted 84 lines · Daniel Borkmann
Quoted 73 lines · Yan Zhai
Quoted 65 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
    include/linux/netdevice.h |  9 +++++++--
    include/linux/skbuff.h    | 10 ++++++++++
    net/Kconfig               | 10 ++++++++++
    net/core/gro.c            |  2 +-
    net/core/gro_cells.c      |  2 +-
    net/core/skbuff.c         |  4 ++++
    6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
       ((dev)->devlink_port = (port));                         \
    })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
    {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
               return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
       return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Quoted 9 lines

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

Quoted 6 lines

Oh wait, one thing that just came to mind.. have you tried u64 per-CPU counter map in XDP? For packets which should not be GRO-aggregated you add count++ into the meta data area, and this forces GRO to not aggregate since meta data that needs to be transported to tc BPF layer mismatches (and therefore the contract/intent is that tc BPF needs to see the different meta data passed to it).

Quoted 8 lines

We did this before accidentally (we put a timestamp for debugging purposes in metadata) and this actually caused about 20% of OoO for TCP in production: all PSH packets are reordered. GRO does not fire the packet to the upper layer when a diff in metadata is found for a non-PSH packet, instead it is queued as a “new flow” on the GRO list and waits for flushing. When a PSH packet arrives, its semantic is to flush this packet immediately and thus precedes earlier packets of the same flow.

Quoted 4 lines

Is that a bug in XDP metadata handling for GRO?

Mismatching metadata should not be taken as separate flows, but as a flush condition.

Definitely a bug as it should flush. If noone is faster I can add it to my backlog todo to fix it, but might probably take a week before I get to it.

Thanks,
Daniel

author
#15 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 104 lines · Daniel Borkmann
Quoted 102 lines · Willem de Bruijn
Quoted 100 lines · Yan Zhai
Quoted 84 lines · Daniel Borkmann
Quoted 73 lines · Yan Zhai
Quoted 65 lines · Daniel Borkmann
Quoted 56 lines · Willem de Bruijn
Quoted 42 lines · Yan Zhai

Software GRO is currently controlled by a single switch, i.e.

ethtool -K dev gro on|off

However, this is not always desired. When GRO is enabled, even if the kernel cannot GRO certain traffic, it has to run through the GRO receive handlers with no benefit.

There are also scenarios that turning off GRO is a requirement. For example, our production environment has a scenario that a TC egress hook may add multiple encapsulation headers to forwarded skbs for load balancing and isolation purpose. The encapsulation is implemented via BPF. But the problem arises then: there is no way to properly offload a double-encapsulated packet, since skb only has network_header and inner_network_header to track one layer of encapsulation, but not two. On the other hand, not all the traffic through this device needs double encapsulation. But we have to turn off GRO completely for any ingress device as a result.

Introduce a bit on skb so that GRO engine can be notified to skip GRO on this skb, rather than having to be 0-or-1 for all traffic.

---
    include/linux/netdevice.h |  9 +++++++--
    include/linux/skbuff.h    | 10 ++++++++++
    net/Kconfig               | 10 ++++++++++
    net/core/gro.c            |  2 +-
    net/core/gro_cells.c      |  2 +-
    net/core/skbuff.c         |  4 ++++
    6 files changed, 33 insertions(+), 4 deletions(-)
include/linux/netdevice.h +1 -1
index c83b390191d4..2ca0870b1221 100644
@@ -2415,11 +2415,16 @@ struct net_device {
       ((dev)->devlink_port = (port));                         \
    })
 
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)

include/linux/netdevice.h struct net_device { · line 2427

Quoted 10 lines
include/linux/netdevice.h +6 -1
    {
-    if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+    if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
               return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+    return skb->gro_disabled;
+#else
       return false;
+#endif

Yet more branches in the hot path.

Quoted 7 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

Quoted 6 lines

Mentioning it here b/c it perhaps fits in this context, longer time ago there was the idea mentioned to have BPF operating as GRO engine which might also help to reduce attack surface by only having to handle packets of interest for the concrete production use case. Perhaps here meta data buffer could be used to pass a notification from XDP to exit early w/o aggregation.

Quoted 9 lines

Metadata is in fact one of our interests as well. We discussed using metadata instead of a skb bit to carry this information internally. Since metadata is opaque atm so it seems the only option is to have a GRO control hook before napi_gro_receive, and let BPF decide netif_receive_skb or napi_gro_receive (echo what Paolo said). With BPF it could indeed be more flexible, but the cons is that it could be even more slower than taking a bit on skb. I am actually open to either approach, as long as it gives us more control on when to enable GRO :)

Quoted 6 lines

Oh wait, one thing that just came to mind.. have you tried u64 per-CPU counter map in XDP? For packets which should not be GRO-aggregated you add count++ into the meta data area, and this forces GRO to not aggregate since meta data that needs to be transported to tc BPF layer mismatches (and therefore the contract/intent is that tc BPF needs to see the different meta data passed to it).

We did this before accidentally (we put a timestamp for debugging purposes in metadata) and this actually caused about 20% of OoO for TCP in production: all PSH packets are reordered. GRO does not fire the packet to the upper layer when a diff in metadata is found for a non-PSH packet, instead it is queued as a “new flow” on the GRO list and waits for flushing. When a PSH packet arrives, its semantic is to flush this packet immediately and thus precedes earlier packets of the same flow.

Quoted 8 lines
Quoted 4 lines

Is that a bug in XDP metadata handling for GRO?

Mismatching metadata should not be taken as separate flows, but as a flush condition.

Definitely a bug as it should flush. If noone is faster I can add it to my backlog todo to fix it, but might probably take a week before I get to it.

In theory we should flush if the same flow has different metadata. However "same flow" is not finally confirmed until GRO proceeds to the TCP layer in this case. So to achieve this flush semantic, metadata should be compared at the leaf protocol handlers instead of initially inside dev_gro_receive. I am not quite sure if it is worthwhile, or just allow skipping GRO from XDP, since it's the XDP program who sets this metadata.

Yan

Quoted 2 lines

Thanks,
Daniel

author
#16 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

In reply to Willem de Bruijn
Quoted 15 lines

include/linux/netdevice.h struct net_device { · line 2427

Quoted 12 lines
include/linux/netdevice.h +7 -2
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Quoted 8 lines

Compile time configurability does not help, as that will be enabled by distros.

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

I share your concern on operating on this hotpath. Will a static_branch + sysctl make it less aggressive? Speaking of performance, I'd hope this can give us more control so we can achieve the best of two worlds: for TCP and some UDP traffic, we can enable GRO, while for some other classes that we know GRO does no good or even harm, let's disable GRO to save more cycles. The key observation is that developers may already know which traffic is blessed by GRO, but lack a way to realize it.

best
Yan

#17 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 18 lines · Yan Zhai
Quoted 18 lines

include/linux/netdevice.h struct net_device { · line 2427

Quoted 12 lines
include/linux/netdevice.h +7 -2
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Compile time configurability does not help, as that will be enabled by distros.

Quoted 8 lines
Quoted 5 lines

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

I share your concern on operating on this hotpath. Will a static_branch + sysctl make it less aggressive?

That is always a possibility. But we have to use it judiciously, cannot add a sysctl for every branch.

I'm still of the opinion that Paolo shared that this seems a lot of complexity for a fairly minor performance optimization for a rare case.

Quoted 7 lines

Speaking of
performance, I'd hope this can give us more control so we can achieve the best of two worlds: for TCP and some UDP traffic, we can enable GRO, while for some other classes that we know GRO does no good or even harm, let's disable GRO to save more cycles. The key observation is that developers may already know which traffic is blessed by GRO, but lack a way to realize it.

Following up also on Daniel's point on using BPF as GRO engine. Even earlier I tried to add an option to selectively enable GRO protocols without BPF. Definitely worthwhile to be able to disable GRO handlers to reduce attack surface to bad input.

Quoted 3 lines

best
Yan

author
#18 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 29 lines · Willem de Bruijn
Quoted 18 lines · Yan Zhai
Quoted 18 lines

include/linux/netdevice.h struct net_device { · line 2427

Quoted 12 lines
include/linux/netdevice.h +7 -2
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Compile time configurability does not help, as that will be enabled by distros.

Quoted 8 lines
Quoted 5 lines

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

I share your concern on operating on this hotpath. Will a static_branch + sysctl make it less aggressive?

Quoted 7 lines

That is always a possibility. But we have to use it judiciously, cannot add a sysctl for every branch.

I'm still of the opinion that Paolo shared that this seems a lot of complexity for a fairly minor performance optimization for a rare case.

Actually combining the discussion in this thread, I think it would be more than the corner cases that we encounter. Let me elaborate below.

Quoted 8 lines
Quoted 7 lines

Speaking of
performance, I'd hope this can give us more control so we can achieve the best of two worlds: for TCP and some UDP traffic, we can enable GRO, while for some other classes that we know GRO does no good or even harm, let's disable GRO to save more cycles. The key observation is that developers may already know which traffic is blessed by GRO, but lack a way to realize it.

Quoted 5 lines

Following up also on Daniel's point on using BPF as GRO engine. Even earlier I tried to add an option to selectively enable GRO protocols without BPF. Definitely worthwhile to be able to disable GRO handlers to reduce attack surface to bad input.

I was probably staring too hard at my own things, which is indeed a corner case. But reducing the attack surface is indeed a good motivation for this patch. I checked briefly with our DoS team today, the DoS scenario will definitely benefit from skipping GRO, for example on SYN/RST floods. XDP is our main weapon to drop attack traffic today, but it does not always drop 100% of the floods, and time by time it does need to fall back to iptables due to the delay of XDP program assembly or the BPF limitation on analyzing the packet. I did an ad hoc measurement just now on a mostly idle server, with ~1.3Mpps SYN flood concentrated on one CPU and dropped them early in raw-PREROUTING. w/ GRO this would consume about 35-41% of the CPU time, while w/o GRO the time dropped to 9-12%. This seems a pretty significant breath room under heavy attacks.

But I am not sure I understand "BPF as GRO engine" here, it seems to me that being able to disable GRO by XDP is already good enough. Any more motivations to do more complex work here?

best
Yan

Quoted 6 lines
Quoted 4 lines

best
Yan

#19 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 54 lines · Yan Zhai
Quoted 29 lines · Willem de Bruijn
Quoted 18 lines · Yan Zhai
Quoted 18 lines

include/linux/netdevice.h struct net_device { · line 2427

Quoted 12 lines
include/linux/netdevice.h +7 -2
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Compile time configurability does not help, as that will be enabled by distros.

Quoted 8 lines
Quoted 5 lines

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

I share your concern on operating on this hotpath. Will a static_branch + sysctl make it less aggressive?

Quoted 7 lines

That is always a possibility. But we have to use it judiciously, cannot add a sysctl for every branch.

I'm still of the opinion that Paolo shared that this seems a lot of complexity for a fairly minor performance optimization for a rare case.

Actually combining the discussion in this thread, I think it would be more than the corner cases that we encounter. Let me elaborate below.

Quoted 8 lines
Quoted 7 lines

Speaking of
performance, I'd hope this can give us more control so we can achieve the best of two worlds: for TCP and some UDP traffic, we can enable GRO, while for some other classes that we know GRO does no good or even harm, let's disable GRO to save more cycles. The key observation is that developers may already know which traffic is blessed by GRO, but lack a way to realize it.

Quoted 5 lines

Following up also on Daniel's point on using BPF as GRO engine. Even earlier I tried to add an option to selectively enable GRO protocols without BPF. Definitely worthwhile to be able to disable GRO handlers to reduce attack surface to bad input.

Quoted 13 lines

I was probably staring too hard at my own things, which is indeed a corner case. But reducing the attack surface is indeed a good motivation for this patch. I checked briefly with our DoS team today, the DoS scenario will definitely benefit from skipping GRO, for example on SYN/RST floods. XDP is our main weapon to drop attack traffic today, but it does not always drop 100% of the floods, and time by time it does need to fall back to iptables due to the delay of XDP program assembly or the BPF limitation on analyzing the packet. I did an ad hoc measurement just now on a mostly idle server, with ~1.3Mpps SYN flood concentrated on one CPU and dropped them early in raw-PREROUTING. w/ GRO this would consume about 35-41% of the CPU time, while w/o GRO the time dropped to 9-12%. This seems a pretty significant breath room under heavy attacks.

A GRO opt-out might make sense.

A long time ago I sent a patch that configured GRO protocols using syscalls, selectively (un)registering handlers. The interface was not very nice, so I did not pursue it further. On the upside, the datapath did not introduce any extra code. The intent was to reduce attack surface of packet parsing code.

A few concerns with an XDP based opt-out. It is more work to enable:
requires compiling and load an XDP program. It adds cycles in the hot path. And I do not entirely understand when an XDP program will be able to detect that a packet should not enter the GRO engine, but cannot drop the packet (your netfilter example above).

Quoted 3 lines

But I am not sure I understand "BPF as GRO engine" here, it seems to me that being able to disable GRO by XDP is already good enough. Any more motivations to do more complex work here?

FWIW, we looked into this a few years ago. Analogous to the BPF flow
dissector: if the BPF program is loaded, use that instead of the C code path. But we did not arrive at a practical implementation at the time. Things may have changed, but one issue is how to store and access the list (or table) of outstanding GRO skbs.

Quoted 9 lines

best
Yan

Quoted 6 lines
Quoted 4 lines

best
Yan

author
#20 lore raw

Re: [RFC net-next 1/9] skb: introduce gro_disabled bit

Quoted 78 lines · Willem de Bruijn
Quoted 54 lines · Yan Zhai
Quoted 29 lines · Willem de Bruijn
Quoted 18 lines · Yan Zhai
Quoted 18 lines

include/linux/netdevice.h struct net_device { · line 2427

Quoted 12 lines
include/linux/netdevice.h +7 -2
-static inline bool netif_elide_gro(const struct net_device *dev)
+static inline bool netif_elide_gro(const struct sk_buff *skb)
 {
-     if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog)
+     if (!(skb->dev->features & NETIF_F_GRO) || skb->dev->xdp_prog)
              return true;
+
+#ifdef CONFIG_SKB_GRO_CONTROL
+     return skb->gro_disabled;
+#else
      return false;
+#endif

Yet more branches in the hot path.

Compile time configurability does not help, as that will be enabled by distros.

Quoted 8 lines
Quoted 5 lines

For a fairly niche use case. Where functionality of GRO already works. So just a performance for a very rare case at the cost of a regression in the common case. A small regression perhaps, but death by a thousand cuts.

I share your concern on operating on this hotpath. Will a static_branch + sysctl make it less aggressive?

Quoted 7 lines

That is always a possibility. But we have to use it judiciously, cannot add a sysctl for every branch.

I'm still of the opinion that Paolo shared that this seems a lot of complexity for a fairly minor performance optimization for a rare case.

Actually combining the discussion in this thread, I think it would be more than the corner cases that we encounter. Let me elaborate below.

Quoted 8 lines
Quoted 7 lines

Speaking of
performance, I'd hope this can give us more control so we can achieve the best of two worlds: for TCP and some UDP traffic, we can enable GRO, while for some other classes that we know GRO does no good or even harm, let's disable GRO to save more cycles. The key observation is that developers may already know which traffic is blessed by GRO, but lack a way to realize it.

Quoted 5 lines

Following up also on Daniel's point on using BPF as GRO engine. Even earlier I tried to add an option to selectively enable GRO protocols without BPF. Definitely worthwhile to be able to disable GRO handlers to reduce attack surface to bad input.

Quoted 13 lines

I was probably staring too hard at my own things, which is indeed a corner case. But reducing the attack surface is indeed a good motivation for this patch. I checked briefly with our DoS team today, the DoS scenario will definitely benefit from skipping GRO, for example on SYN/RST floods. XDP is our main weapon to drop attack traffic today, but it does not always drop 100% of the floods, and time by time it does need to fall back to iptables due to the delay of XDP program assembly or the BPF limitation on analyzing the packet. I did an ad hoc measurement just now on a mostly idle server, with ~1.3Mpps SYN flood concentrated on one CPU and dropped them early in raw-PREROUTING. w/ GRO this would consume about 35-41% of the CPU time, while w/o GRO the time dropped to 9-12%. This seems a pretty significant breath room under heavy attacks.

A GRO opt-out might make sense.

A long time ago I sent a patch that configured GRO protocols using syscalls, selectively (un)registering handlers. The interface was not very nice, so I did not pursue it further. On the upside, the datapath did not introduce any extra code. The intent was to reduce attack surface of packet parsing code.

Quoted 6 lines

A few concerns with an XDP based opt-out. It is more work to enable:
requires compiling and load an XDP program. It adds cycles in the hot path. And I do not entirely understand when an XDP program will be able to detect that a packet should not enter the GRO engine, but cannot drop the packet (your netfilter example above).

Agree that XDP based approach is just offering for XDP users. But given the way GRO works on flows today, it feels really hard to provide an elegant and generic interface.

For DoS scenarios, let me expand it a bit. Packets themselves could be a good indicator that they should not go through GRO, like fragments, or with special flags like SYN/RST/PSH. Under an attack, we sometimes also need conntrack or SYN cookies to help determine if some packets are legit or not. We have a few kfuncs to lookup conntrack entries in XDP today, but I am not sure if we can confidently drop them without completely mirroring full conntrack functionality. Rather, using conntrack as extra heuristics to mark suspicious packets in XDP, like TCP packets out of windows, etc, and still leave verdict to iptables seems a safer thing to do. I did observe a few occurrences in the past where a substantial amount of SYN flood passed through XDP, with some clever tricks in faking flow headers. Those were eventually dealt by SYN cookies, but all of those go through GRO unnecessarily although they all carry a SYN flag. Would be definitely beneficial to save every cycle under attacks.

Quoted 10 lines
Quoted 3 lines

But I am not sure I understand "BPF as GRO engine" here, it seems to me that being able to disable GRO by XDP is already good enough. Any more motivations to do more complex work here?

FWIW, we looked into this a few years ago. Analogous to the BPF flow
dissector: if the BPF program is loaded, use that instead of the C code path. But we did not arrive at a practical implementation at the time. Things may have changed, but one issue is how to store and access the list (or table) of outstanding GRO skbs.

I see, thanks for the explanation.

Yan

Quoted 11 lines
Quoted 9 lines

best
Yan

Quoted 6 lines
Quoted 4 lines

best
Yan

author
#21 lore raw

RFC 2/9 xdp: add XDP_FLAGS_GRO_DISABLED flag

In reply to Yan Zhai

Allow XDP program to set XDP_FLAGS_GRO_DISABLED flag in xdp_buff and xdp_frame, and disable GRO when building an sk_buff if this flag is set.

---
 include/net/xdp.h | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)
include/net/xdp.h +37 -1
index e6770dd40c91..cc3bce8817b0 100644
@@ -75,6 +75,7 @@ enum xdp_buff_flags {
 	XDP_FLAGS_FRAGS_PF_MEMALLOC	= BIT(1), /* xdp paged memory is under
 						   * pressure
 						   */
+	XDP_FLAGS_GRO_DISABLED          = BIT(2), /* GRO disabled */
 };
 
 struct xdp_buff {
@@ -113,12 +114,35 @@ static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp)
 	xdp->flags |= XDP_FLAGS_FRAGS_PF_MEMALLOC;
 }
 
+static __always_inline void xdp_buff_disable_gro(struct xdp_buff *xdp)
+{
+	xdp->flags |= XDP_FLAGS_GRO_DISABLED;
+}
+
+static __always_inline bool xdp_buff_gro_disabled(struct xdp_buff *xdp)
+{
+	return !!(xdp->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_buff_fixup_skb_offloading(struct xdp_buff *xdp, struct sk_buff *skb)
+{
+	if (xdp_buff_gro_disabled(xdp))
+		skb_disable_gro(skb);
+}
+
+static __always_inline void
+xdp_init_buff_minimal(struct xdp_buff *xdp)
+{
+	xdp->flags = 0;
+}
+
 static __always_inline void
 xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
 {
 	xdp->frame_sz = frame_sz;
 	xdp->rxq = rxq;
-	xdp->flags = 0;
+	xdp_init_buff_minimal(xdp);
 }
 
 static __always_inline void
@@ -187,6 +211,18 @@ static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame
 	return !!(frame->flags & XDP_FLAGS_FRAGS_PF_MEMALLOC);
 }
 
+static __always_inline bool xdp_frame_gro_disabled(struct xdp_frame *frame)
+{
+	return !!(frame->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_frame_fixup_skb_offloading(struct xdp_frame *frame, struct sk_buff *skb)
+{
+	if (xdp_frame_gro_disabled(frame))
+		skb_disable_gro(skb);
+}
+
 #define XDP_BULK_QUEUE_SIZE	16
 struct xdp_frame_bulk {
 	int count;
Signature · 1 line
2.30.2
#22 lore raw

Re: [RFC net-next 2/9] xdp: add XDP_FLAGS_GRO_DISABLED flag

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:13 -0700

Quoted 13 lines

Allow XDP program to set XDP_FLAGS_GRO_DISABLED flag in xdp_buff and xdp_frame, and disable GRO when building an sk_buff if this flag is set.

---
 include/net/xdp.h | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)
include/net/xdp.h +0 -0
index e6770dd40c91..cc3bce8817b0 100644

include/net/xdp.h enum xdp_buff_flags { · line 78

Quoted 4 lines
include/net/xdp.h +1 -0
@@ -75,6 +75,7 @@ enum xdp_buff_flags {
 	XDP_FLAGS_FRAGS_PF_MEMALLOC	= BIT(1), /* xdp paged memory is under
 						   * pressure
 						   */
+	XDP_FLAGS_GRO_DISABLED          = BIT(2), /* GRO disabled */

There should be tabs, not spaces.

Quoted 13 lines
include/net/xdp.h +6 -0
 };
 
 struct xdp_buff {
@@ -113,12 +114,35 @@ static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp)
 	xdp->flags |= XDP_FLAGS_FRAGS_PF_MEMALLOC;
 }
 
+static __always_inline void xdp_buff_disable_gro(struct xdp_buff *xdp)
+{
+	xdp->flags |= XDP_FLAGS_GRO_DISABLED;
+}
+
+static __always_inline bool xdp_buff_gro_disabled(struct xdp_buff *xdp)

include/net/xdp.h static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp) · line 132

Quoted 10 lines
include/net/xdp.h +10 -0
+{
+	return !!(xdp->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_buff_fixup_skb_offloading(struct xdp_buff *xdp, struct sk_buff *skb)
+{
+	if (xdp_buff_gro_disabled(xdp))
+		skb_disable_gro(skb);
+}

I don't think this should be named "fixup". "propagate", "update", "promote", ...?

Maybe if is not needed here?

	skb->gro_disabled = xdp_buff_gro_disabled(xdp)

?

include/net/xdp.h static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp) · line 138

Quoted 6 lines
include/net/xdp.h +6 -0
+
+static __always_inline void
+xdp_init_buff_minimal(struct xdp_buff *xdp)
+{
+	xdp->flags = 0;
+}

"xdp_buff_clear_flags"?

Quoted 16 lines
include/net/xdp.h +3 -1
+
 static __always_inline void
 xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
 {
 	xdp->frame_sz = frame_sz;
 	xdp->rxq = rxq;
-	xdp->flags = 0;
+	xdp_init_buff_minimal(xdp);
 }
 
 static __always_inline void
@@ -187,6 +211,18 @@ static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame
 	return !!(frame->flags & XDP_FLAGS_FRAGS_PF_MEMALLOC);
 }
 
+static __always_inline bool xdp_frame_gro_disabled(struct xdp_frame *frame)

include/net/xdp.h static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame · line 224

Quoted 10 lines
include/net/xdp.h +10 -0
+{
+	return !!(frame->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_frame_fixup_skb_offloading(struct xdp_frame *frame, struct sk_buff *skb)
+{
+	if (xdp_frame_gro_disabled(frame))
+		skb_disable_gro(skb);
+}

(same)

include/net/xdp.h static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame · line 228

Quoted 4 lines
include/net/xdp.h +1 -0
+
 #define XDP_BULK_QUEUE_SIZE	16
 struct xdp_frame_bulk {
 	int count;

Thanks,
Olek

author
#23 lore raw

Re: [RFC net-next 2/9] xdp: add XDP_FLAGS_GRO_DISABLED flag

Quoted 51 lines · Alexander Lobakin

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:13 -0700

Quoted 13 lines

Allow XDP program to set XDP_FLAGS_GRO_DISABLED flag in xdp_buff and xdp_frame, and disable GRO when building an sk_buff if this flag is set.

---
 include/net/xdp.h | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)
include/net/xdp.h +0 -0
index e6770dd40c91..cc3bce8817b0 100644

include/net/xdp.h enum xdp_buff_flags { · line 78

Quoted 4 lines
include/net/xdp.h +1 -0
@@ -75,6 +75,7 @@ enum xdp_buff_flags {
      XDP_FLAGS_FRAGS_PF_MEMALLOC     = BIT(1), /* xdp paged memory is under
                                                 * pressure
                                                 */
+     XDP_FLAGS_GRO_DISABLED          = BIT(2), /* GRO disabled */

There should be tabs, not spaces.

Quoted 13 lines
include/net/xdp.h +6 -0
 };
 
 struct xdp_buff {
@@ -113,12 +114,35 @@ static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp)
      xdp->flags |= XDP_FLAGS_FRAGS_PF_MEMALLOC;
 }
 
+static __always_inline void xdp_buff_disable_gro(struct xdp_buff *xdp)
+{
+     xdp->flags |= XDP_FLAGS_GRO_DISABLED;
+}
+
+static __always_inline bool xdp_buff_gro_disabled(struct xdp_buff *xdp)

include/net/xdp.h static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp) · line 132

Quoted 10 lines
include/net/xdp.h +10 -0
+{
+     return !!(xdp->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_buff_fixup_skb_offloading(struct xdp_buff *xdp, struct sk_buff *skb)
+{
+     if (xdp_buff_gro_disabled(xdp))
+             skb_disable_gro(skb);
+}

I don't think this should be named "fixup". "propagate", "update", "promote", ...?

Quoted 6 lines

Maybe if is not needed here?

        skb->gro_disabled = xdp_buff_gro_disabled(xdp)

?

I called it fixup mainly considering current skb fields could be set incorrectly (or maybe not?) For example when XDP load balances traffic, it could encap and send from server A to server B, but that means HW on B may have no good way to calculate the actual flow hash/csum. The original values could be read from A and sent within the encapsulation header, so that B can use this value to "fixup" the things.

But tbh, I am never good at naming, it's too hard for a non-native speaker sometimes... So I am very open on what to use if majority of the participants like it :D

The if is still needed since I added a control config to wrap that bit.

best
Yan

Quoted 45 lines

include/net/xdp.h static __always_inline void xdp_buff_set_frag_pfmemalloc(struct xdp_buff *xdp) · line 138

Quoted 6 lines
include/net/xdp.h +6 -0
+
+static __always_inline void
+xdp_init_buff_minimal(struct xdp_buff *xdp)
+{
+     xdp->flags = 0;
+}

"xdp_buff_clear_flags"?

Quoted 16 lines
include/net/xdp.h +3 -1
+
 static __always_inline void
 xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
 {
      xdp->frame_sz = frame_sz;
      xdp->rxq = rxq;
-     xdp->flags = 0;
+     xdp_init_buff_minimal(xdp);
 }
 
 static __always_inline void
@@ -187,6 +211,18 @@ static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame
      return !!(frame->flags & XDP_FLAGS_FRAGS_PF_MEMALLOC);
 }
 
+static __always_inline bool xdp_frame_gro_disabled(struct xdp_frame *frame)

include/net/xdp.h static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame · line 224

Quoted 10 lines
include/net/xdp.h +10 -0
+{
+     return !!(frame->flags & XDP_FLAGS_GRO_DISABLED);
+}
+
+static __always_inline void
+xdp_frame_fixup_skb_offloading(struct xdp_frame *frame, struct sk_buff *skb)
+{
+     if (xdp_frame_gro_disabled(frame))
+             skb_disable_gro(skb);
+}

(same)

include/net/xdp.h static __always_inline bool xdp_frame_is_frag_pfmemalloc(struct xdp_frame *frame · line 228

Quoted 4 lines
include/net/xdp.h +1 -0
+
 #define XDP_BULK_QUEUE_SIZE  16
 struct xdp_frame_bulk {
      int count;

Thanks,
Olek

author
#24 lore raw

RFC 3/9 xdp: implement bpf_xdp_disable_gro kfunc

In reply to Yan Zhai

Add a kfunc bpf_xdp_disable_gro to toggle XDP_FLAGS_GRO_DISABLED flag.

---
 net/core/xdp.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)
net/core/xdp.c +26 -1
index 41693154e426..d6e5f98a0081 100644
@@ -770,6 +770,20 @@ __bpf_kfunc int bpf_xdp_metadata_rx_vlan_tag(const struct xdp_md *ctx,
 	return -EOPNOTSUPP;
 }
 
+/**
+ * bpf_xdp_disable_gro - Set a flag on underlying XDP buffer, indicating
+ * the stack to skip this packet for GRO processing. This flag which be
+ * passed on when the driver builds the skb.
+ *
+ * Return:
+ * * always returns 0
+ */
+__bpf_kfunc int bpf_xdp_disable_gro(struct xdp_md *ctx)
+{
+	xdp_buff_disable_gro((struct xdp_buff *)ctx);
+	return 0;
+}
+
 __bpf_kfunc_end_defs();
 
 BTF_KFUNCS_START(xdp_metadata_kfunc_ids)
@@ -799,9 +813,20 @@ bool bpf_dev_bound_kfunc_id(u32 btf_id)
 	return btf_id_set8_contains(&xdp_metadata_kfunc_ids, btf_id);
 }
 
+BTF_KFUNCS_START(xdp_common_kfunc_ids)
+BTF_ID_FLAGS(func, bpf_xdp_disable_gro, KF_TRUSTED_ARGS)
+BTF_KFUNCS_END(xdp_common_kfunc_ids)
+
+static const struct btf_kfunc_id_set xdp_common_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &xdp_common_kfunc_ids,
+};
+
 static int __init xdp_metadata_init(void)
 {
-	return register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &xdp_metadata_kfunc_set);
+	int ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &xdp_metadata_kfunc_set);
+
+	return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &xdp_common_kfunc_set);
 }
 late_initcall(xdp_metadata_init);
 
Signature · 1 line
2.30.2
#25 lore raw

Re: [RFC net-next 3/9] xdp: implement bpf_xdp_disable_gro kfunc

kernel test robot noticed the following build warnings:

Hi Yan,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Yan-Zhai/skb-introduce-gro_disabled-bit/20240626-052232
base:   net-next/main
patch link:    https://lore.kernel.org/r/e31ce8f27a672e3c0def5a586a9a10f1d71e9948.1718919473.git.yan%40cloudflare.com
patch subject: [RFC net-next 3/9] xdp: implement bpf_xdp_disable_gro kfunc
config: i386-buildonly-randconfig-001-20240627 (https://download.01.org/0day-ci/archive/20240627/[email protected]/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240627/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> net/core/xdp.c:782: warning: Function parameter or struct member 'ctx' not described in 'bpf_xdp_disable_gro'

vim +782 net/core/xdp.c

772
773 /**
774 * bpf_xdp_disable_gro - Set a flag on underlying XDP buffer, indicating
775 * the stack to skip this packet for GRO processing. This flag which be
776 * passed on when the driver builds the skb.
777 *
778 * Return:
779 * * always returns 0
780 */
781 __bpf_kfunc int bpf_xdp_disable_gro(struct xdp_md *ctx)

 > 782	{

783 xdp_buff_disable_gro((struct xdp_buff *)ctx);
784 return 0;
785 }
786

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
author
#26 lore raw

RFC 4/9 bnxt: apply XDP offloading fixup when building skb

In reply to Yan Zhai

Add a common point to transfer offloading info from XDP context to skb.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +4 -0
index 7dc00c0d8992..0036c4752f0d 100644
@@ -2252,6 +2252,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
 			}
 		}
 	}
+
+	if (xdp_active)
+		xdp_buff_fixup_skb_offloading(&xdp, skb);
+
 	bnxt_deliver_skb(bp, bnapi, skb);
 	rc = 1;
 
Signature · 1 line
2.30.2
author
#27 lore raw

RFC 5/9 ice: apply XDP offloading fixup when building skb

In reply to Yan Zhai

Add a common point to transfer offloading info from XDP context to skb.

---
 drivers/net/ethernet/intel/ice/ice_txrx.c | 2 ++
 drivers/net/ethernet/intel/ice/ice_xsk.c  | 6 +++++-
 include/net/xdp_sock_drv.h                | 2 +-
 3 files changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +2 -0
index 8bb743f78fcb..a247306837ed 100644
@@ -1222,6 +1222,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
 			hard_start = page_address(rx_buf->page) + rx_buf->page_offset -
 				     offset;
+			xdp_init_buff_minimal(xdp);
 			xdp_prepare_buff(xdp, hard_start, offset, size, !!offset);
 #if (PAGE_SIZE > 4096)
 			/* At larger PAGE_SIZE, frame_sz depend on len size */
@@ -1287,6 +1288,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
 		/* populate checksum, VLAN, and protocol */
 		ice_process_skb_fields(rx_ring, rx_desc, skb);
+		xdp_buff_fixup_skb_offloading(xdp, skb);
 
 		ice_trace(clean_rx_irq_indicate, rx_ring, rx_desc, skb);
 		/* send completed skb up the stack */
drivers/net/ethernet/intel/ice/ice_xsk.c +5 -1
index a65955eb23c0..367658acaab8 100644
@@ -845,8 +845,10 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 	xdp_prog = READ_ONCE(rx_ring->xdp_prog);
 	xdp_ring = rx_ring->xdp_ring;
 
-	if (ntc != rx_ring->first_desc)
+	if (ntc != rx_ring->first_desc) {
 		first = *ice_xdp_buf(rx_ring, rx_ring->first_desc);
+		xdp_init_buff_minimal(first);
+	}
 
 	while (likely(total_rx_packets < (unsigned int)budget)) {
 		union ice_32b_rx_flex_desc *rx_desc;
@@ -920,6 +922,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 			break;
 		}
 
+		xdp = first;
 		first = NULL;
 		rx_ring->first_desc = ntc;
 
@@ -934,6 +937,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 		vlan_tci = ice_get_vlan_tci(rx_desc);
 
 		ice_process_skb_fields(rx_ring, rx_desc, skb);
+		xdp_buff_fixup_skb_offloading(xdp, skb);
 		ice_receive_skb(rx_ring, skb, vlan_tci);
 	}
 
include/net/xdp_sock_drv.h +1 -1
index 0a5dca2b2b3f..02243dc064c2 100644
@@ -181,7 +181,7 @@ static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
 	xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
 	xdp->data_meta = xdp->data;
 	xdp->data_end = xdp->data + size;
-	xdp->flags = 0;
+	xdp_init_buff_minimal(xdp);
 }
 
 static inline dma_addr_t xsk_buff_raw_get_dma(struct xsk_buff_pool *pool,
Signature · 1 line
2.30.2
#28 lore raw

Re: [RFC net-next 5/9] ice: apply XDP offloading fixup when building skb

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:22 -0700

Quoted 15 lines

Add a common point to transfer offloading info from XDP context to skb.

---
 drivers/net/ethernet/intel/ice/ice_txrx.c | 2 ++
 drivers/net/ethernet/intel/ice/ice_xsk.c  | 6 +++++-
 include/net/xdp_sock_drv.h                | 2 +-
 3 files changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +0 -0
index 8bb743f78fcb..a247306837ed 100644

drivers/net/ethernet/intel/ice/ice_txrx.c int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) · line 1225

Quoted 4 lines
drivers/net/ethernet/intel/ice/ice_txrx.c +1 -0
@@ -1222,6 +1222,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
 			hard_start = page_address(rx_buf->page) + rx_buf->page_offset -
 				     offset;
+			xdp_init_buff_minimal(xdp);

Two lines below, you have this:

	xdp_buff_clear_frags_flag(xdp);

Which clears frags bit in xdp->flags. I.e. since you always clear flags here, this call becomes redundant.
But I'd say that xdp->flags = 0 really wants to be moved from xdp_init_buff() to xdp_prepare_buff().

Quoted 16 lines
drivers/net/ethernet/intel/ice/ice_txrx.c +1 -0
 			xdp_prepare_buff(xdp, hard_start, offset, size, !!offset);
 #if (PAGE_SIZE > 4096)
 			/* At larger PAGE_SIZE, frame_sz depend on len size */
@@ -1287,6 +1288,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
 		/* populate checksum, VLAN, and protocol */
 		ice_process_skb_fields(rx_ring, rx_desc, skb);
+		xdp_buff_fixup_skb_offloading(xdp, skb);
 
 		ice_trace(clean_rx_irq_indicate, rx_ring, rx_desc, skb);
 		/* send completed skb up the stack */
drivers/net/ethernet/intel/ice/ice_xsk.c +0 -0
index a65955eb23c0..367658acaab8 100644

drivers/net/ethernet/intel/ice/ice_xsk.c int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) · line 850

Quoted 7 lines
drivers/net/ethernet/intel/ice/ice_xsk.c +2 -1
@@ -845,8 +845,10 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 	xdp_prog = READ_ONCE(rx_ring->xdp_prog);
 	xdp_ring = rx_ring->xdp_ring;
 
-	if (ntc != rx_ring->first_desc)
+	if (ntc != rx_ring->first_desc) {
 		first = *ice_xdp_buf(rx_ring, rx_ring->first_desc);
+		xdp_init_buff_minimal(first);

xdp_buff_set_size() always clears flags, this is redundant.

Quoted 25 lines
drivers/net/ethernet/intel/ice/ice_xsk.c +3 -0
+	}
 
 	while (likely(total_rx_packets < (unsigned int)budget)) {
 		union ice_32b_rx_flex_desc *rx_desc;
@@ -920,6 +922,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 			break;
 		}
 
+		xdp = first;
 		first = NULL;
 		rx_ring->first_desc = ntc;
 
@@ -934,6 +937,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
 		vlan_tci = ice_get_vlan_tci(rx_desc);
 
 		ice_process_skb_fields(rx_ring, rx_desc, skb);
+		xdp_buff_fixup_skb_offloading(xdp, skb);
 		ice_receive_skb(rx_ring, skb, vlan_tci);
 	}
 
include/net/xdp_sock_drv.h +0 -0
index 0a5dca2b2b3f..02243dc064c2 100644

include/net/xdp_sock_drv.h static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size) · line 184

Quoted 5 lines
include/net/xdp_sock_drv.h +1 -1
@@ -181,7 +181,7 @@ static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
 	xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
 	xdp->data_meta = xdp->data;
 	xdp->data_end = xdp->data + size;
-	xdp->flags = 0;
+	xdp_init_buff_minimal(xdp);

Why is this done in the patch prefixed with "ice:"?

include/net/xdp_sock_drv.h static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size) · line 187

Quoted 3 lines
include/net/xdp_sock_drv.h +0 -0
 }
 
 static inline dma_addr_t xsk_buff_raw_get_dma(struct xsk_buff_pool *pool,

Thanks,
Olek

author
#29 lore raw

Re: [RFC net-next 5/9] ice: apply XDP offloading fixup when building skb

Quoted 26 lines · Alexander Lobakin

From: Yan Zhai <[email protected]>
Date: Thu, 20 Jun 2024 15:19:22 -0700

Quoted 15 lines

Add a common point to transfer offloading info from XDP context to skb.

---
 drivers/net/ethernet/intel/ice/ice_txrx.c | 2 ++
 drivers/net/ethernet/intel/ice/ice_xsk.c  | 6 +++++-
 include/net/xdp_sock_drv.h                | 2 +-
 3 files changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_txrx.c +0 -0
index 8bb743f78fcb..a247306837ed 100644

drivers/net/ethernet/intel/ice/ice_txrx.c int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) · line 1225

Quoted 4 lines
drivers/net/ethernet/intel/ice/ice_txrx.c +1 -0
@@ -1222,6 +1222,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
                      hard_start = page_address(rx_buf->page) + rx_buf->page_offset -
                                   offset;
+                     xdp_init_buff_minimal(xdp);

Two lines below, you have this:

Quoted 7 lines
        xdp_buff_clear_frags_flag(xdp);

Which clears frags bit in xdp->flags. I.e. since you always clear flags here, this call becomes redundant.
But I'd say that xdp->flags = 0 really wants to be moved from xdp_init_buff() to xdp_prepare_buff().

You are right, there is some redundancy here. I will fix it if people feel good about the use case in general :)

Quoted 46 lines
Quoted 16 lines
drivers/net/ethernet/intel/ice/ice_txrx.c +1 -0
                      xdp_prepare_buff(xdp, hard_start, offset, size, !!offset);
 #if (PAGE_SIZE > 4096)
                      /* At larger PAGE_SIZE, frame_sz depend on len size */
@@ -1287,6 +1288,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
 
              /* populate checksum, VLAN, and protocol */
              ice_process_skb_fields(rx_ring, rx_desc, skb);
+             xdp_buff_fixup_skb_offloading(xdp, skb);
 
              ice_trace(clean_rx_irq_indicate, rx_ring, rx_desc, skb);
              /* send completed skb up the stack */
drivers/net/ethernet/intel/ice/ice_xsk.c +0 -0
index a65955eb23c0..367658acaab8 100644

drivers/net/ethernet/intel/ice/ice_xsk.c int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) · line 850

Quoted 7 lines
drivers/net/ethernet/intel/ice/ice_xsk.c +2 -1
@@ -845,8 +845,10 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
      xdp_prog = READ_ONCE(rx_ring->xdp_prog);
      xdp_ring = rx_ring->xdp_ring;
 
-     if (ntc != rx_ring->first_desc)
+     if (ntc != rx_ring->first_desc) {
              first = *ice_xdp_buf(rx_ring, rx_ring->first_desc);
+             xdp_init_buff_minimal(first);

xdp_buff_set_size() always clears flags, this is redundant.

Quoted 20 lines
drivers/net/ethernet/intel/ice/ice_xsk.c +3 -0
+     }
 
      while (likely(total_rx_packets < (unsigned int)budget)) {
              union ice_32b_rx_flex_desc *rx_desc;
@@ -920,6 +922,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
                      break;
              }
 
+             xdp = first;
              first = NULL;
              rx_ring->first_desc = ntc;
 
@@ -934,6 +937,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
              vlan_tci = ice_get_vlan_tci(rx_desc);
 
              ice_process_skb_fields(rx_ring, rx_desc, skb);
+             xdp_buff_fixup_skb_offloading(xdp, skb);
              ice_receive_skb(rx_ring, skb, vlan_tci);
      }
 
Quoted 13 lines

include/net/xdp_sock_drv.h static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size) · line 184

Quoted 10 lines
include/net/xdp_sock_drv.h +1 -1
index 0a5dca2b2b3f..02243dc064c2 100644
@@ -181,7 +181,7 @@ static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
      xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
      xdp->data_meta = xdp->data;
      xdp->data_end = xdp->data + size;
-     xdp->flags = 0;
+     xdp_init_buff_minimal(xdp);

Why is this done in the patch prefixed with "ice:"?

Good catch, this should be moved to the previous patch.

thanks
Yan

Quoted 6 lines

include/net/xdp_sock_drv.h static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size) · line 187

Quoted 3 lines
include/net/xdp_sock_drv.h +0 -0
 }
 
 static inline dma_addr_t xsk_buff_raw_get_dma(struct xsk_buff_pool *pool,

Thanks,
Olek

author
#30 lore raw

RFC 6/9 veth: apply XDP offloading fixup when building skb

In reply to Yan Zhai

Add a common point to transfer offloading info from XDP context to skb.

---
 drivers/net/veth.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/net/veth.c +4 -0
index 426e68a95067..c799362a839c 100644
@@ -703,6 +703,8 @@ static void veth_xdp_rcv_bulk_skb(struct veth_rq *rq, void **frames,
 			stats->rx_drops++;
 			continue;
 		}
+
+		xdp_frame_fixup_skb_offloading(frames[i], skb);
 		napi_gro_receive(&rq->xdp_napi, skb);
 	}
 }
@@ -855,6 +857,8 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
 	metalen = xdp->data - xdp->data_meta;
 	if (metalen)
 		skb_metadata_set(skb, metalen);
+
+	xdp_buff_fixup_skb_offloading(xdp, skb);
 out:
 	return skb;
 drop:
Signature · 1 line
2.30.2
#31 lore raw

RFC 7/9 mlx5: move xdp_buff scope one level up

In reply to Yan Zhai

This is in preparation for changes.

---
 drivers/net/ethernet/mellanox/mlx5/core/en.h  |   6 +-
 .../ethernet/mellanox/mlx5/core/en/xsk/rx.c   |   6 +-
 .../ethernet/mellanox/mlx5/core/en/xsk/rx.h   |   6 +-
 .../net/ethernet/mellanox/mlx5/core/en_rx.c   | 103 +++++++++---------
 4 files changed, 66 insertions(+), 55 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en.h +4 -2
index 6a343a8f162f..3d26f976f692 100644
@@ -580,14 +580,16 @@ struct mlx5e_mpw_info {
 #define MLX5E_MAX_RX_FRAGS 4
 
 struct mlx5e_rq;
+struct mlx5e_xdp_buff;
 typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq*, struct mlx5_cqe64*);
 typedef struct sk_buff *
 (*mlx5e_fp_skb_from_cqe_mpwrq)(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
 			       struct mlx5_cqe64 *cqe, u16 cqe_bcnt,
-			       u32 head_offset, u32 page_idx);
+			       u32 head_offset, u32 page_idx,
+			       struct mlx5e_xdp_buff *mxbuf);
 typedef struct sk_buff *
 (*mlx5e_fp_skb_from_cqe)(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi,
-			 struct mlx5_cqe64 *cqe, u32 cqe_bcnt);
+			 struct mlx5_cqe64 *cqe, u32 cqe_bcnt, struct mlx5e_xdp_buff *mxbuf);
 typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq);
 typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16);
 typedef void (*mlx5e_fp_shampo_dealloc_hd)(struct mlx5e_rq*, u16, u16, bool);
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +4 -2
index 1b7132fa70de..4dacaa61e106 100644
@@ -249,7 +249,8 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 						    struct mlx5_cqe64 *cqe,
 						    u16 cqe_bcnt,
 						    u32 head_offset,
-						    u32 page_idx)
+						    u32 page_idx,
+						    struct mlx5e_xdp_buff *mxbuf_)
 {
 	struct mlx5e_xdp_buff *mxbuf = xsk_buff_to_mxbuf(wi->alloc_units.xsk_buffs[page_idx]);
 	struct bpf_prog *prog;
@@ -304,7 +305,8 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
 					      struct mlx5e_wqe_frag_info *wi,
 					      struct mlx5_cqe64 *cqe,
-					      u32 cqe_bcnt)
+					      u32 cqe_bcnt,
+					      struct mlx5e_xdp_buff *mxbuf_)
 {
 	struct mlx5e_xdp_buff *mxbuf = xsk_buff_to_mxbuf(*wi->xskp);
 	struct bpf_prog *prog;
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h +4 -2
index cefc0ef6105d..0890c975042c 100644
@@ -16,10 +16,12 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 						    struct mlx5_cqe64 *cqe,
 						    u16 cqe_bcnt,
 						    u32 head_offset,
-						    u32 page_idx);
+						    u32 page_idx,
+						    struct mlx5e_xdp_buff *mxbuf_);
 struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
 					      struct mlx5e_wqe_frag_info *wi,
 					      struct mlx5_cqe64 *cqe,
-					      u32 cqe_bcnt);
+					      u32 cqe_bcnt,
+					      struct mlx5e_xdp_buff *mxbuf_);
 
 #endif /* __MLX5_EN_XSK_RX_H__ */
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +54 -49

342 lines not on this page · raw message

Signature · 1 line
2.30.2
author
#32 lore raw

RFC 8/9 mlx5: apply XDP offloading fixup when building skb

In reply to Yan Zhai

Add a common point to transfer offloading info from XDP context to skb.

---
 .../net/ethernet/mellanox/mlx5/core/en/xsk/rx.c    |  8 ++++++--
 drivers/net/ethernet/mellanox/mlx5/core/en_rx.c    | 14 ++++++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +6 -2
index 4dacaa61e106..9bf49ff2e0dd 100644
@@ -250,7 +250,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 						    u16 cqe_bcnt,
 						    u32 head_offset,
 						    u32 page_idx,
-						    struct mlx5e_xdp_buff *mxbuf_)
+						    struct mlx5e_xdp_buff *mxbuf_caller)
 {
 	struct mlx5e_xdp_buff *mxbuf = xsk_buff_to_mxbuf(wi->alloc_units.xsk_buffs[page_idx]);
 	struct bpf_prog *prog;
@@ -270,6 +270,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 
 	/* mxbuf->rq is set on allocation, but cqe is per-packet so set it here */
 	mxbuf->cqe = cqe;
+	xdp_init_buff_minimal(&mxbuf->xdp);
 	xsk_buff_set_size(&mxbuf->xdp, cqe_bcnt);
 	xsk_buff_dma_sync_for_cpu(&mxbuf->xdp);
 	net_prefetch(mxbuf->xdp.data);
@@ -295,6 +296,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 			__set_bit(page_idx, wi->skip_release_bitmap); /* non-atomic */
 		return NULL; /* page/packet was consumed by XDP */
 	}
+	mxbuf_caller->xdp.flags = mxbuf->xdp.flags;
 
 	/* XDP_PASS: copy the data from the UMEM to a new SKB and reuse the
 	 * frame. On SKB allocation failure, NULL is returned.
@@ -306,7 +308,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
 					      struct mlx5e_wqe_frag_info *wi,
 					      struct mlx5_cqe64 *cqe,
 					      u32 cqe_bcnt,
-					      struct mlx5e_xdp_buff *mxbuf_)
+					      struct mlx5e_xdp_buff *mxbuf_caller)
 {
 	struct mlx5e_xdp_buff *mxbuf = xsk_buff_to_mxbuf(*wi->xskp);
 	struct bpf_prog *prog;
@@ -320,6 +322,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
 
 	/* mxbuf->rq is set on allocation, but cqe is per-packet so set it here */
 	mxbuf->cqe = cqe;
+	xdp_init_buff_minimal(&mxbuf->xdp);
 	xsk_buff_set_size(&mxbuf->xdp, cqe_bcnt);
 	xsk_buff_dma_sync_for_cpu(&mxbuf->xdp);
 	net_prefetch(mxbuf->xdp.data);
@@ -330,6 +333,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
 			wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);
 		return NULL; /* page/packet was consumed by XDP */
 	}
+	mxbuf_caller->xdp.flags = mxbuf->xdp.flags;
 
 	/* XDP_PASS: copy the data from the UMEM to a new SKB. The frame reuse
 	 * will be handled by mlx5e_free_rx_wqe.
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +14 -0
index 1a592a1ab988..0a47889e281e 100644
@@ -1670,6 +1670,8 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi,
 	dma_addr_t addr;
 	u32 frag_size;
 
+	xdp_init_buff_minimal(&mxbuf->xdp);
+
 	va             = page_address(frag_page->page) + wi->offset;
 	data           = va + rx_headroom;
 	frag_size      = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
@@ -1721,6 +1723,7 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi
 	void *va;
 
 	frag_page = wi->frag_page;
+	xdp_init_buff_minimal(&mxbuf->xdp);
 
 	va = page_address(frag_page->page) + wi->offset;
 	frag_consumed_bytes = min_t(u32, frag_info->frag_size, cqe_bcnt);
@@ -1837,6 +1840,7 @@ static void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
 	}
 
 	mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
 
 	if (mlx5e_cqe_regb_chain(cqe))
 		if (!mlx5e_tc_update_skb_nic(cqe, skb)) {
@@ -1885,6 +1889,7 @@ static void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
 	}
 
 	mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
 
 	if (rep->vlan && skb_vlan_tag_present(skb))
 		skb_vlan_pop(skb);
@@ -1935,6 +1940,7 @@ static void mlx5e_handle_rx_cqe_mpwrq_rep(struct mlx5e_rq *rq, struct mlx5_cqe64
 		goto mpwrq_cqe_out;
 
 	mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
 
 	mlx5e_rep_tc_receive(cqe, rq, skb);
 
@@ -2138,6 +2144,8 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
 		return NULL;
 	}
 
+	xdp_init_buff_minimal(&mxbuf->xdp);
+
 	va             = page_address(frag_page->page) + head_offset;
 	data           = va + rx_headroom;
 	frag_size      = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
@@ -2345,6 +2353,8 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq
 	}
 
 	mlx5e_shampo_complete_rx_cqe(rq, cqe, cqe_bcnt, *skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, *skb);
+
 	if (flush && rq->hw_gro_data->skb)
 		mlx5e_shampo_flush_skb(rq, cqe, match);
 free_hd_entry:
@@ -2404,6 +2414,7 @@ static void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cq
 		goto mpwrq_cqe_out;
 
 	mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
 
 	if (mlx5e_cqe_regb_chain(cqe))
 		if (!mlx5e_tc_update_skb_nic(cqe, skb)) {
@@ -2649,6 +2660,8 @@ static void mlx5i_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
 		goto wq_cyc_pop;
 
 	mlx5i_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
+
 	if (unlikely(!skb->dev)) {
 		dev_kfree_skb_any(skb);
 		goto wq_cyc_pop;
@@ -2740,6 +2753,7 @@ static void mlx5e_trap_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe
 
 	mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
 	skb_push(skb, ETH_HLEN);
+	xdp_buff_fixup_skb_offloading(&mxbuf.xdp, skb);
 
 	mlx5_devlink_trap_report(rq->mdev, trap_id, skb,
 				 rq->netdev->devlink_port);
Signature · 1 line
2.30.2
author
#33 lore raw

RFC 9/9 bpf: selftests: test disabling GRO by XDP

In reply to Yan Zhai

Test the case when XDP disables GRO for a packet, the effect is actually reflected on the receiving side.

---
 tools/testing/selftests/bpf/config            |   1 +
 .../selftests/bpf/prog_tests/xdp_offloading.c | 122 ++++++++++++++++++
 .../selftests/bpf/progs/xdp_offloading.c      |  50 +++++++
 3 files changed, 173 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_offloading.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_offloading.c
tools/testing/selftests/bpf/config +1 -0
index 2fb16da78dce..e789392f44bd 100644
@@ -96,3 +96,4 @@ CONFIG_XDP_SOCKETS=y
 CONFIG_XFRM_INTERFACE=y
 CONFIG_TCP_CONG_DCTCP=y
 CONFIG_TCP_CONG_BBR=y
+CONFIG_SKB_GRO_CONTROL=y
tools/testing/selftests/bpf/prog_tests/xdp_offloading.c new file +122 -0

122 lines not on this page · raw message

tools/testing/selftests/bpf/progs/xdp_offloading.c new file +50 -0

50 lines not on this page · raw message

Signature · 1 line
2.30.2