diff options
Diffstat (limited to 'net/tipc/node.c')
| -rw-r--r-- | net/tipc/node.c | 104 | 
1 files changed, 88 insertions, 16 deletions
| diff --git a/net/tipc/node.c b/net/tipc/node.c index 0453bd451ce8..2afc4f8c37a7 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -45,6 +45,7 @@  #include "netlink.h"  #define INVALID_NODE_SIG	0x10000 +#define NODE_CLEANUP_AFTER	300000  /* Flags used to take different actions according to flag type   * TIPC_NOTIFY_NODE_DOWN: notify node is down @@ -96,6 +97,7 @@ struct tipc_bclink_entry {   * @link_id: local and remote bearer ids of changing link, if any   * @publ_list: list of publications   * @rcu: rcu struct for tipc_node + * @delete_at: indicates the time for deleting a down node   */  struct tipc_node {  	u32 addr; @@ -109,6 +111,7 @@ struct tipc_node {  	int action_flags;  	struct list_head list;  	int state; +	bool failover_sent;  	u16 sync_point;  	int link_cnt;  	u16 working_links; @@ -121,6 +124,7 @@ struct tipc_node {  	unsigned long keepalive_intv;  	struct timer_list timer;  	struct rcu_head rcu; +	unsigned long delete_at;  };  /* Node FSM states and events: @@ -160,6 +164,7 @@ static struct tipc_node *tipc_node_find(struct net *net, u32 addr);  static struct tipc_node *tipc_node_find_by_id(struct net *net, u8 *id);  static void tipc_node_put(struct tipc_node *node);  static bool node_is_up(struct tipc_node *n); +static void tipc_node_delete_from_list(struct tipc_node *node);  struct tipc_sock_conn {  	u32 port; @@ -359,13 +364,24 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,  {  	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_node *n, *temp_node; +	struct tipc_link *l; +	int bearer_id;  	int i;  	spin_lock_bh(&tn->node_list_lock);  	n = tipc_node_find(net, addr);  	if (n) { +		if (n->capabilities == capabilities) +			goto exit;  		/* Same node may come back with new capabilities */ +		write_lock_bh(&n->lock);  		n->capabilities = capabilities; +		for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { +			l = n->links[bearer_id].link; +			if (l) +				tipc_link_update_caps(l, capabilities); +		} +		write_unlock_bh(&n->lock);  		goto exit;  	}  	n = kzalloc(sizeof(*n), GFP_ATOMIC); @@ -390,6 +406,7 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr,  	for (i = 0; i < MAX_BEARERS; i++)  		spin_lock_init(&n->links[i].lock);  	n->state = SELF_DOWN_PEER_LEAVING; +	n->delete_at = jiffies + msecs_to_jiffies(NODE_CLEANUP_AFTER);  	n->signature = INVALID_NODE_SIG;  	n->active_links[0] = INVALID_BEARER_ID;  	n->active_links[1] = INVALID_BEARER_ID; @@ -433,11 +450,16 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)  	tipc_link_set_abort_limit(l, tol / n->keepalive_intv);  } -static void tipc_node_delete(struct tipc_node *node) +static void tipc_node_delete_from_list(struct tipc_node *node)  {  	list_del_rcu(&node->list);  	hlist_del_rcu(&node->hash);  	tipc_node_put(node); +} + +static void tipc_node_delete(struct tipc_node *node) +{ +	tipc_node_delete_from_list(node);  	del_timer_sync(&node->timer);  	tipc_node_put(node); @@ -544,6 +566,42 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)  	tipc_node_put(node);  } +static void  tipc_node_clear_links(struct tipc_node *node) +{ +	int i; + +	for (i = 0; i < MAX_BEARERS; i++) { +		struct tipc_link_entry *le = &node->links[i]; + +		if (le->link) { +			kfree(le->link); +			le->link = NULL; +			node->link_cnt--; +		} +	} +} + +/* tipc_node_cleanup - delete nodes that does not + * have active links for NODE_CLEANUP_AFTER time + */ +static int tipc_node_cleanup(struct tipc_node *peer) +{ +	struct tipc_net *tn = tipc_net(peer->net); +	bool deleted = false; + +	spin_lock_bh(&tn->node_list_lock); +	tipc_node_write_lock(peer); + +	if (!node_is_up(peer) && time_after(jiffies, peer->delete_at)) { +		tipc_node_clear_links(peer); +		tipc_node_delete_from_list(peer); +		deleted = true; +	} +	tipc_node_write_unlock(peer); +	spin_unlock_bh(&tn->node_list_lock); +	return deleted; +} +  /* tipc_node_timeout - handle expiration of node timer   */  static void tipc_node_timeout(struct timer_list *t) @@ -551,21 +609,29 @@ static void tipc_node_timeout(struct timer_list *t)  	struct tipc_node *n = from_timer(n, t, timer);  	struct tipc_link_entry *le;  	struct sk_buff_head xmitq; +	int remains = n->link_cnt;  	int bearer_id;  	int rc = 0; +	if (!node_is_up(n) && tipc_node_cleanup(n)) { +		/*Removing the reference of Timer*/ +		tipc_node_put(n); +		return; +	} +  	__skb_queue_head_init(&xmitq); -	for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { +	for (bearer_id = 0; remains && (bearer_id < MAX_BEARERS); bearer_id++) {  		tipc_node_read_lock(n);  		le = &n->links[bearer_id]; -		spin_lock_bh(&le->lock);  		if (le->link) { +			spin_lock_bh(&le->lock);  			/* Link tolerance may change asynchronously: */  			tipc_node_calculate_timer(n, le->link);  			rc = tipc_link_timeout(le->link, &xmitq); +			spin_unlock_bh(&le->lock); +			remains--;  		} -		spin_unlock_bh(&le->lock);  		tipc_node_read_unlock(n);  		tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr);  		if (rc & TIPC_LINK_DOWN_EVT) @@ -615,6 +681,7 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,  		*slot0 = bearer_id;  		*slot1 = bearer_id;  		tipc_node_fsm_evt(n, SELF_ESTABL_CONTACT_EVT); +		n->failover_sent = false;  		n->action_flags |= TIPC_NOTIFY_NODE_UP;  		tipc_link_set_active(nl, true);  		tipc_bcast_add_peer(n->net, nl, xmitq); @@ -846,6 +913,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,  	bool reset = true;  	char *if_name;  	unsigned long intv; +	u16 session;  	*dupl_addr = false;  	*respond = false; @@ -932,9 +1000,10 @@ void tipc_node_check_dest(struct net *net, u32 addr,  			goto exit;  		if_name = strchr(b->name, ':') + 1; +		get_random_bytes(&session, sizeof(u16));  		if (!tipc_link_create(net, if_name, b->identity, b->tolerance,  				      b->net_plane, b->mtu, b->priority, -				      b->window, mod(tipc_net(net)->random), +				      b->window, session,  				      tipc_own_addr(net), addr, peer_id,  				      n->capabilities,  				      tipc_bc_sndlink(n->net), n->bc_entry.link, @@ -1174,6 +1243,7 @@ static void node_lost_contact(struct tipc_node *n,  	uint i;  	pr_debug("Lost contact with %x\n", n->addr); +	n->delete_at = jiffies + msecs_to_jiffies(NODE_CLEANUP_AFTER);  	/* Clean up broadcast state */  	tipc_bcast_remove_peer(n->net, n->bc_entry.link); @@ -1481,7 +1551,7 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id   * tipc_node_check_state - check and if necessary update node state   * @skb: TIPC packet   * @bearer_id: identity of bearer delivering the packet - * Returns true if state is ok, otherwise consumes buffer and returns false + * Returns true if state and msg are ok, otherwise false   */  static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,  				  int bearer_id, struct sk_buff_head *xmitq) @@ -1515,6 +1585,9 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,  		}  	} +	if (!tipc_link_validate_msg(l, hdr)) +		return false; +  	/* Check and update node accesibility if applicable */  	if (state == SELF_UP_PEER_COMING) {  		if (!tipc_link_is_up(l)) @@ -1546,6 +1619,14 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,  			tipc_skb_queue_splice_tail_init(tipc_link_inputq(pl),  							tipc_link_inputq(l));  		} +		/* If parallel link was already down, and this happened before +		 * the tunnel link came up, FAILOVER was never sent. Ensure that +		 * FAILOVER is sent to get peer out of NODE_FAILINGOVER state. +		 */ +		if (n->state != NODE_FAILINGOVER && !n->failover_sent) { +			tipc_link_create_dummy_tnl_msg(l, xmitq); +			n->failover_sent = true; +		}  		/* If pkts arrive out of order, use lowest calculated syncpt */  		if (less(syncpt, n->sync_point))  			n->sync_point = syncpt; @@ -1743,7 +1824,6 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info)  	struct tipc_node *peer;  	u32 addr;  	int err; -	int i;  	/* We identify the peer by its net */  	if (!info->attrs[TIPC_NLA_NET]) @@ -1778,15 +1858,7 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info)  		goto err_out;  	} -	for (i = 0; i < MAX_BEARERS; i++) { -		struct tipc_link_entry *le = &peer->links[i]; - -		if (le->link) { -			kfree(le->link); -			le->link = NULL; -			peer->link_cnt--; -		} -	} +	tipc_node_clear_links(peer);  	tipc_node_write_unlock(peer);  	tipc_node_delete(peer); |