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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
---
 include/linux/skbuff.h | 13 ++++++++-----
 include/net/xdp_meta.h | 21 ++++++++++++++++++++-
 2 files changed, 28 insertions(+), 6 deletions(-)

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__ */
-- 
2.36.1




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux