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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b96c22c071
@ -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,
|
|||||||
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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
|||||||
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)
|
|||||||
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,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)
|
|||||||
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)
|
|||||||
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 @@ err_out:
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
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);
|
return tcf_generic_walker(tn, skb, cb, type, ops);
|
||||||
|
|
||||||
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:
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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]);
|
|
||||||
|
|
||||||
if (parm->index) {
|
parm = nla_data(tb[TCA_POLICE_TBF]);
|
||||||
if (tcf_hash_check(tn, parm->index, a, bind)) {
|
exists = tcf_hash_check(tn, parm->index, a, bind);
|
||||||
if (ovr)
|
if (exists && bind)
|
||||||
goto override;
|
return 0;
|
||||||
/* not replacing */
|
|
||||||
return -EEXIST;
|
if (!exists) {
|
||||||
}
|
|
||||||
} 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 @@ 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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user