net_sched: convert tcf_exts from list to pointer array

As pointed out by Jamal, an action could be shared by
multiple filters, so we can't use list to chain them
any more after we get rid of the original tc_action.
Instead, we could just save pointers to these actions
in tcf_exts, since they are refcount'ed, so convert
the list to an array of pointers.

The "ugly" part is the action API still accepts list
as a parameter, I just introduce a helper function to
convert the array of pointers to a list, instead of
relying on the C99 feature to iterate the array.

Fixes: a85a970af2 ("net_sched: move tc_action into tcf_common")
Reported-by: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
WANG Cong 2016-08-13 22:35:00 -07:00 committed by David S. Miller
parent 2734437ef3
commit 22dc13c837
7 changed files with 85 additions and 41 deletions

View File

@ -8396,12 +8396,14 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter,
struct tcf_exts *exts, u64 *action, u8 *queue) struct tcf_exts *exts, u64 *action, u8 *queue)
{ {
const struct tc_action *a; const struct tc_action *a;
LIST_HEAD(actions);
int err; int err;
if (tc_no_actions(exts)) if (tc_no_actions(exts))
return -EINVAL; return -EINVAL;
tc_for_each_action(a, exts) { tcf_exts_to_list(exts, &actions);
list_for_each_entry(a, &actions, list) {
/* Drop action */ /* Drop action */
if (is_tcf_gact_shot(a)) { if (is_tcf_gact_shot(a)) {

View File

@ -318,6 +318,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
u32 *action, u32 *flow_tag) u32 *action, u32 *flow_tag)
{ {
const struct tc_action *a; const struct tc_action *a;
LIST_HEAD(actions);
if (tc_no_actions(exts)) if (tc_no_actions(exts))
return -EINVAL; return -EINVAL;
@ -325,7 +326,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
*flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; *flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
*action = 0; *action = 0;
tc_for_each_action(a, exts) { tcf_exts_to_list(exts, &actions);
list_for_each_entry(a, &actions, list) {
/* Only support a single action per rule */ /* Only support a single action per rule */
if (*action) if (*action)
return -EINVAL; return -EINVAL;
@ -362,13 +364,15 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
u32 *action, u32 *dest_vport) u32 *action, u32 *dest_vport)
{ {
const struct tc_action *a; const struct tc_action *a;
LIST_HEAD(actions);
if (tc_no_actions(exts)) if (tc_no_actions(exts))
return -EINVAL; return -EINVAL;
*action = 0; *action = 0;
tc_for_each_action(a, exts) { tcf_exts_to_list(exts, &actions);
list_for_each_entry(a, &actions, list) {
/* Only support a single action per rule */ /* Only support a single action per rule */
if (*action) if (*action)
return -EINVAL; return -EINVAL;
@ -503,6 +507,7 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow; struct mlx5e_tc_flow *flow;
struct tc_action *a; struct tc_action *a;
struct mlx5_fc *counter; struct mlx5_fc *counter;
LIST_HEAD(actions);
u64 bytes; u64 bytes;
u64 packets; u64 packets;
u64 lastuse; u64 lastuse;
@ -518,7 +523,8 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse); mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
tc_for_each_action(a, f->exts) tcf_exts_to_list(f->exts, &actions);
list_for_each_entry(a, &actions, list)
tcf_action_stats_update(a, bytes, packets, lastuse); tcf_action_stats_update(a, bytes, packets, lastuse);
return 0; return 0;

View File

@ -1121,6 +1121,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
bool ingress) bool ingress)
{ {
const struct tc_action *a; const struct tc_action *a;
LIST_HEAD(actions);
int err; int err;
if (!tc_single_action(cls->exts)) { if (!tc_single_action(cls->exts)) {
@ -1128,7 +1129,8 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
return -ENOTSUPP; return -ENOTSUPP;
} }
tc_for_each_action(a, cls->exts) { tcf_exts_to_list(cls->exts, &actions);
list_for_each_entry(a, &actions, list) {
if (!is_tcf_mirred_mirror(a) || protocol != htons(ETH_P_ALL)) if (!is_tcf_mirred_mirror(a) || protocol != htons(ETH_P_ALL))
return -ENOTSUPP; return -ENOTSUPP;

View File

@ -176,8 +176,8 @@ int tcf_register_action(struct tc_action_ops *a, struct pernet_operations *ops);
int tcf_unregister_action(struct tc_action_ops *a, int tcf_unregister_action(struct tc_action_ops *a,
struct pernet_operations *ops); struct pernet_operations *ops);
int tcf_action_destroy(struct list_head *actions, int bind); int tcf_action_destroy(struct list_head *actions, int bind);
int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
struct tcf_result *res); int nr_actions, struct tcf_result *res);
int tcf_action_init(struct net *net, struct nlattr *nla, int tcf_action_init(struct net *net, struct nlattr *nla,
struct nlattr *est, char *n, int ovr, struct nlattr *est, char *n, int ovr,
int bind, struct list_head *); int bind, struct list_head *);

View File

@ -59,7 +59,8 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r)
struct tcf_exts { struct tcf_exts {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
__u32 type; /* for backward compat(TCA_OLD_COMPAT) */ __u32 type; /* for backward compat(TCA_OLD_COMPAT) */
struct list_head actions; int nr_actions;
struct tc_action **actions;
#endif #endif
/* Map to export classifier specific extension TLV types to the /* Map to export classifier specific extension TLV types to the
* generic extensions API. Unsupported extensions must be set to 0. * generic extensions API. Unsupported extensions must be set to 0.
@ -72,7 +73,10 @@ static inline void tcf_exts_init(struct tcf_exts *exts, int action, int police)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
exts->type = 0; exts->type = 0;
INIT_LIST_HEAD(&exts->actions); exts->nr_actions = 0;
exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
GFP_KERNEL);
WARN_ON(!exts->actions); /* TODO: propagate the error to callers */
#endif #endif
exts->action = action; exts->action = action;
exts->police = police; exts->police = police;
@ -89,7 +93,7 @@ static inline int
tcf_exts_is_predicative(struct tcf_exts *exts) tcf_exts_is_predicative(struct tcf_exts *exts)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
return !list_empty(&exts->actions); return exts->nr_actions;
#else #else
return 0; return 0;
#endif #endif
@ -108,6 +112,20 @@ tcf_exts_is_available(struct tcf_exts *exts)
return tcf_exts_is_predicative(exts); return tcf_exts_is_predicative(exts);
} }
static inline void tcf_exts_to_list(const struct tcf_exts *exts,
struct list_head *actions)
{
#ifdef CONFIG_NET_CLS_ACT
int i;
for (i = 0; i < exts->nr_actions; i++) {
struct tc_action *a = exts->actions[i];
list_add(&a->list, actions);
}
#endif
}
/** /**
* tcf_exts_exec - execute tc filter extensions * tcf_exts_exec - execute tc filter extensions
* @skb: socket buffer * @skb: socket buffer
@ -124,27 +142,21 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_result *res) struct tcf_result *res)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
if (!list_empty(&exts->actions)) if (exts->nr_actions)
return tcf_action_exec(skb, &exts->actions, res); return tcf_action_exec(skb, exts->actions, exts->nr_actions,
res);
#endif #endif
return 0; return 0;
} }
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
#define tc_no_actions(_exts) \ #define tc_no_actions(_exts) ((_exts)->nr_actions == 0)
(list_empty(&(_exts)->actions)) #define tc_single_action(_exts) ((_exts)->nr_actions == 1)
#define tc_for_each_action(_a, _exts) \
list_for_each_entry(_a, &(_exts)->actions, list)
#define tc_single_action(_exts) \
(list_is_singular(&(_exts)->actions))
#else /* CONFIG_NET_CLS_ACT */ #else /* CONFIG_NET_CLS_ACT */
#define tc_no_actions(_exts) true #define tc_no_actions(_exts) true
#define tc_for_each_action(_a, _exts) while ((void)(_a), 0)
#define tc_single_action(_exts) false #define tc_single_action(_exts) false
#endif /* CONFIG_NET_CLS_ACT */ #endif /* CONFIG_NET_CLS_ACT */

View File

@ -420,18 +420,19 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
return res; return res;
} }
int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
struct tcf_result *res) int nr_actions, struct tcf_result *res)
{ {
const struct tc_action *a; int ret = -1, i;
int ret = -1;
if (skb->tc_verd & TC_NCLS) { if (skb->tc_verd & TC_NCLS) {
skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
ret = TC_ACT_OK; ret = TC_ACT_OK;
goto exec_done; goto exec_done;
} }
list_for_each_entry(a, actions, list) { for (i = 0; i < nr_actions; i++) {
const struct tc_action *a = actions[i];
repeat: repeat:
ret = a->ops->act(skb, a, res); ret = a->ops->act(skb, a, res);
if (ret == TC_ACT_REPEAT) if (ret == TC_ACT_REPEAT)

View File

@ -541,8 +541,12 @@ out:
void tcf_exts_destroy(struct tcf_exts *exts) void tcf_exts_destroy(struct tcf_exts *exts)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND); LIST_HEAD(actions);
INIT_LIST_HEAD(&exts->actions);
tcf_exts_to_list(exts, &actions);
tcf_action_destroy(&actions, TCA_ACT_UNBIND);
kfree(exts->actions);
exts->nr_actions = 0;
#endif #endif
} }
EXPORT_SYMBOL(tcf_exts_destroy); EXPORT_SYMBOL(tcf_exts_destroy);
@ -554,7 +558,6 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
{ {
struct tc_action *act; struct tc_action *act;
INIT_LIST_HEAD(&exts->actions);
if (exts->police && tb[exts->police]) { if (exts->police && tb[exts->police]) {
act = tcf_action_init_1(net, tb[exts->police], rate_tlv, act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
"police", ovr, "police", ovr,
@ -563,14 +566,20 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
return PTR_ERR(act); return PTR_ERR(act);
act->type = exts->type = TCA_OLD_COMPAT; act->type = exts->type = TCA_OLD_COMPAT;
list_add(&act->list, &exts->actions); exts->actions[0] = act;
exts->nr_actions = 1;
} else if (exts->action && tb[exts->action]) { } else if (exts->action && tb[exts->action]) {
int err; LIST_HEAD(actions);
int err, i = 0;
err = tcf_action_init(net, tb[exts->action], rate_tlv, err = tcf_action_init(net, tb[exts->action], rate_tlv,
NULL, ovr, NULL, ovr,
TCA_ACT_BIND, &exts->actions); TCA_ACT_BIND, &actions);
if (err) if (err)
return err; return err;
list_for_each_entry(act, &actions, list)
exts->actions[i++] = act;
exts->nr_actions = i;
} }
} }
#else #else
@ -587,37 +596,49 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
struct tcf_exts *src) struct tcf_exts *src)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
LIST_HEAD(tmp); struct tcf_exts old = *dst;
tcf_tree_lock(tp); tcf_tree_lock(tp);
list_splice_init(&dst->actions, &tmp); dst->nr_actions = src->nr_actions;
list_splice(&src->actions, &dst->actions); dst->actions = src->actions;
dst->type = src->type; dst->type = src->type;
tcf_tree_unlock(tp); tcf_tree_unlock(tp);
tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
tcf_exts_destroy(&old);
#endif #endif
} }
EXPORT_SYMBOL(tcf_exts_change); EXPORT_SYMBOL(tcf_exts_change);
#define tcf_exts_first_act(ext) \ #ifdef CONFIG_NET_CLS_ACT
list_first_entry_or_null(&(exts)->actions, \ static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
struct tc_action, list) {
if (exts->nr_actions == 0)
return NULL;
else
return exts->actions[0];
}
#endif
int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
{ {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
struct nlattr *nest; struct nlattr *nest;
if (exts->action && !list_empty(&exts->actions)) { if (exts->action && exts->nr_actions) {
/* /*
* again for backward compatible mode - we want * again for backward compatible mode - we want
* to work with both old and new modes of entering * to work with both old and new modes of entering
* tc data even if iproute2 was newer - jhs * tc data even if iproute2 was newer - jhs
*/ */
if (exts->type != TCA_OLD_COMPAT) { if (exts->type != TCA_OLD_COMPAT) {
LIST_HEAD(actions);
nest = nla_nest_start(skb, exts->action); nest = nla_nest_start(skb, exts->action);
if (nest == NULL) if (nest == NULL)
goto nla_put_failure; goto nla_put_failure;
if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0)
tcf_exts_to_list(exts, &actions);
if (tcf_action_dump(skb, &actions, 0, 0) < 0)
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
} else if (exts->police) { } else if (exts->police) {