forked from mirrors/linux
		
	inet: move inet->recverr to inet->inet_flags
IP_RECVERR socket option can now be set/get without locking the socket. This patch potentially avoid data-races around inet->recverr. Signed-off-by: Eric Dumazet <edumazet@google.com> Acked-by: Soheil Hassas Yeganeh <soheil@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									b4d84bce4c
								
							
						
					
					
						commit
						6b5f43ea08
					
				
					 9 changed files with 30 additions and 32 deletions
				
			
		|  | @ -230,8 +230,7 @@ struct inet_sock { | |||
| 	__u8			min_ttl; | ||||
| 	__u8			mc_ttl; | ||||
| 	__u8			pmtudisc; | ||||
| 	__u8			recverr:1, | ||||
| 				is_icsk:1, | ||||
| 	__u8			is_icsk:1, | ||||
| 				freebind:1, | ||||
| 				hdrincl:1, | ||||
| 				mc_loop:1, | ||||
|  | @ -270,6 +269,8 @@ enum { | |||
| 	INET_FLAGS_ORIGDSTADDR	= 6, | ||||
| 	INET_FLAGS_CHECKSUM	= 7, | ||||
| 	INET_FLAGS_RECVFRAGSIZE	= 8, | ||||
| 
 | ||||
| 	INET_FLAGS_RECVERR	= 9, | ||||
| }; | ||||
| 
 | ||||
| /* cmsg flags for inet */ | ||||
|  |  | |||
|  | @ -247,7 +247,6 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info) | |||
| 	const u8 offset = iph->ihl << 2; | ||||
| 	const struct dccp_hdr *dh; | ||||
| 	struct dccp_sock *dp; | ||||
| 	struct inet_sock *inet; | ||||
| 	const int type = icmp_hdr(skb)->type; | ||||
| 	const int code = icmp_hdr(skb)->code; | ||||
| 	struct sock *sk; | ||||
|  | @ -361,8 +360,7 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info) | |||
| 	 *							--ANK (980905) | ||||
| 	 */ | ||||
| 
 | ||||
| 	inet = inet_sk(sk); | ||||
| 	if (!sock_owned_by_user(sk) && inet->recverr) { | ||||
| 	if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) { | ||||
| 		sk->sk_err = err; | ||||
| 		sk_error_report(sk); | ||||
| 	} else { /* Only an error on timeout */ | ||||
|  |  | |||
|  | @ -182,7 +182,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, | |||
| 	r->idiag_inode = sock_i_ino(sk); | ||||
| 
 | ||||
| 	memset(&inet_sockopt, 0, sizeof(inet_sockopt)); | ||||
| 	inet_sockopt.recverr	= inet->recverr; | ||||
| 	inet_sockopt.recverr	= inet_test_bit(RECVERR, sk); | ||||
| 	inet_sockopt.is_icsk	= inet->is_icsk; | ||||
| 	inet_sockopt.freebind	= inet->freebind; | ||||
| 	inet_sockopt.hdrincl	= inet->hdrincl; | ||||
|  |  | |||
|  | @ -446,12 +446,11 @@ EXPORT_SYMBOL_GPL(ip_icmp_error); | |||
| 
 | ||||
| void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info) | ||||
| { | ||||
| 	struct inet_sock *inet = inet_sk(sk); | ||||
| 	struct sock_exterr_skb *serr; | ||||
| 	struct iphdr *iph; | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	if (!inet->recverr) | ||||
| 	if (!inet_test_bit(RECVERR, sk)) | ||||
| 		return; | ||||
| 
 | ||||
| 	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); | ||||
|  | @ -617,9 +616,7 @@ EXPORT_SYMBOL(ip_sock_set_freebind); | |||
| 
 | ||||
| void ip_sock_set_recverr(struct sock *sk) | ||||
| { | ||||
| 	lock_sock(sk); | ||||
| 	inet_sk(sk)->recverr = true; | ||||
| 	release_sock(sk); | ||||
| 	inet_set_bit(RECVERR, sk); | ||||
| } | ||||
| EXPORT_SYMBOL(ip_sock_set_recverr); | ||||
| 
 | ||||
|  | @ -978,6 +975,11 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, | |||
| 			return -EINVAL; | ||||
| 		inet_assign_bit(RECVFRAGSIZE, sk, val); | ||||
| 		return 0; | ||||
| 	case IP_RECVERR: | ||||
| 		inet_assign_bit(RECVERR, sk, val); | ||||
| 		if (!val) | ||||
| 			skb_queue_purge(&sk->sk_error_queue); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	err = 0; | ||||
|  | @ -1064,11 +1066,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, | |||
| 			goto e_inval; | ||||
| 		inet->pmtudisc = val; | ||||
| 		break; | ||||
| 	case IP_RECVERR: | ||||
| 		inet->recverr = !!val; | ||||
| 		if (!val) | ||||
| 			skb_queue_purge(&sk->sk_error_queue); | ||||
| 		break; | ||||
| 	case IP_RECVERR_RFC4884: | ||||
| 		if (val < 0 || val > 1) | ||||
| 			goto e_inval; | ||||
|  | @ -1575,6 +1572,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
| 	case IP_RECVFRAGSIZE: | ||||
| 		val = inet_test_bit(RECVFRAGSIZE, sk); | ||||
| 		goto copyval; | ||||
| 	case IP_RECVERR: | ||||
| 		val = inet_test_bit(RECVERR, sk); | ||||
| 		goto copyval; | ||||
| 	} | ||||
| 
 | ||||
| 	if (needs_rtnl) | ||||
|  | @ -1649,9 +1649,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 	case IP_RECVERR: | ||||
| 		val = inet->recverr; | ||||
| 		break; | ||||
| 	case IP_RECVERR_RFC4884: | ||||
| 		val = inet->recverr_rfc4884; | ||||
| 		break; | ||||
|  |  | |||
|  | @ -580,7 +580,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info) | |||
| 	 *      RFC1122: OK.  Passes ICMP errors back to application, as per | ||||
| 	 *	4.1.3.3. | ||||
| 	 */ | ||||
| 	if ((family == AF_INET && !inet_sock->recverr) || | ||||
| 	if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || | ||||
| 	    (family == AF_INET6 && !inet6_sk(sk)->recverr)) { | ||||
| 		if (!harderr || sk->sk_state != TCP_ESTABLISHED) | ||||
| 			goto out; | ||||
|  |  | |||
|  | @ -203,8 +203,9 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) | |||
| 	struct inet_sock *inet = inet_sk(sk); | ||||
| 	const int type = icmp_hdr(skb)->type; | ||||
| 	const int code = icmp_hdr(skb)->code; | ||||
| 	int err = 0; | ||||
| 	int harderr = 0; | ||||
| 	bool recverr; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) | ||||
| 		ipv4_sk_update_pmtu(skb, sk, info); | ||||
|  | @ -218,7 +219,8 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) | |||
| 	   2. Socket is connected (otherwise the error indication | ||||
| 	      is useless without ip_recverr and error is hard. | ||||
| 	 */ | ||||
| 	if (!inet->recverr && sk->sk_state != TCP_ESTABLISHED) | ||||
| 	recverr = inet_test_bit(RECVERR, sk); | ||||
| 	if (!recverr && sk->sk_state != TCP_ESTABLISHED) | ||||
| 		return; | ||||
| 
 | ||||
| 	switch (type) { | ||||
|  | @ -245,7 +247,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (inet->recverr) { | ||||
| 	if (recverr) { | ||||
| 		const struct iphdr *iph = (const struct iphdr *)skb->data; | ||||
| 		u8 *payload = skb->data + (iph->ihl << 2); | ||||
| 
 | ||||
|  | @ -254,7 +256,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) | |||
| 		ip_icmp_error(sk, skb, err, 0, info, payload); | ||||
| 	} | ||||
| 
 | ||||
| 	if (inet->recverr || harderr) { | ||||
| 	if (recverr || harderr) { | ||||
| 		sk->sk_err = err; | ||||
| 		sk_error_report(sk); | ||||
| 	} | ||||
|  | @ -413,7 +415,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, | |||
| 	kfree_skb(skb); | ||||
| error: | ||||
| 	IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); | ||||
| 	if (err == -ENOBUFS && !inet->recverr) | ||||
| 	if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk)) | ||||
| 		err = 0; | ||||
| 	return err; | ||||
| } | ||||
|  | @ -645,7 +647,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
| 			ip_flush_pending_frames(sk); | ||||
| 		else if (!(msg->msg_flags & MSG_MORE)) { | ||||
| 			err = ip_push_pending_frames(sk, &fl4); | ||||
| 			if (err == -ENOBUFS && !inet->recverr) | ||||
| 			if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk)) | ||||
| 				err = 0; | ||||
| 		} | ||||
| 		release_sock(sk); | ||||
|  |  | |||
|  | @ -477,7 +477,6 @@ int tcp_v4_err(struct sk_buff *skb, u32 info) | |||
| 	const struct iphdr *iph = (const struct iphdr *)skb->data; | ||||
| 	struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); | ||||
| 	struct tcp_sock *tp; | ||||
| 	struct inet_sock *inet; | ||||
| 	const int type = icmp_hdr(skb)->type; | ||||
| 	const int code = icmp_hdr(skb)->code; | ||||
| 	struct sock *sk; | ||||
|  | @ -625,8 +624,8 @@ int tcp_v4_err(struct sk_buff *skb, u32 info) | |||
| 	 *							--ANK (980905) | ||||
| 	 */ | ||||
| 
 | ||||
| 	inet = inet_sk(sk); | ||||
| 	if (!sock_owned_by_user(sk) && inet->recverr) { | ||||
| 	if (!sock_owned_by_user(sk) && | ||||
| 	    inet_test_bit(RECVERR, sk)) { | ||||
| 		WRITE_ONCE(sk->sk_err, err); | ||||
| 		sk_error_report(sk); | ||||
| 	} else	{ /* Only an error on timeout */ | ||||
|  |  | |||
|  | @ -779,7 +779,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
| 						  (u8 *)(uh+1)); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (!inet->recverr) { | ||||
| 	if (!inet_test_bit(RECVERR, sk)) { | ||||
| 		if (!harderr || sk->sk_state != TCP_ESTABLISHED) | ||||
| 			goto out; | ||||
| 	} else | ||||
|  | @ -962,7 +962,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4, | |||
| send: | ||||
| 	err = ip_send_skb(sock_net(sk), skb); | ||||
| 	if (err) { | ||||
| 		if (err == -ENOBUFS && !inet->recverr) { | ||||
| 		if (err == -ENOBUFS && | ||||
| 		    !inet_test_bit(RECVERR, sk)) { | ||||
| 			UDP_INC_STATS(sock_net(sk), | ||||
| 				      UDP_MIB_SNDBUFERRORS, is_udplite); | ||||
| 			err = 0; | ||||
|  |  | |||
|  | @ -581,7 +581,7 @@ static void sctp_v4_err_handle(struct sctp_transport *t, struct sk_buff *skb, | |||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!sock_owned_by_user(sk) && inet_sk(sk)->recverr) { | ||||
| 	if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) { | ||||
| 		sk->sk_err = err; | ||||
| 		sk_error_report(sk); | ||||
| 	} else {  /* Only an error on timeout */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet