diff options
author | David S. Miller <davem@davemloft.net> | 2017-11-02 06:59:52 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-02 07:23:39 +0100 |
commit | ed29668d1aa2c6f01e61dd616df13b5241cee7e0 (patch) | |
tree | a086cf6311ed8623b292d3ea8d73c03f53207be0 /net/sched/cls_api.c | |
parent | Merge branch 'samples-pktgen-updates' (diff) | |
parent | Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebi... (diff) | |
download | linux-ed29668d1aa2c6f01e61dd616df13b5241cee7e0.tar.xz linux-ed29668d1aa2c6f01e61dd616df13b5241cee7e0.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Smooth Cong Wang's bug fix into 'net-next'. Basically put
the bulk of the tcf_block_put() logic from 'net' into
tcf_block_put_ext(), but after the offload unbind.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/cls_api.c')
-rw-r--r-- | net/sched/cls_api.c | 38 |
1 files changed, 9 insertions, 29 deletions
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index d9d54b367d23..2c03fcbc7188 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -322,8 +322,8 @@ static void tcf_block_put_final(struct work_struct *work) struct tcf_block *block = container_of(work, struct tcf_block, work); struct tcf_chain *chain, *tmp; - /* At this point, all the chains should have refcnt == 1. */ rtnl_lock(); + /* Only chain 0 should be still here. */ list_for_each_entry_safe(chain, tmp, &block->chain_list, list) tcf_chain_put(chain); rtnl_unlock(); @@ -331,44 +331,24 @@ static void tcf_block_put_final(struct work_struct *work) } /* XXX: Standalone actions are not allowed to jump to any chain, and bound - * actions should be all removed after flushing. However, filters are destroyed - * in RCU callbacks, we have to hold the chains first, otherwise we would - * always race with RCU callbacks on this list without proper locking. + * actions should be all removed after flushing. However, filters are now + * destroyed in tc filter workqueue with RTNL lock, they can not race here. */ -static void tcf_block_put_deferred(struct work_struct *work) -{ - struct tcf_block *block = container_of(work, struct tcf_block, work); - struct tcf_chain *chain; - - rtnl_lock(); - /* Hold a refcnt for all chains, except 0, in case they are gone. */ - list_for_each_entry(chain, &block->chain_list, list) - if (chain->index) - tcf_chain_hold(chain); - - /* No race on the list, because no chain could be destroyed. */ - list_for_each_entry(chain, &block->chain_list, list) - tcf_chain_flush(chain); - - INIT_WORK(&block->work, tcf_block_put_final); - /* Wait for RCU callbacks to release the reference count and make - * sure their works have been queued before this. - */ - rcu_barrier(); - tcf_queue_work(&block->work); - rtnl_unlock(); -} - void tcf_block_put_ext(struct tcf_block *block, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, struct tcf_block_ext_info *ei) { + struct tcf_chain *chain, *tmp; + if (!block) return; tcf_block_offload_unbind(block, q, ei); - INIT_WORK(&block->work, tcf_block_put_deferred); + list_for_each_entry_safe(chain, tmp, &block->chain_list, list) + tcf_chain_flush(chain); + + INIT_WORK(&block->work, tcf_block_put_final); /* Wait for existing RCU callbacks to cool down, make sure their works * have been queued before this. We can not flush pending works here * because we are holding the RTNL lock. |