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
1 changed files with 26 additions and 14 deletions
|
@ -844,6 +844,19 @@ static void neigh_invalidate(struct neighbour *neigh)
|
|||
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. */
|
||||
|
||||
static void neigh_timer_handler(unsigned long arg)
|
||||
|
@ -920,14 +933,7 @@ static void neigh_timer_handler(unsigned long arg)
|
|||
neigh_hold(neigh);
|
||||
}
|
||||
if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
|
||||
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);
|
||||
neigh_probe(neigh);
|
||||
} else {
|
||||
out:
|
||||
write_unlock(&neigh->lock);
|
||||
|
@ -942,7 +948,7 @@ static void neigh_timer_handler(unsigned long arg)
|
|||
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
||||
{
|
||||
int rc;
|
||||
unsigned long now;
|
||||
bool immediate_probe = false;
|
||||
|
||||
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))
|
||||
goto out_unlock_bh;
|
||||
|
||||
now = jiffies;
|
||||
|
||||
if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
|
||||
if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
|
||||
unsigned long next, now = jiffies;
|
||||
|
||||
atomic_set(&neigh->probes, neigh->parms->ucast_probes);
|
||||
neigh->nud_state = NUD_INCOMPLETE;
|
||||
neigh->updated = jiffies;
|
||||
neigh_add_timer(neigh, now + 1);
|
||||
neigh->updated = now;
|
||||
next = now + max(neigh->parms->retrans_time, HZ/2);
|
||||
neigh_add_timer(neigh, next);
|
||||
immediate_probe = true;
|
||||
} else {
|
||||
neigh->nud_state = NUD_FAILED;
|
||||
neigh->updated = jiffies;
|
||||
|
@ -989,7 +997,11 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
|||
rc = 1;
|
||||
}
|
||||
out_unlock_bh:
|
||||
write_unlock_bh(&neigh->lock);
|
||||
if (immediate_probe)
|
||||
neigh_probe(neigh);
|
||||
else
|
||||
write_unlock(&neigh->lock);
|
||||
local_bh_enable();
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(__neigh_event_send);
|
||||
|
|
Loading…
Reference in a new issue