diff options
Diffstat (limited to 'net/tipc/server.c')
-rw-r--r-- | net/tipc/server.c | 70 |
1 files changed, 37 insertions, 33 deletions
diff --git a/net/tipc/server.c b/net/tipc/server.c index 8ee5e86b7870..c0d331f13eee 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c | |||
@@ -132,10 +132,11 @@ static struct tipc_conn *tipc_conn_lookup(struct tipc_server *s, int conid) | |||
132 | 132 | ||
133 | spin_lock_bh(&s->idr_lock); | 133 | spin_lock_bh(&s->idr_lock); |
134 | con = idr_find(&s->conn_idr, conid); | 134 | con = idr_find(&s->conn_idr, conid); |
135 | if (con && test_bit(CF_CONNECTED, &con->flags)) | 135 | if (con) { |
136 | conn_get(con); | 136 | if (!test_bit(CF_CONNECTED, &con->flags) || |
137 | else | 137 | !kref_get_unless_zero(&con->kref)) |
138 | con = NULL; | 138 | con = NULL; |
139 | } | ||
139 | spin_unlock_bh(&s->idr_lock); | 140 | spin_unlock_bh(&s->idr_lock); |
140 | return con; | 141 | return con; |
141 | } | 142 | } |
@@ -183,35 +184,28 @@ static void tipc_register_callbacks(struct socket *sock, struct tipc_conn *con) | |||
183 | write_unlock_bh(&sk->sk_callback_lock); | 184 | write_unlock_bh(&sk->sk_callback_lock); |
184 | } | 185 | } |
185 | 186 | ||
186 | static void tipc_unregister_callbacks(struct tipc_conn *con) | ||
187 | { | ||
188 | struct sock *sk = con->sock->sk; | ||
189 | |||
190 | write_lock_bh(&sk->sk_callback_lock); | ||
191 | sk->sk_user_data = NULL; | ||
192 | write_unlock_bh(&sk->sk_callback_lock); | ||
193 | } | ||
194 | |||
195 | static void tipc_close_conn(struct tipc_conn *con) | 187 | static void tipc_close_conn(struct tipc_conn *con) |
196 | { | 188 | { |
197 | struct tipc_server *s = con->server; | 189 | struct tipc_server *s = con->server; |
190 | struct sock *sk = con->sock->sk; | ||
191 | bool disconnect = false; | ||
198 | 192 | ||
199 | if (test_and_clear_bit(CF_CONNECTED, &con->flags)) { | 193 | write_lock_bh(&sk->sk_callback_lock); |
200 | if (con->sock) | 194 | disconnect = test_and_clear_bit(CF_CONNECTED, &con->flags); |
201 | tipc_unregister_callbacks(con); | 195 | if (disconnect) { |
202 | 196 | sk->sk_user_data = NULL; | |
203 | if (con->conid) | 197 | if (con->conid) |
204 | s->tipc_conn_release(con->conid, con->usr_data); | 198 | s->tipc_conn_release(con->conid, con->usr_data); |
205 | |||
206 | /* We shouldn't flush pending works as we may be in the | ||
207 | * thread. In fact the races with pending rx/tx work structs | ||
208 | * are harmless for us here as we have already deleted this | ||
209 | * connection from server connection list. | ||
210 | */ | ||
211 | if (con->sock) | ||
212 | kernel_sock_shutdown(con->sock, SHUT_RDWR); | ||
213 | conn_put(con); | ||
214 | } | 199 | } |
200 | write_unlock_bh(&sk->sk_callback_lock); | ||
201 | |||
202 | /* Handle concurrent calls from sending and receiving threads */ | ||
203 | if (!disconnect) | ||
204 | return; | ||
205 | |||
206 | /* Don't flush pending works, -just let them expire */ | ||
207 | kernel_sock_shutdown(con->sock, SHUT_RDWR); | ||
208 | conn_put(con); | ||
215 | } | 209 | } |
216 | 210 | ||
217 | static struct tipc_conn *tipc_alloc_conn(struct tipc_server *s) | 211 | static struct tipc_conn *tipc_alloc_conn(struct tipc_server *s) |
@@ -248,9 +242,10 @@ static struct tipc_conn *tipc_alloc_conn(struct tipc_server *s) | |||
248 | 242 | ||
249 | static int tipc_receive_from_sock(struct tipc_conn *con) | 243 | static int tipc_receive_from_sock(struct tipc_conn *con) |
250 | { | 244 | { |
251 | struct msghdr msg = {}; | ||
252 | struct tipc_server *s = con->server; | 245 | struct tipc_server *s = con->server; |
246 | struct sock *sk = con->sock->sk; | ||
253 | struct sockaddr_tipc addr; | 247 | struct sockaddr_tipc addr; |
248 | struct msghdr msg = {}; | ||
254 | struct kvec iov; | 249 | struct kvec iov; |
255 | void *buf; | 250 | void *buf; |
256 | int ret; | 251 | int ret; |
@@ -271,12 +266,15 @@ static int tipc_receive_from_sock(struct tipc_conn *con) | |||
271 | goto out_close; | 266 | goto out_close; |
272 | } | 267 | } |
273 | 268 | ||
274 | s->tipc_conn_recvmsg(sock_net(con->sock->sk), con->conid, &addr, | 269 | read_lock_bh(&sk->sk_callback_lock); |
275 | con->usr_data, buf, ret); | 270 | if (test_bit(CF_CONNECTED, &con->flags)) |
276 | 271 | ret = s->tipc_conn_recvmsg(sock_net(con->sock->sk), con->conid, | |
272 | &addr, con->usr_data, buf, ret); | ||
273 | read_unlock_bh(&sk->sk_callback_lock); | ||
277 | kmem_cache_free(s->rcvbuf_cache, buf); | 274 | kmem_cache_free(s->rcvbuf_cache, buf); |
278 | 275 | if (ret < 0) | |
279 | return 0; | 276 | tipc_conn_terminate(s, con->conid); |
277 | return ret; | ||
280 | 278 | ||
281 | out_close: | 279 | out_close: |
282 | if (ret != -EWOULDBLOCK) | 280 | if (ret != -EWOULDBLOCK) |
@@ -525,11 +523,17 @@ bool tipc_topsrv_kern_subscr(struct net *net, u32 port, u32 type, u32 lower, | |||
525 | void tipc_topsrv_kern_unsubscr(struct net *net, int conid) | 523 | void tipc_topsrv_kern_unsubscr(struct net *net, int conid) |
526 | { | 524 | { |
527 | struct tipc_conn *con; | 525 | struct tipc_conn *con; |
526 | struct tipc_server *srv; | ||
528 | 527 | ||
529 | con = tipc_conn_lookup(tipc_topsrv(net), conid); | 528 | con = tipc_conn_lookup(tipc_topsrv(net), conid); |
530 | if (!con) | 529 | if (!con) |
531 | return; | 530 | return; |
532 | tipc_close_conn(con); | 531 | |
532 | test_and_clear_bit(CF_CONNECTED, &con->flags); | ||
533 | srv = con->server; | ||
534 | if (con->conid) | ||
535 | srv->tipc_conn_release(con->conid, con->usr_data); | ||
536 | conn_put(con); | ||
533 | conn_put(con); | 537 | conn_put(con); |
534 | } | 538 | } |
535 | 539 | ||