Message ID | 20210616224712.3243-3-zeffron@riotgames.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | BPF |
Headers | show |
Series | bpf: support input xdp_md context in BPF_PROG_TEST_RUN | expand |
Context | Check | Description |
---|---|---|
netdev/cover_letter | success | Link |
netdev/fixes_present | success | Link |
netdev/patch_count | success | Link |
netdev/tree_selection | success | Clearly marked for bpf-next |
netdev/subject_prefix | success | Link |
netdev/cc_maintainers | warning | 6 maintainers not CCed: netdev@vger.kernel.org kpsingh@kernel.org andrii@kernel.org john.fastabend@gmail.com songliubraving@fb.com kuba@kernel.org |
netdev/source_inline | success | Was 0 now: 0 |
netdev/verify_signedoff | success | Link |
netdev/module_param | success | Was 0 now: 0 |
netdev/build_32bit | success | Errors and warnings before: 10043 this patch: 10043 |
netdev/kdoc | success | Errors and warnings before: 0 this patch: 0 |
netdev/verify_fixes | success | Link |
netdev/checkpatch | success | total: 0 errors, 0 warnings, 0 checks, 118 lines checked |
netdev/build_allmodconfig_warn | success | Errors and warnings before: 10457 this patch: 10457 |
netdev/header_inline | success | Link |
On 6/16/21 3:47 PM, Zvi Effron wrote: > Support passing a xdp_md via ctx_in/ctx_out in bpf_attr for > BPF_PROG_TEST_RUN. > > The intended use case is to pass some XDP meta data to the test runs of > XDP programs that are used as tail calls. > > For programs that use bpf_prog_test_run_xdp, support xdp_md input and > output. Unlike with an actual xdp_md during a non-test run, data_meta must > be 0 because it must point to the start of the provided user data. From > the initial xdp_md, use data and data_end to adjust the pointers in the > generated xdp_buff. All other non-zero fields are prohibited (with > EINVAL). If the user has set ctx_out/ctx_size_out, copy the (potentially > different) xdp_md back to the userspace. > > We require all fields of input xdp_md except the ones we explicitly > support to be set to zero. The expectation is that in the future we might > add support for more fields and we want to fail explicitly if the user > runs the program on the kernel where we don't yet support them. > > Co-developed-by: Cody Haas <chaas@riotgames.com> > Signed-off-by: Cody Haas <chaas@riotgames.com> > Co-developed-by: Lisa Watanabe <lwatanabe@riotgames.com> > Signed-off-by: Lisa Watanabe <lwatanabe@riotgames.com> > Signed-off-by: Zvi Effron <zeffron@riotgames.com> > --- > include/uapi/linux/bpf.h | 3 -- > net/bpf/test_run.c | 68 ++++++++++++++++++++++++++++++++++++---- > 2 files changed, 62 insertions(+), 9 deletions(-) > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h > index bf9252c7381e..b46a383e8db7 100644 > --- a/include/uapi/linux/bpf.h > +++ b/include/uapi/linux/bpf.h > @@ -324,9 +324,6 @@ union bpf_iter_link_info { > * **BPF_PROG_TYPE_SK_LOOKUP** > * *data_in* and *data_out* must be NULL. > * > - * **BPF_PROG_TYPE_XDP** > - * *ctx_in* and *ctx_out* must be NULL. > - * > * **BPF_PROG_TYPE_RAW_TRACEPOINT**, > * **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE** > * > diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c > index aa47af349ba8..f3054f25409c 100644 > --- a/net/bpf/test_run.c > +++ b/net/bpf/test_run.c > @@ -15,6 +15,7 @@ > #include <linux/error-injection.h> > #include <linux/smp.h> > #include <linux/sock_diag.h> > +#include <net/xdp.h> > > #define CREATE_TRACE_POINTS > #include <trace/events/bpf_test_run.h> > @@ -687,6 +688,22 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, > return ret; > } > > +static int xdp_convert_md_to_buff(struct xdp_md *xdp_md, struct xdp_buff *xdp) > +{ > + if (!xdp_md) > + return 0; > + > + if (xdp_md->egress_ifindex != 0) > + return -EINVAL; > + > + if (xdp_md->ingress_ifindex != 0 || xdp_md->rx_queue_index != 0) > + return -EINVAL; > + > + xdp->data = xdp->data_meta + xdp_md->data; > + > + return 0; > +} > + > int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, > union bpf_attr __user *uattr) > { > @@ -697,35 +714,74 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, > struct netdev_rx_queue *rxqueue; > struct xdp_buff xdp = {}; > u32 retval, duration; > + struct xdp_md *ctx; > u32 max_data_sz; > void *data; > int ret; Let us initialize ret = -EINVAL; > > - if (kattr->test.ctx_in || kattr->test.ctx_out) > - return -EINVAL; > + ctx = bpf_ctx_init(kattr, sizeof(struct xdp_md)); > + if (IS_ERR(ctx)) > + return PTR_ERR(ctx); > + > + if (ctx) { > + /* There can't be user provided data before the meta data */ > + if (ctx->data_meta) > + return -EINVAL; > + if (ctx->data_end != size) > + return -EINVAL; > + if (ctx->data > ctx->data_end) > + return -EINVAL; > + if (unlikely(xdp_metalen_valid(ctx->data))) > + return -EINVAL; For all above four failures, "kfree(ctx)" is missed, I suggest to add a label "free_ctx" in later code and jump there. if (ctx->data_meta || ctx->data_end != size || ctx->data > ctx->data_end || unlikely(xdp_metalen_invalid(ctx->data))) goto free_ctx; > + /* Meta data is allocated from the headroom */ > + headroom -= ctx->data; > + } > > /* XDP have extra tailroom as (most) drivers use full page */ > max_data_sz = 4096 - headroom - tailroom; > > data = bpf_test_init(kattr, max_data_sz, headroom, tailroom); > - if (IS_ERR(data)) > + if (IS_ERR(data)) { > + kfree(ctx); > return PTR_ERR(data); err = PTR_ERR(data); goto free_ctx; > + } > > rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0); > xdp_init_buff(&xdp, headroom + max_data_sz + tailroom, > &rxqueue->xdp_rxq); > xdp_prepare_buff(&xdp, data, headroom, size, true); > > + ret = xdp_convert_md_to_buff(ctx, &xdp); > + if (ret) { > + kfree(data); > + kfree(ctx); > + return ret; goto free_data; > + } > + > bpf_prog_change_xdp(NULL, prog); > ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true); > if (ret) > goto out; > - if (xdp.data != data + headroom || xdp.data_end != xdp.data + size) > - size = xdp.data_end - xdp.data; > - ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration); > + > + if (xdp.data_meta != data + headroom || > + xdp.data_end != xdp.data_meta + size) > + size = xdp.data_end - xdp.data_meta; > + > + if (ctx) { > + ctx->data = xdp.data - xdp.data_meta; > + ctx->data_end = xdp.data_end - xdp.data_meta; > + } > + > + ret = bpf_test_finish(kattr, uattr, xdp.data_meta, size, retval, > + duration); > + if (!ret) > + ret = bpf_ctx_finish(kattr, uattr, ctx, > + sizeof(struct xdp_md)); > + > out: > bpf_prog_change_xdp(prog, NULL); free_data: > kfree(data); free_ctx: > + kfree(ctx); > return ret; > } > >
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index bf9252c7381e..b46a383e8db7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -324,9 +324,6 @@ union bpf_iter_link_info { * **BPF_PROG_TYPE_SK_LOOKUP** * *data_in* and *data_out* must be NULL. * - * **BPF_PROG_TYPE_XDP** - * *ctx_in* and *ctx_out* must be NULL. - * * **BPF_PROG_TYPE_RAW_TRACEPOINT**, * **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE** * diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index aa47af349ba8..f3054f25409c 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -15,6 +15,7 @@ #include <linux/error-injection.h> #include <linux/smp.h> #include <linux/sock_diag.h> +#include <net/xdp.h> #define CREATE_TRACE_POINTS #include <trace/events/bpf_test_run.h> @@ -687,6 +688,22 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, return ret; } +static int xdp_convert_md_to_buff(struct xdp_md *xdp_md, struct xdp_buff *xdp) +{ + if (!xdp_md) + return 0; + + if (xdp_md->egress_ifindex != 0) + return -EINVAL; + + if (xdp_md->ingress_ifindex != 0 || xdp_md->rx_queue_index != 0) + return -EINVAL; + + xdp->data = xdp->data_meta + xdp_md->data; + + return 0; +} + int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr) { @@ -697,35 +714,74 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, struct netdev_rx_queue *rxqueue; struct xdp_buff xdp = {}; u32 retval, duration; + struct xdp_md *ctx; u32 max_data_sz; void *data; int ret; - if (kattr->test.ctx_in || kattr->test.ctx_out) - return -EINVAL; + ctx = bpf_ctx_init(kattr, sizeof(struct xdp_md)); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + if (ctx) { + /* There can't be user provided data before the meta data */ + if (ctx->data_meta) + return -EINVAL; + if (ctx->data_end != size) + return -EINVAL; + if (ctx->data > ctx->data_end) + return -EINVAL; + if (unlikely(xdp_metalen_valid(ctx->data))) + return -EINVAL; + /* Meta data is allocated from the headroom */ + headroom -= ctx->data; + } /* XDP have extra tailroom as (most) drivers use full page */ max_data_sz = 4096 - headroom - tailroom; data = bpf_test_init(kattr, max_data_sz, headroom, tailroom); - if (IS_ERR(data)) + if (IS_ERR(data)) { + kfree(ctx); return PTR_ERR(data); + } rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0); xdp_init_buff(&xdp, headroom + max_data_sz + tailroom, &rxqueue->xdp_rxq); xdp_prepare_buff(&xdp, data, headroom, size, true); + ret = xdp_convert_md_to_buff(ctx, &xdp); + if (ret) { + kfree(data); + kfree(ctx); + return ret; + } + bpf_prog_change_xdp(NULL, prog); ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true); if (ret) goto out; - if (xdp.data != data + headroom || xdp.data_end != xdp.data + size) - size = xdp.data_end - xdp.data; - ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration); + + if (xdp.data_meta != data + headroom || + xdp.data_end != xdp.data_meta + size) + size = xdp.data_end - xdp.data_meta; + + if (ctx) { + ctx->data = xdp.data - xdp.data_meta; + ctx->data_end = xdp.data_end - xdp.data_meta; + } + + ret = bpf_test_finish(kattr, uattr, xdp.data_meta, size, retval, + duration); + if (!ret) + ret = bpf_ctx_finish(kattr, uattr, ctx, + sizeof(struct xdp_md)); + out: bpf_prog_change_xdp(prog, NULL); kfree(data); + kfree(ctx); return ret; }