neigh: reduce arp latency
authorEric Dumazet <eric.dumazet@gmail.com>
Tue, 9 Aug 2011 08:15:58 +0000 (08:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Aug 2011 09:55:28 +0000 (02:55 -0700)
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>
net/core/neighbour.c

index 8fab9b0..4002261 100644 (file)
@@ -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 @@ out:
 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);