forked from mirrors/linux
		
	net: Do delayed neigh confirmation.
When a dst_confirm() happens, mark the confirmation as pending in the dst. Then on the next packet out, when we have the neigh in-hand, do the update. This removes the dependency in dst_confirm() of dst's having an attached neigh. While we're here, remove the explicit 'dst' NULL check, all except 2 or 3 call sites ensure it's not NULL. So just fix those cases up. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									60d354ebeb
								
							
						
					
					
						commit
						5110effee8
					
				
					 6 changed files with 38 additions and 32 deletions
				
			
		|  | @ -51,7 +51,7 @@ struct dst_entry { | ||||||
| 	int			(*input)(struct sk_buff *); | 	int			(*input)(struct sk_buff *); | ||||||
| 	int			(*output)(struct sk_buff *); | 	int			(*output)(struct sk_buff *); | ||||||
| 
 | 
 | ||||||
| 	int			flags; | 	unsigned short		flags; | ||||||
| #define DST_HOST		0x0001 | #define DST_HOST		0x0001 | ||||||
| #define DST_NOXFRM		0x0002 | #define DST_NOXFRM		0x0002 | ||||||
| #define DST_NOPOLICY		0x0004 | #define DST_NOPOLICY		0x0004 | ||||||
|  | @ -62,6 +62,8 @@ struct dst_entry { | ||||||
| #define DST_FAKE_RTABLE		0x0080 | #define DST_FAKE_RTABLE		0x0080 | ||||||
| #define DST_XFRM_TUNNEL		0x0100 | #define DST_XFRM_TUNNEL		0x0100 | ||||||
| 
 | 
 | ||||||
|  | 	unsigned short		pending_confirm; | ||||||
|  | 
 | ||||||
| 	short			error; | 	short			error; | ||||||
| 	short			obsolete; | 	short			obsolete; | ||||||
| 	unsigned short		header_len;	/* more space at head required */ | 	unsigned short		header_len;	/* more space at head required */ | ||||||
|  | @ -371,7 +373,8 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb) | ||||||
| 
 | 
 | ||||||
| extern int dst_discard(struct sk_buff *skb); | extern int dst_discard(struct sk_buff *skb); | ||||||
| extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev, | extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev, | ||||||
| 		       int initial_ref, int initial_obsolete, int flags); | 		       int initial_ref, int initial_obsolete, | ||||||
|  | 		       unsigned short flags); | ||||||
| extern void __dst_free(struct dst_entry *dst); | extern void __dst_free(struct dst_entry *dst); | ||||||
| extern struct dst_entry *dst_destroy(struct dst_entry *dst); | extern struct dst_entry *dst_destroy(struct dst_entry *dst); | ||||||
| 
 | 
 | ||||||
|  | @ -395,14 +398,24 @@ static inline void dst_rcu_free(struct rcu_head *head) | ||||||
| 
 | 
 | ||||||
| static inline void dst_confirm(struct dst_entry *dst) | static inline void dst_confirm(struct dst_entry *dst) | ||||||
| { | { | ||||||
| 	if (dst) { | 	dst->pending_confirm = 1; | ||||||
| 		struct neighbour *n; |  | ||||||
| 
 |  | ||||||
| 		rcu_read_lock(); |  | ||||||
| 		n = dst_get_neighbour_noref(dst); |  | ||||||
| 		neigh_confirm(n); |  | ||||||
| 		rcu_read_unlock(); |  | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, | ||||||
|  | 				   struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hh_cache *hh; | ||||||
|  | 
 | ||||||
|  | 	if (unlikely(dst->pending_confirm)) { | ||||||
|  | 		n->confirmed = jiffies; | ||||||
|  | 		dst->pending_confirm = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hh = &n->hh; | ||||||
|  | 	if ((n->nud_state & NUD_CONNECTED) && hh->hh_len) | ||||||
|  | 		return neigh_hh_output(hh, skb); | ||||||
|  | 	else | ||||||
|  | 		return n->output(n, skb); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline struct neighbour *dst_neigh_lookup(const struct dst_entry *dst, const void *daddr) | static inline struct neighbour *dst_neigh_lookup(const struct dst_entry *dst, const void *daddr) | ||||||
|  |  | ||||||
|  | @ -309,12 +309,6 @@ static inline struct neighbour * neigh_clone(struct neighbour *neigh) | ||||||
| 
 | 
 | ||||||
| #define neigh_hold(n)	atomic_inc(&(n)->refcnt) | #define neigh_hold(n)	atomic_inc(&(n)->refcnt) | ||||||
| 
 | 
 | ||||||
| static inline void neigh_confirm(struct neighbour *neigh) |  | ||||||
| { |  | ||||||
| 	if (neigh) |  | ||||||
| 		neigh->confirmed = jiffies; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	unsigned long now = jiffies; | 	unsigned long now = jiffies; | ||||||
|  | @ -358,15 +352,6 @@ static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) | ||||||
| 	return dev_queue_xmit(skb); | 	return dev_queue_xmit(skb); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline int neigh_output(struct neighbour *n, struct sk_buff *skb) |  | ||||||
| { |  | ||||||
| 	struct hh_cache *hh = &n->hh; |  | ||||||
| 	if ((n->nud_state & NUD_CONNECTED) && hh->hh_len) |  | ||||||
| 		return neigh_hh_output(hh, skb); |  | ||||||
| 	else |  | ||||||
| 		return n->output(n, skb); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline struct neighbour * | static inline struct neighbour * | ||||||
| __neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat) | __neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -152,7 +152,7 @@ EXPORT_SYMBOL(dst_discard); | ||||||
| const u32 dst_default_metrics[RTAX_MAX]; | const u32 dst_default_metrics[RTAX_MAX]; | ||||||
| 
 | 
 | ||||||
| void *dst_alloc(struct dst_ops *ops, struct net_device *dev, | void *dst_alloc(struct dst_ops *ops, struct net_device *dev, | ||||||
| 		int initial_ref, int initial_obsolete, int flags) | 		int initial_ref, int initial_obsolete, unsigned short flags) | ||||||
| { | { | ||||||
| 	struct dst_entry *dst; | 	struct dst_entry *dst; | ||||||
| 
 | 
 | ||||||
|  | @ -188,6 +188,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, | ||||||
| 	dst->__use = 0; | 	dst->__use = 0; | ||||||
| 	dst->lastuse = jiffies; | 	dst->lastuse = jiffies; | ||||||
| 	dst->flags = flags; | 	dst->flags = flags; | ||||||
|  | 	dst->pending_confirm = 0; | ||||||
| 	dst->next = NULL; | 	dst->next = NULL; | ||||||
| 	if (!(flags & DST_NOCOUNT)) | 	if (!(flags & DST_NOCOUNT)) | ||||||
| 		dst_entries_add(ops, 1); | 		dst_entries_add(ops, 1); | ||||||
|  |  | ||||||
|  | @ -198,7 +198,7 @@ static inline int ip_finish_output2(struct sk_buff *skb) | ||||||
| 	if (unlikely(!neigh)) | 	if (unlikely(!neigh)) | ||||||
| 		neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); | 		neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); | ||||||
| 	if (neigh) { | 	if (neigh) { | ||||||
| 		int res = neigh_output(neigh, skb); | 		int res = dst_neigh_output(dst, neigh, skb); | ||||||
| 
 | 
 | ||||||
| 		rcu_read_unlock_bh(); | 		rcu_read_unlock_bh(); | ||||||
| 		return res; | 		return res; | ||||||
|  |  | ||||||
|  | @ -740,13 +740,13 @@ void tcp_update_metrics(struct sock *sk) | ||||||
| 	if (sysctl_tcp_nometrics_save) | 	if (sysctl_tcp_nometrics_save) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	dst_confirm(dst); |  | ||||||
| 
 |  | ||||||
| 	if (dst && (dst->flags & DST_HOST)) { | 	if (dst && (dst->flags & DST_HOST)) { | ||||||
| 		const struct inet_connection_sock *icsk = inet_csk(sk); | 		const struct inet_connection_sock *icsk = inet_csk(sk); | ||||||
| 		int m; | 		int m; | ||||||
| 		unsigned long rtt; | 		unsigned long rtt; | ||||||
| 
 | 
 | ||||||
|  | 		dst_confirm(dst); | ||||||
|  | 
 | ||||||
| 		if (icsk->icsk_backoff || !tp->srtt) { | 		if (icsk->icsk_backoff || !tp->srtt) { | ||||||
| 			/* This session failed to estimate rtt. Why?
 | 			/* This session failed to estimate rtt. Why?
 | ||||||
| 			 * Probably, no packets returned in time. | 			 * Probably, no packets returned in time. | ||||||
|  | @ -3869,9 +3869,11 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) | ||||||
| 			tcp_cong_avoid(sk, ack, prior_in_flight); | 			tcp_cong_avoid(sk, ack, prior_in_flight); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) | 	if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) { | ||||||
| 		dst_confirm(__sk_dst_get(sk)); | 		struct dst_entry *dst = __sk_dst_get(sk); | ||||||
| 
 | 		if (dst) | ||||||
|  | 			dst_confirm(dst); | ||||||
|  | 	} | ||||||
| 	return 1; | 	return 1; | ||||||
| 
 | 
 | ||||||
| no_queue: | no_queue: | ||||||
|  | @ -6140,9 +6142,14 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | ||||||
| 
 | 
 | ||||||
| 		case TCP_FIN_WAIT1: | 		case TCP_FIN_WAIT1: | ||||||
| 			if (tp->snd_una == tp->write_seq) { | 			if (tp->snd_una == tp->write_seq) { | ||||||
|  | 				struct dst_entry *dst; | ||||||
|  | 
 | ||||||
| 				tcp_set_state(sk, TCP_FIN_WAIT2); | 				tcp_set_state(sk, TCP_FIN_WAIT2); | ||||||
| 				sk->sk_shutdown |= SEND_SHUTDOWN; | 				sk->sk_shutdown |= SEND_SHUTDOWN; | ||||||
| 				dst_confirm(__sk_dst_get(sk)); | 
 | ||||||
|  | 				dst = __sk_dst_get(sk); | ||||||
|  | 				if (dst) | ||||||
|  | 					dst_confirm(dst); | ||||||
| 
 | 
 | ||||||
| 				if (!sock_flag(sk, SOCK_DEAD)) | 				if (!sock_flag(sk, SOCK_DEAD)) | ||||||
| 					/* Wake up lingering close() */ | 					/* Wake up lingering close() */ | ||||||
|  |  | ||||||
|  | @ -125,7 +125,7 @@ static int ip6_finish_output2(struct sk_buff *skb) | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
| 	neigh = dst_get_neighbour_noref(dst); | 	neigh = dst_get_neighbour_noref(dst); | ||||||
| 	if (neigh) { | 	if (neigh) { | ||||||
| 		int res = neigh_output(neigh, skb); | 		int res = dst_neigh_output(dst, neigh, skb); | ||||||
| 
 | 
 | ||||||
| 		rcu_read_unlock(); | 		rcu_read_unlock(); | ||||||
| 		return res; | 		return res; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 David S. Miller
						David S. Miller