Skip to content

Commit

Permalink
net: avoid synchronize_rcu() in dev_deactivate_many
Browse files Browse the repository at this point in the history
dev_deactivate_many() issues one synchronize_rcu() call after qdiscs set
to noop_qdisc.

This call is here to make sure they are no outstanding qdisc-less
dev_queue_xmit calls before returning to caller.

But in dismantle phase, we dont have to wait, because we wont activate
again the device, and we are going to wait one rcu grace period later in
rollback_registered_many().

After this patch, device dismantle uses one synchronize_net() and one
rcu_barrier() call only, so we have a ~30% speedup and a smaller RTNL
latency.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Patrick McHardy <kaber@trash.net>,
CC: Ben Greear <greearb@candelatech.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and David S. Miller committed May 23, 2011
1 parent 6df427f commit 3137663
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions net/sched/sch_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -815,9 +815,17 @@ static bool some_qdisc_is_busy(struct net_device *dev)
return false;
}

/**
* dev_deactivate_many - deactivate transmissions on several devices
* @head: list of devices to deactivate
*
* This function returns only when all outstanding transmissions
* have completed, unless all devices are in dismantle phase.
*/
void dev_deactivate_many(struct list_head *head)
{
struct net_device *dev;
bool sync_needed = false;

list_for_each_entry(dev, head, unreg_list) {
netdev_for_each_tx_queue(dev, dev_deactivate_queue,
Expand All @@ -827,10 +835,15 @@ void dev_deactivate_many(struct list_head *head)
&noop_qdisc);

dev_watchdog_down(dev);
sync_needed |= !dev->dismantle;
}

/* Wait for outstanding qdisc-less dev_queue_xmit calls. */
synchronize_rcu();
/* Wait for outstanding qdisc-less dev_queue_xmit calls.
* This is avoided if all devices are in dismantle phase :
* Caller will call synchronize_net() for us
*/
if (sync_needed)
synchronize_net();

/* Wait for outstanding qdisc_run calls. */
list_for_each_entry(dev, head, unreg_list)
Expand Down

0 comments on commit 3137663

Please sign in to comment.