diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-02-17 22:26:36 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-02-22 13:19:31 -0500 |
commit | eaefd1105bc431ef329599e307a07f2a36ae7872 (patch) | |
tree | 658eeed417654c8f6015d4d5f957f5a1b5e9521f | |
parent | 04cfa852ff8dab923640500ee850d19e75bacabc (diff) |
net: add __rcu annotations to sk_wq and wq
Add proper RCU annotations/verbs to sk_wq and wq members
Fix __sctp_write_space() sk_sleep() abuse (and sock->wq access)
Fix sunrpc sk_sleep() abuse too
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/net.h | 3 | ||||
-rw-r--r-- | include/net/sock.h | 7 | ||||
-rw-r--r-- | net/sctp/socket.c | 9 | ||||
-rw-r--r-- | net/socket.c | 23 | ||||
-rw-r--r-- | net/sunrpc/svcsock.c | 32 | ||||
-rw-r--r-- | net/unix/af_unix.c | 2 |
6 files changed, 46 insertions, 30 deletions
diff --git a/include/linux/net.h b/include/linux/net.h index 16faa130088c..94de83c0f877 100644 --- a/include/linux/net.h +++ b/include/linux/net.h | |||
@@ -118,6 +118,7 @@ enum sock_shutdown_cmd { | |||
118 | }; | 118 | }; |
119 | 119 | ||
120 | struct socket_wq { | 120 | struct socket_wq { |
121 | /* Note: wait MUST be first field of socket_wq */ | ||
121 | wait_queue_head_t wait; | 122 | wait_queue_head_t wait; |
122 | struct fasync_struct *fasync_list; | 123 | struct fasync_struct *fasync_list; |
123 | struct rcu_head rcu; | 124 | struct rcu_head rcu; |
@@ -142,7 +143,7 @@ struct socket { | |||
142 | 143 | ||
143 | unsigned long flags; | 144 | unsigned long flags; |
144 | 145 | ||
145 | struct socket_wq *wq; | 146 | struct socket_wq __rcu *wq; |
146 | 147 | ||
147 | struct file *file; | 148 | struct file *file; |
148 | struct sock *sk; | 149 | struct sock *sk; |
diff --git a/include/net/sock.h b/include/net/sock.h index e3893a2b5d25..da0534d3401c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -281,7 +281,7 @@ struct sock { | |||
281 | int sk_rcvbuf; | 281 | int sk_rcvbuf; |
282 | 282 | ||
283 | struct sk_filter __rcu *sk_filter; | 283 | struct sk_filter __rcu *sk_filter; |
284 | struct socket_wq *sk_wq; | 284 | struct socket_wq __rcu *sk_wq; |
285 | 285 | ||
286 | #ifdef CONFIG_NET_DMA | 286 | #ifdef CONFIG_NET_DMA |
287 | struct sk_buff_head sk_async_wait_queue; | 287 | struct sk_buff_head sk_async_wait_queue; |
@@ -1266,7 +1266,8 @@ static inline void sk_set_socket(struct sock *sk, struct socket *sock) | |||
1266 | 1266 | ||
1267 | static inline wait_queue_head_t *sk_sleep(struct sock *sk) | 1267 | static inline wait_queue_head_t *sk_sleep(struct sock *sk) |
1268 | { | 1268 | { |
1269 | return &sk->sk_wq->wait; | 1269 | BUILD_BUG_ON(offsetof(struct socket_wq, wait) != 0); |
1270 | return &rcu_dereference_raw(sk->sk_wq)->wait; | ||
1270 | } | 1271 | } |
1271 | /* Detach socket from process context. | 1272 | /* Detach socket from process context. |
1272 | * Announce socket dead, detach it from wait queue and inode. | 1273 | * Announce socket dead, detach it from wait queue and inode. |
@@ -1287,7 +1288,7 @@ static inline void sock_orphan(struct sock *sk) | |||
1287 | static inline void sock_graft(struct sock *sk, struct socket *parent) | 1288 | static inline void sock_graft(struct sock *sk, struct socket *parent) |
1288 | { | 1289 | { |
1289 | write_lock_bh(&sk->sk_callback_lock); | 1290 | write_lock_bh(&sk->sk_callback_lock); |
1290 | rcu_assign_pointer(sk->sk_wq, parent->wq); | 1291 | sk->sk_wq = parent->wq; |
1291 | parent->sk = sk; | 1292 | parent->sk = sk; |
1292 | sk_set_socket(sk, parent); | 1293 | sk_set_socket(sk, parent); |
1293 | security_sock_graft(sk, parent); | 1294 | security_sock_graft(sk, parent); |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8e02550ff3e8..b53b2ebbb198 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -6102,15 +6102,16 @@ static void __sctp_write_space(struct sctp_association *asoc) | |||
6102 | wake_up_interruptible(&asoc->wait); | 6102 | wake_up_interruptible(&asoc->wait); |
6103 | 6103 | ||
6104 | if (sctp_writeable(sk)) { | 6104 | if (sctp_writeable(sk)) { |
6105 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 6105 | wait_queue_head_t *wq = sk_sleep(sk); |
6106 | wake_up_interruptible(sk_sleep(sk)); | 6106 | |
6107 | if (wq && waitqueue_active(wq)) | ||
6108 | wake_up_interruptible(wq); | ||
6107 | 6109 | ||
6108 | /* Note that we try to include the Async I/O support | 6110 | /* Note that we try to include the Async I/O support |
6109 | * here by modeling from the current TCP/UDP code. | 6111 | * here by modeling from the current TCP/UDP code. |
6110 | * We have not tested with it yet. | 6112 | * We have not tested with it yet. |
6111 | */ | 6113 | */ |
6112 | if (sock->wq->fasync_list && | 6114 | if (!(sk->sk_shutdown & SEND_SHUTDOWN)) |
6113 | !(sk->sk_shutdown & SEND_SHUTDOWN)) | ||
6114 | sock_wake_async(sock, | 6115 | sock_wake_async(sock, |
6115 | SOCK_WAKE_SPACE, POLL_OUT); | 6116 | SOCK_WAKE_SPACE, POLL_OUT); |
6116 | } | 6117 | } |
diff --git a/net/socket.c b/net/socket.c index ac2219f90d5d..9fa1e3b4366e 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -240,17 +240,19 @@ static struct kmem_cache *sock_inode_cachep __read_mostly; | |||
240 | static struct inode *sock_alloc_inode(struct super_block *sb) | 240 | static struct inode *sock_alloc_inode(struct super_block *sb) |
241 | { | 241 | { |
242 | struct socket_alloc *ei; | 242 | struct socket_alloc *ei; |
243 | struct socket_wq *wq; | ||
243 | 244 | ||
244 | ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); | 245 | ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); |
245 | if (!ei) | 246 | if (!ei) |
246 | return NULL; | 247 | return NULL; |
247 | ei->socket.wq = kmalloc(sizeof(struct socket_wq), GFP_KERNEL); | 248 | wq = kmalloc(sizeof(*wq), GFP_KERNEL); |
248 | if (!ei->socket.wq) { | 249 | if (!wq) { |
249 | kmem_cache_free(sock_inode_cachep, ei); | 250 | kmem_cache_free(sock_inode_cachep, ei); |
250 | return NULL; | 251 | return NULL; |
251 | } | 252 | } |
252 | init_waitqueue_head(&ei->socket.wq->wait); | 253 | init_waitqueue_head(&wq->wait); |
253 | ei->socket.wq->fasync_list = NULL; | 254 | wq->fasync_list = NULL; |
255 | RCU_INIT_POINTER(ei->socket.wq, wq); | ||
254 | 256 | ||
255 | ei->socket.state = SS_UNCONNECTED; | 257 | ei->socket.state = SS_UNCONNECTED; |
256 | ei->socket.flags = 0; | 258 | ei->socket.flags = 0; |
@@ -273,9 +275,11 @@ static void wq_free_rcu(struct rcu_head *head) | |||
273 | static void sock_destroy_inode(struct inode *inode) | 275 | static void sock_destroy_inode(struct inode *inode) |
274 | { | 276 | { |
275 | struct socket_alloc *ei; | 277 | struct socket_alloc *ei; |
278 | struct socket_wq *wq; | ||
276 | 279 | ||
277 | ei = container_of(inode, struct socket_alloc, vfs_inode); | 280 | ei = container_of(inode, struct socket_alloc, vfs_inode); |
278 | call_rcu(&ei->socket.wq->rcu, wq_free_rcu); | 281 | wq = rcu_dereference_protected(ei->socket.wq, 1); |
282 | call_rcu(&wq->rcu, wq_free_rcu); | ||
279 | kmem_cache_free(sock_inode_cachep, ei); | 283 | kmem_cache_free(sock_inode_cachep, ei); |
280 | } | 284 | } |
281 | 285 | ||
@@ -524,7 +528,7 @@ void sock_release(struct socket *sock) | |||
524 | module_put(owner); | 528 | module_put(owner); |
525 | } | 529 | } |
526 | 530 | ||
527 | if (sock->wq->fasync_list) | 531 | if (rcu_dereference_protected(sock->wq, 1)->fasync_list) |
528 | printk(KERN_ERR "sock_release: fasync list not empty!\n"); | 532 | printk(KERN_ERR "sock_release: fasync list not empty!\n"); |
529 | 533 | ||
530 | percpu_sub(sockets_in_use, 1); | 534 | percpu_sub(sockets_in_use, 1); |
@@ -1108,15 +1112,16 @@ static int sock_fasync(int fd, struct file *filp, int on) | |||
1108 | { | 1112 | { |
1109 | struct socket *sock = filp->private_data; | 1113 | struct socket *sock = filp->private_data; |
1110 | struct sock *sk = sock->sk; | 1114 | struct sock *sk = sock->sk; |
1115 | struct socket_wq *wq; | ||
1111 | 1116 | ||
1112 | if (sk == NULL) | 1117 | if (sk == NULL) |
1113 | return -EINVAL; | 1118 | return -EINVAL; |
1114 | 1119 | ||
1115 | lock_sock(sk); | 1120 | lock_sock(sk); |
1121 | wq = rcu_dereference_protected(sock->wq, sock_owned_by_user(sk)); | ||
1122 | fasync_helper(fd, filp, on, &wq->fasync_list); | ||
1116 | 1123 | ||
1117 | fasync_helper(fd, filp, on, &sock->wq->fasync_list); | 1124 | if (!wq->fasync_list) |
1118 | |||
1119 | if (!sock->wq->fasync_list) | ||
1120 | sock_reset_flag(sk, SOCK_FASYNC); | 1125 | sock_reset_flag(sk, SOCK_FASYNC); |
1121 | else | 1126 | else |
1122 | sock_set_flag(sk, SOCK_FASYNC); | 1127 | sock_set_flag(sk, SOCK_FASYNC); |
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index d802e941d365..b7d435c3f19e 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -420,6 +420,7 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, | |||
420 | static void svc_udp_data_ready(struct sock *sk, int count) | 420 | static void svc_udp_data_ready(struct sock *sk, int count) |
421 | { | 421 | { |
422 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; | 422 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; |
423 | wait_queue_head_t *wq = sk_sleep(sk); | ||
423 | 424 | ||
424 | if (svsk) { | 425 | if (svsk) { |
425 | dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n", | 426 | dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n", |
@@ -428,8 +429,8 @@ static void svc_udp_data_ready(struct sock *sk, int count) | |||
428 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | 429 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); |
429 | svc_xprt_enqueue(&svsk->sk_xprt); | 430 | svc_xprt_enqueue(&svsk->sk_xprt); |
430 | } | 431 | } |
431 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 432 | if (wq && waitqueue_active(wq)) |
432 | wake_up_interruptible(sk_sleep(sk)); | 433 | wake_up_interruptible(wq); |
433 | } | 434 | } |
434 | 435 | ||
435 | /* | 436 | /* |
@@ -438,6 +439,7 @@ static void svc_udp_data_ready(struct sock *sk, int count) | |||
438 | static void svc_write_space(struct sock *sk) | 439 | static void svc_write_space(struct sock *sk) |
439 | { | 440 | { |
440 | struct svc_sock *svsk = (struct svc_sock *)(sk->sk_user_data); | 441 | struct svc_sock *svsk = (struct svc_sock *)(sk->sk_user_data); |
442 | wait_queue_head_t *wq = sk_sleep(sk); | ||
441 | 443 | ||
442 | if (svsk) { | 444 | if (svsk) { |
443 | dprintk("svc: socket %p(inet %p), write_space busy=%d\n", | 445 | dprintk("svc: socket %p(inet %p), write_space busy=%d\n", |
@@ -445,10 +447,10 @@ static void svc_write_space(struct sock *sk) | |||
445 | svc_xprt_enqueue(&svsk->sk_xprt); | 447 | svc_xprt_enqueue(&svsk->sk_xprt); |
446 | } | 448 | } |
447 | 449 | ||
448 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) { | 450 | if (wq && waitqueue_active(wq)) { |
449 | dprintk("RPC svc_write_space: someone sleeping on %p\n", | 451 | dprintk("RPC svc_write_space: someone sleeping on %p\n", |
450 | svsk); | 452 | svsk); |
451 | wake_up_interruptible(sk_sleep(sk)); | 453 | wake_up_interruptible(wq); |
452 | } | 454 | } |
453 | } | 455 | } |
454 | 456 | ||
@@ -739,6 +741,7 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) | |||
739 | static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) | 741 | static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) |
740 | { | 742 | { |
741 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; | 743 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; |
744 | wait_queue_head_t *wq; | ||
742 | 745 | ||
743 | dprintk("svc: socket %p TCP (listen) state change %d\n", | 746 | dprintk("svc: socket %p TCP (listen) state change %d\n", |
744 | sk, sk->sk_state); | 747 | sk, sk->sk_state); |
@@ -761,8 +764,9 @@ static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) | |||
761 | printk("svc: socket %p: no user data\n", sk); | 764 | printk("svc: socket %p: no user data\n", sk); |
762 | } | 765 | } |
763 | 766 | ||
764 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 767 | wq = sk_sleep(sk); |
765 | wake_up_interruptible_all(sk_sleep(sk)); | 768 | if (wq && waitqueue_active(wq)) |
769 | wake_up_interruptible_all(wq); | ||
766 | } | 770 | } |
767 | 771 | ||
768 | /* | 772 | /* |
@@ -771,6 +775,7 @@ static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) | |||
771 | static void svc_tcp_state_change(struct sock *sk) | 775 | static void svc_tcp_state_change(struct sock *sk) |
772 | { | 776 | { |
773 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; | 777 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; |
778 | wait_queue_head_t *wq = sk_sleep(sk); | ||
774 | 779 | ||
775 | dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n", | 780 | dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n", |
776 | sk, sk->sk_state, sk->sk_user_data); | 781 | sk, sk->sk_state, sk->sk_user_data); |
@@ -781,13 +786,14 @@ static void svc_tcp_state_change(struct sock *sk) | |||
781 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); | 786 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); |
782 | svc_xprt_enqueue(&svsk->sk_xprt); | 787 | svc_xprt_enqueue(&svsk->sk_xprt); |
783 | } | 788 | } |
784 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 789 | if (wq && waitqueue_active(wq)) |
785 | wake_up_interruptible_all(sk_sleep(sk)); | 790 | wake_up_interruptible_all(wq); |
786 | } | 791 | } |
787 | 792 | ||
788 | static void svc_tcp_data_ready(struct sock *sk, int count) | 793 | static void svc_tcp_data_ready(struct sock *sk, int count) |
789 | { | 794 | { |
790 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; | 795 | struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; |
796 | wait_queue_head_t *wq = sk_sleep(sk); | ||
791 | 797 | ||
792 | dprintk("svc: socket %p TCP data ready (svsk %p)\n", | 798 | dprintk("svc: socket %p TCP data ready (svsk %p)\n", |
793 | sk, sk->sk_user_data); | 799 | sk, sk->sk_user_data); |
@@ -795,8 +801,8 @@ static void svc_tcp_data_ready(struct sock *sk, int count) | |||
795 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | 801 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); |
796 | svc_xprt_enqueue(&svsk->sk_xprt); | 802 | svc_xprt_enqueue(&svsk->sk_xprt); |
797 | } | 803 | } |
798 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 804 | if (wq && waitqueue_active(wq)) |
799 | wake_up_interruptible(sk_sleep(sk)); | 805 | wake_up_interruptible(wq); |
800 | } | 806 | } |
801 | 807 | ||
802 | /* | 808 | /* |
@@ -1531,6 +1537,7 @@ static void svc_sock_detach(struct svc_xprt *xprt) | |||
1531 | { | 1537 | { |
1532 | struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); | 1538 | struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); |
1533 | struct sock *sk = svsk->sk_sk; | 1539 | struct sock *sk = svsk->sk_sk; |
1540 | wait_queue_head_t *wq; | ||
1534 | 1541 | ||
1535 | dprintk("svc: svc_sock_detach(%p)\n", svsk); | 1542 | dprintk("svc: svc_sock_detach(%p)\n", svsk); |
1536 | 1543 | ||
@@ -1539,8 +1546,9 @@ static void svc_sock_detach(struct svc_xprt *xprt) | |||
1539 | sk->sk_data_ready = svsk->sk_odata; | 1546 | sk->sk_data_ready = svsk->sk_odata; |
1540 | sk->sk_write_space = svsk->sk_owspace; | 1547 | sk->sk_write_space = svsk->sk_owspace; |
1541 | 1548 | ||
1542 | if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) | 1549 | wq = sk_sleep(sk); |
1543 | wake_up_interruptible(sk_sleep(sk)); | 1550 | if (wq && waitqueue_active(wq)) |
1551 | wake_up_interruptible(wq); | ||
1544 | } | 1552 | } |
1545 | 1553 | ||
1546 | /* | 1554 | /* |
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d8d98d5b508c..217fb7f34d52 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -1171,7 +1171,7 @@ restart: | |||
1171 | newsk->sk_type = sk->sk_type; | 1171 | newsk->sk_type = sk->sk_type; |
1172 | init_peercred(newsk); | 1172 | init_peercred(newsk); |
1173 | newu = unix_sk(newsk); | 1173 | newu = unix_sk(newsk); |
1174 | newsk->sk_wq = &newu->peer_wq; | 1174 | RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); |
1175 | otheru = unix_sk(other); | 1175 | otheru = unix_sk(other); |
1176 | 1176 | ||
1177 | /* copy address information from listening to new sock*/ | 1177 | /* copy address information from listening to new sock*/ |