Skip to content

Commit 145dd5f

Browse files
Paolo Abenidavem330
authored andcommitted
net: flush the softnet backlog in process context
Currently in process_backlog(), the process_queue dequeuing is performed with local IRQ disabled, to protect against flush_backlog(), which runs in hard IRQ context. This patch moves the flush operation to a work queue and runs the callback with bottom half disabled to protect the process_queue against dequeuing. Since process_queue is now always manipulated in bottom half context, the irq disable/enable pair around the dequeue operation are removed. To keep the flush time as low as possible, the flush works are scheduled on all online cpu simultaneously, using the high priority work-queue and statically allocated, per cpu, work structs. Overall this change increases the time required to destroy a device to improve slightly the packets reinjection performances. Acked-by: Hannes Frederic Sowa <[email protected]> Signed-off-by: Paolo Abeni <[email protected]> Acked-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 72f4af4 commit 145dd5f

File tree

1 file changed

+50
-22
lines changed

1 file changed

+50
-22
lines changed

net/core/dev.c

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4292,15 +4292,25 @@ int netif_receive_skb(struct sk_buff *skb)
42924292
}
42934293
EXPORT_SYMBOL(netif_receive_skb);
42944294

4295-
/* Network device is going away, flush any packets still pending
4296-
* Called with irqs disabled.
4297-
*/
4298-
static void flush_backlog(void *arg)
4295+
struct flush_work {
4296+
struct net_device *dev;
4297+
struct work_struct work;
4298+
};
4299+
4300+
DEFINE_PER_CPU(struct flush_work, flush_works);
4301+
4302+
/* Network device is going away, flush any packets still pending */
4303+
static void flush_backlog(struct work_struct *work)
42994304
{
4300-
struct net_device *dev = arg;
4301-
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
4305+
struct flush_work *flush = container_of(work, typeof(*flush), work);
4306+
struct net_device *dev = flush->dev;
43024307
struct sk_buff *skb, *tmp;
4308+
struct softnet_data *sd;
4309+
4310+
local_bh_disable();
4311+
sd = this_cpu_ptr(&softnet_data);
43034312

4313+
local_irq_disable();
43044314
rps_lock(sd);
43054315
skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {
43064316
if (skb->dev == dev) {
@@ -4310,6 +4320,7 @@ static void flush_backlog(void *arg)
43104320
}
43114321
}
43124322
rps_unlock(sd);
4323+
local_irq_enable();
43134324

43144325
skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
43154326
if (skb->dev == dev) {
@@ -4318,6 +4329,27 @@ static void flush_backlog(void *arg)
43184329
input_queue_head_incr(sd);
43194330
}
43204331
}
4332+
local_bh_enable();
4333+
}
4334+
4335+
static void flush_all_backlogs(struct net_device *dev)
4336+
{
4337+
unsigned int cpu;
4338+
4339+
get_online_cpus();
4340+
4341+
for_each_online_cpu(cpu) {
4342+
struct flush_work *flush = per_cpu_ptr(&flush_works, cpu);
4343+
4344+
INIT_WORK(&flush->work, flush_backlog);
4345+
flush->dev = dev;
4346+
queue_work_on(cpu, system_highpri_wq, &flush->work);
4347+
}
4348+
4349+
for_each_online_cpu(cpu)
4350+
flush_work(&per_cpu_ptr(&flush_works, cpu)->work);
4351+
4352+
put_online_cpus();
43214353
}
43224354

43234355
static int napi_gro_complete(struct sk_buff *skb)
@@ -4805,8 +4837,9 @@ static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
48054837

48064838
static int process_backlog(struct napi_struct *napi, int quota)
48074839
{
4808-
int work = 0;
48094840
struct softnet_data *sd = container_of(napi, struct softnet_data, backlog);
4841+
bool again = true;
4842+
int work = 0;
48104843

48114844
/* Check if we have pending ipi, its better to send them now,
48124845
* not waiting net_rx_action() end.
@@ -4817,23 +4850,20 @@ static int process_backlog(struct napi_struct *napi, int quota)
48174850
}
48184851

48194852
napi->weight = weight_p;
4820-
local_irq_disable();
4821-
while (1) {
4853+
while (again) {
48224854
struct sk_buff *skb;
48234855

48244856
while ((skb = __skb_dequeue(&sd->process_queue))) {
48254857
rcu_read_lock();
4826-
local_irq_enable();
48274858
__netif_receive_skb(skb);
48284859
rcu_read_unlock();
4829-
local_irq_disable();
48304860
input_queue_head_incr(sd);
4831-
if (++work >= quota) {
4832-
local_irq_enable();
4861+
if (++work >= quota)
48334862
return work;
4834-
}
4863+
48354864
}
48364865

4866+
local_irq_disable();
48374867
rps_lock(sd);
48384868
if (skb_queue_empty(&sd->input_pkt_queue)) {
48394869
/*
@@ -4845,16 +4875,14 @@ static int process_backlog(struct napi_struct *napi, int quota)
48454875
* and we dont need an smp_mb() memory barrier.
48464876
*/
48474877
napi->state = 0;
4848-
rps_unlock(sd);
4849-
4850-
break;
4878+
again = false;
4879+
} else {
4880+
skb_queue_splice_tail_init(&sd->input_pkt_queue,
4881+
&sd->process_queue);
48514882
}
4852-
4853-
skb_queue_splice_tail_init(&sd->input_pkt_queue,
4854-
&sd->process_queue);
48554883
rps_unlock(sd);
4884+
local_irq_enable();
48564885
}
4857-
local_irq_enable();
48584886

48594887
return work;
48604888
}
@@ -6707,7 +6735,7 @@ static void rollback_registered_many(struct list_head *head)
67076735
unlist_netdevice(dev);
67086736

67096737
dev->reg_state = NETREG_UNREGISTERING;
6710-
on_each_cpu(flush_backlog, dev, 1);
6738+
flush_all_backlogs(dev);
67116739
}
67126740

67136741
synchronize_net();

0 commit comments

Comments
 (0)