1930 lines · 48.6 KB · C
RawDownload
/* * net/tipc/node.c: TIPC node management routines * * Copyright (c) 2000-2006, 2012-2016, 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"#include "netlink.h" #define INVALID_NODE_SIG	0x10000 /* Flags used to take different actions according to flag type * TIPC_NOTIFY_NODE_DOWN: notify node is down * TIPC_NOTIFY_NODE_UP: notify node is up * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type */enum {	TIPC_NOTIFY_NODE_DOWN		= (1 << 3),	TIPC_NOTIFY_NODE_UP		= (1 << 4),	TIPC_NOTIFY_LINK_UP		= (1 << 6),	TIPC_NOTIFY_LINK_DOWN		= (1 << 7)}; struct tipc_link_entry {	struct tipc_link *link;	spinlock_t lock; /* per link */	u32 mtu;	struct sk_buff_head inputq;	struct tipc_media_addr maddr;}; struct tipc_bclink_entry {	struct tipc_link *link;	struct sk_buff_head inputq1;	struct sk_buff_head arrvq;	struct sk_buff_head inputq2;	struct sk_buff_head namedq;}; /** * struct tipc_node - TIPC node structure * @addr: network address of node * @ref: reference counter to node object * @lock: rwlock governing access to structure * @net: the applicable net namespace * @hash: links to adjacent nodes in unsorted hash chain * @inputq: pointer to input queue containing messages for msg event * @namedq: pointer to name table input queue with name table messages * @active_links: bearer ids of active links, used as index into links[] array * @links: array containing references to all links to node * @action_flags: bit mask of different types of node actions * @state: connectivity state vs peer node * @sync_point: sequence number where synch/failover is finished * @list: links to adjacent nodes in sorted list of cluster's nodes * @working_links: number of working links to node (both active and standby) * @link_cnt: number of links to node * @capabilities: bitmap, indicating peer node's functional capabilities * @signature: node instance identifier * @link_id: local and remote bearer ids of changing link, if any * @publ_list: list of publications * @rcu: rcu struct for tipc_node */struct tipc_node {	u32 addr;	struct kref kref;	rwlock_t lock;	struct net *net;	struct hlist_node hash;	int active_links[2];	struct tipc_link_entry links[MAX_BEARERS];	struct tipc_bclink_entry bc_entry;	int action_flags;	struct list_head list;	int state;	u16 sync_point;	int link_cnt;	u16 working_links;	u16 capabilities;	u32 signature;	u32 link_id;	struct list_head publ_list;	struct list_head conn_sks;	unsigned long keepalive_intv;	struct timer_list timer;	struct rcu_head rcu;}; /* 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 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);static struct tipc_node *tipc_node_find(struct net *net, u32 addr);static void tipc_node_put(struct tipc_node *node);static bool tipc_node_is_up(struct tipc_node *n); struct tipc_sock_conn {	u32 port;	u32 peer_port;	u32 peer_node;	struct list_head list;}; static struct tipc_link *node_active_link(struct tipc_node *n, int sel){	int bearer_id = n->active_links[sel & 1]; 	if (unlikely(bearer_id == INVALID_BEARER_ID))		return NULL; 	return n->links[bearer_id].link;} int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel){	struct tipc_node *n;	int bearer_id;	unsigned int mtu = MAX_MSG_SIZE; 	n = tipc_node_find(net, addr);	if (unlikely(!n))		return mtu; 	bearer_id = n->active_links[sel & 1];	if (likely(bearer_id != INVALID_BEARER_ID))		mtu = n->links[bearer_id].mtu;	tipc_node_put(n);	return mtu;} u16 tipc_node_get_capabilities(struct net *net, u32 addr){	struct tipc_node *n;	u16 caps; 	n = tipc_node_find(net, addr);	if (unlikely(!n))		return TIPC_NODE_CAPABILITIES;	caps = n->capabilities;	tipc_node_put(n);	return caps;} /* * 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 *n = container_of(kref, struct tipc_node, kref); 	kfree(n->bc_entry.link);	kfree_rcu(n, rcu);} static 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 */static struct tipc_node *tipc_node_find(struct net *net, u32 addr){	struct tipc_net *tn = tipc_net(net);	struct tipc_node *node;	unsigned int thash = tipc_hashfn(addr); 	if (unlikely(!in_own_cluster_exact(net, addr)))		return NULL; 	rcu_read_lock();	hlist_for_each_entry_rcu(node, &tn->node_htable[thash], hash) {		if (node->addr != addr)			continue;		if (!kref_get_unless_zero(&node->kref))			node = NULL;		break;	}	rcu_read_unlock();	return node;} static void tipc_node_read_lock(struct tipc_node *n){	read_lock_bh(&n->lock);} static void tipc_node_read_unlock(struct tipc_node *n){	read_unlock_bh(&n->lock);} static void tipc_node_write_lock(struct tipc_node *n){	write_lock_bh(&n->lock);} static void tipc_node_write_unlock(struct tipc_node *n){	struct net *net = n->net;	u32 addr = 0;	u32 flags = n->action_flags;	u32 link_id = 0;	struct list_head *publ_list; 	if (likely(!flags)) {		write_unlock_bh(&n->lock);		return;	} 	addr = n->addr;	link_id = n->link_id;	publ_list = &n->publ_list; 	n->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |			     TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP); 	write_unlock_bh(&n->lock); 	if (flags & TIPC_NOTIFY_NODE_DOWN)		tipc_publ_notify(net, publ_list, addr); 	if (flags & TIPC_NOTIFY_NODE_UP)		tipc_named_node_up(net, addr); 	if (flags & TIPC_NOTIFY_LINK_UP)		tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,				     TIPC_NODE_SCOPE, link_id, addr); 	if (flags & TIPC_NOTIFY_LINK_DOWN)		tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,				      link_id, addr);} 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, *temp_node;	int i; 	spin_lock_bh(&tn->node_list_lock);	n = tipc_node_find(net, addr);	if (n) {		/* Same node may come back with new capabilities */		n->capabilities = capabilities;		goto exit;	}	n = kzalloc(sizeof(*n), GFP_ATOMIC);	if (!n) {		pr_warn("Node creation failed, no memory\n");		goto exit;	}	n->addr = addr;	n->net = net;	n->capabilities = capabilities;	kref_init(&n->kref);	rwlock_init(&n->lock);	INIT_HLIST_NODE(&n->hash);	INIT_LIST_HEAD(&n->list);	INIT_LIST_HEAD(&n->publ_list);	INIT_LIST_HEAD(&n->conn_sks);	skb_queue_head_init(&n->bc_entry.namedq);	skb_queue_head_init(&n->bc_entry.inputq1);	__skb_queue_head_init(&n->bc_entry.arrvq);	skb_queue_head_init(&n->bc_entry.inputq2);	for (i = 0; i < MAX_BEARERS; i++)		spin_lock_init(&n->links[i].lock);	n->state = SELF_DOWN_PEER_LEAVING;	n->signature = INVALID_NODE_SIG;	n->active_links[0] = INVALID_BEARER_ID;	n->active_links[1] = INVALID_BEARER_ID;	if (!tipc_link_bc_create(net, tipc_own_addr(net), n->addr,				 U16_MAX,				 tipc_link_window(tipc_bc_sndlink(net)),				 n->capabilities,				 &n->bc_entry.inputq1,				 &n->bc_entry.namedq,				 tipc_bc_sndlink(net),				 &n->bc_entry.link)) {		pr_warn("Broadcast rcv link creation failed, no memory\n");		kfree(n);		n = NULL;		goto exit;	}	tipc_node_get(n);	setup_timer(&n->timer, tipc_node_timeout, (unsigned long)n);	n->keepalive_intv = U32_MAX;	hlist_add_head_rcu(&n->hash, &tn->node_htable[tipc_hashfn(addr)]);	list_for_each_entry_rcu(temp_node, &tn->node_list, list) {		if (n->addr < temp_node->addr)			break;	}	list_add_tail_rcu(&n->list, &temp_node->list);exit:	spin_unlock_bh(&tn->node_list_lock);	return n;} static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l){	unsigned long tol = tipc_link_tolerance(l);	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 */	tipc_link_set_abort_limit(l, tol / 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);	tipc_node_put(node); 	del_timer_sync(&node->timer);	tipc_node_put(node);} void tipc_node_stop(struct net *net){	struct tipc_net *tn = tipc_net(net);	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)		tipc_node_delete(node);	spin_unlock_bh(&tn->node_list_lock);} void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr){	struct tipc_node *n; 	if (in_own_node(net, addr))		return; 	n = tipc_node_find(net, addr);	if (!n) {		pr_warn("Node subscribe rejected, unknown node 0x%x\n", addr);		return;	}	tipc_node_write_lock(n);	list_add_tail(subscr, &n->publ_list);	tipc_node_write_unlock(n);	tipc_node_put(n);} void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr){	struct tipc_node *n; 	if (in_own_node(net, addr))		return; 	n = tipc_node_find(net, addr);	if (!n) {		pr_warn("Node unsubscribe rejected, unknown node 0x%x\n", addr);		return;	}	tipc_node_write_lock(n);	list_del_init(subscr);	tipc_node_write_unlock(n);	tipc_node_put(n);} 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_write_lock(node);	list_add_tail(&conn->list, &node->conn_sks);	tipc_node_write_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_write_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_write_unlock(node);	tipc_node_put(node);}

Lines 1–500 of 1930.