On Thu, 26 Jul 2018 18:31:01 +0200, Jiri Pirko wrote:
> From: Jiri Pirko <j...@mellanox.com>
> 
> 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: Jiri Pirko <j...@mellanox.com>

I don't have any better ideas :)

One question below.

> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 75cce2819de9..76035cd6e3bf 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -262,6 +262,25 @@ static void tcf_chain_hold(struct tcf_chain *chain)
>       ++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,
>                                         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);
>  
> +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);
>  
>  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);
>  
> +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)
>  {
>       if (chain->explicitly_created)
> @@ -1803,17 +1838,26 @@ static int tc_ctl_chain(struct sk_buff *skb, struct 
> nlmsghdr *n,
>       chain = tcf_chain_lookup(block, chain_index);
>       if (n->nlmsg_type == RTM_NEWCHAIN) {
>               if (chain) {
> -                     NL_SET_ERR_MSG(extack, "Filter chain already exists");
> -                     return -EEXIST;
> -             }
> -             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;
> +                     if (tcf_chain_is_zombie(chain)) {
> +                             /* The chain exists only because there is
> +                              * some action referencing it, meaning it
> +                              * is a zombie.
> +                              */
> +                             tcf_chain_hold(chain);

I'm not 100% sure why this is needed?  In my tree below I see:

        switch (n->nlmsg_type) {
        case RTM_NEWCHAIN:
                err = tc_chain_tmplt_add(chain, net, tca, extack);
                if (err)
                        goto errout;
                /* In case the chain was successfully added, take a reference
                 * to the chain. This ensures that an empty chain
                 * does not disappear at the end of this function.
                 */
                tcf_chain_hold(chain);
                chain->explicitly_created = true;

so one reference will be taken..  do we need two? 

> +                     } else {
> +                             NL_SET_ERR_MSG(extack, "Filter chain already 
> exists");
> +                             return -EEXIST;
> +                     }
> +             } 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 {
>               if (!chain) {
> @@ -1944,6 +1988,8 @@ static int tc_dump_chain(struct sk_buff *skb, struct 
> netlink_callback *cb)
>                       index++;
>                       continue;
>               }
> +             if (tcf_chain_is_zombie(chain))
> +                     continue;
>               err = tc_chain_fill_node(chain, net, skb, block,
>                                        NETLINK_CB(cb->skb).portid,
>                                        cb->nlh->nlmsg_seq, NLM_F_MULTI,

Reply via email to