973 lines · 24.8 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" static void node_lost_contact(struct tipc_node *n_ptr);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){	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;	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 sk_buff_head xmitq;	struct tipc_link *l;	struct tipc_media_addr *maddr;	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);		l = n->links[bearer_id].link;		if (l) {			/* Link tolerance may change asynchronously: */			tipc_node_calculate_timer(n, l);			rc = tipc_link_timeout(l, &xmitq);			if (rc & TIPC_LINK_DOWN_EVT)				tipc_link_reset(l);		}		tipc_node_unlock(n);		maddr = &n->links[bearer_id].maddr;		tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);	}	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 * * Link becomes active (alone or shared) or standby, depending on its priority. */void tipc_node_link_up(struct tipc_node *n, int bearer_id){	int *slot0 = &n->active_links[0];	int *slot1 = &n->active_links[1];	struct tipc_link_entry *links = n->links;	struct tipc_link *l = n->links[bearer_id].link; 	/* Leave room for tunnel header when returning 'mtu' to users: */	links[bearer_id].mtu = l->mtu - INT_H_SIZE; 	n->working_links++;	n->action_flags |= TIPC_NOTIFY_LINK_UP;	n->link_id = l->peer_bearer_id << 16 | l->bearer_id; 	pr_debug("Established link <%s> on network plane %c\n",		 l->name, l->net_plane); 	/* No active links ? => take both active slots */	if (*slot0 < 0) {		*slot0 = bearer_id;		*slot1 = bearer_id;		node_established_contact(n);		return;	} 	/* Lower prio than current active ? => no slot */	if (l->priority < links[*slot0].link->priority) {		pr_debug("New link <%s> becomes standby\n", l->name);		return;	}	tipc_link_dup_queue_xmit(links[*slot0].link, l); 	/* Same prio as current active ? => take one slot */	if (l->priority == links[*slot0].link->priority) {		*slot0 = bearer_id;		return;	} 	/* Higher prio than current active => take both active slots */	pr_debug("Old link <%s> now standby\n", links[*slot0].link->name);	*slot0 = bearer_id;	*slot1 = bearer_id;} /** * tipc_node_link_down - handle loss of link */void tipc_node_link_down(struct tipc_node *n, int bearer_id){	int *slot0 = &n->active_links[0];	int *slot1 = &n->active_links[1];	int i, highest = 0;	struct tipc_link *l, *_l; 	l = n->links[bearer_id].link;	n->working_links--;	n->action_flags |= TIPC_NOTIFY_LINK_DOWN;	n->link_id = l->peer_bearer_id << 16 | l->bearer_id; 	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->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_failover_send_queue(l);	else		node_lost_contact(n);} bool tipc_node_is_up(struct tipc_node *n){	return n->active_links[0] != INVALID_BEARER_ID;} void tipc_node_check_dest(struct tipc_node *n, struct tipc_bearer *b,			  bool *link_up, bool *addr_match,			  struct tipc_media_addr *maddr){	struct tipc_link *l = n->links[b->identity].link;	struct tipc_media_addr *curr = &n->links[b->identity].maddr; 	*link_up = l && tipc_link_is_up(l);	*addr_match = l && !memcmp(curr, maddr, sizeof(*maddr));} bool tipc_node_update_dest(struct tipc_node *n,  struct tipc_bearer *b,			   struct tipc_media_addr *maddr){	struct tipc_link *l = n->links[b->identity].link;	struct tipc_media_addr *curr = &n->links[b->identity].maddr;	struct sk_buff_head *inputq = &n->links[b->identity].inputq; 	if (!l) {		l = tipc_link_create(n, b, maddr, inputq, &n->bclink.namedq);		if (!l)			return false;		tipc_node_calculate_timer(n, l);		if (n->link_cnt == 1) {			if (!mod_timer(&n->timer, jiffies + n->keepalive_intv))				tipc_node_get(n);		}	}	memcpy(&l->media_addr, maddr, sizeof(*maddr));	memcpy(curr, maddr, sizeof(*maddr));	tipc_link_reset(l);	return true;} void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr){	n_ptr->links[l_ptr->bearer_id].link = l_ptr;	n_ptr->link_cnt++;} void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr){	int i; 	for (i = 0; i < MAX_BEARERS; i++) {		if (l_ptr != n_ptr->links[i].link)			continue;		n_ptr->links[i].link = NULL;		n_ptr->link_cnt--;	}} /* tipc_node_fsm_evt - node finite state machine * Determines when contact is allowed with peer node */static void tipc_node_fsm_evt(struct tipc_node *n, int evt){	int state = n->state; 	switch (state) {	case SELF_DOWN_PEER_DOWN:		switch (evt) {		case SELF_ESTABL_CONTACT_EVT:			state = SELF_UP_PEER_COMING;			break;		case PEER_ESTABL_CONTACT_EVT:			state = SELF_COMING_PEER_UP;			break;		case SELF_LOST_CONTACT_EVT:		case PEER_LOST_CONTACT_EVT:			break;		default:			pr_err("Unknown node fsm evt %x/%x\n", state, evt);		}		break;	case SELF_UP_PEER_UP:		switch (evt) {		case SELF_LOST_CONTACT_EVT:			state = SELF_DOWN_PEER_LEAVING;			break;		case PEER_LOST_CONTACT_EVT:			state = SELF_LEAVING_PEER_DOWN;			break;		case SELF_ESTABL_CONTACT_EVT:		case PEER_ESTABL_CONTACT_EVT:			break;		default:			pr_err("Unknown node fsm evt %x/%x\n", state, evt);		}		break;	case SELF_DOWN_PEER_LEAVING:		switch (evt) {		case PEER_LOST_CONTACT_EVT:			state = SELF_DOWN_PEER_DOWN;			break;		case SELF_ESTABL_CONTACT_EVT:		case PEER_ESTABL_CONTACT_EVT:		case SELF_LOST_CONTACT_EVT:			break;		default:			pr_err("Unknown node fsm evt %x/%x\n", state, evt);		}		break;	case SELF_UP_PEER_COMING:		switch (evt) {		case PEER_ESTABL_CONTACT_EVT:			state = SELF_UP_PEER_UP;			break;		case SELF_LOST_CONTACT_EVT:			state = SELF_DOWN_PEER_LEAVING;			break;		case SELF_ESTABL_CONTACT_EVT:		case PEER_LOST_CONTACT_EVT:			break;		default:			pr_err("Unknown node fsm evt %x/%x\n", state, evt);		}		break;	case SELF_COMING_PEER_UP:		switch (evt) {		case SELF_ESTABL_CONTACT_EVT:			state = SELF_UP_PEER_UP;			break;		case PEER_LOST_CONTACT_EVT:			state = SELF_LEAVING_PEER_DOWN;			break;		case SELF_LOST_CONTACT_EVT:

Lines 1–500 of 973.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t