aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sched/cls_api.c37
1 files changed, 8 insertions, 29 deletions
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 231181c602ed..b2d310745487 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -280,8 +280,8 @@ static void tcf_block_put_final(struct work_struct *work)
280 struct tcf_block *block = container_of(work, struct tcf_block, work); 280 struct tcf_block *block = container_of(work, struct tcf_block, work);
281 struct tcf_chain *chain, *tmp; 281 struct tcf_chain *chain, *tmp;
282 282
283 /* At this point, all the chains should have refcnt == 1. */
284 rtnl_lock(); 283 rtnl_lock();
284 /* Only chain 0 should be still here. */
285 list_for_each_entry_safe(chain, tmp, &block->chain_list, list) 285 list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
286 tcf_chain_put(chain); 286 tcf_chain_put(chain);
287 rtnl_unlock(); 287 rtnl_unlock();
@@ -289,23 +289,17 @@ static void tcf_block_put_final(struct work_struct *work)
289} 289}
290 290
291/* XXX: Standalone actions are not allowed to jump to any chain, and bound 291/* XXX: Standalone actions are not allowed to jump to any chain, and bound
292 * actions should be all removed after flushing. However, filters are destroyed 292 * actions should be all removed after flushing. However, filters are now
293 * in RCU callbacks, we have to hold the chains first, otherwise we would 293 * destroyed in tc filter workqueue with RTNL lock, they can not race here.
294 * always race with RCU callbacks on this list without proper locking.
295 */ 294 */
296static void tcf_block_put_deferred(struct work_struct *work) 295void tcf_block_put(struct tcf_block *block)
297{ 296{
298 struct tcf_block *block = container_of(work, struct tcf_block, work); 297 struct tcf_chain *chain, *tmp;
299 struct tcf_chain *chain;
300 298
301 rtnl_lock(); 299 if (!block)
302 /* Hold a refcnt for all chains, except 0, in case they are gone. */ 300 return;
303 list_for_each_entry(chain, &block->chain_list, list)
304 if (chain->index)
305 tcf_chain_hold(chain);
306 301
307 /* No race on the list, because no chain could be destroyed. */ 302 list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
308 list_for_each_entry(chain, &block->chain_list, list)
309 tcf_chain_flush(chain); 303 tcf_chain_flush(chain);
310 304
311 INIT_WORK(&block->work, tcf_block_put_final); 305 INIT_WORK(&block->work, tcf_block_put_final);
@@ -314,21 +308,6 @@ static void tcf_block_put_deferred(struct work_struct *work)
314 */ 308 */
315 rcu_barrier(); 309 rcu_barrier();
316 tcf_queue_work(&block->work); 310 tcf_queue_work(&block->work);
317 rtnl_unlock();
318}
319
320void tcf_block_put(struct tcf_block *block)
321{
322 if (!block)
323 return;
324
325 INIT_WORK(&block->work, tcf_block_put_deferred);
326 /* Wait for existing RCU callbacks to cool down, make sure their works
327 * have been queued before this. We can not flush pending works here
328 * because we are holding the RTNL lock.
329 */
330 rcu_barrier();
331 tcf_queue_work(&block->work);
332} 311}
333EXPORT_SYMBOL(tcf_block_put); 312EXPORT_SYMBOL(tcf_block_put);
334 313