aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYing Xue <ying.xue@windriver.com>2013-12-26 21:18:28 -0500
committerDavid S. Miller <davem@davemloft.net>2013-12-29 22:24:07 -0500
commit84602761ca4495dd409be936dfa93ed20c946684 (patch)
tree5936e501cc24cc66cf298a8e6713cbb04b4427b0
parent8eb9bff0edefcce50116ec50397a60dd626022d6 (diff)
tipc: fix deadlock during socket release
A deadlock might occur if name table is withdrawn in socket release routine, and while packets are still being received from bearer. CPU0 CPU1 T0: recv_msg() release() T1: tipc_recv_msg() tipc_withdraw() T2: [grab node lock] [grab port lock] T3: tipc_link_wakeup_ports() tipc_nametbl_withdraw() T4: [grab port lock]* named_cluster_distribute() T5: wakeupdispatch() tipc_link_send() T6: [grab node lock]* The opposite order of holding port lock and node lock on above two different paths may result in a deadlock. If socket lock instead of port lock is used to protect port instance in tipc_withdraw(), the reverse order of holding port lock and node lock will be eliminated, as a result, the deadlock is killed as well. Reported-by: Lars Everbrand <lars.everbrand@ericsson.com> Reviewed-by: Erik Hugne <erik.hugne@ericsson.com> Signed-off-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/tipc/port.c45
-rw-r--r--net/tipc/port.h6
-rw-r--r--net/tipc/socket.c46
3 files changed, 49 insertions, 48 deletions
diff --git a/net/tipc/port.c b/net/tipc/port.c
index c081a7632302..d43f3182b1d4 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -251,18 +251,15 @@ struct tipc_port *tipc_createport(struct sock *sk,
251 return p_ptr; 251 return p_ptr;
252} 252}
253 253
254int tipc_deleteport(u32 ref) 254int tipc_deleteport(struct tipc_port *p_ptr)
255{ 255{
256 struct tipc_port *p_ptr;
257 struct sk_buff *buf = NULL; 256 struct sk_buff *buf = NULL;
258 257
259 tipc_withdraw(ref, 0, NULL); 258 tipc_withdraw(p_ptr, 0, NULL);
260 p_ptr = tipc_port_lock(ref);
261 if (!p_ptr)
262 return -EINVAL;
263 259
264 tipc_ref_discard(ref); 260 spin_lock_bh(p_ptr->lock);
265 tipc_port_unlock(p_ptr); 261 tipc_ref_discard(p_ptr->ref);
262 spin_unlock_bh(p_ptr->lock);
266 263
267 k_cancel_timer(&p_ptr->timer); 264 k_cancel_timer(&p_ptr->timer);
268 if (p_ptr->connected) { 265 if (p_ptr->connected) {
@@ -704,47 +701,36 @@ int tipc_set_portimportance(u32 ref, unsigned int imp)
704} 701}
705 702
706 703
707int tipc_publish(u32 ref, unsigned int scope, struct tipc_name_seq const *seq) 704int tipc_publish(struct tipc_port *p_ptr, unsigned int scope,
705 struct tipc_name_seq const *seq)
708{ 706{
709 struct tipc_port *p_ptr;
710 struct publication *publ; 707 struct publication *publ;
711 u32 key; 708 u32 key;
712 int res = -EINVAL;
713 709
714 p_ptr = tipc_port_lock(ref); 710 if (p_ptr->connected)
715 if (!p_ptr)
716 return -EINVAL; 711 return -EINVAL;
712 key = p_ptr->ref + p_ptr->pub_count + 1;
713 if (key == p_ptr->ref)
714 return -EADDRINUSE;
717 715
718 if (p_ptr->connected)
719 goto exit;
720 key = ref + p_ptr->pub_count + 1;
721 if (key == ref) {
722 res = -EADDRINUSE;
723 goto exit;
724 }
725 publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, 716 publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper,
726 scope, p_ptr->ref, key); 717 scope, p_ptr->ref, key);
727 if (publ) { 718 if (publ) {
728 list_add(&publ->pport_list, &p_ptr->publications); 719 list_add(&publ->pport_list, &p_ptr->publications);
729 p_ptr->pub_count++; 720 p_ptr->pub_count++;
730 p_ptr->published = 1; 721 p_ptr->published = 1;
731 res = 0; 722 return 0;
732 } 723 }
733exit: 724 return -EINVAL;
734 tipc_port_unlock(p_ptr);
735 return res;
736} 725}
737 726
738int tipc_withdraw(u32 ref, unsigned int scope, struct tipc_name_seq const *seq) 727int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope,
728 struct tipc_name_seq const *seq)
739{ 729{
740 struct tipc_port *p_ptr;
741 struct publication *publ; 730 struct publication *publ;
742 struct publication *tpubl; 731 struct publication *tpubl;
743 int res = -EINVAL; 732 int res = -EINVAL;
744 733
745 p_ptr = tipc_port_lock(ref);
746 if (!p_ptr)
747 return -EINVAL;
748 if (!seq) { 734 if (!seq) {
749 list_for_each_entry_safe(publ, tpubl, 735 list_for_each_entry_safe(publ, tpubl,
750 &p_ptr->publications, pport_list) { 736 &p_ptr->publications, pport_list) {
@@ -771,7 +757,6 @@ int tipc_withdraw(u32 ref, unsigned int scope, struct tipc_name_seq const *seq)
771 } 757 }
772 if (list_empty(&p_ptr->publications)) 758 if (list_empty(&p_ptr->publications))
773 p_ptr->published = 0; 759 p_ptr->published = 0;
774 tipc_port_unlock(p_ptr);
775 return res; 760 return res;
776} 761}
777 762
diff --git a/net/tipc/port.h b/net/tipc/port.h
index 912253597343..34f12bd4074e 100644
--- a/net/tipc/port.h
+++ b/net/tipc/port.h
@@ -116,7 +116,7 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err);
116 116
117void tipc_acknowledge(u32 port_ref, u32 ack); 117void tipc_acknowledge(u32 port_ref, u32 ack);
118 118
119int tipc_deleteport(u32 portref); 119int tipc_deleteport(struct tipc_port *p_ptr);
120 120
121int tipc_portimportance(u32 portref, unsigned int *importance); 121int tipc_portimportance(u32 portref, unsigned int *importance);
122int tipc_set_portimportance(u32 portref, unsigned int importance); 122int tipc_set_portimportance(u32 portref, unsigned int importance);
@@ -127,9 +127,9 @@ int tipc_set_portunreliable(u32 portref, unsigned int isunreliable);
127int tipc_portunreturnable(u32 portref, unsigned int *isunreturnable); 127int tipc_portunreturnable(u32 portref, unsigned int *isunreturnable);
128int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable); 128int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable);
129 129
130int tipc_publish(u32 portref, unsigned int scope, 130int tipc_publish(struct tipc_port *p_ptr, unsigned int scope,
131 struct tipc_name_seq const *name_seq); 131 struct tipc_name_seq const *name_seq);
132int tipc_withdraw(u32 portref, unsigned int scope, 132int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope,
133 struct tipc_name_seq const *name_seq); 133 struct tipc_name_seq const *name_seq);
134 134
135int tipc_connect(u32 portref, struct tipc_portid const *port); 135int tipc_connect(u32 portref, struct tipc_portid const *port);
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 3b61851bb927..e741416d1d24 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -354,7 +354,7 @@ static int release(struct socket *sock)
354 * Delete TIPC port; this ensures no more messages are queued 354 * Delete TIPC port; this ensures no more messages are queued
355 * (also disconnects an active connection & sends a 'FIN-' to peer) 355 * (also disconnects an active connection & sends a 'FIN-' to peer)
356 */ 356 */
357 res = tipc_deleteport(tport->ref); 357 res = tipc_deleteport(tport);
358 358
359 /* Discard any remaining (connection-based) messages in receive queue */ 359 /* Discard any remaining (connection-based) messages in receive queue */
360 __skb_queue_purge(&sk->sk_receive_queue); 360 __skb_queue_purge(&sk->sk_receive_queue);
@@ -386,30 +386,46 @@ static int release(struct socket *sock)
386 */ 386 */
387static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len) 387static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
388{ 388{
389 struct sock *sk = sock->sk;
389 struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; 390 struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
390 u32 portref = tipc_sk_port(sock->sk)->ref; 391 struct tipc_port *tport = tipc_sk_port(sock->sk);
392 int res = -EINVAL;
391 393
392 if (unlikely(!uaddr_len)) 394 lock_sock(sk);
393 return tipc_withdraw(portref, 0, NULL); 395 if (unlikely(!uaddr_len)) {
396 res = tipc_withdraw(tport, 0, NULL);
397 goto exit;
398 }
394 399
395 if (uaddr_len < sizeof(struct sockaddr_tipc)) 400 if (uaddr_len < sizeof(struct sockaddr_tipc)) {
396 return -EINVAL; 401 res = -EINVAL;
397 if (addr->family != AF_TIPC) 402 goto exit;
398 return -EAFNOSUPPORT; 403 }
404 if (addr->family != AF_TIPC) {
405 res = -EAFNOSUPPORT;
406 goto exit;
407 }
399 408
400 if (addr->addrtype == TIPC_ADDR_NAME) 409 if (addr->addrtype == TIPC_ADDR_NAME)
401 addr->addr.nameseq.upper = addr->addr.nameseq.lower; 410 addr->addr.nameseq.upper = addr->addr.nameseq.lower;
402 else if (addr->addrtype != TIPC_ADDR_NAMESEQ) 411 else if (addr->addrtype != TIPC_ADDR_NAMESEQ) {
403 return -EAFNOSUPPORT; 412 res = -EAFNOSUPPORT;
413 goto exit;
414 }
404 415
405 if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) && 416 if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) &&
406 (addr->addr.nameseq.type != TIPC_TOP_SRV) && 417 (addr->addr.nameseq.type != TIPC_TOP_SRV) &&
407 (addr->addr.nameseq.type != TIPC_CFG_SRV)) 418 (addr->addr.nameseq.type != TIPC_CFG_SRV)) {
408 return -EACCES; 419 res = -EACCES;
420 goto exit;
421 }
409 422
410 return (addr->scope > 0) ? 423 res = (addr->scope > 0) ?
411 tipc_publish(portref, addr->scope, &addr->addr.nameseq) : 424 tipc_publish(tport, addr->scope, &addr->addr.nameseq) :
412 tipc_withdraw(portref, -addr->scope, &addr->addr.nameseq); 425 tipc_withdraw(tport, -addr->scope, &addr->addr.nameseq);
426exit:
427 release_sock(sk);
428 return res;
413} 429}
414 430
415/** 431/**