diff options
Diffstat (limited to 'net/core/dev.c')
| -rw-r--r-- | net/core/dev.c | 59 | 
1 files changed, 44 insertions, 15 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 1e0a1847c3b..09cb3f6dc40 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3300,18 +3300,18 @@ ncls:  				&& !skb_pfmemalloc_protocol(skb))  		goto drop; -	rx_handler = rcu_dereference(skb->dev->rx_handler);  	if (vlan_tx_tag_present(skb)) {  		if (pt_prev) {  			ret = deliver_skb(skb, pt_prev, orig_dev);  			pt_prev = NULL;  		} -		if (vlan_do_receive(&skb, !rx_handler)) +		if (vlan_do_receive(&skb))  			goto another_round;  		else if (unlikely(!skb))  			goto unlock;  	} +	rx_handler = rcu_dereference(skb->dev->rx_handler);  	if (rx_handler) {  		if (pt_prev) {  			ret = deliver_skb(skb, pt_prev, orig_dev); @@ -3331,6 +3331,9 @@ ncls:  		}  	} +	if (vlan_tx_nonzero_tag_present(skb)) +		skb->pkt_type = PACKET_OTHERHOST; +  	/* deliver only exact match when indicated */  	null_or_dev = deliver_exact ? skb->dev : NULL; @@ -3471,17 +3474,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 +3559,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; @@ -3631,20 +3649,22 @@ gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)  }  EXPORT_SYMBOL(napi_skb_finish); -void skb_gro_reset_offset(struct sk_buff *skb) +static void skb_gro_reset_offset(struct sk_buff *skb)  { +	const struct skb_shared_info *pinfo = skb_shinfo(skb); +	const skb_frag_t *frag0 = &pinfo->frags[0]; +  	NAPI_GRO_CB(skb)->data_offset = 0;  	NAPI_GRO_CB(skb)->frag0 = NULL;  	NAPI_GRO_CB(skb)->frag0_len = 0;  	if (skb->mac_header == skb->tail && -	    !PageHighMem(skb_frag_page(&skb_shinfo(skb)->frags[0]))) { -		NAPI_GRO_CB(skb)->frag0 = -			skb_frag_address(&skb_shinfo(skb)->frags[0]); -		NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(&skb_shinfo(skb)->frags[0]); +	    pinfo->nr_frags && +	    !PageHighMem(skb_frag_page(frag0))) { +		NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0); +		NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);  	}  } -EXPORT_SYMBOL(skb_gro_reset_offset);  gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)  { @@ -3876,7 +3896,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); @@ -3981,8 +4001,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);  | 
