diff options
Diffstat (limited to 'net/core/sock.c')
| -rw-r--r-- | net/core/sock.c | 49 | 
1 files changed, 40 insertions, 9 deletions
diff --git a/net/core/sock.c b/net/core/sock.c index b0ba569bc97..bbb25be7ddf 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -631,7 +631,7 @@ set_rcvbuf:  	case SO_TIMESTAMPING:  		if (val & ~SOF_TIMESTAMPING_MASK) { -			ret = EINVAL; +			ret = -EINVAL;  			break;  		}  		sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE, @@ -919,13 +919,19 @@ static inline void sock_lock_init(struct sock *sk)  			af_family_keys + sk->sk_family);  } +/* + * Copy all fields from osk to nsk but nsk->sk_refcnt must not change yet, + * even temporarly, because of RCU lookups. sk_node should also be left as is. + */  static void sock_copy(struct sock *nsk, const struct sock *osk)  {  #ifdef CONFIG_SECURITY_NETWORK  	void *sptr = nsk->sk_security;  #endif - -	memcpy(nsk, osk, osk->sk_prot->obj_size); +	BUILD_BUG_ON(offsetof(struct sock, sk_copy_start) != +		     sizeof(osk->sk_node) + sizeof(osk->sk_refcnt)); +	memcpy(&nsk->sk_copy_start, &osk->sk_copy_start, +	       osk->sk_prot->obj_size - offsetof(struct sock, sk_copy_start));  #ifdef CONFIG_SECURITY_NETWORK  	nsk->sk_security = sptr;  	security_sk_clone(osk, nsk); @@ -939,8 +945,23 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,  	struct kmem_cache *slab;  	slab = prot->slab; -	if (slab != NULL) -		sk = kmem_cache_alloc(slab, priority); +	if (slab != NULL) { +		sk = kmem_cache_alloc(slab, priority & ~__GFP_ZERO); +		if (!sk) +			return sk; +		if (priority & __GFP_ZERO) { +			/* +			 * caches using SLAB_DESTROY_BY_RCU should let +			 * sk_node.next un-modified. Special care is taken +			 * when initializing object to zero. +			 */ +			if (offsetof(struct sock, sk_node.next) != 0) +				memset(sk, 0, offsetof(struct sock, sk_node.next)); +			memset(&sk->sk_node.pprev, 0, +			       prot->obj_size - offsetof(struct sock, +							 sk_node.pprev)); +		} +	}  	else  		sk = kmalloc(prot->obj_size, priority); @@ -1125,6 +1146,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)  		newsk->sk_err	   = 0;  		newsk->sk_priority = 0; +		/* +		 * Before updating sk_refcnt, we must commit prior changes to memory +		 * (Documentation/RCU/rculist_nulls.txt for details) +		 */ +		smp_wmb();  		atomic_set(&newsk->sk_refcnt, 2);  		/* @@ -1715,7 +1741,7 @@ EXPORT_SYMBOL(sock_no_sendpage);  static void sock_def_wakeup(struct sock *sk)  {  	read_lock(&sk->sk_callback_lock); -	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) +	if (sk_has_sleeper(sk))  		wake_up_interruptible_all(sk->sk_sleep);  	read_unlock(&sk->sk_callback_lock);  } @@ -1723,7 +1749,7 @@ static void sock_def_wakeup(struct sock *sk)  static void sock_def_error_report(struct sock *sk)  {  	read_lock(&sk->sk_callback_lock); -	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) +	if (sk_has_sleeper(sk))  		wake_up_interruptible_poll(sk->sk_sleep, POLLERR);  	sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR);  	read_unlock(&sk->sk_callback_lock); @@ -1732,7 +1758,7 @@ static void sock_def_error_report(struct sock *sk)  static void sock_def_readable(struct sock *sk, int len)  {  	read_lock(&sk->sk_callback_lock); -	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) +	if (sk_has_sleeper(sk))  		wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN |  						POLLRDNORM | POLLRDBAND);  	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); @@ -1747,7 +1773,7 @@ static void sock_def_write_space(struct sock *sk)  	 * progress.  --DaveM  	 */  	if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) { -		if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) +		if (sk_has_sleeper(sk))  			wake_up_interruptible_sync_poll(sk->sk_sleep, POLLOUT |  						POLLWRNORM | POLLWRBAND); @@ -1840,6 +1866,11 @@ void sock_init_data(struct socket *sock, struct sock *sk)  	sk->sk_stamp = ktime_set(-1L, 0); +	/* +	 * Before updating sk_refcnt, we must commit prior changes to memory +	 * (Documentation/RCU/rculist_nulls.txt for details) +	 */ +	smp_wmb();  	atomic_set(&sk->sk_refcnt, 1);  	atomic_set(&sk->sk_wmem_alloc, 1);  	atomic_set(&sk->sk_drops, 0);  |