/* 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 flush and wait
- * for them inside the loop, otherwise we race with RCU callbacks on
- * this list.
+ * 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.
*/
- list_for_each_entry_safe(chain, tmp, &block->chain_list, list) {
+
+ /* Wait for existing RCU callbacks to cool down. */
+ rcu_barrier();
+
+ /* 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);
- rcu_barrier();
- }
+ /* Wait for RCU callbacks to release the reference count. */
+ rcu_barrier();
+
+ /* 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);
kfree(block);