Commit 1f3ed383 authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

net: sched: don't dump chains only held by actions

In case a chain is empty and not explicitly created by a user,
such chain should not exist. The only exception is if there is
an action "goto chain" pointing to it. In that case, don't show the
chain in the dump. Track the chain references held by actions and
use them to find out if a chain should or should not be shown
in chain dump.
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7a49d3d4
...@@ -39,7 +39,10 @@ bool tcf_queue_work(struct rcu_work *rwork, work_func_t func); ...@@ -39,7 +39,10 @@ bool tcf_queue_work(struct rcu_work *rwork, work_func_t func);
#ifdef CONFIG_NET_CLS #ifdef CONFIG_NET_CLS
struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
bool create); bool create);
struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block,
u32 chain_index);
void tcf_chain_put(struct tcf_chain *chain); void tcf_chain_put(struct tcf_chain *chain);
void tcf_chain_put_by_act(struct tcf_chain *chain);
void tcf_block_netif_keep_dst(struct tcf_block *block); void tcf_block_netif_keep_dst(struct tcf_block *block);
int tcf_block_get(struct tcf_block **p_block, int tcf_block_get(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
......
...@@ -314,6 +314,7 @@ struct tcf_chain { ...@@ -314,6 +314,7 @@ struct tcf_chain {
struct tcf_block *block; struct tcf_block *block;
u32 index; /* chain index */ u32 index; /* chain index */
unsigned int refcnt; unsigned int refcnt;
unsigned int action_refcnt;
bool explicitly_created; bool explicitly_created;
const struct tcf_proto_ops *tmplt_ops; const struct tcf_proto_ops *tmplt_ops;
void *tmplt_priv; void *tmplt_priv;
......
...@@ -36,7 +36,7 @@ static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp) ...@@ -36,7 +36,7 @@ static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
if (!tp) if (!tp)
return -EINVAL; return -EINVAL;
a->goto_chain = tcf_chain_get(tp->chain->block, chain_index, true); a->goto_chain = tcf_chain_get_by_act(tp->chain->block, chain_index);
if (!a->goto_chain) if (!a->goto_chain)
return -ENOMEM; return -ENOMEM;
return 0; return 0;
...@@ -44,7 +44,7 @@ static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp) ...@@ -44,7 +44,7 @@ static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
static void tcf_action_goto_chain_fini(struct tc_action *a) static void tcf_action_goto_chain_fini(struct tc_action *a)
{ {
tcf_chain_put(a->goto_chain); tcf_chain_put_by_act(a->goto_chain);
} }
static void tcf_action_goto_chain_exec(const struct tc_action *a, static void tcf_action_goto_chain_exec(const struct tc_action *a,
......
...@@ -262,6 +262,25 @@ static void tcf_chain_hold(struct tcf_chain *chain) ...@@ -262,6 +262,25 @@ static void tcf_chain_hold(struct tcf_chain *chain)
++chain->refcnt; ++chain->refcnt;
} }
static void tcf_chain_hold_by_act(struct tcf_chain *chain)
{
++chain->action_refcnt;
}
static void tcf_chain_release_by_act(struct tcf_chain *chain)
{
--chain->action_refcnt;
}
static bool tcf_chain_is_zombie(struct tcf_chain *chain)
{
/* In case all the references are action references, this
* chain is a zombie and should not be listed in the chain
* dump list.
*/
return chain->refcnt == chain->action_refcnt;
}
static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
u32 chain_index) u32 chain_index)
{ {
...@@ -298,6 +317,15 @@ struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, ...@@ -298,6 +317,15 @@ struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
} }
EXPORT_SYMBOL(tcf_chain_get); EXPORT_SYMBOL(tcf_chain_get);
struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
{
struct tcf_chain *chain = tcf_chain_get(block, chain_index, true);
tcf_chain_hold_by_act(chain);
return chain;
}
EXPORT_SYMBOL(tcf_chain_get_by_act);
static void tc_chain_tmplt_del(struct tcf_chain *chain); static void tc_chain_tmplt_del(struct tcf_chain *chain);
void tcf_chain_put(struct tcf_chain *chain) void tcf_chain_put(struct tcf_chain *chain)
...@@ -310,6 +338,13 @@ void tcf_chain_put(struct tcf_chain *chain) ...@@ -310,6 +338,13 @@ void tcf_chain_put(struct tcf_chain *chain)
} }
EXPORT_SYMBOL(tcf_chain_put); EXPORT_SYMBOL(tcf_chain_put);
void tcf_chain_put_by_act(struct tcf_chain *chain)
{
tcf_chain_release_by_act(chain);
tcf_chain_put(chain);
}
EXPORT_SYMBOL(tcf_chain_put_by_act);
static void tcf_chain_put_explicitly_created(struct tcf_chain *chain) static void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
{ {
if (chain->explicitly_created) if (chain->explicitly_created)
...@@ -1803,20 +1838,29 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1803,20 +1838,29 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
chain = tcf_chain_lookup(block, chain_index); chain = tcf_chain_lookup(block, chain_index);
if (n->nlmsg_type == RTM_NEWCHAIN) { if (n->nlmsg_type == RTM_NEWCHAIN) {
if (chain) { if (chain) {
NL_SET_ERR_MSG(extack, "Filter chain already exists"); if (tcf_chain_is_zombie(chain)) {
return -EEXIST; /* The chain exists only because there is
} * some action referencing it, meaning it
if (!(n->nlmsg_flags & NLM_F_CREATE)) { * is a zombie.
NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); */
return -ENOENT; tcf_chain_hold(chain);
} } else {
chain = tcf_chain_create(block, chain_index); NL_SET_ERR_MSG(extack, "Filter chain already exists");
if (!chain) { return -EEXIST;
NL_SET_ERR_MSG(extack, "Failed to create filter chain"); }
return -ENOMEM; } else {
if (!(n->nlmsg_flags & NLM_F_CREATE)) {
NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
return -ENOENT;
}
chain = tcf_chain_create(block, chain_index);
if (!chain) {
NL_SET_ERR_MSG(extack, "Failed to create filter chain");
return -ENOMEM;
}
} }
} else { } else {
if (!chain) { if (!chain || tcf_chain_is_zombie(chain)) {
NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
return -EINVAL; return -EINVAL;
} }
...@@ -1944,6 +1988,8 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1944,6 +1988,8 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
index++; index++;
continue; continue;
} }
if (tcf_chain_is_zombie(chain))
continue;
err = tc_chain_fill_node(chain, net, skb, block, err = tc_chain_fill_node(chain, net, skb, block,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
......
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