diff mbox series

[RFC,bpf-next,21/52] net, xdp: allow metadata > 32

Message ID 20220628194812.1453059-22-alexandr.lobakin@intel.com (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series bpf, xdp: introduce and use Generic Hints/metadata | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-1 fail Logs for Kernel LATEST on ubuntu-latest with gcc
bpf/vmtest-bpf-next-VM_Test-2 fail Logs for Kernel LATEST on ubuntu-latest with llvm-15
bpf/vmtest-bpf-next-VM_Test-3 fail Logs for Kernel LATEST on z15 with gcc
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/apply fail Patch does not apply to bpf-next

Commit Message

Alexander Lobakin June 28, 2022, 7:47 p.m. UTC
Hardware/driver-prepended XDP metadata might be much bigger than 32
bytes, especially if it includes a piece of a descriptor.
Relax the restriction and allow metadata larger than 32 bytes and
make __skb_metadata_differs() work with bigger lengths. The new
restriction is pretty much mechanical -- skb_shared_info::meta_len
is a u8 and XDP_PACKET_HEADROOM is 256 (minus
`sizeof(struct xdp_frame)`).
The requirement of having its length aligned to 4 bytes is still
valid.

Signed-off-by: Alexander Lobakin <alexandr.lobakin@intel.com>
---
 include/linux/skbuff.h | 13 ++++++++-----
 include/net/xdp_meta.h | 21 ++++++++++++++++++++-
 2 files changed, 28 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 82edf0359ab3..a825ea7f375d 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4096,10 +4096,13 @@  static inline bool __skb_metadata_differs(const struct sk_buff *skb_a,
 {
 	const void *a = skb_metadata_end(skb_a);
 	const void *b = skb_metadata_end(skb_b);
-	/* Using more efficient varaiant than plain call to memcmp(). */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
 	u64 diffs = 0;
 
+	if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
+	    BITS_PER_LONG != 64)
+		goto slow;
+
+	/* Using more efficient variant than plain call to memcmp(). */
 	switch (meta_len) {
 #define __it(x, op) (x -= sizeof(u##op))
 #define __it_diff(a, b, op) (*(u##op *)__it(a, op)) ^ (*(u##op *)__it(b, op))
@@ -4119,11 +4122,11 @@  static inline bool __skb_metadata_differs(const struct sk_buff *skb_a,
 		fallthrough;
 	case  4: diffs |= __it_diff(a, b, 32);
 		break;
+	default:
+slow:
+		return memcmp(a - meta_len, b - meta_len, meta_len);
 	}
 	return diffs;
-#else
-	return memcmp(a - meta_len, b - meta_len, meta_len);
-#endif
 }
 
 static inline bool skb_metadata_differs(const struct sk_buff *skb_a,
diff --git a/include/net/xdp_meta.h b/include/net/xdp_meta.h
index e1f3df9ceb93..3a40189d71c6 100644
--- a/include/net/xdp_meta.h
+++ b/include/net/xdp_meta.h
@@ -5,6 +5,7 @@ 
 #define __LINUX_NET_XDP_META_H__
 
 #include <net/xdp.h>
+#include <uapi/linux/bpf.h>
 
 /* Drivers not supporting XDP metadata can use this helper, which
  * rejects any room expansion for metadata as a result.
@@ -21,9 +22,27 @@  xdp_data_meta_unsupported(const struct xdp_buff *xdp)
 	return unlikely(xdp->data_meta > xdp->data);
 }
 
+/**
+ * xdp_metalen_invalid -- check if the length of a frame's metadata is valid
+ * @metalen: the length of the frame's metadata
+ *
+ * skb_shared_info::meta_len is of 1 byte long, thus it can't be longer than
+ * 255, but this always can change. XDP_PACKET_HEADROOM is 256, and this is a
+ * UAPI. sizeof(struct xdp_frame) is reserved since xdp_frame is being placed
+ * at xdp_buff::data_hard_start whilst being constructed on XDP_REDIRECT.
+ * The 32-bit alignment requirement is arbitrary, kept for simplicity and,
+ * sometimes, speed.
+ */
 static inline bool xdp_metalen_invalid(unsigned long metalen)
 {
-	return (metalen & (sizeof(__u32) - 1)) || (metalen > 32);
+	typeof(metalen) max;
+
+	max = min_t(typeof(max),
+		    (typeof_member(struct skb_shared_info, meta_len))~0UL,
+		    XDP_PACKET_HEADROOM - sizeof(struct xdp_frame));
+	BUILD_BUG_ON(!__builtin_constant_p(max));
+
+	return (metalen & (sizeof(u32) - 1)) || metalen > max;
 }
 
 #endif /* __LINUX_NET_XDP_META_H__ */