Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd28ca0a authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 68c3e5a7
Loading
Loading
Loading
Loading
+26 −14
Original line number Original line Diff line number Diff line
@@ -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 @@ static void neigh_timer_handler(unsigned long arg)
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);