Re: [PATCH bpf-next v7 5/8] bpf: Update the struct_ops of a bpf_link.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Mar 15, 2023 at 7:37 PM Kui-Feng Lee <kuifeng@xxxxxxxx> wrote:
>
> By improving the BPF_LINK_UPDATE command of bpf(), it should allow you
> to conveniently switch between different struct_ops on a single
> bpf_link. This would enable smoother transitions from one struct_ops
> to another.
>
> The struct_ops maps passing along with BPF_LINK_UPDATE should have the
> BPF_F_LINK flag.
>
> Signed-off-by: Kui-Feng Lee <kuifeng@xxxxxxxx>
> ---
>  include/linux/bpf.h            |  2 ++
>  include/uapi/linux/bpf.h       |  8 +++++--
>  kernel/bpf/bpf_struct_ops.c    | 38 ++++++++++++++++++++++++++++++++++
>  kernel/bpf/syscall.c           | 20 ++++++++++++++++++
>  net/bpf/bpf_dummy_struct_ops.c |  6 ++++++
>  net/ipv4/bpf_tcp_ca.c          |  6 ++++++
>  tools/include/uapi/linux/bpf.h |  8 +++++--
>  7 files changed, 84 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 455b14bf8f28..56e6ab7559ef 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -1474,6 +1474,7 @@ struct bpf_link_ops {
>         void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
>         int (*fill_link_info)(const struct bpf_link *link,
>                               struct bpf_link_info *info);
> +       int (*update_map)(struct bpf_link *link, struct bpf_map *new_map);
>  };
>
>  struct bpf_tramp_link {
> @@ -1516,6 +1517,7 @@ struct bpf_struct_ops {
>                            void *kdata, const void *udata);
>         int (*reg)(void *kdata);
>         void (*unreg)(void *kdata);
> +       int (*update)(void *kdata, void *old_kdata);
>         int (*validate)(void *kdata);
>         const struct btf_type *type;
>         const struct btf_type *value_type;
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 42f40ee083bf..24e1dec4ad97 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -1555,8 +1555,12 @@ union bpf_attr {
>
>         struct { /* struct used by BPF_LINK_UPDATE command */
>                 __u32           link_fd;        /* link fd */
> -               /* new program fd to update link with */
> -               __u32           new_prog_fd;
> +               union {
> +                       /* new program fd to update link with */
> +                       __u32           new_prog_fd;
> +                       /* new struct_ops map fd to update link with */
> +                       __u32           new_map_fd;
> +               };
>                 __u32           flags;          /* extra flags */
>                 /* expected link's program fd; is specified only if
>                  * BPF_F_REPLACE flag is set in flags */
> diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> index 8ce6c7581ca3..5a9e10b92423 100644
> --- a/kernel/bpf/bpf_struct_ops.c
> +++ b/kernel/bpf/bpf_struct_ops.c
> @@ -807,10 +807,48 @@ static int bpf_struct_ops_map_link_fill_link_info(const struct bpf_link *link,
>         return 0;
>  }
>
> +static int bpf_struct_ops_map_link_update(struct bpf_link *link, struct bpf_map *new_map)
> +{
> +       struct bpf_struct_ops_map *st_map, *old_st_map;
> +       struct bpf_struct_ops_link *st_link;
> +       struct bpf_map *old_map;
> +       int err = 0;
> +
> +       st_link = container_of(link, struct bpf_struct_ops_link, link);
> +       st_map = container_of(new_map, struct bpf_struct_ops_map, map);
> +
> +       if (!bpf_struct_ops_valid_to_reg(new_map))
> +               return -EINVAL;
> +
> +       mutex_lock(&update_mutex);
> +
> +       old_map = rcu_dereference_protected(st_link->map, lockdep_is_held(&update_mutex));
> +       old_st_map = container_of(old_map, struct bpf_struct_ops_map, map);
> +       /* The new and old struct_ops must be the same type. */
> +       if (st_map->st_ops != old_st_map->st_ops) {
> +               err = -EINVAL;
> +               goto err_out;
> +       }
> +
> +       err = st_map->st_ops->update(st_map->kvalue.data, old_st_map->kvalue.data);
> +       if (err)
> +               goto err_out;
> +
> +       bpf_map_inc(new_map);
> +       rcu_assign_pointer(st_link->map, new_map);
> +       bpf_map_put(old_map);
> +
> +err_out:
> +       mutex_unlock(&update_mutex);
> +
> +       return err;
> +}
> +
>  static const struct bpf_link_ops bpf_struct_ops_map_lops = {
>         .dealloc = bpf_struct_ops_map_link_dealloc,
>         .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,
>  };
>
>  int bpf_struct_ops_link_create(union bpf_attr *attr)
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 5a45e3bf34e2..6fa10d108278 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -4676,6 +4676,21 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
>         return ret;
>  }
>
> +static int link_update_map(struct bpf_link *link, union bpf_attr *attr)
> +{
> +       struct bpf_map *new_map;
> +       int ret = 0;
> +
> +       new_map = bpf_map_get(attr->link_update.new_map_fd);
> +       if (IS_ERR(new_map))
> +               return -EINVAL;

I was expecting a check for the BPF_F_LINK flag here. Isn't it
necessary to verify that here?



> +
> +       ret = link->ops->update_map(link, new_map);
> +
> +       bpf_map_put(new_map);
> +       return ret;
> +}
> +
>  #define BPF_LINK_UPDATE_LAST_FIELD link_update.old_prog_fd
>

[...]




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux