diff mbox series

[RFC,v2,net-next,04/28] net/sched: act_api: add init_ops to struct tc_action_op

Message ID 20230517110232.29349-4-jhs@mojatatu.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series Introducing P4TC | expand

Checks

Context Check Description
netdev/series_format fail Series longer than 15 patches (and no cover letter)
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 70 this patch: 70
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 86 this patch: 86
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 41 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Jamal Hadi Salim May 17, 2023, 11:02 a.m. UTC
The initialisation of P4TC action instances require access to a struct p4tc_act
(which appears in later patches) to help us to retrieve information like the
dynamic action parameters etc. In order to retrieve struct p4tc_act we need the
pipeline name or id and the action name or id. Also recall that P4TC
action IDs are dynamic and  are net namespace specific. The init callback from
tc_action_ops parameters had no way of supplying us that information. To solve
this issue, we decided to create a new tc_action_ops callback (init_ops), that
provides us with the tc_action_ops struct which then provides us with the
pipeline and action name. In addition we add a new refcount to struct
tc_action_ops called dyn_ref, which accounts for how many action instances we
have of a specific dynamic action.

Co-developed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Co-developed-by: Pedro Tammela <pctammela@mojatatu.com>
Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 include/net/act_api.h |  6 ++++++
 net/sched/act_api.c   | 11 ++++++++---
 2 files changed, 14 insertions(+), 3 deletions(-)

Comments

Simon Horman June 5, 2023, 9:51 a.m. UTC | #1
CC: Dan Carpenter

On Wed, May 17, 2023 at 07:02:08AM -0400, Jamal Hadi Salim wrote:
> The initialisation of P4TC action instances require access to a struct p4tc_act
> (which appears in later patches) to help us to retrieve information like the
> dynamic action parameters etc. In order to retrieve struct p4tc_act we need the
> pipeline name or id and the action name or id. Also recall that P4TC
> action IDs are dynamic and  are net namespace specific. The init callback from
> tc_action_ops parameters had no way of supplying us that information. To solve
> this issue, we decided to create a new tc_action_ops callback (init_ops), that
> provides us with the tc_action_ops struct which then provides us with the
> pipeline and action name. In addition we add a new refcount to struct
> tc_action_ops called dyn_ref, which accounts for how many action instances we
> have of a specific dynamic action.
> 
> Co-developed-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Victor Nogueira <victor@mojatatu.com>
> Co-developed-by: Pedro Tammela <pctammela@mojatatu.com>
> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>

Hi Jamal, Victor and Pedro,

> index bc4e178873e4..0ba5a4b5db6f 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -1006,7 +1006,7 @@ int tcf_register_action(struct tc_action_ops *act,
>  	struct tc_action_ops *a;
>  	int ret;
>  
> -	if (!act->act || !act->dump || !act->init)
> +	if (!act->act || !act->dump || (!act->init && !act->init_ops))
>  		return -EINVAL;
>  
>  	/* We have to register pernet ops before making the action ops visible,
> @@ -1494,8 +1494,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  			}
>  		}
>  
> -		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> -				userflags.value | flags, extack);
> +		if (a_o->init)
> +			err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> +					userflags.value | flags, extack);
> +		else if (a_o->init_ops)
> +			err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
> +					    tp, a_o, userflags.value | flags,
> +					    extack);

By my reading the initialisation of a occurs here.
Which is now conditional.

>  	} else {
>  		err = a_o->init(net, nla, est, &a, tp, userflags.value | flags,
>  				extack);

A bit further down, outside of the else clause above, the code looks like
this.

        if (!police && tb[TCA_ACT_COOKIE])
                tcf_set_action_cookie(&a->user_cookie, user_cookie);

        if (!police)
                a->hw_stats = hw_stats;

Which causes Smatch to complain that a may be used uninitialised.
Is this the case?
Dan Carpenter June 5, 2023, 11:09 a.m. UTC | #2
On Mon, Jun 05, 2023 at 11:51:14AM +0200, Simon Horman wrote:
> > @@ -1494,8 +1494,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
> >  			}
> >  		}
> >  
> > -		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > -				userflags.value | flags, extack);
> > +		if (a_o->init)
> > +			err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > +					userflags.value | flags, extack);
> > +		else if (a_o->init_ops)
> > +			err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
> > +					    tp, a_o, userflags.value | flags,
> > +					    extack);
> 
> By my reading the initialisation of a occurs here.
> Which is now conditional.
> 

Right.  Presumably the author knows that one (and only one) of the
->init or ->init_ops pointers is set.  This kind of relationship between
two variables is something that Smatch tries to track inside a function
but outside of functions, like here, then Smatch doesn't track it.
I can't really think of a scalable way to track this.

So there are a couple options:

1) Ignore the warning.
2) Remove the second if.

	if (a_o->init)
		err = a_o->init();
	else
		err = a_o->init_ops();

I kind of like this, because I think it communicates the if ->init()
isn't set then ->init_ops() must be.

3) Add a return.

	if (a_o->init) {
		err = a_o->init();
	} else if (a_o->init_ops) {
		err = a_o->init_ops();
	} else {
		WARN_ON(1);
		return ERR_PTR(-EINVAL);
	}

4) Add an unreachable.  But the last time I suggested this it led to
link errors and I didn't get a chance to investigate so probably don't
do this:

	if (a_o->init) {
		err = a_o->init();
	} else if (a_o->init_ops) {
		err = a_o->init_ops();
	} else {
		unreachable();
	}

regards,
dan carpenter
Jamal Hadi Salim June 5, 2023, 2:01 p.m. UTC | #3
On Mon, Jun 5, 2023 at 7:39 AM Dan Carpenter via p4tc-discussions
<p4tc-discussions@netdevconf.info> wrote:
>
> On Mon, Jun 05, 2023 at 11:51:14AM +0200, Simon Horman wrote:
> > > @@ -1494,8 +1494,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
> > >                     }
> > >             }
> > >
> > > -           err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > -                           userflags.value | flags, extack);
> > > +           if (a_o->init)
> > > +                   err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > +                                   userflags.value | flags, extack);
> > > +           else if (a_o->init_ops)
> > > +                   err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
> > > +                                       tp, a_o, userflags.value | flags,
> > > +                                       extack);
> >
> > By my reading the initialisation of a occurs here.
> > Which is now conditional.
> >
>
> Right.  Presumably the author knows that one (and only one) of the
> ->init or ->init_ops pointers is set.

Yes, this is correct and the code above checks i.e
 -     if (!act->act || !act->dump || !act->init)
 +     if (!act->act || !act->dump || (!act->init && !act->init_ops))
               return -EINVAL;

> This kind of relationship between
> two variables is something that Smatch tries to track inside a function
> but outside of functions, like here, then Smatch doesn't track it.
> I can't really think of a scalable way to track this.

Could you have used the statement i referred to above as part of the state?

> So there are a couple options:
>
> 1) Ignore the warning.
> 2) Remove the second if.
>
>         if (a_o->init)
>                 err = a_o->init();
>         else
>                 err = a_o->init_ops();
>
> I kind of like this, because I think it communicates the if ->init()
> isn't set then ->init_ops() must be.

I like this approach - we'll refactor to remove the !police. (note
police using some old tc versions is still a pariah and has typically
to be checked separately, at some point we should audit the code and
remove any police specific checks).

cheers,
jamal

> 3) Add a return.
>
>         if (a_o->init) {
>                 err = a_o->init();
>         } else if (a_o->init_ops) {
>                 err = a_o->init_ops();
>         } else {
>                 WARN_ON(1);
>                 return ERR_PTR(-EINVAL);
>         }
>
> 4) Add an unreachable.  But the last time I suggested this it led to
> link errors and I didn't get a chance to investigate so probably don't
> do this:
>
>         if (a_o->init) {
>                 err = a_o->init();
>         } else if (a_o->init_ops) {
>                 err = a_o->init_ops();
>         } else {
>                 unreachable();
>         }
>
> regards,
> dan carpenter
>
> _______________________________________________
> p4tc-discussions mailing list -- p4tc-discussions@netdevconf.info
> To unsubscribe send an email to p4tc-discussions-leave@netdevconf.info
Dan Carpenter June 5, 2023, 2:57 p.m. UTC | #4
On Mon, Jun 05, 2023 at 10:01:44AM -0400, Jamal Hadi Salim wrote:
> On Mon, Jun 5, 2023 at 7:39 AM Dan Carpenter via p4tc-discussions
> <p4tc-discussions@netdevconf.info> wrote:
> >
> > On Mon, Jun 05, 2023 at 11:51:14AM +0200, Simon Horman wrote:
> > > > @@ -1494,8 +1494,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
> > > >                     }
> > > >             }
> > > >
> > > > -           err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > > -                           userflags.value | flags, extack);
> > > > +           if (a_o->init)
> > > > +                   err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > > +                                   userflags.value | flags, extack);
> > > > +           else if (a_o->init_ops)
> > > > +                   err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
> > > > +                                       tp, a_o, userflags.value | flags,
> > > > +                                       extack);
> > >
> > > By my reading the initialisation of a occurs here.
> > > Which is now conditional.
> > >
> >
> > Right.  Presumably the author knows that one (and only one) of the
> > ->init or ->init_ops pointers is set.
> 
> Yes, this is correct and the code above checks i.e
>  -     if (!act->act || !act->dump || !act->init)
>  +     if (!act->act || !act->dump || (!act->init && !act->init_ops))
>                return -EINVAL;
> 

Ah.  Right.

> > This kind of relationship between
> > two variables is something that Smatch tries to track inside a function
> > but outside of functions, like here, then Smatch doesn't track it.
> > I can't really think of a scalable way to track this.
> 
> Could you have used the statement i referred to above as part of the state?
> 

If the if statement were in the same function then Smatch would be able
to parse this but that relationship information doesn't carry across the
function boundary.  It's actually quite a bit more complicated than just
the function boundary even...  I don't know if this is even possible but
if it were then it would be like a 5-7 year time frame to make it work...

regards,
dan carpenter
Jamal Hadi Salim June 5, 2023, 3:27 p.m. UTC | #5
On Mon, Jun 5, 2023 at 10:59 AM Dan Carpenter <dan.carpenter@linaro.org> wrote:
>
> On Mon, Jun 05, 2023 at 10:01:44AM -0400, Jamal Hadi Salim wrote:
> > On Mon, Jun 5, 2023 at 7:39 AM Dan Carpenter via p4tc-discussions
> > <p4tc-discussions@netdevconf.info> wrote:
> > >
> > > On Mon, Jun 05, 2023 at 11:51:14AM +0200, Simon Horman wrote:
> > > > > @@ -1494,8 +1494,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
> > > > >                     }
> > > > >             }
> > > > >
> > > > > -           err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > > > -                           userflags.value | flags, extack);
> > > > > +           if (a_o->init)
> > > > > +                   err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
> > > > > +                                   userflags.value | flags, extack);
> > > > > +           else if (a_o->init_ops)
> > > > > +                   err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
> > > > > +                                       tp, a_o, userflags.value | flags,
> > > > > +                                       extack);
> > > >
> > > > By my reading the initialisation of a occurs here.
> > > > Which is now conditional.
> > > >
> > >
> > > Right.  Presumably the author knows that one (and only one) of the
> > > ->init or ->init_ops pointers is set.
> >
> > Yes, this is correct and the code above checks i.e
> >  -     if (!act->act || !act->dump || !act->init)
> >  +     if (!act->act || !act->dump || (!act->init && !act->init_ops))
> >                return -EINVAL;
> >
>
> Ah.  Right.
>
> > > This kind of relationship between
> > > two variables is something that Smatch tries to track inside a function
> > > but outside of functions, like here, then Smatch doesn't track it.
> > > I can't really think of a scalable way to track this.
> >
> > Could you have used the statement i referred to above as part of the state?
> >
>
> If the if statement were in the same function then Smatch would be able
> to parse this but that relationship information doesn't carry across the
> function boundary.  It's actually quite a bit more complicated than just
> the function boundary even...  I don't know if this is even possible but
> if it were then it would be like a 5-7 year time frame to make it work...

Understood. I guess this semantically is at least a layer above, so it
owuld be complex.

cheers,
jamal
> regards,
> dan carpenter
>
>
diff mbox series

Patch

diff --git a/include/net/act_api.h b/include/net/act_api.h
index a414c0f94ff1..363f7f8b5586 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -108,6 +108,7 @@  struct tc_action_ops {
 	char    kind[ACTNAMSIZ];
 	enum tca_id  id; /* identifier should match kind */
 	unsigned int	net_id;
+	refcount_t dyn_ref;
 	size_t	size;
 	struct module		*owner;
 	int     (*act)(struct sk_buff *, const struct tc_action *,
@@ -119,6 +120,11 @@  struct tc_action_ops {
 			struct nlattr *est, struct tc_action **act,
 			struct tcf_proto *tp,
 			u32 flags, struct netlink_ext_ack *extack);
+	/* This should be merged with the original init action */
+	int     (*init_ops)(struct net *net, struct nlattr *nla,
+			    struct nlattr *est, struct tc_action **act,
+			   struct tcf_proto *tp, struct tc_action_ops *ops,
+			   u32 flags, struct netlink_ext_ack *extack);
 	int     (*walk)(struct net *, struct sk_buff *,
 			struct netlink_callback *, int,
 			const struct tc_action_ops *,
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index bc4e178873e4..0ba5a4b5db6f 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -1006,7 +1006,7 @@  int tcf_register_action(struct tc_action_ops *act,
 	struct tc_action_ops *a;
 	int ret;
 
-	if (!act->act || !act->dump || !act->init)
+	if (!act->act || !act->dump || (!act->init && !act->init_ops))
 		return -EINVAL;
 
 	/* We have to register pernet ops before making the action ops visible,
@@ -1494,8 +1494,13 @@  struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 			}
 		}
 
-		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
-				userflags.value | flags, extack);
+		if (a_o->init)
+			err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
+					userflags.value | flags, extack);
+		else if (a_o->init_ops)
+			err = a_o->init_ops(net, tb[TCA_ACT_OPTIONS], est, &a,
+					    tp, a_o, userflags.value | flags,
+					    extack);
 	} else {
 		err = a_o->init(net, nla, est, &a, tp, userflags.value | flags,
 				extack);