On Wed, Dec 20, 2023 at 6:09 AM D. Wythe <alibuda@xxxxxxxxxxxxxxxxx> wrote: > > From: "D. Wythe" <alibuda@xxxxxxxxxxxxxxxxx> > > To support the prog update, we need to ensure that the prog seen > within the hook is always valid. Considering that hooks are always > protected by rcu_read_lock(), which provide us the ability to > access the prog under rcu. > > Signed-off-by: D. Wythe <alibuda@xxxxxxxxxxxxxxxxx> > --- > net/netfilter/nf_bpf_link.c | 63 ++++++++++++++++++++++++++++++++++----------- > 1 file changed, 48 insertions(+), 15 deletions(-) > > diff --git a/net/netfilter/nf_bpf_link.c b/net/netfilter/nf_bpf_link.c > index e502ec0..9bc91d1 100644 > --- a/net/netfilter/nf_bpf_link.c > +++ b/net/netfilter/nf_bpf_link.c > @@ -8,17 +8,8 @@ > #include <net/netfilter/nf_bpf_link.h> > #include <uapi/linux/netfilter_ipv4.h> > > -static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb, > - const struct nf_hook_state *s) > -{ > - const struct bpf_prog *prog = bpf_prog; > - struct bpf_nf_ctx ctx = { > - .state = s, > - .skb = skb, > - }; > - > - return bpf_prog_run(prog, &ctx); > -} > +/* protect link update in parallel */ > +static DEFINE_MUTEX(bpf_nf_mutex); > > struct bpf_nf_link { > struct bpf_link link; > @@ -26,8 +17,20 @@ struct bpf_nf_link { > struct net *net; > u32 dead; > const struct nf_defrag_hook *defrag_hook; > + struct rcu_head head; I have to point out the same issues as before, but will ask them differently... Why do you think above rcu_head is necessary? > }; > > +static unsigned int nf_hook_run_bpf(void *bpf_link, struct sk_buff *skb, > + const struct nf_hook_state *s) > +{ > + const struct bpf_nf_link *nf_link = bpf_link; > + struct bpf_nf_ctx ctx = { > + .state = s, > + .skb = skb, > + }; > + return bpf_prog_run(rcu_dereference_raw(nf_link->link.prog), &ctx); > +} > + > #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) > static const struct nf_defrag_hook * > get_proto_defrag_hook(struct bpf_nf_link *link, > @@ -126,8 +129,7 @@ static void bpf_nf_link_release(struct bpf_link *link) > static void bpf_nf_link_dealloc(struct bpf_link *link) > { > struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); > - > - kfree(nf_link); > + kfree_rcu(nf_link, head); Why is this needed ? Have you looked at tcx_link_lops ? > } > > static int bpf_nf_link_detach(struct bpf_link *link) > @@ -162,7 +164,34 @@ static int bpf_nf_link_fill_link_info(const struct bpf_link *link, > static int bpf_nf_link_update(struct bpf_link *link, struct bpf_prog *new_prog, > struct bpf_prog *old_prog) > { > - return -EOPNOTSUPP; > + struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); > + int err = 0; > + > + mutex_lock(&bpf_nf_mutex); Why do you need this mutex? What race does it solve? > + > + if (nf_link->dead) { > + err = -EPERM; > + goto out; > + } > + > + /* target old_prog mismatch */ > + if (old_prog && link->prog != old_prog) { > + err = -EPERM; > + goto out; > + } > + > + old_prog = link->prog; > + if (old_prog == new_prog) { > + /* don't need update */ > + bpf_prog_put(new_prog); > + goto out; > + } > + > + old_prog = xchg(&link->prog, new_prog); > + bpf_prog_put(old_prog); > +out: > + mutex_unlock(&bpf_nf_mutex); > + return err; > } > > static const struct bpf_link_ops bpf_nf_link_lops = { > @@ -226,7 +255,11 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) > > link->hook_ops.hook = nf_hook_run_bpf; > link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF; > - link->hook_ops.priv = prog; > + > + /* bpf_nf_link_release & bpf_nf_link_dealloc() can ensures that link remains > + * valid at all times within nf_hook_run_bpf(). > + */ > + link->hook_ops.priv = link; > > link->hook_ops.pf = attr->link_create.netfilter.pf; > link->hook_ops.priority = attr->link_create.netfilter.priority; > -- > 1.8.3.1 >