diff options
-rw-r--r-- | net/sched/cls_api.c | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 1451a56a8f93..ddae7053b745 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -282,7 +282,8 @@ static void tcf_block_put_final(struct work_struct *work) struct tcf_chain *chain, *tmp; rtnl_lock(); - /* Only chain 0 should be still here. */ + + /* At this point, all the chains should have refcnt == 1. */ list_for_each_entry_safe(chain, tmp, &block->chain_list, list) tcf_chain_put(chain); rtnl_unlock(); @@ -290,17 +291,23 @@ 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 now - * destroyed in tc filter workqueue with RTNL lock, they can not race here. + * actions should be all removed after flushing. */ void tcf_block_put(struct tcf_block *block) { - struct tcf_chain *chain, *tmp; + struct tcf_chain *chain; if (!block) return; - list_for_each_entry_safe(chain, tmp, &block->chain_list, list) + /* Hold a refcnt for all chains, except 0, so that they don't disappear + * while we are iterating. + */ + list_for_each_entry(chain, &block->chain_list, list) + if (chain->index) + tcf_chain_hold(chain); + + list_for_each_entry(chain, &block->chain_list, list) tcf_chain_flush(chain); INIT_WORK(&block->work, tcf_block_put_final); |