Commit b96c22c0 authored by David S. Miller's avatar David S. Miller

Merge branch 'tc_action-fixes'

Cong Wang says:

====================
net_sched: tc action fixes and updates

This patchset fixes a few regressions caused by the previous
code refactor and more. Thanks to Jamal for catching them!

Note, patch 3/7 and 4/7 are not strictly necessary for this patchset,
I just want to carry them together.

---
v4: adjust an indention for Jamal
    add two more patches

v3: avoid list for fast path, suggested by Jamal

v2: replace flex_array with regular dynamic array
    keep tcf_action_stats_update() in act_api.h
    fix macro typos found by Amir
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f4abf05f b5ac8518
...@@ -8396,12 +8396,14 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, ...@@ -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)) {
......
...@@ -318,6 +318,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
......
...@@ -1121,6 +1121,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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, ...@@ -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;
......
...@@ -176,8 +176,8 @@ int tcf_register_action(struct tc_action_ops *a, struct pernet_operations *ops); ...@@ -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 *);
...@@ -189,30 +189,17 @@ int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); ...@@ -189,30 +189,17 @@ int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int);
#define tc_no_actions(_exts) \ #endif /* CONFIG_NET_CLS_ACT */
(list_empty(&(_exts)->actions))
#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))
static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes, static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
u64 packets, u64 lastuse) u64 packets, u64 lastuse)
{ {
#ifdef CONFIG_NET_CLS_ACT
if (!a->ops->stats_update) if (!a->ops->stats_update)
return; return;
a->ops->stats_update(a, bytes, packets, lastuse); a->ops->stats_update(a, bytes, packets, lastuse);
#endif
} }
#else /* CONFIG_NET_CLS_ACT */
#define tc_no_actions(_exts) true
#define tc_for_each_action(_a, _exts) while ((void)(_a), 0)
#define tc_single_action(_exts) false
#define tcf_action_stats_update(a, bytes, packets, lastuse)
#endif /* CONFIG_NET_CLS_ACT */
#endif #endif
...@@ -59,7 +59,8 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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,12 +142,25 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, ...@@ -124,12 +142,25 @@ 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
#define tc_no_actions(_exts) ((_exts)->nr_actions == 0)
#define tc_single_action(_exts) ((_exts)->nr_actions == 1)
#else /* CONFIG_NET_CLS_ACT */
#define tc_no_actions(_exts) true
#define tc_single_action(_exts) false
#endif /* CONFIG_NET_CLS_ACT */
int tcf_exts_validate(struct net *net, struct tcf_proto *tp, int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
struct nlattr **tb, struct nlattr *rate_tlv, struct nlattr **tb, struct nlattr *rate_tlv,
struct tcf_exts *exts, bool ovr); struct tcf_exts *exts, bool ovr);
......
...@@ -64,7 +64,6 @@ int __tcf_hash_release(struct tc_action *p, bool bind, bool strict) ...@@ -64,7 +64,6 @@ int __tcf_hash_release(struct tc_action *p, bool bind, bool strict)
if (p->tcfa_bindcnt <= 0 && p->tcfa_refcnt <= 0) { if (p->tcfa_bindcnt <= 0 && p->tcfa_refcnt <= 0) {
if (p->ops->cleanup) if (p->ops->cleanup)
p->ops->cleanup(p, bind); p->ops->cleanup(p, bind);
list_del(&p->list);
tcf_hash_destroy(p->hinfo, p); tcf_hash_destroy(p->hinfo, p);
ret = ACT_P_DELETED; ret = ACT_P_DELETED;
} }
...@@ -421,18 +420,19 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) ...@@ -421,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)
...@@ -754,16 +754,6 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla, ...@@ -754,16 +754,6 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
return ERR_PTR(err); return ERR_PTR(err);
} }
static void cleanup_a(struct list_head *actions)
{
struct tc_action *a, *tmp;
list_for_each_entry_safe(a, tmp, actions, list) {
list_del(&a->list);
kfree(a);
}
}
static int tca_action_flush(struct net *net, struct nlattr *nla, static int tca_action_flush(struct net *net, struct nlattr *nla,
struct nlmsghdr *n, u32 portid) struct nlmsghdr *n, u32 portid)
{ {
...@@ -905,7 +895,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, ...@@ -905,7 +895,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
return ret; return ret;
} }
err: err:
cleanup_a(&actions); tcf_action_destroy(&actions, 0);
return ret; return ret;
} }
...@@ -942,15 +932,9 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, ...@@ -942,15 +932,9 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions); ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions);
if (ret) if (ret)
goto done; return ret;
/* dump then free all the actions after update; inserted policy return tcf_add_notify(net, n, &actions, portid);
* stays intact
*/
ret = tcf_add_notify(net, n, &actions, portid);
cleanup_a(&actions);
done:
return ret;
} }
static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n) static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n)
......
...@@ -63,49 +63,8 @@ static int tcf_act_police_walker(struct net *net, struct sk_buff *skb, ...@@ -63,49 +63,8 @@ static int tcf_act_police_walker(struct net *net, struct sk_buff *skb,
const struct tc_action_ops *ops) const struct tc_action_ops *ops)
{ {
struct tc_action_net *tn = net_generic(net, police_net_id); struct tc_action_net *tn = net_generic(net, police_net_id);
struct tcf_hashinfo *hinfo = tn->hinfo;
int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
struct nlattr *nest;
spin_lock_bh(&hinfo->lock);
s_i = cb->args[0];
for (i = 0; i < (POL_TAB_MASK + 1); i++) {
struct hlist_head *head;
struct tc_action *p;
head = &hinfo->htab[tcf_hash(i, POL_TAB_MASK)];
hlist_for_each_entry_rcu(p, head, tcfa_head) {
index++;
if (index < s_i)
continue;
nest = nla_nest_start(skb, index);
if (nest == NULL)
goto nla_put_failure;
if (type == RTM_DELACTION)
err = tcf_action_dump_1(skb, p, 0, 1);
else
err = tcf_action_dump_1(skb, p, 0, 0);
if (err < 0) {
index--;
nla_nest_cancel(skb, nest);
goto done;
}
nla_nest_end(skb, nest);
n_i++;
}
}
done:
spin_unlock_bh(&hinfo->lock);
if (n_i)
cb->args[0] += n_i;
return n_i;
nla_put_failure: return tcf_generic_walker(tn, skb, cb, type, ops);
nla_nest_cancel(skb, nest);
goto done;
} }
static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
...@@ -125,6 +84,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, ...@@ -125,6 +84,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
struct tcf_police *police; struct tcf_police *police;
struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
struct tc_action_net *tn = net_generic(net, police_net_id); struct tc_action_net *tn = net_generic(net, police_net_id);
bool exists = false;
int size; int size;
if (nla == NULL) if (nla == NULL)
...@@ -139,24 +99,24 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, ...@@ -139,24 +99,24 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
size = nla_len(tb[TCA_POLICE_TBF]); size = nla_len(tb[TCA_POLICE_TBF]);
if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat))
return -EINVAL; return -EINVAL;
parm = nla_data(tb[TCA_POLICE_TBF]); parm = nla_data(tb[TCA_POLICE_TBF]);
exists = tcf_hash_check(tn, parm->index, a, bind);
if (exists && bind)
return 0;
if (parm->index) { if (!exists) {
if (tcf_hash_check(tn, parm->index, a, bind)) {
if (ovr)
goto override;
/* not replacing */
return -EEXIST;
}
} else {
ret = tcf_hash_create(tn, parm->index, NULL, a, ret = tcf_hash_create(tn, parm->index, NULL, a,
&act_police_ops, bind, false); &act_police_ops, bind, false);
if (ret) if (ret)
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else {
tcf_hash_release(*a, bind);
if (!ovr)
return -EEXIST;
} }
override:
police = to_police(*a); police = to_police(*a);
if (parm->rate.rate) { if (parm->rate.rate) {
err = -ENOMEM; err = -ENOMEM;
......
...@@ -541,8 +541,12 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -541,8 +541,12 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
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, ...@@ -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, ...@@ -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, ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment