summaryrefslogtreecommitdiff
path: root/kernel/bpf/offload.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/bpf/offload.c')
-rw-r--r--kernel/bpf/offload.c27
1 files changed, 26 insertions, 1 deletions
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c
index 0d6f5569588c..f7bfff1760cc 100644
--- a/kernel/bpf/offload.c
+++ b/kernel/bpf/offload.c
@@ -96,6 +96,22 @@ err_free:
return err;
}
+/*
+ * @prog takes the place of a copy that the caller is about to free. Nothing
+ * keeps the netdev while a program is loaded: once the lock is dropped, no
+ * unregistration is left that has got the old copy from offload->prog.
+ */
+void bpf_prog_dev_bound_moved(struct bpf_prog *prog)
+{
+ if (!bpf_prog_is_dev_bound(prog->aux))
+ return;
+
+ down_read(&bpf_devs_lock);
+ if (prog->aux->offload)
+ prog->aux->offload->prog = prog;
+ up_read(&bpf_devs_lock);
+}
+
static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
{
struct bpf_prog_offload *offload = prog->aux->offload;
@@ -250,7 +266,14 @@ int bpf_prog_dev_bound_init(struct bpf_prog *prog, union bpf_attr *attr)
prog->aux->offload_requested = !(attr->prog_flags & BPF_F_XDP_DEV_BOUND_ONLY);
down_write(&bpf_devs_lock);
- err = __bpf_prog_dev_bound_init(prog, netdev);
+ /*
+ * Behind bpf_dev_bound_netdev_unregister() nobody would take the
+ * program off the device, which goes once the reference is dropped.
+ */
+ if (READ_ONCE(netdev->reg_state) == NETREG_REGISTERED)
+ err = __bpf_prog_dev_bound_init(prog, netdev);
+ else
+ err = -EINVAL;
up_write(&bpf_devs_lock);
out:
@@ -698,6 +721,8 @@ static bool __bpf_offload_dev_match(struct bpf_prog *prog,
return false;
if (offload->netdev == netdev)
return true;
+ if (!bpf_prog_is_offloaded(prog->aux))
+ return false;
ondev1 = bpf_offload_find_netdev(offload->netdev);
ondev2 = bpf_offload_find_netdev(netdev);