|
@@ -3471,17 +3471,31 @@ out:
|
|
|
return netif_receive_skb(skb);
|
|
|
}
|
|
|
|
|
|
-inline void napi_gro_flush(struct napi_struct *napi)
|
|
|
+/* napi->gro_list contains packets ordered by age.
|
|
|
+ * youngest packets at the head of it.
|
|
|
+ * Complete skbs in reverse order to reduce latencies.
|
|
|
+ */
|
|
|
+void napi_gro_flush(struct napi_struct *napi, bool flush_old)
|
|
|
{
|
|
|
- struct sk_buff *skb, *next;
|
|
|
+ struct sk_buff *skb, *prev = NULL;
|
|
|
|
|
|
- for (skb = napi->gro_list; skb; skb = next) {
|
|
|
- next = skb->next;
|
|
|
+ /* scan list and build reverse chain */
|
|
|
+ for (skb = napi->gro_list; skb != NULL; skb = skb->next) {
|
|
|
+ skb->prev = prev;
|
|
|
+ prev = skb;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (skb = prev; skb; skb = prev) {
|
|
|
skb->next = NULL;
|
|
|
+
|
|
|
+ if (flush_old && NAPI_GRO_CB(skb)->age == jiffies)
|
|
|
+ return;
|
|
|
+
|
|
|
+ prev = skb->prev;
|
|
|
napi_gro_complete(skb);
|
|
|
+ napi->gro_count--;
|
|
|
}
|
|
|
|
|
|
- napi->gro_count = 0;
|
|
|
napi->gro_list = NULL;
|
|
|
}
|
|
|
EXPORT_SYMBOL(napi_gro_flush);
|
|
@@ -3542,6 +3556,7 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
|
|
|
|
|
napi->gro_count++;
|
|
|
NAPI_GRO_CB(skb)->count = 1;
|
|
|
+ NAPI_GRO_CB(skb)->age = jiffies;
|
|
|
skb_shinfo(skb)->gso_size = skb_gro_len(skb);
|
|
|
skb->next = napi->gro_list;
|
|
|
napi->gro_list = skb;
|
|
@@ -3878,7 +3893,7 @@ void napi_complete(struct napi_struct *n)
|
|
|
if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state)))
|
|
|
return;
|
|
|
|
|
|
- napi_gro_flush(n);
|
|
|
+ napi_gro_flush(n, false);
|
|
|
local_irq_save(flags);
|
|
|
__napi_complete(n);
|
|
|
local_irq_restore(flags);
|
|
@@ -3983,8 +3998,17 @@ static void net_rx_action(struct softirq_action *h)
|
|
|
local_irq_enable();
|
|
|
napi_complete(n);
|
|
|
local_irq_disable();
|
|
|
- } else
|
|
|
+ } else {
|
|
|
+ if (n->gro_list) {
|
|
|
+ /* flush too old packets
|
|
|
+ * If HZ < 1000, flush all packets.
|
|
|
+ */
|
|
|
+ local_irq_enable();
|
|
|
+ napi_gro_flush(n, HZ >= 1000);
|
|
|
+ local_irq_disable();
|
|
|
+ }
|
|
|
list_move_tail(&n->poll_list, &sd->poll_list);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
netpoll_poll_unlock(have);
|