]> www.infradead.org Git - nvme.git/commitdiff
bpf: enable detaching links of struct_ops objects.
authorKui-Feng Lee <thinker.li@gmail.com>
Thu, 30 May 2024 06:59:40 +0000 (23:59 -0700)
committerMartin KaFai Lau <martin.lau@kernel.org>
Thu, 30 May 2024 22:34:13 +0000 (15:34 -0700)
Implement the detach callback in bpf_link_ops for struct_ops so that user
programs can detach a struct_ops link. The subsystems that struct_ops
objects are registered to can also use this callback to detach the links
being passed to them.

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
Link: https://lore.kernel.org/r/20240530065946.979330-3-thinker.li@gmail.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
kernel/bpf/bpf_struct_ops.c

index 1542dded7489b4c9aee6a5a3d7a4cb18cb51359d..58314b1fc39c708e5ba40596f4f480f279a35906 100644 (file)
@@ -1057,9 +1057,6 @@ static void bpf_struct_ops_map_link_dealloc(struct bpf_link *link)
        st_map = (struct bpf_struct_ops_map *)
                rcu_dereference_protected(st_link->map, true);
        if (st_map) {
-               /* st_link->map can be NULL if
-                * bpf_struct_ops_link_create() fails to register.
-                */
                st_map->st_ops_desc->st_ops->unreg(&st_map->kvalue.data, link);
                bpf_map_put(&st_map->map);
        }
@@ -1075,7 +1072,8 @@ static void bpf_struct_ops_map_link_show_fdinfo(const struct bpf_link *link,
        st_link = container_of(link, struct bpf_struct_ops_link, link);
        rcu_read_lock();
        map = rcu_dereference(st_link->map);
-       seq_printf(seq, "map_id:\t%d\n", map->id);
+       if (map)
+               seq_printf(seq, "map_id:\t%d\n", map->id);
        rcu_read_unlock();
 }
 
@@ -1088,7 +1086,8 @@ static int bpf_struct_ops_map_link_fill_link_info(const struct bpf_link *link,
        st_link = container_of(link, struct bpf_struct_ops_link, link);
        rcu_read_lock();
        map = rcu_dereference(st_link->map);
-       info->struct_ops.map_id = map->id;
+       if (map)
+               info->struct_ops.map_id = map->id;
        rcu_read_unlock();
        return 0;
 }
@@ -1113,6 +1112,10 @@ static int bpf_struct_ops_map_link_update(struct bpf_link *link, struct bpf_map
        mutex_lock(&update_mutex);
 
        old_map = rcu_dereference_protected(st_link->map, lockdep_is_held(&update_mutex));
+       if (!old_map) {
+               err = -ENOLINK;
+               goto err_out;
+       }
        if (expected_old_map && old_map != expected_old_map) {
                err = -EPERM;
                goto err_out;
@@ -1139,8 +1142,37 @@ err_out:
        return err;
 }
 
+static int bpf_struct_ops_map_link_detach(struct bpf_link *link)
+{
+       struct bpf_struct_ops_link *st_link = container_of(link, struct bpf_struct_ops_link, link);
+       struct bpf_struct_ops_map *st_map;
+       struct bpf_map *map;
+
+       mutex_lock(&update_mutex);
+
+       map = rcu_dereference_protected(st_link->map, lockdep_is_held(&update_mutex));
+       if (!map) {
+               mutex_unlock(&update_mutex);
+               return 0;
+       }
+       st_map = container_of(map, struct bpf_struct_ops_map, map);
+
+       st_map->st_ops_desc->st_ops->unreg(&st_map->kvalue.data, link);
+
+       RCU_INIT_POINTER(st_link->map, NULL);
+       /* Pair with bpf_map_get() in bpf_struct_ops_link_create() or
+        * bpf_map_inc() in bpf_struct_ops_map_link_update().
+        */
+       bpf_map_put(&st_map->map);
+
+       mutex_unlock(&update_mutex);
+
+       return 0;
+}
+
 static const struct bpf_link_ops bpf_struct_ops_map_lops = {
        .dealloc = bpf_struct_ops_map_link_dealloc,
+       .detach = bpf_struct_ops_map_link_detach,
        .show_fdinfo = bpf_struct_ops_map_link_show_fdinfo,
        .fill_link_info = bpf_struct_ops_map_link_fill_link_info,
        .update_map = bpf_struct_ops_map_link_update,
@@ -1176,13 +1208,19 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
        if (err)
                goto err_out;
 
+       /* Hold the update_mutex such that the subsystem cannot
+        * do link->ops->detach() before the link is fully initialized.
+        */
+       mutex_lock(&update_mutex);
        err = st_map->st_ops_desc->st_ops->reg(st_map->kvalue.data, &link->link);
        if (err) {
+               mutex_unlock(&update_mutex);
                bpf_link_cleanup(&link_primer);
                link = NULL;
                goto err_out;
        }
        RCU_INIT_POINTER(link->map, map);
+       mutex_unlock(&update_mutex);
 
        return bpf_link_settle(&link_primer);