History

Merge branch 'introduce-bpf_dynptr_copy-kfunc'

Mykyta Yatsenko says: ==================== introduce bpf_dynptr_copy kfunc From: Mykyta Yatsenko <yatsenko@meta.com> Introduce a new kfunc, bpf_dynptr_copy, which enables copying of data from one dynptr to another. This functionality may be useful in scenarios such as capturing XDP data to a ring buffer. The patch set is split into 3 patches: 1. Refactor bpf_dynptr_read and bpf_dynptr_write by extracting code into static functions, that allows calling them with no compiler warnings 2. Introduce bpf_dynptr_copy 3. Add tests for bpf_dynptr_copy v2->v3: * Implemented bpf_memcmp in dynptr_success.c test, as __builtin_memcmp was not inlined on GCC-BPF. ====================

Andrii Nakryiko committed 1 year ago

Commit ee2fdd02 parents b0f2ee6 064e9aa3 files · +211 −9Browse files

Unified · Split

3 files
  1. kernel/bpf/helpers.c +72 −4
  2. tools/testing/selftests/bpf/prog_tests/dynptr.c +21 −0
  3. tools/testing/selftests/bpf/progs/dynptr_success.c +118 −5
kernel/bpf/helpers.c+72−4
@@ -1759,8 +1759,8 @@ static const struct bpf_func_proto bpf_dynptr_from_mem_proto = { · 1758 unmodified lines
17591759 .arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT | MEM_WRITE,
17601760 };
17611761
1762−BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src,
1763− u32, offset, u64, flags)
1762+static int __bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr_kern *src,
1763+ u32 offset, u64 flags)
17641764 {
17651765 enum bpf_dynptr_type type;
17661766 int err;
@@ -1793,6 +1793,12 @@ BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern · 26 unmodified lines
17931793 }
17941794 }
17951795
1796+BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src,
1797+ u32, offset, u64, flags)
1798+{
1799+ return __bpf_dynptr_read(dst, len, src, offset, flags);
1800+}
1801+
17961802 static const struct bpf_func_proto bpf_dynptr_read_proto = {
17971803 .func = bpf_dynptr_read,
17981804 .gpl_only = false,
@@ -1804,8 +1810,8 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = { · 5 unmodified lines
18041810 .arg5_type = ARG_ANYTHING,
18051811 };
18061812
1807−BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, void *, src,
1808− u32, len, u64, flags)
1813+static int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src,
1814+ u32 len, u64 flags)
18091815 {
18101816 enum bpf_dynptr_type type;
18111817 int err;
@@ -1843,6 +1849,12 @@ BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, v · 31 unmodified lines
18431849 }
18441850 }
18451851
1852+BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, void *, src,
1853+ u32, len, u64, flags)
1854+{
1855+ return __bpf_dynptr_write(dst, offset, src, len, flags);
1856+}
1857+
18461858 static const struct bpf_func_proto bpf_dynptr_write_proto = {
18471859 .func = bpf_dynptr_write,
18481860 .gpl_only = false,
@@ -2758,6 +2770,61 @@ __bpf_kfunc int bpf_dynptr_clone(const struct bpf_dynptr *p, · 909 unmodified lines
27582770 return 0;
27592771 }
27602772
2773+/**
2774+ * bpf_dynptr_copy() - Copy data from one dynptr to another.
2775+ * @dst_ptr: Destination dynptr - where data should be copied to
2776+ * @dst_off: Offset into the destination dynptr
2777+ * @src_ptr: Source dynptr - where data should be copied from
2778+ * @src_off: Offset into the source dynptr
2779+ * @size: Length of the data to copy from source to destination
2780+ *
2781+ * Copies data from source dynptr to destination dynptr.
2782+ * Returns 0 on success; negative error, otherwise.
2783+ */
2784+__bpf_kfunc int bpf_dynptr_copy(struct bpf_dynptr *dst_ptr, u32 dst_off,
2785+ struct bpf_dynptr *src_ptr, u32 src_off, u32 size)
2786+{
2787+ struct bpf_dynptr_kern *dst = (struct bpf_dynptr_kern *)dst_ptr;
2788+ struct bpf_dynptr_kern *src = (struct bpf_dynptr_kern *)src_ptr;
2789+ void *src_slice, *dst_slice;
2790+ char buf[256];
2791+ u32 off;
2792+
2793+ src_slice = bpf_dynptr_slice(src_ptr, src_off, NULL, size);
2794+ dst_slice = bpf_dynptr_slice_rdwr(dst_ptr, dst_off, NULL, size);
2795+
2796+ if (src_slice && dst_slice) {
2797+ memmove(dst_slice, src_slice, size);
2798+ return 0;
2799+ }
2800+
2801+ if (src_slice)
2802+ return __bpf_dynptr_write(dst, dst_off, src_slice, size, 0);
2803+
2804+ if (dst_slice)
2805+ return __bpf_dynptr_read(dst_slice, size, src, src_off, 0);
2806+
2807+ if (bpf_dynptr_check_off_len(dst, dst_off, size) ||
2808+ bpf_dynptr_check_off_len(src, src_off, size))
2809+ return -E2BIG;
2810+
2811+ off = 0;
2812+ while (off < size) {
2813+ u32 chunk_sz = min_t(u32, sizeof(buf), size - off);
2814+ int err;
2815+
2816+ err = __bpf_dynptr_read(buf, chunk_sz, src, src_off + off, 0);
2817+ if (err)
2818+ return err;
2819+ err = __bpf_dynptr_write(dst, dst_off + off, buf, chunk_sz, 0);
2820+ if (err)
2821+ return err;
2822+
2823+ off += chunk_sz;
2824+ }
2825+ return 0;
2826+}
2827+
27612828 __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj)
27622829 {
27632830 return obj;
@@ -3206,6 +3273,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_is_null) · 442 unmodified lines
32063273 BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly)
32073274 BTF_ID_FLAGS(func, bpf_dynptr_size)
32083275 BTF_ID_FLAGS(func, bpf_dynptr_clone)
3276+BTF_ID_FLAGS(func, bpf_dynptr_copy)
32093277 #ifdef CONFIG_NET
32103278 BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
32113279 #endif
tools/testing/selftests/bpf/prog_tests/dynptr.c+21−0
@@ -10,6 +10,7 @@ enum test_setup_type { · 9 unmodified lines
1010 SETUP_SYSCALL_SLEEP,
1111 SETUP_SKB_PROG,
1212 SETUP_SKB_PROG_TP,
13+ SETUP_XDP_PROG,
1314 };
1415
1516 static struct {
@@ -18,6 +19,8 @@ static struct { · 2 unmodified lines
1819 } success_tests[] = {
1920 {"test_read_write", SETUP_SYSCALL_SLEEP},
2021 {"test_dynptr_data", SETUP_SYSCALL_SLEEP},
22+ {"test_dynptr_copy", SETUP_SYSCALL_SLEEP},
23+ {"test_dynptr_copy_xdp", SETUP_XDP_PROG},
2124 {"test_ringbuf", SETUP_SYSCALL_SLEEP},
2225 {"test_skb_readonly", SETUP_SKB_PROG},
2326 {"test_dynptr_skb_data", SETUP_SKB_PROG},
@@ -120,6 +123,24 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ · 96 unmodified lines
120123
121124 break;
122125 }
126+ case SETUP_XDP_PROG:
127+ {
128+ char data[5000];
129+ int err, prog_fd;
130+ LIBBPF_OPTS(bpf_test_run_opts, opts,
131+ .data_in = &data,
132+ .data_size_in = sizeof(data),
133+ .repeat = 1,
134+ );
135+
136+ prog_fd = bpf_program__fd(prog);
137+ err = bpf_prog_test_run_opts(prog_fd, &opts);
138+
139+ if (!ASSERT_OK(err, "test_run"))
140+ goto cleanup;
141+
142+ break;
143+ }
123144 }
124145
125146 ASSERT_EQ(skel->bss->err, 0, "err");
tools/testing/selftests/bpf/progs/dynptr_success.c+118−5
@@ -1,20 +1,19 @@
11 // SPDX-License-Identifier: GPL-2.0
22 /* Copyright (c) 2022 Facebook */
33
4+#include <vmlinux.h>
45 #include <string.h>
56 #include <stdbool.h>
6−#include <linux/bpf.h>
77 #include <bpf/bpf_helpers.h>
88 #include <bpf/bpf_tracing.h>
99 #include "bpf_misc.h"
10−#include "bpf_kfuncs.h"
1110 #include "errno.h"
1211
1312 char _license[] SEC("license") = "GPL";
1413
1514 int pid, err, val;
1615
17−struct sample {
16+struct ringbuf_sample {
1817 int pid;
1918 int seq;
2019 long value;
@@ -121,7 +120,7 @@ int test_dynptr_data(void *ctx) · 100 unmodified lines
121120
122121 static int ringbuf_callback(__u32 index, void *data)
123122 {
124− struct sample *sample;
123+ struct ringbuf_sample *sample;
125124
126125 struct bpf_dynptr *ptr = (struct bpf_dynptr *)data;
127126
@@ -138,7 +137,7 @@ SEC("?tp/syscalls/sys_enter_nanosleep") · 10 unmodified lines
138137 int test_ringbuf(void *ctx)
139138 {
140139 struct bpf_dynptr ptr;
141− struct sample *sample;
140+ struct ringbuf_sample *sample;
142141
143142 if (bpf_get_current_pid_tgid() >> 32 != pid)
144143 return 0;
@@ -567,3 +566,117 @@ int BPF_PROG(test_dynptr_skb_tp_btf, void *skb, void *location) · 422 unmodified lines
567566
568567 return 1;
569568 }
569+
570+static inline int bpf_memcmp(const char *a, const char *b, u32 size)
571+{
572+ int i;
573+
574+ bpf_for(i, 0, size) {
575+ if (a[i] != b[i])
576+ return a[i] < b[i] ? -1 : 1;
577+ }
578+ return 0;
579+}
580+
581+SEC("?tp/syscalls/sys_enter_nanosleep")
582+int test_dynptr_copy(void *ctx)
583+{
584+ char data[] = "hello there, world!!";
585+ char buf[32] = {'\0'};
586+ __u32 sz = sizeof(data);
587+ struct bpf_dynptr src, dst;
588+
589+ bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &src);
590+ bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &dst);
591+
592+ /* Test basic case of copying contiguous memory backed dynptrs */
593+ err = bpf_dynptr_write(&src, 0, data, sz, 0);
594+ err = err ?: bpf_dynptr_copy(&dst, 0, &src, 0, sz);
595+ err = err ?: bpf_dynptr_read(buf, sz, &dst, 0, 0);
596+ err = err ?: bpf_memcmp(data, buf, sz);
597+
598+ /* Test that offsets are handled correctly */
599+ err = err ?: bpf_dynptr_copy(&dst, 3, &src, 5, sz - 5);
600+ err = err ?: bpf_dynptr_read(buf, sz - 5, &dst, 3, 0);
601+ err = err ?: bpf_memcmp(data + 5, buf, sz - 5);
602+
603+ bpf_ringbuf_discard_dynptr(&src, 0);
604+ bpf_ringbuf_discard_dynptr(&dst, 0);
605+ return 0;
606+}
607+
608+SEC("xdp")
609+int test_dynptr_copy_xdp(struct xdp_md *xdp)
610+{
611+ struct bpf_dynptr ptr_buf, ptr_xdp;
612+ char data[] = "qwertyuiopasdfghjkl";
613+ char buf[32] = {'\0'};
614+ __u32 len = sizeof(data);
615+ int i, chunks = 200;
616+
617+ /* ptr_xdp is backed by non-contiguous memory */
618+ bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp);
619+ bpf_ringbuf_reserve_dynptr(&ringbuf, len * chunks, 0, &ptr_buf);
620+
621+ /* Destination dynptr is backed by non-contiguous memory */
622+ bpf_for(i, 0, chunks) {
623+ err = bpf_dynptr_write(&ptr_buf, i * len, data, len, 0);
624+ if (err)
625+ goto out;
626+ }
627+
628+ err = bpf_dynptr_copy(&ptr_xdp, 0, &ptr_buf, 0, len * chunks);
629+ if (err)
630+ goto out;
631+
632+ bpf_for(i, 0, chunks) {
633+ __builtin_memset(buf, 0, sizeof(buf));
634+ err = bpf_dynptr_read(&buf, len, &ptr_xdp, i * len, 0);
635+ if (err)
636+ goto out;
637+ if (bpf_memcmp(data, buf, len) != 0)
638+ goto out;
639+ }
640+
641+ /* Source dynptr is backed by non-contiguous memory */
642+ __builtin_memset(buf, 0, sizeof(buf));
643+ bpf_for(i, 0, chunks) {
644+ err = bpf_dynptr_write(&ptr_buf, i * len, buf, len, 0);
645+ if (err)
646+ goto out;
647+ }
648+
649+ err = bpf_dynptr_copy(&ptr_buf, 0, &ptr_xdp, 0, len * chunks);
650+ if (err)
651+ goto out;
652+
653+ bpf_for(i, 0, chunks) {
654+ __builtin_memset(buf, 0, sizeof(buf));
655+ err = bpf_dynptr_read(&buf, len, &ptr_buf, i * len, 0);
656+ if (err)
657+ goto out;
658+ if (bpf_memcmp(data, buf, len) != 0)
659+ goto out;
660+ }
661+
662+ /* Both source and destination dynptrs are backed by non-contiguous memory */
663+ err = bpf_dynptr_copy(&ptr_xdp, 2, &ptr_xdp, len, len * (chunks - 1));
664+ if (err)
665+ goto out;
666+
667+ bpf_for(i, 0, chunks - 1) {
668+ __builtin_memset(buf, 0, sizeof(buf));
669+ err = bpf_dynptr_read(&buf, len, &ptr_xdp, 2 + i * len, 0);
670+ if (err)
671+ goto out;
672+ if (bpf_memcmp(data, buf, len) != 0)
673+ goto out;
674+ }
675+
676+ if (bpf_dynptr_copy(&ptr_xdp, 2000, &ptr_xdp, 0, len * chunks) != -E2BIG)
677+ err = 1;
678+
679+out:
680+ bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
681+ return XDP_DROP;
682+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t