diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-09 04:15:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-08-12 05:55:28 -0400 |
commit | cd28ca0a3dd17c68d24b839602a0e6268ad28b5d (patch) | |
tree | 3e77945743e2c74c36b6942ad5bfe46a07eb9caa | |
parent | 68c3e5a7b91513010d2536e4bcd7bdd54d0e6acf (diff) |
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>
-rw-r--r-- | net/core/neighbour.c | 40 |
1 files changed, 26 insertions, 14 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8fab9b0bb203..4002261f20d1 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c | |||
@@ -844,6 +844,19 @@ static void neigh_invalidate(struct neighbour *neigh) | |||
844 | skb_queue_purge(&neigh->arp_queue); | 844 | skb_queue_purge(&neigh->arp_queue); |
845 | } | 845 | } |
846 | 846 | ||
847 | static void neigh_probe(struct neighbour *neigh) | ||
848 | __releases(neigh->lock) | ||
849 | { | ||
850 | struct sk_buff *skb = skb_peek(&neigh->arp_queue); | ||
851 | /* keep skb alive even if arp_queue overflows */ | ||
852 | if (skb) | ||
853 | skb = skb_copy(skb, GFP_ATOMIC); | ||
854 | write_unlock(&neigh->lock); | ||
855 | neigh->ops->solicit(neigh, skb); | ||
856 | atomic_inc(&neigh->probes); | ||
857 | kfree_skb(skb); | ||
858 | } | ||
859 | |||
847 | /* Called when a timer expires for a neighbour entry. */ | 860 | /* Called when a timer expires for a neighbour entry. */ |
848 | 861 | ||
849 | static void neigh_timer_handler(unsigned long arg) | 862 | static void neigh_timer_handler(unsigned long arg) |
@@ -920,14 +933,7 @@ static void neigh_timer_handler(unsigned long arg) | |||
920 | neigh_hold(neigh); | 933 | neigh_hold(neigh); |
921 | } | 934 | } |
922 | if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { | 935 | if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { |
923 | struct sk_buff *skb = skb_peek(&neigh->arp_queue); | 936 | neigh_probe(neigh); |
924 | /* keep skb alive even if arp_queue overflows */ | ||
925 | if (skb) | ||
926 | skb = skb_copy(skb, GFP_ATOMIC); | ||
927 | write_unlock(&neigh->lock); | ||
928 | neigh->ops->solicit(neigh, skb); | ||
929 | atomic_inc(&neigh->probes); | ||
930 | kfree_skb(skb); | ||
931 | } else { | 937 | } else { |
932 | out: | 938 | out: |
933 | write_unlock(&neigh->lock); | 939 | write_unlock(&neigh->lock); |
@@ -942,7 +948,7 @@ out: | |||
942 | int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | 948 | int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) |
943 | { | 949 | { |
944 | int rc; | 950 | int rc; |
945 | unsigned long now; | 951 | bool immediate_probe = false; |
946 | 952 | ||
947 | write_lock_bh(&neigh->lock); | 953 | write_lock_bh(&neigh->lock); |
948 | 954 | ||
@@ -950,14 +956,16 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | |||
950 | if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) | 956 | if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) |
951 | goto out_unlock_bh; | 957 | goto out_unlock_bh; |
952 | 958 | ||
953 | now = jiffies; | ||
954 | |||
955 | if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { | 959 | if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { |
956 | if (neigh->parms->mcast_probes + neigh->parms->app_probes) { | 960 | if (neigh->parms->mcast_probes + neigh->parms->app_probes) { |
961 | unsigned long next, now = jiffies; | ||
962 | |||
957 | atomic_set(&neigh->probes, neigh->parms->ucast_probes); | 963 | atomic_set(&neigh->probes, neigh->parms->ucast_probes); |
958 | neigh->nud_state = NUD_INCOMPLETE; | 964 | neigh->nud_state = NUD_INCOMPLETE; |
959 | neigh->updated = jiffies; | 965 | neigh->updated = now; |
960 | neigh_add_timer(neigh, now + 1); | 966 | next = now + max(neigh->parms->retrans_time, HZ/2); |
967 | neigh_add_timer(neigh, next); | ||
968 | immediate_probe = true; | ||
961 | } else { | 969 | } else { |
962 | neigh->nud_state = NUD_FAILED; | 970 | neigh->nud_state = NUD_FAILED; |
963 | neigh->updated = jiffies; | 971 | neigh->updated = jiffies; |
@@ -989,7 +997,11 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | |||
989 | rc = 1; | 997 | rc = 1; |
990 | } | 998 | } |
991 | out_unlock_bh: | 999 | out_unlock_bh: |
992 | write_unlock_bh(&neigh->lock); | 1000 | if (immediate_probe) |
1001 | neigh_probe(neigh); | ||
1002 | else | ||
1003 | write_unlock(&neigh->lock); | ||
1004 | local_bh_enable(); | ||
993 | return rc; | 1005 | return rc; |
994 | } | 1006 | } |
995 | EXPORT_SYMBOL(__neigh_event_send); | 1007 | EXPORT_SYMBOL(__neigh_event_send); |