Merge tag 'pm-5.15-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-microblaze.git] / net / bpf / test_run.c
index aa47af3..2eb0e55 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/vmalloc.h>
 #include <linux/etherdevice.h>
 #include <linux/filter.h>
+#include <linux/rcupdate_trace.h>
 #include <linux/sched/signal.h>
 #include <net/bpf_sk_storage.h>
 #include <net/sock.h>
@@ -15,6 +16,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>
@@ -87,17 +89,19 @@ reset:
 static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
                        u32 *retval, u32 *time, bool xdp)
 {
-       struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = { NULL };
+       struct bpf_prog_array_item item = {.prog = prog};
+       struct bpf_run_ctx *old_ctx;
+       struct bpf_cg_run_ctx run_ctx;
        struct bpf_test_timer t = { NO_MIGRATE };
        enum bpf_cgroup_storage_type stype;
        int ret;
 
        for_each_cgroup_storage_type(stype) {
-               storage[stype] = bpf_cgroup_storage_alloc(prog, stype);
-               if (IS_ERR(storage[stype])) {
-                       storage[stype] = NULL;
+               item.cgroup_storage[stype] = bpf_cgroup_storage_alloc(prog, stype);
+               if (IS_ERR(item.cgroup_storage[stype])) {
+                       item.cgroup_storage[stype] = NULL;
                        for_each_cgroup_storage_type(stype)
-                               bpf_cgroup_storage_free(storage[stype]);
+                               bpf_cgroup_storage_free(item.cgroup_storage[stype]);
                        return -ENOMEM;
                }
        }
@@ -106,22 +110,19 @@ static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
                repeat = 1;
 
        bpf_test_timer_enter(&t);
+       old_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
        do {
-               ret = bpf_cgroup_storage_set(storage);
-               if (ret)
-                       break;
-
+               run_ctx.prog_item = &item;
                if (xdp)
                        *retval = bpf_prog_run_xdp(prog, ctx);
                else
-                       *retval = BPF_PROG_RUN(prog, ctx);
-
-               bpf_cgroup_storage_unset();
+                       *retval = bpf_prog_run(prog, ctx);
        } while (bpf_test_timer_continue(&t, repeat, &ret, time));
+       bpf_reset_run_ctx(old_ctx);
        bpf_test_timer_leave(&t);
 
        for_each_cgroup_storage_type(stype)
-               bpf_cgroup_storage_free(storage[stype]);
+               bpf_cgroup_storage_free(item.cgroup_storage[stype]);
 
        return ret;
 }
@@ -326,7 +327,7 @@ __bpf_prog_test_run_raw_tp(void *data)
        struct bpf_raw_tp_test_run_info *info = data;
 
        rcu_read_lock();
-       info->retval = BPF_PROG_RUN(info->prog, info->ctx);
+       info->retval = bpf_prog_run(info->prog, info->ctx);
        rcu_read_unlock();
 }
 
@@ -687,6 +688,64 @@ out:
        return ret;
 }
 
+static int xdp_convert_md_to_buff(struct xdp_md *xdp_md, struct xdp_buff *xdp)
+{
+       unsigned int ingress_ifindex, rx_queue_index;
+       struct netdev_rx_queue *rxqueue;
+       struct net_device *device;
+
+       if (!xdp_md)
+               return 0;
+
+       if (xdp_md->egress_ifindex != 0)
+               return -EINVAL;
+
+       ingress_ifindex = xdp_md->ingress_ifindex;
+       rx_queue_index = xdp_md->rx_queue_index;
+
+       if (!ingress_ifindex && rx_queue_index)
+               return -EINVAL;
+
+       if (ingress_ifindex) {
+               device = dev_get_by_index(current->nsproxy->net_ns,
+                                         ingress_ifindex);
+               if (!device)
+                       return -ENODEV;
+
+               if (rx_queue_index >= device->real_num_rx_queues)
+                       goto free_dev;
+
+               rxqueue = __netif_get_rx_queue(device, rx_queue_index);
+
+               if (!xdp_rxq_info_is_reg(&rxqueue->xdp_rxq))
+                       goto free_dev;
+
+               xdp->rxq = &rxqueue->xdp_rxq;
+               /* The device is now tracked in the xdp->rxq for later
+                * dev_put()
+                */
+       }
+
+       xdp->data = xdp->data_meta + xdp_md->data;
+       return 0;
+
+free_dev:
+       dev_put(device);
+       return -EINVAL;
+}
+
+static void xdp_convert_buff_to_md(struct xdp_buff *xdp, struct xdp_md *xdp_md)
+{
+       if (!xdp_md)
+               return;
+
+       xdp_md->data = xdp->data - xdp->data_meta;
+       xdp_md->data_end = xdp->data_end - xdp->data_meta;
+
+       if (xdp_md->ingress_ifindex)
+               dev_put(xdp->rxq->dev);
+}
+
 int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
                          union bpf_attr __user *uattr)
 {
@@ -697,35 +756,73 @@ 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;
+       int ret = -EINVAL;
 
-       if (kattr->test.ctx_in || kattr->test.ctx_out)
+       if (prog->expected_attach_type == BPF_XDP_DEVMAP ||
+           prog->expected_attach_type == BPF_XDP_CPUMAP)
                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 || 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))
-               return PTR_ERR(data);
+       if (IS_ERR(data)) {
+               ret = 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)
+               goto free_data;
+
        bpf_prog_change_xdp(NULL, prog);
        ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true);
+       /* We convert the xdp_buff back to an xdp_md before checking the return
+        * code so the reference count of any held netdevice will be decremented
+        * even if the test run failed.
+        */
+       xdp_convert_buff_to_md(&xdp, ctx);
        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;
+
+       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;
 }
 
@@ -892,7 +989,7 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat
        bpf_test_timer_enter(&t);
        do {
                ctx.selected_sk = NULL;
-               retval = BPF_PROG_SK_LOOKUP_RUN_ARRAY(progs, ctx, BPF_PROG_RUN);
+               retval = BPF_PROG_SK_LOOKUP_RUN_ARRAY(progs, ctx, bpf_prog_run);
        } while (bpf_test_timer_continue(&t, repeat, &ret, &duration));
        bpf_test_timer_leave(&t);
 
@@ -948,7 +1045,10 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog,
                        goto out;
                }
        }
+
+       rcu_read_lock_trace();
        retval = bpf_prog_run_pin_on_cpu(prog, ctx);
+       rcu_read_unlock_trace();
 
        if (copy_to_user(&uattr->test.retval, &retval, sizeof(u32))) {
                err = -EFAULT;