tipc: eliminate race during node creation

Instances of struct node are created in the function tipc_disc_rcv()
under the assumption that there is no race between received discovery
messages arriving from the same node. This assumption is wrong.
When we use more than one bearer, it is possible that discovery
messages from the same node arrive at the same moment, resulting in
creation of two instances of struct tipc_node. This may later cause
confusion during link establishment, and may result in one of the links
never becoming activated.

We fix this by making lookup and potential creation of nodes atomic.
Instead of first looking up the node, and in case of failure, create it,
we now start with looking up the node inside node_link_create(), and
return a reference to that one if found. Otherwise, we go ahead and
create the node as we did before.

Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Reviewed-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jon Paul Maloy 2015-02-03 08:59:19 -05:00 committed by David S. Miller
parent 7d24dcdb3f
commit b45db71b52
2 changed files with 7 additions and 13 deletions

View File

@ -1,7 +1,7 @@
/* /*
* net/tipc/discover.c * net/tipc/discover.c
* *
* Copyright (c) 2003-2006, 2014, Ericsson AB * Copyright (c) 2003-2006, 2014-2015, Ericsson AB
* Copyright (c) 2005-2006, 2010-2011, Wind River Systems * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
* All rights reserved. * All rights reserved.
* *
@ -47,7 +47,6 @@
/* indicates no timer in use */ /* indicates no timer in use */
#define TIPC_LINK_REQ_INACTIVE 0xffffffff #define TIPC_LINK_REQ_INACTIVE 0xffffffff
/** /**
* struct tipc_link_req - information about an ongoing link setup request * struct tipc_link_req - information about an ongoing link setup request
* @bearer_id: identity of bearer issuing requests * @bearer_id: identity of bearer issuing requests
@ -163,13 +162,9 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf,
if (!tipc_in_scope(bearer->domain, onode)) if (!tipc_in_scope(bearer->domain, onode))
return; return;
/* Locate, or if necessary, create, node: */ node = tipc_node_create(net, onode);
node = tipc_node_find(net, onode);
if (!node)
node = tipc_node_create(net, onode);
if (!node) if (!node)
return; return;
tipc_node_lock(node); tipc_node_lock(node);
link = node->links[bearer->identity]; link = node->links[bearer->identity];

View File

@ -96,14 +96,14 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
struct tipc_node *n_ptr, *temp_node; struct tipc_node *n_ptr, *temp_node;
spin_lock_bh(&tn->node_list_lock); spin_lock_bh(&tn->node_list_lock);
n_ptr = tipc_node_find(net, addr);
if (n_ptr)
goto exit;
n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC); n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
if (!n_ptr) { if (!n_ptr) {
spin_unlock_bh(&tn->node_list_lock);
pr_warn("Node creation failed, no memory\n"); pr_warn("Node creation failed, no memory\n");
return NULL; goto exit;
} }
n_ptr->addr = addr; n_ptr->addr = addr;
n_ptr->net = net; n_ptr->net = net;
spin_lock_init(&n_ptr->lock); spin_lock_init(&n_ptr->lock);
@ -123,9 +123,8 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
list_add_tail_rcu(&n_ptr->list, &temp_node->list); list_add_tail_rcu(&n_ptr->list, &temp_node->list);
n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN; n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
n_ptr->signature = INVALID_NODE_SIG; n_ptr->signature = INVALID_NODE_SIG;
tn->num_nodes++; tn->num_nodes++;
exit:
spin_unlock_bh(&tn->node_list_lock); spin_unlock_bh(&tn->node_list_lock);
return n_ptr; return n_ptr;
} }