net: Convert GRO SKB handling to list_head.
Manage pending per-NAPI GRO packets via list_head. Return an SKB pointer from the GRO receive handlers. When GRO receive handlers return non-NULL, it means that this SKB needs to be completed at this time and removed from the NAPI queue. Several operations are greatly simplified by this transformation, especially timing out the oldest SKB in the list when gro_count exceeds MAX_GRO_SKBS, and napi_gro_flush() which walks the queue in reverse order. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
9ff3b40e41
commit
d4546c2509
@@ -4881,36 +4881,25 @@ out:
|
||||
*/
|
||||
void napi_gro_flush(struct napi_struct *napi, bool flush_old)
|
||||
{
|
||||
struct sk_buff *skb, *prev = NULL;
|
||||
|
||||
/* 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;
|
||||
struct sk_buff *skb, *p;
|
||||
|
||||
list_for_each_entry_safe_reverse(skb, p, &napi->gro_list, list) {
|
||||
if (flush_old && NAPI_GRO_CB(skb)->age == jiffies)
|
||||
return;
|
||||
|
||||
prev = skb->prev;
|
||||
list_del_init(&skb->list);
|
||||
napi_gro_complete(skb);
|
||||
napi->gro_count--;
|
||||
}
|
||||
|
||||
napi->gro_list = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(napi_gro_flush);
|
||||
|
||||
static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *p;
|
||||
unsigned int maclen = skb->dev->hard_header_len;
|
||||
u32 hash = skb_get_hash_raw(skb);
|
||||
struct sk_buff *p;
|
||||
|
||||
for (p = napi->gro_list; p; p = p->next) {
|
||||
list_for_each_entry(p, &napi->gro_list, list) {
|
||||
unsigned long diffs;
|
||||
|
||||
NAPI_GRO_CB(p)->flush = 0;
|
||||
@@ -4977,12 +4966,12 @@ static void gro_pull_from_frag0(struct sk_buff *skb, int grow)
|
||||
|
||||
static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff **pp = NULL;
|
||||
struct list_head *head = &offload_base;
|
||||
struct packet_offload *ptype;
|
||||
__be16 type = skb->protocol;
|
||||
struct list_head *head = &offload_base;
|
||||
int same_flow;
|
||||
struct sk_buff *pp = NULL;
|
||||
enum gro_result ret;
|
||||
int same_flow;
|
||||
int grow;
|
||||
|
||||
if (netif_elide_gro(skb->dev))
|
||||
@@ -5039,11 +5028,8 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
|
||||
ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED;
|
||||
|
||||
if (pp) {
|
||||
struct sk_buff *nskb = *pp;
|
||||
|
||||
*pp = nskb->next;
|
||||
nskb->next = NULL;
|
||||
napi_gro_complete(nskb);
|
||||
list_del_init(&pp->list);
|
||||
napi_gro_complete(pp);
|
||||
napi->gro_count--;
|
||||
}
|
||||
|
||||
@@ -5054,15 +5040,10 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
|
||||
goto normal;
|
||||
|
||||
if (unlikely(napi->gro_count >= MAX_GRO_SKBS)) {
|
||||
struct sk_buff *nskb = napi->gro_list;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
/* locate the end of the list to select the 'oldest' flow */
|
||||
while (nskb->next) {
|
||||
pp = &nskb->next;
|
||||
nskb = *pp;
|
||||
}
|
||||
*pp = NULL;
|
||||
nskb->next = NULL;
|
||||
nskb = list_last_entry(&napi->gro_list, struct sk_buff, list);
|
||||
list_del(&nskb->list);
|
||||
napi_gro_complete(nskb);
|
||||
} else {
|
||||
napi->gro_count++;
|
||||
@@ -5071,8 +5052,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
|
||||
NAPI_GRO_CB(skb)->age = jiffies;
|
||||
NAPI_GRO_CB(skb)->last = skb;
|
||||
skb_shinfo(skb)->gso_size = skb_gro_len(skb);
|
||||
skb->next = napi->gro_list;
|
||||
napi->gro_list = skb;
|
||||
list_add(&skb->list, &napi->gro_list);
|
||||
ret = GRO_HELD;
|
||||
|
||||
pull:
|
||||
@@ -5478,7 +5458,7 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
|
||||
NAPIF_STATE_IN_BUSY_POLL)))
|
||||
return false;
|
||||
|
||||
if (n->gro_list) {
|
||||
if (!list_empty(&n->gro_list)) {
|
||||
unsigned long timeout = 0;
|
||||
|
||||
if (work_done)
|
||||
@@ -5687,7 +5667,7 @@ static enum hrtimer_restart napi_watchdog(struct hrtimer *timer)
|
||||
/* Note : we use a relaxed variant of napi_schedule_prep() not setting
|
||||
* NAPI_STATE_MISSED, since we do not react to a device IRQ.
|
||||
*/
|
||||
if (napi->gro_list && !napi_disable_pending(napi) &&
|
||||
if (!list_empty(&napi->gro_list) && !napi_disable_pending(napi) &&
|
||||
!test_and_set_bit(NAPI_STATE_SCHED, &napi->state))
|
||||
__napi_schedule_irqoff(napi);
|
||||
|
||||
@@ -5701,7 +5681,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
|
||||
hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
|
||||
napi->timer.function = napi_watchdog;
|
||||
napi->gro_count = 0;
|
||||
napi->gro_list = NULL;
|
||||
INIT_LIST_HEAD(&napi->gro_list);
|
||||
napi->skb = NULL;
|
||||
napi->poll = poll;
|
||||
if (weight > NAPI_POLL_WEIGHT)
|
||||
@@ -5734,6 +5714,14 @@ void napi_disable(struct napi_struct *n)
|
||||
}
|
||||
EXPORT_SYMBOL(napi_disable);
|
||||
|
||||
static void gro_list_free(struct list_head *head)
|
||||
{
|
||||
struct sk_buff *skb, *p;
|
||||
|
||||
list_for_each_entry_safe(skb, p, head, list)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* Must be called in process context */
|
||||
void netif_napi_del(struct napi_struct *napi)
|
||||
{
|
||||
@@ -5743,8 +5731,8 @@ void netif_napi_del(struct napi_struct *napi)
|
||||
list_del_init(&napi->dev_list);
|
||||
napi_free_frags(napi);
|
||||
|
||||
kfree_skb_list(napi->gro_list);
|
||||
napi->gro_list = NULL;
|
||||
gro_list_free(&napi->gro_list);
|
||||
INIT_LIST_HEAD(&napi->gro_list);
|
||||
napi->gro_count = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(netif_napi_del);
|
||||
@@ -5787,7 +5775,7 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (n->gro_list) {
|
||||
if (!list_empty(&n->gro_list)) {
|
||||
/* flush too old packets
|
||||
* If HZ < 1000, flush all packets.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user