1331 lines · 34.3 KB · C
RawDownload
/* * net/tipc/node.c: TIPC node management routines * * Copyright (c) 2000-2006, 2012-2015, Ericsson AB * Copyright (c) 2005-2006, 2010-2014, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its *    contributors may be used to endorse or promote products derived from *    this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "core.h"#include "link.h"#include "node.h"#include "name_distr.h"#include "socket.h"#include "bcast.h"#include "discover.h" /* Node FSM states and events: */enum {	SELF_DOWN_PEER_DOWN    = 0xdd,	SELF_UP_PEER_UP        = 0xaa,	SELF_DOWN_PEER_LEAVING = 0xd1,	SELF_UP_PEER_COMING    = 0xac,	SELF_COMING_PEER_UP    = 0xca,	SELF_LEAVING_PEER_DOWN = 0x1d,	NODE_FAILINGOVER       = 0xf0,	NODE_SYNCHING          = 0xcc}; enum {	SELF_ESTABL_CONTACT_EVT = 0xece,	SELF_LOST_CONTACT_EVT   = 0x1ce,	PEER_ESTABL_CONTACT_EVT = 0x9ece,	PEER_LOST_CONTACT_EVT   = 0x91ce,	NODE_FAILOVER_BEGIN_EVT = 0xfbe,	NODE_FAILOVER_END_EVT   = 0xfee,	NODE_SYNCH_BEGIN_EVT    = 0xcbe,	NODE_SYNCH_END_EVT      = 0xcee}; static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,				  struct sk_buff_head *xmitq,				  struct tipc_media_addr **maddr);static void tipc_node_link_down(struct tipc_node *n, int bearer_id,				bool delete);static void node_lost_contact(struct tipc_node *n, struct sk_buff_head *inputq);static void node_established_contact(struct tipc_node *n_ptr);static void tipc_node_delete(struct tipc_node *node);static void tipc_node_timeout(unsigned long data);static void tipc_node_fsm_evt(struct tipc_node *n, int evt); struct tipc_sock_conn {	u32 port;	u32 peer_port;	u32 peer_node;	struct list_head list;}; static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {	[TIPC_NLA_NODE_UNSPEC]		= { .type = NLA_UNSPEC },	[TIPC_NLA_NODE_ADDR]		= { .type = NLA_U32 },	[TIPC_NLA_NODE_UP]		= { .type = NLA_FLAG }}; /* * A trivial power-of-two bitmask technique is used for speed, since this * operation is done for every incoming TIPC packet. The number of hash table * entries has been chosen so that no hash chain exceeds 8 nodes and will * usually be much smaller (typically only a single node). */static unsigned int tipc_hashfn(u32 addr){	return addr & (NODE_HTABLE_SIZE - 1);} static void tipc_node_kref_release(struct kref *kref){	struct tipc_node *node = container_of(kref, struct tipc_node, kref); 	tipc_node_delete(node);} void tipc_node_put(struct tipc_node *node){	kref_put(&node->kref, tipc_node_kref_release);} static void tipc_node_get(struct tipc_node *node){	kref_get(&node->kref);} /* * tipc_node_find - locate specified node object, if it exists */struct tipc_node *tipc_node_find(struct net *net, u32 addr){	struct tipc_net *tn = net_generic(net, tipc_net_id);	struct tipc_node *node; 	if (unlikely(!in_own_cluster_exact(net, addr)))		return NULL; 	rcu_read_lock();	hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)],				 hash) {		if (node->addr == addr) {			tipc_node_get(node);			rcu_read_unlock();			return node;		}	}	rcu_read_unlock();	return NULL;} struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities){	struct tipc_net *tn = net_generic(net, tipc_net_id);	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) {		pr_warn("Node creation failed, no memory\n");		goto exit;	}	n_ptr->addr = addr;	n_ptr->net = net;	n_ptr->capabilities = capabilities;	kref_init(&n_ptr->kref);	spin_lock_init(&n_ptr->lock);	INIT_HLIST_NODE(&n_ptr->hash);	INIT_LIST_HEAD(&n_ptr->list);	INIT_LIST_HEAD(&n_ptr->publ_list);	INIT_LIST_HEAD(&n_ptr->conn_sks);	skb_queue_head_init(&n_ptr->bclink.namedq);	__skb_queue_head_init(&n_ptr->bclink.deferdq);	hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);	list_for_each_entry_rcu(temp_node, &tn->node_list, list) {		if (n_ptr->addr < temp_node->addr)			break;	}	list_add_tail_rcu(&n_ptr->list, &temp_node->list);	n_ptr->state = SELF_DOWN_PEER_LEAVING;	n_ptr->signature = INVALID_NODE_SIG;	n_ptr->active_links[0] = INVALID_BEARER_ID;	n_ptr->active_links[1] = INVALID_BEARER_ID;	tipc_node_get(n_ptr);	setup_timer(&n_ptr->timer, tipc_node_timeout, (unsigned long)n_ptr);	n_ptr->keepalive_intv = U32_MAX;exit:	spin_unlock_bh(&tn->node_list_lock);	return n_ptr;} static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l){	unsigned long tol = l->tolerance;	unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4;	unsigned long keepalive_intv = msecs_to_jiffies(intv); 	/* Link with lowest tolerance determines timer interval */	if (keepalive_intv < n->keepalive_intv)		n->keepalive_intv = keepalive_intv; 	/* Ensure link's abort limit corresponds to current interval */	l->abort_limit = l->tolerance / jiffies_to_msecs(n->keepalive_intv);} static void tipc_node_delete(struct tipc_node *node){	list_del_rcu(&node->list);	hlist_del_rcu(&node->hash);	kfree_rcu(node, rcu);} void tipc_node_stop(struct net *net){	struct tipc_net *tn = net_generic(net, tipc_net_id);	struct tipc_node *node, *t_node; 	spin_lock_bh(&tn->node_list_lock);	list_for_each_entry_safe(node, t_node, &tn->node_list, list) {		if (del_timer(&node->timer))			tipc_node_put(node);		tipc_node_put(node);	}	spin_unlock_bh(&tn->node_list_lock);} int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port){	struct tipc_node *node;	struct tipc_sock_conn *conn;	int err = 0; 	if (in_own_node(net, dnode))		return 0; 	node = tipc_node_find(net, dnode);	if (!node) {		pr_warn("Connecting sock to node 0x%x failed\n", dnode);		return -EHOSTUNREACH;	}	conn = kmalloc(sizeof(*conn), GFP_ATOMIC);	if (!conn) {		err = -EHOSTUNREACH;		goto exit;	}	conn->peer_node = dnode;	conn->port = port;	conn->peer_port = peer_port; 	tipc_node_lock(node);	list_add_tail(&conn->list, &node->conn_sks);	tipc_node_unlock(node);exit:	tipc_node_put(node);	return err;} void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port){	struct tipc_node *node;	struct tipc_sock_conn *conn, *safe; 	if (in_own_node(net, dnode))		return; 	node = tipc_node_find(net, dnode);	if (!node)		return; 	tipc_node_lock(node);	list_for_each_entry_safe(conn, safe, &node->conn_sks, list) {		if (port != conn->port)			continue;		list_del(&conn->list);		kfree(conn);	}	tipc_node_unlock(node);	tipc_node_put(node);} /* tipc_node_timeout - handle expiration of node timer */static void tipc_node_timeout(unsigned long data){	struct tipc_node *n = (struct tipc_node *)data;	struct tipc_link_entry *le;	struct sk_buff_head xmitq;	int bearer_id;	int rc = 0; 	__skb_queue_head_init(&xmitq); 	for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {		tipc_node_lock(n);		le = &n->links[bearer_id];		if (le->link) {			/* Link tolerance may change asynchronously: */			tipc_node_calculate_timer(n, le->link);			rc = tipc_link_timeout(le->link, &xmitq);		}		tipc_node_unlock(n);		tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr);		if (rc & TIPC_LINK_DOWN_EVT)			tipc_node_link_down(n, bearer_id, false);	}	if (!mod_timer(&n->timer, jiffies + n->keepalive_intv))		tipc_node_get(n);	tipc_node_put(n);} /** * __tipc_node_link_up - handle addition of link * Node lock must be held by caller * Link becomes active (alone or shared) or standby, depending on its priority. */static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,				struct sk_buff_head *xmitq){	int *slot0 = &n->active_links[0];	int *slot1 = &n->active_links[1];	struct tipc_link *ol = node_active_link(n, 0);	struct tipc_link *nl = n->links[bearer_id].link; 	if (!nl || !tipc_link_is_up(nl))		return; 	n->working_links++;	n->action_flags |= TIPC_NOTIFY_LINK_UP;	n->link_id = nl->peer_bearer_id << 16 | bearer_id; 	/* Leave room for tunnel header when returning 'mtu' to users: */	n->links[bearer_id].mtu = nl->mtu - INT_H_SIZE; 	tipc_bearer_add_dest(n->net, bearer_id, n->addr); 	pr_debug("Established link <%s> on network plane %c\n",		 nl->name, nl->net_plane); 	/* First link? => give it both slots */	if (!ol) {		*slot0 = bearer_id;		*slot1 = bearer_id;		tipc_link_build_bcast_sync_msg(nl, xmitq);		node_established_contact(n);		return;	} 	/* Second link => redistribute slots */	if (nl->priority > ol->priority) {		pr_debug("Old link <%s> becomes standby\n", ol->name);		*slot0 = bearer_id;		*slot1 = bearer_id;	} else if (nl->priority == ol->priority) {		*slot0 = bearer_id;	} else {		pr_debug("New link <%s> is standby\n", nl->name);	} 	/* Prepare synchronization with first link */	tipc_link_tnl_prepare(ol, nl, SYNCH_MSG, xmitq);} /** * tipc_node_link_up - handle addition of link * * Link becomes active (alone or shared) or standby, depending on its priority. */static void tipc_node_link_up(struct tipc_node *n, int bearer_id,			      struct sk_buff_head *xmitq){	tipc_node_lock(n);	__tipc_node_link_up(n, bearer_id, xmitq);	tipc_node_unlock(n);} /** * __tipc_node_link_down - handle loss of link */static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,				  struct sk_buff_head *xmitq,				  struct tipc_media_addr **maddr){	struct tipc_link_entry *le = &n->links[*bearer_id];	int *slot0 = &n->active_links[0];	int *slot1 = &n->active_links[1];	int i, highest = 0;	struct tipc_link *l, *_l, *tnl; 	l = n->links[*bearer_id].link;	if (!l || tipc_link_is_reset(l))		return; 	n->working_links--;	n->action_flags |= TIPC_NOTIFY_LINK_DOWN;	n->link_id = l->peer_bearer_id << 16 | *bearer_id; 	tipc_bearer_remove_dest(n->net, *bearer_id, n->addr); 	pr_debug("Lost link <%s> on network plane %c\n",		 l->name, l->net_plane); 	/* Select new active link if any available */	*slot0 = INVALID_BEARER_ID;	*slot1 = INVALID_BEARER_ID;	for (i = 0; i < MAX_BEARERS; i++) {		_l = n->links[i].link;		if (!_l || !tipc_link_is_up(_l))			continue;		if (_l == l)			continue;		if (_l->priority < highest)			continue;		if (_l->priority > highest) {			highest = _l->priority;			*slot0 = i;			*slot1 = i;			continue;		}		*slot1 = i;	} 	if (!tipc_node_is_up(n)) {		tipc_link_reset(l);		node_lost_contact(n, &le->inputq);		return;	} 	/* There is still a working link => initiate failover */	tnl = node_active_link(n, 0);	tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT);	tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);	n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);	tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq);	tipc_link_reset(l);	tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);	tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT);	*maddr = &n->links[tnl->bearer_id].maddr;	*bearer_id = tnl->bearer_id;} static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete){	struct tipc_link_entry *le = &n->links[bearer_id];	struct tipc_media_addr *maddr;	struct sk_buff_head xmitq; 	__skb_queue_head_init(&xmitq); 	tipc_node_lock(n);	__tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);	if (delete && le->link) {		kfree(le->link);		le->link = NULL;		n->link_cnt--;	}	tipc_node_unlock(n); 	tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);	tipc_sk_rcv(n->net, &le->inputq);} bool tipc_node_is_up(struct tipc_node *n){	return n->active_links[0] != INVALID_BEARER_ID;} void tipc_node_check_dest(struct net *net, u32 onode,			  struct tipc_bearer *b,			  u16 capabilities, u32 signature,			  struct tipc_media_addr *maddr,			  bool *respond, bool *dupl_addr){	struct tipc_node *n;	struct tipc_link *l;	struct tipc_link_entry *le;	bool addr_match = false;	bool sign_match = false;	bool link_up = false;	bool accept_addr = false;	bool reset = true; 	*dupl_addr = false;	*respond = false; 	n = tipc_node_create(net, onode, capabilities);	if (!n)		return; 	tipc_node_lock(n); 	le = &n->links[b->identity]; 	/* Prepare to validate requesting node's signature and media address */	l = le->link;	link_up = l && tipc_link_is_up(l);	addr_match = l && !memcmp(&le->maddr, maddr, sizeof(*maddr));	sign_match = (signature == n->signature); 	/* These three flags give us eight permutations: */ 	if (sign_match && addr_match && link_up) {		/* All is fine. Do nothing. */		reset = false;	} else if (sign_match && addr_match && !link_up) {

Lines 1–500 of 1331.