diff mbox series

[bpf-next,v5,2/3] bpf: Add xdp dynptrs

Message ID 20220831183224.3754305-3-joannelkoong@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Add skb + xdp dynptrs | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1697 this patch: 1697
netdev/cc_maintainers warning 12 maintainers not CCed: edumazet@google.com john.fastabend@gmail.com jolsa@kernel.org pabeni@redhat.com song@kernel.org yhs@fb.com haoluo@google.com martin.lau@linux.dev kpsingh@kernel.org hawk@kernel.org davem@davemloft.net sdf@google.com
netdev/build_clang success Errors and warnings before: 173 this patch: 173
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 1691 this patch: 1691
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 92 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-4 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-5 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-1 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-17 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-6 success Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-9 fail Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 fail Logs for test_progs_no_alu32 on s390x with gcc

Commit Message

Joanne Koong Aug. 31, 2022, 6:32 p.m. UTC
Add xdp dynptrs, which are dynptrs whose underlying pointer points
to a xdp_buff. The dynptr acts on xdp data. xdp dynptrs have two main
benefits. One is that they allow operations on sizes that are not
statically known at compile-time (eg variable-sized accesses).
Another is that parsing the packet data through dynptrs (instead of
through direct access of xdp->data and xdp->data_end) can be more
ergonomic and less brittle (eg does not need manual if checking for
being within bounds of data_end).

For reads and writes on the dynptr, this includes reading/writing
from/to and across fragments. For data slices, direct access to
data in fragments is also permitted, but access across fragments
is not.

Any helper calls that change the underlying packet buffer (eg
bpf_xdp_adjust_head) invalidates any data slices of the associated
dynptr. The stack trace for this is check_helper_call() ->
clear_all_pkt_pointers() -> __clear_all_pkt_pointers() ->
mark_reg_unknown().

For examples of how xdp dynptrs can be used, please see the attached
selftests.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
 include/linux/bpf.h            |  6 ++++-
 include/linux/filter.h         | 18 +++++++++++++
 include/uapi/linux/bpf.h       | 25 +++++++++++++++---
 kernel/bpf/helpers.c           | 12 +++++++++
 kernel/bpf/verifier.c          | 18 +++++++++----
 net/core/filter.c              | 46 +++++++++++++++++++++++++++++-----
 tools/include/uapi/linux/bpf.h | 25 +++++++++++++++---
 7 files changed, 132 insertions(+), 18 deletions(-)

Comments

kernel test robot Sept. 1, 2022, 3:09 a.m. UTC | #1
Hi Joanne,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on bpf-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/Add-skb-xdp-dynptrs/20220901-024122
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
config: arc-randconfig-r043-20220831
compiler: arc-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/76ffb4e05d0ed50e427c9eb6664915de23d82fff
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Joanne-Koong/Add-skb-xdp-dynptrs/20220901-024122
        git checkout 76ffb4e05d0ed50e427c9eb6664915de23d82fff
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from kernel/sysctl.c:35:
   include/linux/filter.h:1543:5: warning: no previous prototype for '__bpf_skb_load_bytes' [-Wmissing-prototypes]
    1543 | int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
   include/linux/filter.h:1548:5: warning: no previous prototype for '__bpf_skb_store_bytes' [-Wmissing-prototypes]
    1548 | int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1554:5: warning: no previous prototype for '__bpf_xdp_load_bytes' [-Wmissing-prototypes]
    1554 | int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1559:5: warning: no previous prototype for '__bpf_xdp_store_bytes' [-Wmissing-prototypes]
    1559 | int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1564:7: warning: no previous prototype for 'bpf_xdp_pointer' [-Wmissing-prototypes]
    1564 | void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
         |       ^~~~~~~~~~~~~~~
--
   In file included from kernel/kallsyms.c:25:
   include/linux/filter.h:1543:5: warning: no previous prototype for '__bpf_skb_load_bytes' [-Wmissing-prototypes]
    1543 | int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
   include/linux/filter.h:1548:5: warning: no previous prototype for '__bpf_skb_store_bytes' [-Wmissing-prototypes]
    1548 | int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1554:5: warning: no previous prototype for '__bpf_xdp_load_bytes' [-Wmissing-prototypes]
    1554 | int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1559:5: warning: no previous prototype for '__bpf_xdp_store_bytes' [-Wmissing-prototypes]
    1559 | int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1564:7: warning: no previous prototype for 'bpf_xdp_pointer' [-Wmissing-prototypes]
    1564 | void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
         |       ^~~~~~~~~~~~~~~
   kernel/kallsyms.c:570:12: warning: no previous prototype for 'arch_get_kallsym' [-Wmissing-prototypes]
     570 | int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value,
         |            ^~~~~~~~~~~~~~~~
--
   arc-elf-ld: drivers/target/target_core_hba.o: in function `__bpf_skb_load_bytes':
   target_core_hba.c:(.text+0x1d8): multiple definition of `__bpf_skb_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb44): first defined here
   arc-elf-ld: drivers/target/target_core_hba.o: in function `__bpf_skb_store_bytes':
   target_core_hba.c:(.text+0x1e0): multiple definition of `__bpf_skb_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb4c): first defined here
   arc-elf-ld: drivers/target/target_core_hba.o: in function `__bpf_xdp_load_bytes':
>> target_core_hba.c:(.text+0x1e8): multiple definition of `__bpf_xdp_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb54): first defined here
   arc-elf-ld: drivers/target/target_core_hba.o: in function `__bpf_xdp_store_bytes':
>> target_core_hba.c:(.text+0x1f0): multiple definition of `__bpf_xdp_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb5c): first defined here
   arc-elf-ld: drivers/target/target_core_hba.o: in function `bpf_xdp_pointer':
>> target_core_hba.c:(.text+0x1f8): multiple definition of `bpf_xdp_pointer'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb64): first defined here
   arc-elf-ld: drivers/target/target_core_tpg.o: in function `__bpf_skb_load_bytes':
   target_core_tpg.c:(.text+0x664): multiple definition of `__bpf_skb_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb44): first defined here
   arc-elf-ld: drivers/target/target_core_tpg.o: in function `__bpf_skb_store_bytes':
   target_core_tpg.c:(.text+0x66c): multiple definition of `__bpf_skb_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb4c): first defined here
   arc-elf-ld: drivers/target/target_core_tpg.o: in function `__bpf_xdp_load_bytes':
   target_core_tpg.c:(.text+0x674): multiple definition of `__bpf_xdp_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb54): first defined here
   arc-elf-ld: drivers/target/target_core_tpg.o: in function `__bpf_xdp_store_bytes':
   target_core_tpg.c:(.text+0x67c): multiple definition of `__bpf_xdp_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb5c): first defined here
   arc-elf-ld: drivers/target/target_core_tpg.o: in function `bpf_xdp_pointer':
   target_core_tpg.c:(.text+0x684): multiple definition of `bpf_xdp_pointer'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb64): first defined here
   arc-elf-ld: drivers/target/target_core_transport.o: in function `__bpf_skb_load_bytes':
   target_core_transport.c:(.text+0x4640): multiple definition of `__bpf_skb_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb44): first defined here
   arc-elf-ld: drivers/target/target_core_transport.o: in function `__bpf_skb_store_bytes':
   target_core_transport.c:(.text+0x4648): multiple definition of `__bpf_skb_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb4c): first defined here
   arc-elf-ld: drivers/target/target_core_transport.o: in function `__bpf_xdp_load_bytes':
   target_core_transport.c:(.text+0x4650): multiple definition of `__bpf_xdp_load_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb54): first defined here
   arc-elf-ld: drivers/target/target_core_transport.o: in function `__bpf_xdp_store_bytes':
   target_core_transport.c:(.text+0x4658): multiple definition of `__bpf_xdp_store_bytes'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb5c): first defined here
   arc-elf-ld: drivers/target/target_core_transport.o: in function `bpf_xdp_pointer':
   target_core_transport.c:(.text+0x4660): multiple definition of `bpf_xdp_pointer'; drivers/target/target_core_device.o:target_core_device.c:(.text+0xb64): first defined here
--
   In file included from kernel/bpf/core.c:21:
   include/linux/filter.h:1543:5: warning: no previous prototype for '__bpf_skb_load_bytes' [-Wmissing-prototypes]
    1543 | int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
   include/linux/filter.h:1548:5: warning: no previous prototype for '__bpf_skb_store_bytes' [-Wmissing-prototypes]
    1548 | int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1554:5: warning: no previous prototype for '__bpf_xdp_load_bytes' [-Wmissing-prototypes]
    1554 | int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1559:5: warning: no previous prototype for '__bpf_xdp_store_bytes' [-Wmissing-prototypes]
    1559 | int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1564:7: warning: no previous prototype for 'bpf_xdp_pointer' [-Wmissing-prototypes]
    1564 | void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
         |       ^~~~~~~~~~~~~~~
   kernel/bpf/core.c:1623:12: warning: no previous prototype for 'bpf_probe_read_kernel' [-Wmissing-prototypes]
    1623 | u64 __weak bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)
         |            ^~~~~~~~~~~~~~~~~~~~~
--
   In file included from include/linux/bpf_verifier.h:9,
                    from kernel/bpf/btf.c:19:
   include/linux/filter.h:1543:5: warning: no previous prototype for '__bpf_skb_load_bytes' [-Wmissing-prototypes]
    1543 | int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
   include/linux/filter.h:1548:5: warning: no previous prototype for '__bpf_skb_store_bytes' [-Wmissing-prototypes]
    1548 | int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1554:5: warning: no previous prototype for '__bpf_xdp_load_bytes' [-Wmissing-prototypes]
    1554 | int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1559:5: warning: no previous prototype for '__bpf_xdp_store_bytes' [-Wmissing-prototypes]
    1559 | int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
         |     ^~~~~~~~~~~~~~~~~~~~~
>> include/linux/filter.h:1564:7: warning: no previous prototype for 'bpf_xdp_pointer' [-Wmissing-prototypes]
    1564 | void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
         |       ^~~~~~~~~~~~~~~
   kernel/bpf/btf.c: In function 'btf_seq_show':
   kernel/bpf/btf.c:6612:29: warning: function 'btf_seq_show' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
    6612 |         seq_vprintf((struct seq_file *)show->target, fmt, args);
         |                             ^~~~~~~~
   kernel/bpf/btf.c: In function 'btf_snprintf_show':
   kernel/bpf/btf.c:6649:9: warning: function 'btf_snprintf_show' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
    6649 |         len = vsnprintf(show->target, ssnprintf->len_left, fmt, args);
         |         ^~~
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 26ad1422a157..f07d127f559f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -411,11 +411,15 @@  enum bpf_type_flag {
 	/* DYNPTR points to sk_buff */
 	DYNPTR_TYPE_SKB		= BIT(11 + BPF_BASE_TYPE_BITS),
 
+	/* DYNPTR points to xdp_buff */
+	DYNPTR_TYPE_XDP		= BIT(12 + BPF_BASE_TYPE_BITS),
+
 	__BPF_TYPE_FLAG_MAX,
 	__BPF_TYPE_LAST_FLAG	= __BPF_TYPE_FLAG_MAX - 1,
 };
 
-#define DYNPTR_TYPE_FLAG_MASK	(DYNPTR_TYPE_LOCAL | DYNPTR_TYPE_RINGBUF | DYNPTR_TYPE_SKB)
+#define DYNPTR_TYPE_FLAG_MASK	(DYNPTR_TYPE_LOCAL | DYNPTR_TYPE_RINGBUF | DYNPTR_TYPE_SKB \
+				 | DYNPTR_TYPE_XDP)
 
 /* Max number of base types. */
 #define BPF_BASE_TYPE_LIMIT	(1UL << BPF_BASE_TYPE_BITS)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index a3a415344001..09311cb4375d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1536,6 +1536,9 @@  static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u32 ifind
 int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len);
 int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
 			  u32 len, u64 flags);
+int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
+int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
+void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len);
 #else /* CONFIG_NET */
 int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
 {
@@ -1547,6 +1550,21 @@  int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
 {
 	return -EOPNOTSUPP;
 }
+
+int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
+{
+	return -EOPNOTSUPP;
+}
+
+int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
+{
+	return -EOPNOTSUPP;
+}
+
+void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
+{
+	return NULL;
+}
 #endif /* CONFIG_NET */
 
 #endif /* __LINUX_FILTER_H__ */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b4e5f7d81a20..992f7565a41e 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5315,13 +5315,18 @@  union bpf_attr {
  *		      and try again.
  *
  *		    * The data slice is automatically invalidated anytime
- *		      **bpf_dynptr_write**\ () or a helper call that changes
- *		      the underlying packet buffer (eg **bpf_skb_pull_data**\ ())
+ *		      **bpf_dynptr_write**\ () is called.
+ *
+ *		For skb-type and xdp-type dynptrs:
+ *		    * The data slice is automatically invalidated anytime a
+ *		      helper call that changes the underlying packet buffer
+ *		      (eg **bpf_skb_pull_data**\ (), **bpf_xdp_adjust_head**\ ())
  *		      is called.
  *	Return
  *		Pointer to the underlying dynptr data, NULL if the dynptr is
  *		read-only, if the dynptr is invalid, or if the offset and length
- *		is out of bounds or in a paged buffer for skb-type dynptrs.
+ *		is out of bounds or in a paged buffer for skb-type dynptrs or
+ *		across fragments for xdp-type dynptrs.
  *
  * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
  *	Description
@@ -5420,6 +5425,19 @@  union bpf_attr {
  *		*flags* is currently unused, it must be 0 for now.
  *	Return
  *		0 on success or -EINVAL if flags is not 0.
+ *
+ * long bpf_dynptr_from_xdp(struct xdp_buff *xdp_md, u64 flags, struct bpf_dynptr *ptr)
+ *	Description
+ *		Get a dynptr to the data in *xdp_md*. *xdp_md* must be the BPF program
+ *		context.
+ *
+ *		Calls that change the *xdp_md*'s underlying packet buffer
+ *		(eg **bpf_xdp_adjust_head**\ ()) do not invalidate the dynptr, but
+ *		they do invalidate any data slices associated with the dynptr.
+ *
+ *		*flags* is currently unused, it must be 0 for now.
+ *	Return
+ *		0 on success, -EINVAL if flags is not 0.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5632,6 +5650,7 @@  union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6),	\
 	FN(ktime_get_tai_ns),		\
 	FN(dynptr_from_skb),		\
+	FN(dynptr_from_xdp),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 98fcb4704a9e..befafae34a63 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -1507,6 +1507,8 @@  BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, struct bpf_dynptr_kern *, src
 		return 0;
 	case BPF_DYNPTR_TYPE_SKB:
 		return __bpf_skb_load_bytes(src->data, src->offset + offset, dst, len);
+	case BPF_DYNPTR_TYPE_XDP:
+		return __bpf_xdp_load_bytes(src->data, src->offset + offset, dst, len);
 	default:
 		WARN_ONCE(true, "bpf_dynptr_read: unknown dynptr type %d\n", type);
 		return -EFAULT;
@@ -1549,6 +1551,10 @@  BPF_CALL_5(bpf_dynptr_write, struct bpf_dynptr_kern *, dst, u32, offset, void *,
 	case BPF_DYNPTR_TYPE_SKB:
 		return __bpf_skb_store_bytes(dst->data, dst->offset + offset, src, len,
 					     flags);
+	case BPF_DYNPTR_TYPE_XDP:
+		if (flags)
+			return -EINVAL;
+		return __bpf_xdp_store_bytes(dst->data, dst->offset + offset, src, len);
 	default:
 		WARN_ONCE(true, "bpf_dynptr_write: unknown dynptr type %d\n", type);
 		return -EFAULT;
@@ -1600,6 +1606,12 @@  BPF_CALL_3(bpf_dynptr_data, struct bpf_dynptr_kern *, ptr, u32, offset, u32, len
 		data = skb->data;
 		break;
 	}
+	case BPF_DYNPTR_TYPE_XDP:
+		/* if the requested data in across fragments, then it cannot
+		 * be accessed directly - bpf_xdp_pointer will return NULL
+		 */
+		return (unsigned long)bpf_xdp_pointer(ptr->data,
+						      ptr->offset + offset, len);
 	default:
 		WARN_ONCE(true, "bpf_dynptr_data: unknown dynptr type %d\n", type);
 		return 0;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7f67fac66735..f5ff0f26b7cc 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -686,6 +686,8 @@  static enum bpf_dynptr_type arg_to_dynptr_type(enum bpf_arg_type arg_type)
 		return BPF_DYNPTR_TYPE_RINGBUF;
 	case DYNPTR_TYPE_SKB:
 		return BPF_DYNPTR_TYPE_SKB;
+	case DYNPTR_TYPE_XDP:
+		return BPF_DYNPTR_TYPE_XDP;
 	default:
 		return BPF_DYNPTR_TYPE_INVALID;
 	}
@@ -1411,7 +1413,7 @@  static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)
 static bool reg_is_dynptr_slice_pkt(const struct bpf_reg_state *reg)
 {
 	return base_type(reg->type) == PTR_TO_MEM &&
-		reg->type & DYNPTR_TYPE_SKB;
+		(reg->type & DYNPTR_TYPE_SKB || reg->type & DYNPTR_TYPE_XDP);
 }
 
 /* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */
@@ -6088,6 +6090,9 @@  static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 			case DYNPTR_TYPE_SKB:
 				err_extra = "skb ";
 				break;
+			case DYNPTR_TYPE_XDP:
+				err_extra = "xdp ";
+				break;
 			default:
 				break;
 			}
@@ -6519,7 +6524,7 @@  static int check_func_proto(const struct bpf_func_proto *fn, int func_id)
 /* Packet data might have moved, any old PTR_TO_PACKET[_META,_END]
  * are now invalid, so turn them into unknown SCALAR_VALUE.
  *
- * This applies to dynptr slices belonging to skb dynptrs,
+ * This applies to dynptr slices belonging to skb or xdp dynptrs,
  * since these slices point to packet data.
  */
 static void __clear_all_pkt_pointers(struct bpf_verifier_env *env,
@@ -7470,9 +7475,12 @@  static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 		mark_reg_known_zero(env, regs, BPF_REG_0);
 		regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag;
 		regs[BPF_REG_0].mem_size = meta.mem_size;
-		if (func_id == BPF_FUNC_dynptr_data &&
-		    dynptr_type == BPF_DYNPTR_TYPE_SKB)
-			regs[BPF_REG_0].type |= DYNPTR_TYPE_SKB;
+		if (func_id == BPF_FUNC_dynptr_data) {
+			if (dynptr_type == BPF_DYNPTR_TYPE_SKB)
+				regs[BPF_REG_0].type |= DYNPTR_TYPE_SKB;
+			else if (dynptr_type == BPF_DYNPTR_TYPE_XDP)
+				regs[BPF_REG_0].type |= DYNPTR_TYPE_XDP;
+		}
 		break;
 	case RET_PTR_TO_MEM_OR_BTF_ID:
 	{
diff --git a/net/core/filter.c b/net/core/filter.c
index 0e2238516d8b..cca8c7ab2829 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3844,7 +3844,29 @@  static const struct bpf_func_proto sk_skb_change_head_proto = {
 	.arg3_type	= ARG_ANYTHING,
 };
 
-BPF_CALL_1(bpf_xdp_get_buff_len, struct  xdp_buff*, xdp)
+BPF_CALL_3(bpf_dynptr_from_xdp, struct xdp_buff*, xdp, u64, flags,
+	   struct bpf_dynptr_kern *, ptr)
+{
+	if (flags) {
+		bpf_dynptr_set_null(ptr);
+		return -EINVAL;
+	}
+
+	bpf_dynptr_init(ptr, xdp, BPF_DYNPTR_TYPE_XDP, 0, xdp_get_buff_len(xdp));
+
+	return 0;
+}
+
+static const struct bpf_func_proto bpf_dynptr_from_xdp_proto = {
+	.func		= bpf_dynptr_from_xdp,
+	.gpl_only	= false,
+	.ret_type	= RET_INTEGER,
+	.arg1_type	= ARG_PTR_TO_CTX,
+	.arg2_type	= ARG_ANYTHING,
+	.arg3_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_XDP | MEM_UNINIT,
+};
+
+BPF_CALL_1(bpf_xdp_get_buff_len, struct xdp_buff*, xdp)
 {
 	return xdp_get_buff_len(xdp);
 }
@@ -3946,7 +3968,7 @@  static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
 	}
 }
 
-static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
+void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
 {
 	struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
 	u32 size = xdp->data_end - xdp->data;
@@ -3977,8 +3999,7 @@  static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
 	return offset + len <= size ? addr + offset : NULL;
 }
 
-BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset,
-	   void *, buf, u32, len)
+int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
 {
 	void *ptr;
 
@@ -3994,6 +4015,12 @@  BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset,
 	return 0;
 }
 
+BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset,
+	   void *, buf, u32, len)
+{
+	return __bpf_xdp_load_bytes(xdp, offset, buf, len);
+}
+
 static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
 	.func		= bpf_xdp_load_bytes,
 	.gpl_only	= false,
@@ -4004,8 +4031,7 @@  static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
 	.arg4_type	= ARG_CONST_SIZE,
 };
 
-BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset,
-	   void *, buf, u32, len)
+int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len)
 {
 	void *ptr;
 
@@ -4021,6 +4047,12 @@  BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset,
 	return 0;
 }
 
+BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset,
+	   void *, buf, u32, len)
+{
+	return __bpf_xdp_store_bytes(xdp, offset, buf, len);
+}
+
 static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
 	.func		= bpf_xdp_store_bytes,
 	.gpl_only	= false,
@@ -8010,6 +8042,8 @@  xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return &bpf_tcp_raw_check_syncookie_ipv6_proto;
 #endif
 #endif
+	case BPF_FUNC_dynptr_from_xdp:
+		return &bpf_dynptr_from_xdp_proto;
 	default:
 		return bpf_sk_base_func_proto(func_id);
 	}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 52e891b00a35..b22ffbb6f382 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5315,13 +5315,18 @@  union bpf_attr {
  *		      and try again.
  *
  *		    * The data slice is automatically invalidated anytime
- *		      **bpf_dynptr_write**\ () or a helper call that changes
- *		      the underlying packet buffer (eg **bpf_skb_pull_data**\ ())
+ *		      **bpf_dynptr_write**\ () is called.
+ *
+ *		For skb-type and xdp-type dynptrs:
+ *		    * The data slice is automatically invalidated anytime a
+ *		      helper call that changes the underlying packet buffer
+ *		      (eg **bpf_skb_pull_data**\ (), **bpf_xdp_adjust_head**\ ())
  *		      is called.
  *	Return
  *		Pointer to the underlying dynptr data, NULL if the dynptr is
  *		read-only, if the dynptr is invalid, or if the offset and length
- *		is out of bounds or in a paged buffer for skb-type dynptrs.
+ *		is out of bounds or in a paged buffer for skb-type dynptrs or
+ *		across fragments for xdp-type dynptrs.
  *
  * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
  *	Description
@@ -5420,6 +5425,19 @@  union bpf_attr {
  *		*flags* is currently unused, it must be 0 for now.
  *	Return
  *		0 on success or -EINVAL if flags is not 0.
+ *
+ * long bpf_dynptr_from_xdp(struct xdp_buff *xdp_md, u64 flags, struct bpf_dynptr *ptr)
+ *	Description
+ *		Get a dynptr to the data in *xdp_md*. *xdp_md* must be the BPF program
+ *		context.
+ *
+ *		Calls that change the *xdp_md*'s underlying packet buffer
+ *		(eg **bpf_xdp_adjust_head**\ ()) do not invalidate the dynptr, but
+ *		they do invalidate any data slices associated with the dynptr.
+ *
+ *		*flags* is currently unused, it must be 0 for now.
+ *	Return
+ *		0 on success, -EINVAL if flags is not 0.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5632,6 +5650,7 @@  union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6),	\
 	FN(ktime_get_tai_ns),		\
 	FN(dynptr_from_skb),		\
+	FN(dynptr_from_xdp),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper