diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 5b40cb89ff0aaa5492ba5c51b38df6bdc3bc85a8..a580a40d0208a7c9a46dd1246440209e52434d83 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -1,7 +1,7 @@ /* * 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 * All rights reserved. * @@ -47,7 +47,6 @@ /* indicates no timer in use */ #define TIPC_LINK_REQ_INACTIVE 0xffffffff - /** * struct tipc_link_req - information about an ongoing link setup request * @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)) return; - /* Locate, or if necessary, create, node: */ - node = tipc_node_find(net, onode); - if (!node) - node = tipc_node_create(net, onode); + node = tipc_node_create(net, onode); if (!node) return; - tipc_node_lock(node); link = node->links[bearer->identity]; diff --git a/net/tipc/link.c b/net/tipc/link.c index 2846ad802e43f3361250b1ebe2b8e57109ae55c2..77c7ccd492b54c1d662be3d326d64e6faf2a679d 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -127,6 +127,21 @@ static unsigned int align(unsigned int i) return (i + 3) & ~3u; } +static void tipc_link_release(struct kref *kref) +{ + kfree(container_of(kref, struct tipc_link, ref)); +} + +static void tipc_link_get(struct tipc_link *l_ptr) +{ + kref_get(&l_ptr->ref); +} + +static void tipc_link_put(struct tipc_link *l_ptr) +{ + kref_put(&l_ptr->ref, tipc_link_release); +} + static void link_init_max_pkt(struct tipc_link *l_ptr) { struct tipc_node *node = l_ptr->owner; @@ -222,11 +237,13 @@ static void link_timeout(unsigned long data) tipc_link_push_packets(l_ptr); tipc_node_unlock(l_ptr->owner); + tipc_link_put(l_ptr); } static void link_set_timer(struct tipc_link *link, unsigned long time) { - mod_timer(&link->timer, jiffies + time); + if (!mod_timer(&link->timer, jiffies + time)) + tipc_link_get(link); } /** @@ -267,7 +284,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, pr_warn("Link creation failed, no memory\n"); return NULL; } - + kref_init(&l_ptr->ref); l_ptr->addr = peer; if_name = strchr(b_ptr->name, ':') + 1; sprintf(l_ptr->name, "%u.%u.%u:%s-%u.%u.%u:unknown", @@ -305,46 +322,48 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, skb_queue_head_init(&l_ptr->waiting_sks); link_reset_statistics(l_ptr); - tipc_node_attach_link(n_ptr, l_ptr); - setup_timer(&l_ptr->timer, link_timeout, (unsigned long)l_ptr); - link_state_event(l_ptr, STARTING_EVT); return l_ptr; } +/** + * link_delete - Conditional deletion of link. + * If timer still running, real delete is done when it expires + * @link: link to be deleted + */ +void tipc_link_delete(struct tipc_link *link) +{ + tipc_link_reset_fragments(link); + tipc_node_detach_link(link->owner, link); + tipc_link_put(link); +} + void tipc_link_delete_list(struct net *net, unsigned int bearer_id, bool shutting_down) { struct tipc_net *tn = net_generic(net, tipc_net_id); - struct tipc_link *l_ptr; - struct tipc_node *n_ptr; + struct tipc_link *link; + struct tipc_node *node; rcu_read_lock(); - list_for_each_entry_rcu(n_ptr, &tn->node_list, list) { - tipc_node_lock(n_ptr); - l_ptr = n_ptr->links[bearer_id]; - if (l_ptr) { - tipc_link_reset(l_ptr); - if (shutting_down || !tipc_node_is_up(n_ptr)) { - tipc_node_detach_link(l_ptr->owner, l_ptr); - tipc_link_reset_fragments(l_ptr); - tipc_node_unlock(n_ptr); - - /* Nobody else can access this link now: */ - del_timer_sync(&l_ptr->timer); - kfree(l_ptr); - } else { - /* Detach/delete when failover is finished: */ - l_ptr->flags |= LINK_STOPPED; - tipc_node_unlock(n_ptr); - del_timer_sync(&l_ptr->timer); - } + list_for_each_entry_rcu(node, &tn->node_list, list) { + tipc_node_lock(node); + link = node->links[bearer_id]; + if (!link) { + tipc_node_unlock(node); continue; } - tipc_node_unlock(n_ptr); + tipc_link_reset(link); + if (del_timer(&link->timer)) + tipc_link_put(link); + link->flags |= LINK_STOPPED; + /* Delete link now, or when failover is finished: */ + if (shutting_down || !tipc_node_is_up(node)) + tipc_link_delete(link); + tipc_node_unlock(node); } rcu_read_unlock(); } @@ -630,7 +649,9 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event) break; case STARTING_EVT: l_ptr->flags |= LINK_STARTED; - /* fall through */ + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; case TIMEOUT_EVT: tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0, 0); l_ptr->fsm_msg_cnt++; @@ -1837,10 +1858,8 @@ static struct sk_buff *tipc_link_failover_rcv(struct tipc_link *l_ptr, } } exit: - if ((l_ptr->exp_msg_count == 0) && (l_ptr->flags & LINK_STOPPED)) { - tipc_node_detach_link(l_ptr->owner, l_ptr); - kfree(l_ptr); - } + if ((!l_ptr->exp_msg_count) && (l_ptr->flags & LINK_STOPPED)) + tipc_link_delete(l_ptr); return buf; } diff --git a/net/tipc/link.h b/net/tipc/link.h index 9df7fa4d3bdd3f7a38e1f5ff6225f79ac74a6534..3e3432b3044e01c867feb2d0e04af9b102531082 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -103,6 +103,7 @@ struct tipc_stats { * @media_addr: media address to use when sending messages over link * @timer: link timer * @owner: pointer to peer node + * @refcnt: reference counter for permanent references (owner node & timer) * @flags: execution state flags for link endpoint instance * @checkpoint: reference point for triggering link continuity checking * @peer_session: link session # being used by peer end of link @@ -142,6 +143,7 @@ struct tipc_link { struct tipc_media_addr media_addr; struct timer_list timer; struct tipc_node *owner; + struct kref ref; /* Management and link supervision data */ unsigned int flags; @@ -200,6 +202,7 @@ struct tipc_port; struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, struct tipc_bearer *b_ptr, const struct tipc_media_addr *media_addr); +void tipc_link_delete(struct tipc_link *link); void tipc_link_delete_list(struct net *net, unsigned int bearer_id, bool shutting_down); void tipc_link_failover_send_queue(struct tipc_link *l_ptr); diff --git a/net/tipc/node.c b/net/tipc/node.c index ee5d33cfcf8032ffc40b3c029471ecf75b34015a..842bd7ad4b171bbe9d7ee6592ad16ea39f9bf28f 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -96,14 +96,14 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr) struct tipc_node *n_ptr, *temp_node; 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); if (!n_ptr) { - spin_unlock_bh(&tn->node_list_lock); pr_warn("Node creation failed, no memory\n"); - return NULL; + goto exit; } - n_ptr->addr = addr; n_ptr->net = net; 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); n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN; n_ptr->signature = INVALID_NODE_SIG; - tn->num_nodes++; - +exit: spin_unlock_bh(&tn->node_list_lock); return n_ptr; } @@ -406,6 +405,10 @@ static void node_lost_contact(struct tipc_node *n_ptr) l_ptr->reset_checkpoint = l_ptr->next_in_no; l_ptr->exp_msg_count = 0; tipc_link_reset_fragments(l_ptr); + + /* Link marked for deletion after failover? => do it now */ + if (l_ptr->flags & LINK_STOPPED) + tipc_link_delete(l_ptr); } n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;