net: sched: use reference counting action init
Change action API to assume that action init function always takes reference to action, even when overwriting existing action. This is necessary because action API continues to use action pointer after init function is done. At this point action becomes accessible for concurrent modifications, so user must always hold reference to it. Implement helper put list function to atomically release list of actions after action API init code is done using them. Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
4e8ddd7f17
commit
cae422f379
|
@ -627,6 +627,18 @@ static int tcf_action_put(struct tc_action *p)
|
||||||
return __tcf_action_put(p, false);
|
return __tcf_action_put(p, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tcf_action_put_lst(struct list_head *actions)
|
||||||
|
{
|
||||||
|
struct tc_action *a, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(a, tmp, actions, list) {
|
||||||
|
const struct tc_action_ops *ops = a->ops;
|
||||||
|
|
||||||
|
if (tcf_action_put(a))
|
||||||
|
module_put(ops->owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
|
tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
|
||||||
{
|
{
|
||||||
|
@ -835,17 +847,6 @@ err_out:
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_a(struct list_head *actions, int ovr)
|
|
||||||
{
|
|
||||||
struct tc_action *a;
|
|
||||||
|
|
||||||
if (!ovr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list_for_each_entry(a, actions, list)
|
|
||||||
refcount_dec(&a->tcfa_refcnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
|
int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
|
||||||
struct nlattr *est, char *name, int ovr, int bind,
|
struct nlattr *est, char *name, int ovr, int bind,
|
||||||
struct list_head *actions, size_t *attr_size,
|
struct list_head *actions, size_t *attr_size,
|
||||||
|
@ -874,11 +875,6 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
|
||||||
}
|
}
|
||||||
|
|
||||||
*attr_size = tcf_action_full_attrs_size(sz);
|
*attr_size = tcf_action_full_attrs_size(sz);
|
||||||
|
|
||||||
/* Remove the temp refcnt which was necessary to protect against
|
|
||||||
* destroying an existing action which was being replaced
|
|
||||||
*/
|
|
||||||
cleanup_a(actions, ovr);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -1209,7 +1205,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
err:
|
err:
|
||||||
tcf_action_destroy(&actions, 0);
|
tcf_action_put_lst(&actions);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,8 +1247,11 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
|
||||||
&attr_size, true, extack);
|
&attr_size, true, extack);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
ret = tcf_add_notify(net, n, &actions, portid, attr_size, extack);
|
||||||
|
if (ovr)
|
||||||
|
tcf_action_put_lst(&actions);
|
||||||
|
|
||||||
return tcf_add_notify(net, n, &actions, portid, attr_size, extack);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
|
static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
|
||||||
|
|
Loading…
Reference in New Issue