neigh: reduce arp latency
Remove the artificial HZ latency on arp resolution. Instead of firing a timer in one jiffy (up to 10 ms if HZ=100), lets send the ARP message immediately. Before patch : # arp -d 192.168.20.108 ; ping -c 3 192.168.20.108 PING 192.168.20.108 (192.168.20.108) 56(84) bytes of data. 64 bytes from 192.168.20.108: icmp_seq=1 ttl=64 time=9.91 ms 64 bytes from 192.168.20.108: icmp_seq=2 ttl=64 time=0.065 ms 64 bytes from 192.168.20.108: icmp_seq=3 ttl=64 time=0.061 ms After patch : $ arp -d 192.168.20.108 ; ping -c 3 192.168.20.108 PING 192.168.20.108 (192.168.20.108) 56(84) bytes of data. 64 bytes from 192.168.20.108: icmp_seq=1 ttl=64 time=0.152 ms 64 bytes from 192.168.20.108: icmp_seq=2 ttl=64 time=0.064 ms 64 bytes from 192.168.20.108: icmp_seq=3 ttl=64 time=0.074 ms Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									68c3e5a7b9
								
							
						
					
					
						commit
						cd28ca0a3d
					
				| @ -844,6 +844,19 @@ static void neigh_invalidate(struct neighbour *neigh) | |||||||
| 	skb_queue_purge(&neigh->arp_queue); | 	skb_queue_purge(&neigh->arp_queue); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void neigh_probe(struct neighbour *neigh) | ||||||
|  | 	__releases(neigh->lock) | ||||||
|  | { | ||||||
|  | 	struct sk_buff *skb = skb_peek(&neigh->arp_queue); | ||||||
|  | 	/* keep skb alive even if arp_queue overflows */ | ||||||
|  | 	if (skb) | ||||||
|  | 		skb = skb_copy(skb, GFP_ATOMIC); | ||||||
|  | 	write_unlock(&neigh->lock); | ||||||
|  | 	neigh->ops->solicit(neigh, skb); | ||||||
|  | 	atomic_inc(&neigh->probes); | ||||||
|  | 	kfree_skb(skb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Called when a timer expires for a neighbour entry. */ | /* Called when a timer expires for a neighbour entry. */ | ||||||
| 
 | 
 | ||||||
| static void neigh_timer_handler(unsigned long arg) | static void neigh_timer_handler(unsigned long arg) | ||||||
| @ -920,14 +933,7 @@ static void neigh_timer_handler(unsigned long arg) | |||||||
| 			neigh_hold(neigh); | 			neigh_hold(neigh); | ||||||
| 	} | 	} | ||||||
| 	if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { | 	if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { | ||||||
| 		struct sk_buff *skb = skb_peek(&neigh->arp_queue); | 		neigh_probe(neigh); | ||||||
| 		/* keep skb alive even if arp_queue overflows */ |  | ||||||
| 		if (skb) |  | ||||||
| 			skb = skb_copy(skb, GFP_ATOMIC); |  | ||||||
| 		write_unlock(&neigh->lock); |  | ||||||
| 		neigh->ops->solicit(neigh, skb); |  | ||||||
| 		atomic_inc(&neigh->probes); |  | ||||||
| 		kfree_skb(skb); |  | ||||||
| 	} else { | 	} else { | ||||||
| out: | out: | ||||||
| 		write_unlock(&neigh->lock); | 		write_unlock(&neigh->lock); | ||||||
| @ -942,7 +948,7 @@ out: | |||||||
| int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	int rc; | 	int rc; | ||||||
| 	unsigned long now; | 	bool immediate_probe = false; | ||||||
| 
 | 
 | ||||||
| 	write_lock_bh(&neigh->lock); | 	write_lock_bh(&neigh->lock); | ||||||
| 
 | 
 | ||||||
| @ -950,14 +956,16 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | |||||||
| 	if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) | 	if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) | ||||||
| 		goto out_unlock_bh; | 		goto out_unlock_bh; | ||||||
| 
 | 
 | ||||||
| 	now = jiffies; |  | ||||||
| 
 |  | ||||||
| 	if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { | 	if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { | ||||||
| 		if (neigh->parms->mcast_probes + neigh->parms->app_probes) { | 		if (neigh->parms->mcast_probes + neigh->parms->app_probes) { | ||||||
|  | 			unsigned long next, now = jiffies; | ||||||
|  | 
 | ||||||
| 			atomic_set(&neigh->probes, neigh->parms->ucast_probes); | 			atomic_set(&neigh->probes, neigh->parms->ucast_probes); | ||||||
| 			neigh->nud_state     = NUD_INCOMPLETE; | 			neigh->nud_state     = NUD_INCOMPLETE; | ||||||
| 			neigh->updated = jiffies; | 			neigh->updated = now; | ||||||
| 			neigh_add_timer(neigh, now + 1); | 			next = now + max(neigh->parms->retrans_time, HZ/2); | ||||||
|  | 			neigh_add_timer(neigh, next); | ||||||
|  | 			immediate_probe = true; | ||||||
| 		} else { | 		} else { | ||||||
| 			neigh->nud_state = NUD_FAILED; | 			neigh->nud_state = NUD_FAILED; | ||||||
| 			neigh->updated = jiffies; | 			neigh->updated = jiffies; | ||||||
| @ -989,7 +997,11 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | |||||||
| 		rc = 1; | 		rc = 1; | ||||||
| 	} | 	} | ||||||
| out_unlock_bh: | out_unlock_bh: | ||||||
| 	write_unlock_bh(&neigh->lock); | 	if (immediate_probe) | ||||||
|  | 		neigh_probe(neigh); | ||||||
|  | 	else | ||||||
|  | 		write_unlock(&neigh->lock); | ||||||
|  | 	local_bh_enable(); | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(__neigh_event_send); | EXPORT_SYMBOL(__neigh_event_send); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user