diff options
Diffstat (limited to 'net/tipc')
36 files changed, 3245 insertions, 3251 deletions
| diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig index c890848f9d56..91c8a8e031db 100644 --- a/net/tipc/Kconfig +++ b/net/tipc/Kconfig @@ -20,18 +20,6 @@ menuconfig TIPC  	  If in doubt, say N. -config TIPC_PORTS -	int "Maximum number of ports in a node" -	depends on TIPC -	range 127 65535 -	default "8191" -	help -	  Specifies how many ports can be supported by a node. -	  Can range from 127 to 65535 ports; default is 8191. - -	  Setting this to a smaller value saves some memory, -	  setting it to higher allows for more ports. -  config TIPC_MEDIA_IB  	bool "InfiniBand media type support"  	depends on TIPC && INFINIBAND_IPOIB diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 333e4592772c..599b1a540d2b 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -4,11 +4,11 @@  obj-$(CONFIG_TIPC) := tipc.o -tipc-y	+= addr.o bcast.o bearer.o config.o \ +tipc-y	+= addr.o bcast.o bearer.o \  	   core.o link.o discover.o msg.o  \  	   name_distr.o  subscr.o name_table.o net.o  \ -	   netlink.o node.o socket.o log.o eth_media.o \ -	   server.o +	   netlink.o netlink_compat.o node.o socket.o eth_media.o \ +	   server.o socket.o  tipc-$(CONFIG_TIPC_MEDIA_IB)	+= ib_media.o  tipc-$(CONFIG_SYSCTL)		+= sysctl.o diff --git a/net/tipc/addr.c b/net/tipc/addr.c index 357b74b26f9e..48fd3b5a73fb 100644 --- a/net/tipc/addr.c +++ b/net/tipc/addr.c @@ -34,8 +34,51 @@   * POSSIBILITY OF SUCH DAMAGE.   */ -#include "core.h" +#include <linux/kernel.h>  #include "addr.h" +#include "core.h" + +/** + * in_own_cluster - test for cluster inclusion; <0.0.0> always matches + */ +int in_own_cluster(struct net *net, u32 addr) +{ +	return in_own_cluster_exact(net, addr) || !addr; +} + +int in_own_cluster_exact(struct net *net, u32 addr) +{ +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	return !((addr ^ tn->own_addr) >> 12); +} + +/** + * in_own_node - test for node inclusion; <0.0.0> always matches + */ +int in_own_node(struct net *net, u32 addr) +{ +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	return (addr == tn->own_addr) || !addr; +} + +/** + * addr_domain - convert 2-bit scope value to equivalent message lookup domain + * + * Needed when address of a named message must be looked up a second time + * after a network hop. + */ +u32 addr_domain(struct net *net, u32 sc) +{ +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	if (likely(sc == TIPC_NODE_SCOPE)) +		return tn->own_addr; +	if (sc == TIPC_CLUSTER_SCOPE) +		return tipc_cluster_mask(tn->own_addr); +	return tipc_zone_mask(tn->own_addr); +}  /**   * tipc_addr_domain_valid - validates a network domain address diff --git a/net/tipc/addr.h b/net/tipc/addr.h index a74acf9ee804..c700c2d28e09 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -37,7 +37,10 @@  #ifndef _TIPC_ADDR_H  #define _TIPC_ADDR_H -#include "core.h" +#include <linux/types.h> +#include <linux/tipc.h> +#include <net/net_namespace.h> +#include <net/netns/generic.h>  #define TIPC_ZONE_MASK		0xff000000u  #define TIPC_CLUSTER_MASK	0xfffff000u @@ -52,42 +55,10 @@ static inline u32 tipc_cluster_mask(u32 addr)  	return addr & TIPC_CLUSTER_MASK;  } -static inline int in_own_cluster_exact(u32 addr) -{ -	return !((addr ^ tipc_own_addr) >> 12); -} - -/** - * in_own_node - test for node inclusion; <0.0.0> always matches - */ -static inline int in_own_node(u32 addr) -{ -	return (addr == tipc_own_addr) || !addr; -} - -/** - * in_own_cluster - test for cluster inclusion; <0.0.0> always matches - */ -static inline int in_own_cluster(u32 addr) -{ -	return in_own_cluster_exact(addr) || !addr; -} - -/** - * addr_domain - convert 2-bit scope value to equivalent message lookup domain - * - * Needed when address of a named message must be looked up a second time - * after a network hop. - */ -static inline u32 addr_domain(u32 sc) -{ -	if (likely(sc == TIPC_NODE_SCOPE)) -		return tipc_own_addr; -	if (sc == TIPC_CLUSTER_SCOPE) -		return tipc_cluster_mask(tipc_own_addr); -	return tipc_zone_mask(tipc_own_addr); -} - +int in_own_cluster(struct net *net, u32 addr); +int in_own_cluster_exact(struct net *net, u32 addr); +int in_own_node(struct net *net, u32 addr); +u32 addr_domain(struct net *net, u32 sc);  int tipc_addr_domain_valid(u32);  int tipc_addr_node_valid(u32 addr);  int tipc_in_scope(u32 domain, u32 addr); diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index a9e174fc0f91..3e41704832de 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -1,7 +1,7 @@  /*   * net/tipc/bcast.c: TIPC broadcast code   * - * Copyright (c) 2004-2006, 2014, Ericsson AB + * Copyright (c) 2004-2006, 2014-2015, Ericsson AB   * Copyright (c) 2004, Intel Corporation.   * Copyright (c) 2005, 2010-2011, Wind River Systems   * All rights reserved. @@ -35,77 +35,14 @@   * POSSIBILITY OF SUCH DAMAGE.   */ -#include "core.h" -#include "link.h"  #include "socket.h"  #include "msg.h"  #include "bcast.h"  #include "name_distr.h" +#include "core.h"  #define	MAX_PKT_DEFAULT_MCAST	1500	/* bcast link max packet size (fixed) */  #define	BCLINK_WIN_DEFAULT	20	/* bcast link window size (default) */ -#define	BCBEARER		MAX_BEARERS - -/** - * struct tipc_bcbearer_pair - a pair of bearers used by broadcast link - * @primary: pointer to primary bearer - * @secondary: pointer to secondary bearer - * - * Bearers must have same priority and same set of reachable destinations - * to be paired. - */ - -struct tipc_bcbearer_pair { -	struct tipc_bearer *primary; -	struct tipc_bearer *secondary; -}; - -/** - * struct tipc_bcbearer - bearer used by broadcast link - * @bearer: (non-standard) broadcast bearer structure - * @media: (non-standard) broadcast media structure - * @bpairs: array of bearer pairs - * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort() - * @remains: temporary node map used by tipc_bcbearer_send() - * @remains_new: temporary node map used tipc_bcbearer_send() - * - * Note: The fields labelled "temporary" are incorporated into the bearer - * to avoid consuming potentially limited stack space through the use of - * large local variables within multicast routines.  Concurrent access is - * prevented through use of the spinlock "bclink_lock". - */ -struct tipc_bcbearer { -	struct tipc_bearer bearer; -	struct tipc_media media; -	struct tipc_bcbearer_pair bpairs[MAX_BEARERS]; -	struct tipc_bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1]; -	struct tipc_node_map remains; -	struct tipc_node_map remains_new; -}; - -/** - * struct tipc_bclink - link used for broadcast messages - * @lock: spinlock governing access to structure - * @link: (non-standard) broadcast link structure - * @node: (non-standard) node structure representing b'cast link's peer node - * @flags: represent bclink states - * @bcast_nodes: map of broadcast-capable nodes - * @retransmit_to: node that most recently requested a retransmit - * - * Handles sequence numbering, fragmentation, bundling, etc. - */ -struct tipc_bclink { -	spinlock_t lock; -	struct tipc_link link; -	struct tipc_node node; -	unsigned int flags; -	struct tipc_node_map bcast_nodes; -	struct tipc_node *retransmit_to; -}; - -static struct tipc_bcbearer *bcbearer; -static struct tipc_bclink *bclink; -static struct tipc_link *bcl;  const char tipc_bclink_name[] = "broadcast-link"; @@ -115,38 +52,50 @@ static void tipc_nmap_diff(struct tipc_node_map *nm_a,  static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);  static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node); -static void tipc_bclink_lock(void) +static void tipc_bclink_lock(struct net *net)  { -	spin_lock_bh(&bclink->lock); +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	spin_lock_bh(&tn->bclink->lock);  } -static void tipc_bclink_unlock(void) +static void tipc_bclink_unlock(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_node *node = NULL; -	if (likely(!bclink->flags)) { -		spin_unlock_bh(&bclink->lock); +	if (likely(!tn->bclink->flags)) { +		spin_unlock_bh(&tn->bclink->lock);  		return;  	} -	if (bclink->flags & TIPC_BCLINK_RESET) { -		bclink->flags &= ~TIPC_BCLINK_RESET; -		node = tipc_bclink_retransmit_to(); +	if (tn->bclink->flags & TIPC_BCLINK_RESET) { +		tn->bclink->flags &= ~TIPC_BCLINK_RESET; +		node = tipc_bclink_retransmit_to(net);  	} -	spin_unlock_bh(&bclink->lock); +	spin_unlock_bh(&tn->bclink->lock);  	if (node)  		tipc_link_reset_all(node);  } +void tipc_bclink_input(struct net *net) +{ +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	tipc_sk_mcast_rcv(net, &tn->bclink->arrvq, &tn->bclink->inputq); +} +  uint  tipc_bclink_get_mtu(void)  {  	return MAX_PKT_DEFAULT_MCAST;  } -void tipc_bclink_set_flags(unsigned int flags) +void tipc_bclink_set_flags(struct net *net, unsigned int flags)  { -	bclink->flags |= flags; +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	tn->bclink->flags |= flags;  }  static u32 bcbuf_acks(struct sk_buff *buf) @@ -164,31 +113,40 @@ static void bcbuf_decr_acks(struct sk_buff *buf)  	bcbuf_set_acks(buf, bcbuf_acks(buf) - 1);  } -void tipc_bclink_add_node(u32 addr) +void tipc_bclink_add_node(struct net *net, u32 addr)  { -	tipc_bclink_lock(); -	tipc_nmap_add(&bclink->bcast_nodes, addr); -	tipc_bclink_unlock(); +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	tipc_bclink_lock(net); +	tipc_nmap_add(&tn->bclink->bcast_nodes, addr); +	tipc_bclink_unlock(net);  } -void tipc_bclink_remove_node(u32 addr) +void tipc_bclink_remove_node(struct net *net, u32 addr)  { -	tipc_bclink_lock(); -	tipc_nmap_remove(&bclink->bcast_nodes, addr); -	tipc_bclink_unlock(); +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	tipc_bclink_lock(net); +	tipc_nmap_remove(&tn->bclink->bcast_nodes, addr); +	tipc_bclink_unlock(net);  } -static void bclink_set_last_sent(void) +static void bclink_set_last_sent(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl; +  	if (bcl->next_out)  		bcl->fsm_msg_cnt = mod(buf_seqno(bcl->next_out) - 1);  	else  		bcl->fsm_msg_cnt = mod(bcl->next_out_no - 1);  } -u32 tipc_bclink_get_last_sent(void) +u32 tipc_bclink_get_last_sent(struct net *net)  { -	return bcl->fsm_msg_cnt; +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	return tn->bcl->fsm_msg_cnt;  }  static void bclink_update_last_sent(struct tipc_node *node, u32 seqno) @@ -203,9 +161,11 @@ static void bclink_update_last_sent(struct tipc_node *node, u32 seqno)   *   * Called with bclink_lock locked   */ -struct tipc_node *tipc_bclink_retransmit_to(void) +struct tipc_node *tipc_bclink_retransmit_to(struct net *net)  { -	return bclink->retransmit_to; +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	return tn->bclink->retransmit_to;  }  /** @@ -215,9 +175,10 @@ struct tipc_node *tipc_bclink_retransmit_to(void)   *   * Called with bclink_lock locked   */ -static void bclink_retransmit_pkt(u32 after, u32 to) +static void bclink_retransmit_pkt(struct tipc_net *tn, u32 after, u32 to)  {  	struct sk_buff *skb; +	struct tipc_link *bcl = tn->bcl;  	skb_queue_walk(&bcl->outqueue, skb) {  		if (more(buf_seqno(skb), after)) { @@ -232,13 +193,11 @@ static void bclink_retransmit_pkt(u32 after, u32 to)   *   * Called with no locks taken   */ -void tipc_bclink_wakeup_users(void) +void tipc_bclink_wakeup_users(struct net *net)  { -	struct sk_buff *skb; - -	while ((skb = skb_dequeue(&bclink->link.waiting_sks))) -		tipc_sk_rcv(skb); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	tipc_sk_rcv(net, &tn->bclink->link.wakeupq);  }  /** @@ -253,10 +212,12 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)  	struct sk_buff *skb, *tmp;  	struct sk_buff *next;  	unsigned int released = 0; +	struct net *net = n_ptr->net; +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	tipc_bclink_lock(); +	tipc_bclink_lock(net);  	/* Bail out if tx queue is empty (no clean up is required) */ -	skb = skb_peek(&bcl->outqueue); +	skb = skb_peek(&tn->bcl->outqueue);  	if (!skb)  		goto exit; @@ -267,43 +228,43 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)  		 * acknowledge sent messages only (if other nodes still exist)  		 * or both sent and unsent messages (otherwise)  		 */ -		if (bclink->bcast_nodes.count) -			acked = bcl->fsm_msg_cnt; +		if (tn->bclink->bcast_nodes.count) +			acked = tn->bcl->fsm_msg_cnt;  		else -			acked = bcl->next_out_no; +			acked = tn->bcl->next_out_no;  	} else {  		/*  		 * Bail out if specified sequence number does not correspond  		 * to a message that has been sent and not yet acknowledged  		 */  		if (less(acked, buf_seqno(skb)) || -		    less(bcl->fsm_msg_cnt, acked) || +		    less(tn->bcl->fsm_msg_cnt, acked) ||  		    less_eq(acked, n_ptr->bclink.acked))  			goto exit;  	}  	/* Skip over packets that node has previously acknowledged */ -	skb_queue_walk(&bcl->outqueue, skb) { +	skb_queue_walk(&tn->bcl->outqueue, skb) {  		if (more(buf_seqno(skb), n_ptr->bclink.acked))  			break;  	}  	/* Update packets that node is now acknowledging */ -	skb_queue_walk_from_safe(&bcl->outqueue, skb, tmp) { +	skb_queue_walk_from_safe(&tn->bcl->outqueue, skb, tmp) {  		if (more(buf_seqno(skb), acked))  			break; -		next = tipc_skb_queue_next(&bcl->outqueue, skb); -		if (skb != bcl->next_out) { +		next = tipc_skb_queue_next(&tn->bcl->outqueue, skb); +		if (skb != tn->bcl->next_out) {  			bcbuf_decr_acks(skb);  		} else {  			bcbuf_set_acks(skb, 0); -			bcl->next_out = next; -			bclink_set_last_sent(); +			tn->bcl->next_out = next; +			bclink_set_last_sent(net);  		}  		if (bcbuf_acks(skb) == 0) { -			__skb_unlink(skb, &bcl->outqueue); +			__skb_unlink(skb, &tn->bcl->outqueue);  			kfree_skb(skb);  			released = 1;  		} @@ -311,15 +272,14 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)  	n_ptr->bclink.acked = acked;  	/* Try resolving broadcast link congestion, if necessary */ -	if (unlikely(bcl->next_out)) { -		tipc_link_push_packets(bcl); -		bclink_set_last_sent(); +	if (unlikely(tn->bcl->next_out)) { +		tipc_link_push_packets(tn->bcl); +		bclink_set_last_sent(net);  	} -	if (unlikely(released && !skb_queue_empty(&bcl->waiting_sks))) +	if (unlikely(released && !skb_queue_empty(&tn->bcl->wakeupq)))  		n_ptr->action_flags |= TIPC_WAKEUP_BCAST_USERS; -  exit: -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  }  /** @@ -327,9 +287,12 @@ exit:   *   * RCU and node lock set   */ -void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) +void tipc_bclink_update_link_state(struct tipc_node *n_ptr, +				   u32 last_sent)  {  	struct sk_buff *buf; +	struct net *net = n_ptr->net; +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	/* Ignore "stale" link state info */  	if (less_eq(last_sent, n_ptr->bclink.last_in)) @@ -359,18 +322,18 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)  		struct sk_buff *skb = skb_peek(&n_ptr->bclink.deferred_queue);  		u32 to = skb ? buf_seqno(skb) - 1 : n_ptr->bclink.last_sent; -		tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, +		tipc_msg_init(tn->own_addr, msg, BCAST_PROTOCOL, STATE_MSG,  			      INT_H_SIZE, n_ptr->addr);  		msg_set_non_seq(msg, 1); -		msg_set_mc_netid(msg, tipc_net_id); +		msg_set_mc_netid(msg, tn->net_id);  		msg_set_bcast_ack(msg, n_ptr->bclink.last_in);  		msg_set_bcgap_after(msg, n_ptr->bclink.last_in);  		msg_set_bcgap_to(msg, to); -		tipc_bclink_lock(); -		tipc_bearer_send(MAX_BEARERS, buf, NULL); -		bcl->stats.sent_nacks++; -		tipc_bclink_unlock(); +		tipc_bclink_lock(net); +		tipc_bearer_send(net, MAX_BEARERS, buf, NULL); +		tn->bcl->stats.sent_nacks++; +		tipc_bclink_unlock(net);  		kfree_skb(buf);  		n_ptr->bclink.oos_state++; @@ -383,9 +346,9 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)   * Delay any upcoming NACK by this node if another node has already   * requested the first message this node is going to ask for.   */ -static void bclink_peek_nack(struct tipc_msg *msg) +static void bclink_peek_nack(struct net *net, struct tipc_msg *msg)  { -	struct tipc_node *n_ptr = tipc_node_find(msg_destnode(msg)); +	struct tipc_node *n_ptr = tipc_node_find(net, msg_destnode(msg));  	if (unlikely(!n_ptr))  		return; @@ -400,17 +363,23 @@ static void bclink_peek_nack(struct tipc_msg *msg)  	tipc_node_unlock(n_ptr);  } -/* tipc_bclink_xmit - broadcast buffer chain to all nodes in cluster +/* tipc_bclink_xmit - deliver buffer chain to all nodes in cluster   *                    and to identified node local sockets + * @net: the applicable net namespace   * @list: chain of buffers containing message   * Consumes the buffer chain, except when returning -ELINKCONG   * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE   */ -int tipc_bclink_xmit(struct sk_buff_head *list) +int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl; +	struct tipc_bclink *bclink = tn->bclink;  	int rc = 0;  	int bc = 0;  	struct sk_buff *skb; +	struct sk_buff_head arrvq; +	struct sk_buff_head inputq;  	/* Prepare clone of message for local node */  	skb = tipc_msg_reassemble(list); @@ -419,32 +388,35 @@ int tipc_bclink_xmit(struct sk_buff_head *list)  		return -EHOSTUNREACH;  	} -	/* Broadcast to all other nodes */ +	/* Broadcast to all nodes */  	if (likely(bclink)) { -		tipc_bclink_lock(); +		tipc_bclink_lock(net);  		if (likely(bclink->bcast_nodes.count)) { -			rc = __tipc_link_xmit(bcl, list); +			rc = __tipc_link_xmit(net, bcl, list);  			if (likely(!rc)) {  				u32 len = skb_queue_len(&bcl->outqueue); -				bclink_set_last_sent(); +				bclink_set_last_sent(net);  				bcl->stats.queue_sz_counts++;  				bcl->stats.accu_queue_sz += len;  			}  			bc = 1;  		} -		tipc_bclink_unlock(); +		tipc_bclink_unlock(net);  	}  	if (unlikely(!bc))  		__skb_queue_purge(list); -	/* Deliver message clone */ -	if (likely(!rc)) -		tipc_sk_mcast_rcv(skb); -	else +	if (unlikely(rc)) {  		kfree_skb(skb); - +		return rc; +	} +	/* Deliver message clone */ +	__skb_queue_head_init(&arrvq); +	skb_queue_head_init(&inputq); +	__skb_queue_tail(&arrvq, skb); +	tipc_sk_mcast_rcv(net, &arrvq, &inputq);  	return rc;  } @@ -455,19 +427,21 @@ int tipc_bclink_xmit(struct sk_buff_head *list)   */  static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)  { +	struct tipc_net *tn = net_generic(node->net, tipc_net_id); +  	bclink_update_last_sent(node, seqno);  	node->bclink.last_in = seqno;  	node->bclink.oos_state = 0; -	bcl->stats.recv_info++; +	tn->bcl->stats.recv_info++;  	/*  	 * Unicast an ACK periodically, ensuring that  	 * all nodes in the cluster don't ACK at the same time  	 */ -	if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) { +	if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {  		tipc_link_proto_xmit(node->active_links[node->addr & 1],  				     STATE_MSG, 0, 0, 0, 0, 0); -		bcl->stats.sent_acks++; +		tn->bcl->stats.sent_acks++;  	}  } @@ -476,19 +450,24 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)   *   * RCU is locked, no other locks set   */ -void tipc_bclink_rcv(struct sk_buff *buf) +void tipc_bclink_rcv(struct net *net, struct sk_buff *buf)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl;  	struct tipc_msg *msg = buf_msg(buf);  	struct tipc_node *node;  	u32 next_in;  	u32 seqno;  	int deferred = 0; +	int pos = 0; +	struct sk_buff *iskb; +	struct sk_buff_head *arrvq, *inputq;  	/* Screen out unwanted broadcast messages */ -	if (msg_mc_netid(msg) != tipc_net_id) +	if (msg_mc_netid(msg) != tn->net_id)  		goto exit; -	node = tipc_node_find(msg_prevnode(msg)); +	node = tipc_node_find(net, msg_prevnode(msg));  	if (unlikely(!node))  		goto exit; @@ -500,18 +479,18 @@ void tipc_bclink_rcv(struct sk_buff *buf)  	if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) {  		if (msg_type(msg) != STATE_MSG)  			goto unlock; -		if (msg_destnode(msg) == tipc_own_addr) { +		if (msg_destnode(msg) == tn->own_addr) {  			tipc_bclink_acknowledge(node, msg_bcast_ack(msg));  			tipc_node_unlock(node); -			tipc_bclink_lock(); +			tipc_bclink_lock(net);  			bcl->stats.recv_nacks++; -			bclink->retransmit_to = node; -			bclink_retransmit_pkt(msg_bcgap_after(msg), +			tn->bclink->retransmit_to = node; +			bclink_retransmit_pkt(tn, msg_bcgap_after(msg),  					      msg_bcgap_to(msg)); -			tipc_bclink_unlock(); +			tipc_bclink_unlock(net);  		} else {  			tipc_node_unlock(node); -			bclink_peek_nack(msg); +			bclink_peek_nack(net, msg);  		}  		goto exit;  	} @@ -519,52 +498,54 @@ void tipc_bclink_rcv(struct sk_buff *buf)  	/* Handle in-sequence broadcast message */  	seqno = msg_seqno(msg);  	next_in = mod(node->bclink.last_in + 1); +	arrvq = &tn->bclink->arrvq; +	inputq = &tn->bclink->inputq;  	if (likely(seqno == next_in)) {  receive:  		/* Deliver message to destination */  		if (likely(msg_isdata(msg))) { -			tipc_bclink_lock(); +			tipc_bclink_lock(net);  			bclink_accept_pkt(node, seqno); -			tipc_bclink_unlock(); +			spin_lock_bh(&inputq->lock); +			__skb_queue_tail(arrvq, buf); +			spin_unlock_bh(&inputq->lock); +			node->action_flags |= TIPC_BCAST_MSG_EVT; +			tipc_bclink_unlock(net);  			tipc_node_unlock(node); -			if (likely(msg_mcast(msg))) -				tipc_sk_mcast_rcv(buf); -			else -				kfree_skb(buf);  		} else if (msg_user(msg) == MSG_BUNDLER) { -			tipc_bclink_lock(); +			tipc_bclink_lock(net);  			bclink_accept_pkt(node, seqno);  			bcl->stats.recv_bundles++;  			bcl->stats.recv_bundled += msg_msgcnt(msg); -			tipc_bclink_unlock(); +			pos = 0; +			while (tipc_msg_extract(buf, &iskb, &pos)) { +				spin_lock_bh(&inputq->lock); +				__skb_queue_tail(arrvq, iskb); +				spin_unlock_bh(&inputq->lock); +			} +			node->action_flags |= TIPC_BCAST_MSG_EVT; +			tipc_bclink_unlock(net);  			tipc_node_unlock(node); -			tipc_link_bundle_rcv(buf);  		} else if (msg_user(msg) == MSG_FRAGMENTER) {  			tipc_buf_append(&node->bclink.reasm_buf, &buf);  			if (unlikely(!buf && !node->bclink.reasm_buf))  				goto unlock; -			tipc_bclink_lock(); +			tipc_bclink_lock(net);  			bclink_accept_pkt(node, seqno);  			bcl->stats.recv_fragments++;  			if (buf) {  				bcl->stats.recv_fragmented++;  				msg = buf_msg(buf); -				tipc_bclink_unlock(); +				tipc_bclink_unlock(net);  				goto receive;  			} -			tipc_bclink_unlock(); -			tipc_node_unlock(node); -		} else if (msg_user(msg) == NAME_DISTRIBUTOR) { -			tipc_bclink_lock(); -			bclink_accept_pkt(node, seqno); -			tipc_bclink_unlock(); +			tipc_bclink_unlock(net);  			tipc_node_unlock(node); -			tipc_named_rcv(buf);  		} else { -			tipc_bclink_lock(); +			tipc_bclink_lock(net);  			bclink_accept_pkt(node, seqno); -			tipc_bclink_unlock(); +			tipc_bclink_unlock(net);  			tipc_node_unlock(node);  			kfree_skb(buf);  		} @@ -602,14 +583,14 @@ receive:  		buf = NULL;  	} -	tipc_bclink_lock(); +	tipc_bclink_lock(net);  	if (deferred)  		bcl->stats.deferred_recv++;  	else  		bcl->stats.duplicates++; -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  unlock:  	tipc_node_unlock(node); @@ -620,7 +601,7 @@ exit:  u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)  {  	return (n_ptr->bclink.recv_permitted && -		(tipc_bclink_get_last_sent() != n_ptr->bclink.acked)); +		(tipc_bclink_get_last_sent(n_ptr->net) != n_ptr->bclink.acked));  } @@ -633,11 +614,15 @@ u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)   * Returns 0 (packet sent successfully) under all circumstances,   * since the broadcast link's pseudo-bearer never blocks   */ -static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1, +static int tipc_bcbearer_send(struct net *net, struct sk_buff *buf, +			      struct tipc_bearer *unused1,  			      struct tipc_media_addr *unused2)  {  	int bp_index;  	struct tipc_msg *msg = buf_msg(buf); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_bcbearer *bcbearer = tn->bcbearer; +	struct tipc_bclink *bclink = tn->bclink;  	/* Prepare broadcast link message for reliable transmission,  	 * if first time trying to send it; @@ -647,8 +632,8 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,  	if (likely(!msg_non_seq(buf_msg(buf)))) {  		bcbuf_set_acks(buf, bclink->bcast_nodes.count);  		msg_set_non_seq(msg, 1); -		msg_set_mc_netid(msg, tipc_net_id); -		bcl->stats.sent_info++; +		msg_set_mc_netid(msg, tn->net_id); +		tn->bcl->stats.sent_info++;  		if (WARN_ON(!bclink->bcast_nodes.count)) {  			dump_stack(); @@ -677,13 +662,14 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,  		if (bp_index == 0) {  			/* Use original buffer for first bearer */ -			tipc_bearer_send(b->identity, buf, &b->bcast_addr); +			tipc_bearer_send(net, b->identity, buf, &b->bcast_addr);  		} else {  			/* Avoid concurrent buffer access */  			tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC);  			if (!tbuf)  				break; -			tipc_bearer_send(b->identity, tbuf, &b->bcast_addr); +			tipc_bearer_send(net, b->identity, tbuf, +					 &b->bcast_addr);  			kfree_skb(tbuf); /* Bearer keeps a clone */  		}  		if (bcbearer->remains_new.count == 0) @@ -698,15 +684,18 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,  /**   * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer   */ -void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action) +void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, +			u32 node, bool action)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_bcbearer *bcbearer = tn->bcbearer;  	struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;  	struct tipc_bcbearer_pair *bp_curr;  	struct tipc_bearer *b;  	int b_index;  	int pri; -	tipc_bclink_lock(); +	tipc_bclink_lock(net);  	if (action)  		tipc_nmap_add(nm_ptr, node); @@ -718,7 +707,7 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)  	rcu_read_lock();  	for (b_index = 0; b_index < MAX_BEARERS; b_index++) { -		b = rcu_dereference_rtnl(bearer_list[b_index]); +		b = rcu_dereference_rtnl(tn->bearer_list[b_index]);  		if (!b || !b->nodes.count)  			continue; @@ -753,7 +742,7 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)  		bp_curr++;  	} -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  }  static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb, @@ -807,19 +796,21 @@ msg_full:  	return -EMSGSIZE;  } -int tipc_nl_add_bc_link(struct tipc_nl_msg *msg) +int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)  {  	int err;  	void *hdr;  	struct nlattr *attrs;  	struct nlattr *prop; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl;  	if (!bcl)  		return 0; -	tipc_bclink_lock(); +	tipc_bclink_lock(net); -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_LINK_GET);  	if (!hdr)  		return -EMSGSIZE; @@ -852,7 +843,7 @@ int tipc_nl_add_bc_link(struct tipc_nl_msg *msg)  	if (err)  		goto attr_msg_full; -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  	nla_nest_end(msg->skb, attrs);  	genlmsg_end(msg->skb, hdr); @@ -863,79 +854,49 @@ prop_msg_full:  attr_msg_full:  	nla_nest_cancel(msg->skb, attrs);  msg_full: -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  	genlmsg_cancel(msg->skb, hdr);  	return -EMSGSIZE;  } -int tipc_bclink_stats(char *buf, const u32 buf_size) +int tipc_bclink_reset_stats(struct net *net)  { -	int ret; -	struct tipc_stats *s; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl;  	if (!bcl) -		return 0; - -	tipc_bclink_lock(); - -	s = &bcl->stats; - -	ret = tipc_snprintf(buf, buf_size, "Link <%s>\n" -			    "  Window:%u packets\n", -			    bcl->name, bcl->queue_limit[0]); -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  RX packets:%u fragments:%u/%u bundles:%u/%u\n", -			     s->recv_info, s->recv_fragments, -			     s->recv_fragmented, s->recv_bundles, -			     s->recv_bundled); -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  TX packets:%u fragments:%u/%u bundles:%u/%u\n", -			     s->sent_info, s->sent_fragments, -			     s->sent_fragmented, s->sent_bundles, -			     s->sent_bundled); -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  RX naks:%u defs:%u dups:%u\n", -			     s->recv_nacks, s->deferred_recv, s->duplicates); -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  TX naks:%u acks:%u dups:%u\n", -			     s->sent_nacks, s->sent_acks, s->retransmitted); -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  Congestion link:%u  Send queue max:%u avg:%u\n", -			     s->link_congs, s->max_queue_sz, -			     s->queue_sz_counts ? -			     (s->accu_queue_sz / s->queue_sz_counts) : 0); - -	tipc_bclink_unlock(); -	return ret; -} - -int tipc_bclink_reset_stats(void) -{ -	if (!bcl)  		return -ENOPROTOOPT; -	tipc_bclink_lock(); +	tipc_bclink_lock(net);  	memset(&bcl->stats, 0, sizeof(bcl->stats)); -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  	return 0;  } -int tipc_bclink_set_queue_limits(u32 limit) +int tipc_bclink_set_queue_limits(struct net *net, u32 limit)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *bcl = tn->bcl; +  	if (!bcl)  		return -ENOPROTOOPT;  	if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN))  		return -EINVAL; -	tipc_bclink_lock(); +	tipc_bclink_lock(net);  	tipc_link_set_queue_limits(bcl, limit); -	tipc_bclink_unlock(); +	tipc_bclink_unlock(net);  	return 0;  } -int tipc_bclink_init(void) +int tipc_bclink_init(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_bcbearer *bcbearer; +	struct tipc_bclink *bclink; +	struct tipc_link *bcl; +  	bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC);  	if (!bcbearer)  		return -ENOMEM; @@ -954,30 +915,39 @@ int tipc_bclink_init(void)  	spin_lock_init(&bclink->lock);  	__skb_queue_head_init(&bcl->outqueue);  	__skb_queue_head_init(&bcl->deferred_queue); -	skb_queue_head_init(&bcl->waiting_sks); +	skb_queue_head_init(&bcl->wakeupq);  	bcl->next_out_no = 1;  	spin_lock_init(&bclink->node.lock); -	__skb_queue_head_init(&bclink->node.waiting_sks); +	__skb_queue_head_init(&bclink->arrvq); +	skb_queue_head_init(&bclink->inputq);  	bcl->owner = &bclink->node; +	bcl->owner->net = net;  	bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;  	tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);  	bcl->bearer_id = MAX_BEARERS; -	rcu_assign_pointer(bearer_list[MAX_BEARERS], &bcbearer->bearer); +	rcu_assign_pointer(tn->bearer_list[MAX_BEARERS], &bcbearer->bearer);  	bcl->state = WORKING_WORKING; +	bcl->pmsg = (struct tipc_msg *)&bcl->proto_msg; +	msg_set_prevnode(bcl->pmsg, tn->own_addr);  	strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME); +	tn->bcbearer = bcbearer; +	tn->bclink = bclink; +	tn->bcl = bcl;  	return 0;  } -void tipc_bclink_stop(void) +void tipc_bclink_stop(struct net *net)  { -	tipc_bclink_lock(); -	tipc_link_purge_queues(bcl); -	tipc_bclink_unlock(); +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	RCU_INIT_POINTER(bearer_list[BCBEARER], NULL); +	tipc_bclink_lock(net); +	tipc_link_purge_queues(tn->bcl); +	tipc_bclink_unlock(net); + +	RCU_INIT_POINTER(tn->bearer_list[BCBEARER], NULL);  	synchronize_net(); -	kfree(bcbearer); -	kfree(bclink); +	kfree(tn->bcbearer); +	kfree(tn->bclink);  }  /** @@ -1037,50 +1007,3 @@ static void tipc_nmap_diff(struct tipc_node_map *nm_a,  		}  	}  } - -/** - * tipc_port_list_add - add a port to a port list, ensuring no duplicates - */ -void tipc_port_list_add(struct tipc_port_list *pl_ptr, u32 port) -{ -	struct tipc_port_list *item = pl_ptr; -	int i; -	int item_sz = PLSIZE; -	int cnt = pl_ptr->count; - -	for (; ; cnt -= item_sz, item = item->next) { -		if (cnt < PLSIZE) -			item_sz = cnt; -		for (i = 0; i < item_sz; i++) -			if (item->ports[i] == port) -				return; -		if (i < PLSIZE) { -			item->ports[i] = port; -			pl_ptr->count++; -			return; -		} -		if (!item->next) { -			item->next = kmalloc(sizeof(*item), GFP_ATOMIC); -			if (!item->next) { -				pr_warn("Incomplete multicast delivery, no memory\n"); -				return; -			} -			item->next->next = NULL; -		} -	} -} - -/** - * tipc_port_list_free - free dynamically created entries in port_list chain - * - */ -void tipc_port_list_free(struct tipc_port_list *pl_ptr) -{ -	struct tipc_port_list *item; -	struct tipc_port_list *next; - -	for (item = pl_ptr->next; item; item = next) { -		next = item->next; -		kfree(item); -	} -} diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 644d79129fba..43f397fbac55 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -1,7 +1,7 @@  /*   * net/tipc/bcast.h: Include file for TIPC broadcast code   * - * Copyright (c) 2003-2006, 2014, Ericsson AB + * Copyright (c) 2003-2006, 2014-2015, Ericsson AB   * Copyright (c) 2005, 2010-2011, Wind River Systems   * All rights reserved.   * @@ -37,39 +37,73 @@  #ifndef _TIPC_BCAST_H  #define _TIPC_BCAST_H -#include "netlink.h" - -#define MAX_NODES 4096 -#define WSIZE 32 -#define TIPC_BCLINK_RESET 1 +#include <linux/tipc_config.h> +#include "link.h" +#include "node.h"  /** - * struct tipc_node_map - set of node identifiers - * @count: # of nodes in set - * @map: bitmap of node identifiers that are in the set + * struct tipc_bcbearer_pair - a pair of bearers used by broadcast link + * @primary: pointer to primary bearer + * @secondary: pointer to secondary bearer + * + * Bearers must have same priority and same set of reachable destinations + * to be paired.   */ -struct tipc_node_map { -	u32 count; -	u32 map[MAX_NODES / WSIZE]; + +struct tipc_bcbearer_pair { +	struct tipc_bearer *primary; +	struct tipc_bearer *secondary;  }; -#define PLSIZE 32 +#define TIPC_BCLINK_RESET	1 +#define	BCBEARER		MAX_BEARERS  /** - * struct tipc_port_list - set of node local destination ports - * @count: # of ports in set (only valid for first entry in list) - * @next: pointer to next entry in list - * @ports: array of port references + * struct tipc_bcbearer - bearer used by broadcast link + * @bearer: (non-standard) broadcast bearer structure + * @media: (non-standard) broadcast media structure + * @bpairs: array of bearer pairs + * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort() + * @remains: temporary node map used by tipc_bcbearer_send() + * @remains_new: temporary node map used tipc_bcbearer_send() + * + * Note: The fields labelled "temporary" are incorporated into the bearer + * to avoid consuming potentially limited stack space through the use of + * large local variables within multicast routines.  Concurrent access is + * prevented through use of the spinlock "bclink_lock".   */ -struct tipc_port_list { -	int count; -	struct tipc_port_list *next; -	u32 ports[PLSIZE]; +struct tipc_bcbearer { +	struct tipc_bearer bearer; +	struct tipc_media media; +	struct tipc_bcbearer_pair bpairs[MAX_BEARERS]; +	struct tipc_bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1]; +	struct tipc_node_map remains; +	struct tipc_node_map remains_new;  }; +/** + * struct tipc_bclink - link used for broadcast messages + * @lock: spinlock governing access to structure + * @link: (non-standard) broadcast link structure + * @node: (non-standard) node structure representing b'cast link's peer node + * @flags: represent bclink states + * @bcast_nodes: map of broadcast-capable nodes + * @retransmit_to: node that most recently requested a retransmit + * + * Handles sequence numbering, fragmentation, bundling, etc. + */ +struct tipc_bclink { +	spinlock_t lock; +	struct tipc_link link; +	struct tipc_node node; +	unsigned int flags; +	struct sk_buff_head arrvq; +	struct sk_buff_head inputq; +	struct tipc_node_map bcast_nodes; +	struct tipc_node *retransmit_to; +};  struct tipc_node; -  extern const char tipc_bclink_name[];  /** @@ -81,27 +115,26 @@ static inline int tipc_nmap_equal(struct tipc_node_map *nm_a,  	return !memcmp(nm_a, nm_b, sizeof(*nm_a));  } -void tipc_port_list_add(struct tipc_port_list *pl_ptr, u32 port); -void tipc_port_list_free(struct tipc_port_list *pl_ptr); - -int tipc_bclink_init(void); -void tipc_bclink_stop(void); -void tipc_bclink_set_flags(unsigned int flags); -void tipc_bclink_add_node(u32 addr); -void tipc_bclink_remove_node(u32 addr); -struct tipc_node *tipc_bclink_retransmit_to(void); +int tipc_bclink_init(struct net *net); +void tipc_bclink_stop(struct net *net); +void tipc_bclink_set_flags(struct net *tn, unsigned int flags); +void tipc_bclink_add_node(struct net *net, u32 addr); +void tipc_bclink_remove_node(struct net *net, u32 addr); +struct tipc_node *tipc_bclink_retransmit_to(struct net *tn);  void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked); -void tipc_bclink_rcv(struct sk_buff *buf); -u32  tipc_bclink_get_last_sent(void); +void tipc_bclink_rcv(struct net *net, struct sk_buff *buf); +u32  tipc_bclink_get_last_sent(struct net *net);  u32  tipc_bclink_acks_missing(struct tipc_node *n_ptr); -void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent); -int  tipc_bclink_stats(char *stats_buf, const u32 buf_size); -int  tipc_bclink_reset_stats(void); -int  tipc_bclink_set_queue_limits(u32 limit); -void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action); +void tipc_bclink_update_link_state(struct tipc_node *node, +				   u32 last_sent); +int  tipc_bclink_reset_stats(struct net *net); +int  tipc_bclink_set_queue_limits(struct net *net, u32 limit); +void tipc_bcbearer_sort(struct net *net, struct tipc_node_map *nm_ptr, +			u32 node, bool action);  uint  tipc_bclink_get_mtu(void); -int tipc_bclink_xmit(struct sk_buff_head *list); -void tipc_bclink_wakeup_users(void); -int tipc_nl_add_bc_link(struct tipc_nl_msg *msg); +int tipc_bclink_xmit(struct net *net, struct sk_buff_head *list); +void tipc_bclink_wakeup_users(struct net *net); +int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg); +void tipc_bclink_input(struct net *net);  #endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 463db5b15b8b..48852c2dcc03 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -34,11 +34,12 @@   * POSSIBILITY OF SUCH DAMAGE.   */ +#include <net/sock.h>  #include "core.h" -#include "config.h"  #include "bearer.h"  #include "link.h"  #include "discover.h" +#include "bcast.h"  #define MAX_ADDR_STR 60 @@ -67,9 +68,8 @@ static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = {  	[TIPC_NLA_MEDIA_PROP]		= { .type = NLA_NESTED }  }; -struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; - -static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down); +static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, +			   bool shutting_down);  /**   * tipc_media_find - locates specified media object by name @@ -111,38 +111,18 @@ void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)  	m_ptr = media_find_id(a->media_id);  	if (m_ptr && !m_ptr->addr2str(a, addr_str, sizeof(addr_str))) -		ret = tipc_snprintf(buf, len, "%s(%s)", m_ptr->name, addr_str); +		ret = scnprintf(buf, len, "%s(%s)", m_ptr->name, addr_str);  	else {  		u32 i; -		ret = tipc_snprintf(buf, len, "UNKNOWN(%u)", a->media_id); +		ret = scnprintf(buf, len, "UNKNOWN(%u)", a->media_id);  		for (i = 0; i < sizeof(a->value); i++) -			ret += tipc_snprintf(buf - ret, len + ret, +			ret += scnprintf(buf - ret, len + ret,  					    "-%02x", a->value[i]);  	}  }  /** - * tipc_media_get_names - record names of registered media in buffer - */ -struct sk_buff *tipc_media_get_names(void) -{ -	struct sk_buff *buf; -	int i; - -	buf = tipc_cfg_reply_alloc(MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME)); -	if (!buf) -		return NULL; - -	for (i = 0; media_info_array[i] != NULL; i++) { -		tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME, -				    media_info_array[i]->name, -				    strlen(media_info_array[i]->name) + 1); -	} -	return buf; -} - -/**   * bearer_name_validate - validate & (optionally) deconstruct bearer name   * @name: ptr to bearer name string   * @name_parts: ptr to area for bearer name components (or NULL if not needed) @@ -190,68 +170,43 @@ static int bearer_name_validate(const char *name,  /**   * tipc_bearer_find - locates bearer object with matching bearer name   */ -struct tipc_bearer *tipc_bearer_find(const char *name) +struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	u32 i;  	for (i = 0; i < MAX_BEARERS; i++) { -		b_ptr = rtnl_dereference(bearer_list[i]); +		b_ptr = rtnl_dereference(tn->bearer_list[i]);  		if (b_ptr && (!strcmp(b_ptr->name, name)))  			return b_ptr;  	}  	return NULL;  } -/** - * tipc_bearer_get_names - record names of bearers in buffer - */ -struct sk_buff *tipc_bearer_get_names(void) -{ -	struct sk_buff *buf; -	struct tipc_bearer *b; -	int i, j; - -	buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME)); -	if (!buf) -		return NULL; - -	for (i = 0; media_info_array[i] != NULL; i++) { -		for (j = 0; j < MAX_BEARERS; j++) { -			b = rtnl_dereference(bearer_list[j]); -			if (!b) -				continue; -			if (b->media == media_info_array[i]) { -				tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME, -						    b->name, -						    strlen(b->name) + 1); -			} -		} -	} -	return buf; -} - -void tipc_bearer_add_dest(u32 bearer_id, u32 dest) +void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	rcu_read_lock(); -	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); +	b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);  	if (b_ptr) { -		tipc_bcbearer_sort(&b_ptr->nodes, dest, true); +		tipc_bcbearer_sort(net, &b_ptr->nodes, dest, true);  		tipc_disc_add_dest(b_ptr->link_req);  	}  	rcu_read_unlock();  } -void tipc_bearer_remove_dest(u32 bearer_id, u32 dest) +void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	rcu_read_lock(); -	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); +	b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);  	if (b_ptr) { -		tipc_bcbearer_sort(&b_ptr->nodes, dest, false); +		tipc_bcbearer_sort(net, &b_ptr->nodes, dest, false);  		tipc_disc_remove_dest(b_ptr->link_req);  	}  	rcu_read_unlock(); @@ -260,8 +215,10 @@ void tipc_bearer_remove_dest(u32 bearer_id, u32 dest)  /**   * tipc_enable_bearer - enable bearer with the given name   */ -int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority) +static int tipc_enable_bearer(struct net *net, const char *name, +			      u32 disc_domain, u32 priority)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	struct tipc_media *m_ptr;  	struct tipc_bearer_names b_names; @@ -271,7 +228,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)  	u32 i;  	int res = -EINVAL; -	if (!tipc_own_addr) { +	if (!tn->own_addr) {  		pr_warn("Bearer <%s> rejected, not supported in standalone mode\n",  			name);  		return -ENOPROTOOPT; @@ -281,11 +238,11 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)  		return -EINVAL;  	}  	if (tipc_addr_domain_valid(disc_domain) && -	    (disc_domain != tipc_own_addr)) { -		if (tipc_in_scope(disc_domain, tipc_own_addr)) { -			disc_domain = tipc_own_addr & TIPC_CLUSTER_MASK; +	    (disc_domain != tn->own_addr)) { +		if (tipc_in_scope(disc_domain, tn->own_addr)) { +			disc_domain = tn->own_addr & TIPC_CLUSTER_MASK;  			res = 0;   /* accept any node in own cluster */ -		} else if (in_own_cluster_exact(disc_domain)) +		} else if (in_own_cluster_exact(net, disc_domain))  			res = 0;   /* accept specified node in own cluster */  	}  	if (res) { @@ -313,7 +270,7 @@ restart:  	bearer_id = MAX_BEARERS;  	with_this_prio = 1;  	for (i = MAX_BEARERS; i-- != 0; ) { -		b_ptr = rtnl_dereference(bearer_list[i]); +		b_ptr = rtnl_dereference(tn->bearer_list[i]);  		if (!b_ptr) {  			bearer_id = i;  			continue; @@ -347,7 +304,7 @@ restart:  	strcpy(b_ptr->name, name);  	b_ptr->media = m_ptr; -	res = m_ptr->enable_media(b_ptr); +	res = m_ptr->enable_media(net, b_ptr);  	if (res) {  		pr_warn("Bearer <%s> rejected, enable failure (%d)\n",  			name, -res); @@ -361,15 +318,15 @@ restart:  	b_ptr->net_plane = bearer_id + 'A';  	b_ptr->priority = priority; -	res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr); +	res = tipc_disc_create(net, b_ptr, &b_ptr->bcast_addr);  	if (res) { -		bearer_disable(b_ptr, false); +		bearer_disable(net, b_ptr, false);  		pr_warn("Bearer <%s> rejected, discovery object creation failed\n",  			name);  		return -EINVAL;  	} -	rcu_assign_pointer(bearer_list[bearer_id], b_ptr); +	rcu_assign_pointer(tn->bearer_list[bearer_id], b_ptr);  	pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",  		name, @@ -380,11 +337,11 @@ restart:  /**   * tipc_reset_bearer - Reset all links established over this bearer   */ -static int tipc_reset_bearer(struct tipc_bearer *b_ptr) +static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr)  {  	pr_info("Resetting bearer <%s>\n", b_ptr->name); -	tipc_link_reset_list(b_ptr->identity); -	tipc_disc_reset(b_ptr); +	tipc_link_reset_list(net, b_ptr->identity); +	tipc_disc_reset(net, b_ptr);  	return 0;  } @@ -393,49 +350,35 @@ static int tipc_reset_bearer(struct tipc_bearer *b_ptr)   *   * Note: This routine assumes caller holds RTNL lock.   */ -static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down) +static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, +			   bool shutting_down)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	u32 i;  	pr_info("Disabling bearer <%s>\n", b_ptr->name);  	b_ptr->media->disable_media(b_ptr); -	tipc_link_delete_list(b_ptr->identity, shutting_down); +	tipc_link_delete_list(net, b_ptr->identity, shutting_down);  	if (b_ptr->link_req)  		tipc_disc_delete(b_ptr->link_req);  	for (i = 0; i < MAX_BEARERS; i++) { -		if (b_ptr == rtnl_dereference(bearer_list[i])) { -			RCU_INIT_POINTER(bearer_list[i], NULL); +		if (b_ptr == rtnl_dereference(tn->bearer_list[i])) { +			RCU_INIT_POINTER(tn->bearer_list[i], NULL);  			break;  		}  	}  	kfree_rcu(b_ptr, rcu);  } -int tipc_disable_bearer(const char *name) -{ -	struct tipc_bearer *b_ptr; -	int res; - -	b_ptr = tipc_bearer_find(name); -	if (b_ptr == NULL) { -		pr_warn("Attempt to disable unknown bearer <%s>\n", name); -		res = -EINVAL; -	} else { -		bearer_disable(b_ptr, false); -		res = 0; -	} -	return res; -} - -int tipc_enable_l2_media(struct tipc_bearer *b) +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b)  {  	struct net_device *dev;  	char *driver_name = strchr((const char *)b->name, ':') + 1;  	/* Find device with specified name */ -	dev = dev_get_by_name(&init_net, driver_name); +	dev = dev_get_by_name(net, driver_name);  	if (!dev)  		return -ENODEV; @@ -474,8 +417,8 @@ void tipc_disable_l2_media(struct tipc_bearer *b)   * @b_ptr: the bearer through which the packet is to be sent   * @dest: peer destination address   */ -int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, -		     struct tipc_media_addr *dest) +int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, +		     struct tipc_bearer *b, struct tipc_media_addr *dest)  {  	struct sk_buff *clone;  	struct net_device *dev; @@ -511,15 +454,16 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,   * The media send routine must not alter the buffer being passed in   * as it may be needed for later retransmission!   */ -void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf, +void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf,  		      struct tipc_media_addr *dest)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	rcu_read_lock(); -	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]); +	b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);  	if (likely(b_ptr)) -		b_ptr->media->send_msg(buf, b_ptr, dest); +		b_ptr->media->send_msg(net, buf, b_ptr, dest);  	rcu_read_unlock();  } @@ -539,17 +483,12 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,  {  	struct tipc_bearer *b_ptr; -	if (!net_eq(dev_net(dev), &init_net)) { -		kfree_skb(buf); -		return NET_RX_DROP; -	} -  	rcu_read_lock();  	b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);  	if (likely(b_ptr)) {  		if (likely(buf->pkt_type <= PACKET_BROADCAST)) {  			buf->next = NULL; -			tipc_rcv(buf, b_ptr); +			tipc_rcv(dev_net(dev), buf, b_ptr);  			rcu_read_unlock();  			return NET_RX_SUCCESS;  		} @@ -572,11 +511,9 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,  static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,  				void *ptr)  { -	struct tipc_bearer *b_ptr;  	struct net_device *dev = netdev_notifier_info_to_dev(ptr); - -	if (!net_eq(dev_net(dev), &init_net)) -		return NOTIFY_DONE; +	struct net *net = dev_net(dev); +	struct tipc_bearer *b_ptr;  	b_ptr = rtnl_dereference(dev->tipc_ptr);  	if (!b_ptr) @@ -590,16 +527,16 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,  			break;  	case NETDEV_DOWN:  	case NETDEV_CHANGEMTU: -		tipc_reset_bearer(b_ptr); +		tipc_reset_bearer(net, b_ptr);  		break;  	case NETDEV_CHANGEADDR:  		b_ptr->media->raw2addr(b_ptr, &b_ptr->addr,  				       (char *)dev->dev_addr); -		tipc_reset_bearer(b_ptr); +		tipc_reset_bearer(net, b_ptr);  		break;  	case NETDEV_UNREGISTER:  	case NETDEV_CHANGENAME: -		bearer_disable(b_ptr, false); +		bearer_disable(dev_net(dev), b_ptr, false);  		break;  	}  	return NOTIFY_OK; @@ -632,16 +569,17 @@ void tipc_bearer_cleanup(void)  	dev_remove_pack(&tipc_packet_type);  } -void tipc_bearer_stop(void) +void tipc_bearer_stop(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	u32 i;  	for (i = 0; i < MAX_BEARERS; i++) { -		b_ptr = rtnl_dereference(bearer_list[i]); +		b_ptr = rtnl_dereference(tn->bearer_list[i]);  		if (b_ptr) { -			bearer_disable(b_ptr, true); -			bearer_list[i] = NULL; +			bearer_disable(net, b_ptr, true); +			tn->bearer_list[i] = NULL;  		}  	}  } @@ -654,7 +592,7 @@ static int __tipc_nl_add_bearer(struct tipc_nl_msg *msg,  	struct nlattr *attrs;  	struct nlattr *prop; -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_BEARER_GET);  	if (!hdr)  		return -EMSGSIZE; @@ -698,6 +636,8 @@ int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb)  	int i = cb->args[0];  	struct tipc_bearer *bearer;  	struct tipc_nl_msg msg; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	if (i == MAX_BEARERS)  		return 0; @@ -708,7 +648,7 @@ int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb)  	rtnl_lock();  	for (i = 0; i < MAX_BEARERS; i++) { -		bearer = rtnl_dereference(bearer_list[i]); +		bearer = rtnl_dereference(tn->bearer_list[i]);  		if (!bearer)  			continue; @@ -730,6 +670,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info)  	struct tipc_bearer *bearer;  	struct tipc_nl_msg msg;  	struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; +	struct net *net = genl_info_net(info);  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; @@ -753,7 +694,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info)  	msg.seq = info->snd_seq;  	rtnl_lock(); -	bearer = tipc_bearer_find(name); +	bearer = tipc_bearer_find(net, name);  	if (!bearer) {  		err = -EINVAL;  		goto err_out; @@ -778,6 +719,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)  	char *name;  	struct tipc_bearer *bearer;  	struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; +	struct net *net = sock_net(skb->sk);  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; @@ -794,13 +736,13 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)  	name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);  	rtnl_lock(); -	bearer = tipc_bearer_find(name); +	bearer = tipc_bearer_find(net, name);  	if (!bearer) {  		rtnl_unlock();  		return -EINVAL;  	} -	bearer_disable(bearer, false); +	bearer_disable(net, bearer, false);  	rtnl_unlock();  	return 0; @@ -811,11 +753,13 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)  	int err;  	char *bearer;  	struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	u32 domain;  	u32 prio;  	prio = TIPC_MEDIA_LINK_PRI; -	domain = tipc_own_addr & TIPC_CLUSTER_MASK; +	domain = tn->own_addr & TIPC_CLUSTER_MASK;  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; @@ -847,7 +791,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)  	}  	rtnl_lock(); -	err = tipc_enable_bearer(bearer, domain, prio); +	err = tipc_enable_bearer(net, bearer, domain, prio);  	if (err) {  		rtnl_unlock();  		return err; @@ -863,6 +807,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)  	char *name;  	struct tipc_bearer *b;  	struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; +	struct net *net = genl_info_net(info);  	if (!info->attrs[TIPC_NLA_BEARER])  		return -EINVAL; @@ -878,7 +823,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)  	name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);  	rtnl_lock(); -	b = tipc_bearer_find(name); +	b = tipc_bearer_find(net, name);  	if (!b) {  		rtnl_unlock();  		return -EINVAL; @@ -913,7 +858,7 @@ static int __tipc_nl_add_media(struct tipc_nl_msg *msg,  	struct nlattr *attrs;  	struct nlattr *prop; -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_MEDIA_GET);  	if (!hdr)  		return -EMSGSIZE; diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 2c1230ac5dfe..6b17795ff8bc 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -37,12 +37,13 @@  #ifndef _TIPC_BEARER_H  #define _TIPC_BEARER_H -#include "bcast.h"  #include "netlink.h"  #include <net/genetlink.h>  #define MAX_BEARERS	2  #define MAX_MEDIA	2 +#define MAX_NODES	4096 +#define WSIZE		32  /* Identifiers associated with TIPC message header media address info   * - address info field is 32 bytes long @@ -59,6 +60,16 @@  #define TIPC_MEDIA_TYPE_IB	2  /** + * struct tipc_node_map - set of node identifiers + * @count: # of nodes in set + * @map: bitmap of node identifiers that are in the set + */ +struct tipc_node_map { +	u32 count; +	u32 map[MAX_NODES / WSIZE]; +}; + +/**   * struct tipc_media_addr - destination address used by TIPC bearers   * @value: address info (format defined by media)   * @media_id: TIPC media type identifier @@ -89,10 +100,10 @@ struct tipc_bearer;   * @name: media name   */  struct tipc_media { -	int (*send_msg)(struct sk_buff *buf, +	int (*send_msg)(struct net *net, struct sk_buff *buf,  			struct tipc_bearer *b_ptr,  			struct tipc_media_addr *dest); -	int (*enable_media)(struct tipc_bearer *b_ptr); +	int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr);  	void (*disable_media)(struct tipc_bearer *b_ptr);  	int (*addr2str)(struct tipc_media_addr *addr,  			char *strbuf, @@ -157,17 +168,11 @@ struct tipc_bearer_names {  	char if_name[TIPC_MAX_IF_NAME];  }; -struct tipc_link; - -extern struct tipc_bearer __rcu *bearer_list[]; -  /*   * TIPC routines available to supported media types   */ -void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *tb_ptr); -int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority); -int tipc_disable_bearer(const char *name); +void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr);  /*   * Routines made available to TIPC by supported media types @@ -191,21 +196,19 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info);  int tipc_media_set_priority(const char *name, u32 new_value);  int tipc_media_set_window(const char *name, u32 new_value);  void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); -struct sk_buff *tipc_media_get_names(void); -int tipc_enable_l2_media(struct tipc_bearer *b); +int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b);  void tipc_disable_l2_media(struct tipc_bearer *b); -int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b, -		     struct tipc_media_addr *dest); +int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, +		     struct tipc_bearer *b, struct tipc_media_addr *dest); -struct sk_buff *tipc_bearer_get_names(void); -void tipc_bearer_add_dest(u32 bearer_id, u32 dest); -void tipc_bearer_remove_dest(u32 bearer_id, u32 dest); -struct tipc_bearer *tipc_bearer_find(const char *name); +void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); +void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); +struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name);  struct tipc_media *tipc_media_find(const char *name);  int tipc_bearer_setup(void);  void tipc_bearer_cleanup(void); -void tipc_bearer_stop(void); -void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf, +void tipc_bearer_stop(struct net *net); +void tipc_bearer_send(struct net *net, u32 bearer_id, struct sk_buff *buf,  		      struct tipc_media_addr *dest);  #endif	/* _TIPC_BEARER_H */ diff --git a/net/tipc/config.c b/net/tipc/config.c deleted file mode 100644 index 876f4c6a2631..000000000000 --- a/net/tipc/config.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * net/tipc/config.c: TIPC configuration management code - * - * Copyright (c) 2002-2006, Ericsson AB - * Copyright (c) 2004-2007, 2010-2013, 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 "socket.h" -#include "name_table.h" -#include "config.h" -#include "server.h" - -#define REPLY_TRUNCATED "<truncated>\n" - -static const void *req_tlv_area;	/* request message TLV area */ -static int req_tlv_space;		/* request message TLV area size */ -static int rep_headroom;		/* reply message headroom to use */ - -struct sk_buff *tipc_cfg_reply_alloc(int payload_size) -{ -	struct sk_buff *buf; - -	buf = alloc_skb(rep_headroom + payload_size, GFP_ATOMIC); -	if (buf) -		skb_reserve(buf, rep_headroom); -	return buf; -} - -int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, -			void *tlv_data, int tlv_data_size) -{ -	struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf); -	int new_tlv_space = TLV_SPACE(tlv_data_size); - -	if (skb_tailroom(buf) < new_tlv_space) -		return 0; -	skb_put(buf, new_tlv_space); -	tlv->tlv_type = htons(tlv_type); -	tlv->tlv_len  = htons(TLV_LENGTH(tlv_data_size)); -	if (tlv_data_size && tlv_data) -		memcpy(TLV_DATA(tlv), tlv_data, tlv_data_size); -	return 1; -} - -static struct sk_buff *tipc_cfg_reply_unsigned_type(u16 tlv_type, u32 value) -{ -	struct sk_buff *buf; -	__be32 value_net; - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(value))); -	if (buf) { -		value_net = htonl(value); -		tipc_cfg_append_tlv(buf, tlv_type, &value_net, -				    sizeof(value_net)); -	} -	return buf; -} - -static struct sk_buff *tipc_cfg_reply_unsigned(u32 value) -{ -	return tipc_cfg_reply_unsigned_type(TIPC_TLV_UNSIGNED, value); -} - -struct sk_buff *tipc_cfg_reply_string_type(u16 tlv_type, char *string) -{ -	struct sk_buff *buf; -	int string_len = strlen(string) + 1; - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(string_len)); -	if (buf) -		tipc_cfg_append_tlv(buf, tlv_type, string, string_len); -	return buf; -} - -static struct sk_buff *tipc_show_stats(void) -{ -	struct sk_buff *buf; -	struct tlv_desc *rep_tlv; -	char *pb; -	int pb_len; -	int str_len; -	u32 value; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	value = ntohl(*(u32 *)TLV_DATA(req_tlv_area)); -	if (value != 0) -		return tipc_cfg_reply_error_string("unsupported argument"); - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); -	if (buf == NULL) -		return NULL; - -	rep_tlv = (struct tlv_desc *)buf->data; -	pb = TLV_DATA(rep_tlv); -	pb_len = ULTRA_STRING_MAX_LEN; - -	str_len = tipc_snprintf(pb, pb_len, "TIPC version " TIPC_MOD_VER "\n"); -	str_len += 1;	/* for "\0" */ -	skb_put(buf, TLV_SPACE(str_len)); -	TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); - -	return buf; -} - -static struct sk_buff *cfg_enable_bearer(void) -{ -	struct tipc_bearer_config *args; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_CONFIG)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	args = (struct tipc_bearer_config *)TLV_DATA(req_tlv_area); -	if (tipc_enable_bearer(args->name, -			       ntohl(args->disc_domain), -			       ntohl(args->priority))) -		return tipc_cfg_reply_error_string("unable to enable bearer"); - -	return tipc_cfg_reply_none(); -} - -static struct sk_buff *cfg_disable_bearer(void) -{ -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_NAME)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	if (tipc_disable_bearer((char *)TLV_DATA(req_tlv_area))) -		return tipc_cfg_reply_error_string("unable to disable bearer"); - -	return tipc_cfg_reply_none(); -} - -static struct sk_buff *cfg_set_own_addr(void) -{ -	u32 addr; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	addr = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); -	if (addr == tipc_own_addr) -		return tipc_cfg_reply_none(); -	if (!tipc_addr_node_valid(addr)) -		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE -						   " (node address)"); -	if (tipc_own_addr) -		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -						   " (cannot change node address once assigned)"); -	if (!tipc_net_start(addr)) -		return tipc_cfg_reply_none(); - -	return tipc_cfg_reply_error_string("cannot change to network mode"); -} - -static struct sk_buff *cfg_set_max_ports(void) -{ -	u32 value; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); -	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); -	if (value == tipc_max_ports) -		return tipc_cfg_reply_none(); -	if (value < 127 || value > 65535) -		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE -						   " (max ports must be 127-65535)"); -	return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -		" (cannot change max ports while TIPC is active)"); -} - -static struct sk_buff *cfg_set_netid(void) -{ -	u32 value; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); -	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); -	if (value == tipc_net_id) -		return tipc_cfg_reply_none(); -	if (value < 1 || value > 9999) -		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE -						   " (network id must be 1-9999)"); -	if (tipc_own_addr) -		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -			" (cannot change network id once TIPC has joined a network)"); -	tipc_net_id = value; -	return tipc_cfg_reply_none(); -} - -struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area, -				int request_space, int reply_headroom) -{ -	struct sk_buff *rep_tlv_buf; - -	rtnl_lock(); - -	/* Save request and reply details in a well-known location */ -	req_tlv_area = request_area; -	req_tlv_space = request_space; -	rep_headroom = reply_headroom; - -	/* Check command authorization */ -	if (likely(in_own_node(orig_node))) { -		/* command is permitted */ -	} else { -		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -							  " (cannot be done remotely)"); -		goto exit; -	} - -	/* Call appropriate processing routine */ -	switch (cmd) { -	case TIPC_CMD_NOOP: -		rep_tlv_buf = tipc_cfg_reply_none(); -		break; -	case TIPC_CMD_GET_NODES: -		rep_tlv_buf = tipc_node_get_nodes(req_tlv_area, req_tlv_space); -		break; -	case TIPC_CMD_GET_LINKS: -		rep_tlv_buf = tipc_node_get_links(req_tlv_area, req_tlv_space); -		break; -	case TIPC_CMD_SHOW_LINK_STATS: -		rep_tlv_buf = tipc_link_cmd_show_stats(req_tlv_area, req_tlv_space); -		break; -	case TIPC_CMD_RESET_LINK_STATS: -		rep_tlv_buf = tipc_link_cmd_reset_stats(req_tlv_area, req_tlv_space); -		break; -	case TIPC_CMD_SHOW_NAME_TABLE: -		rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); -		break; -	case TIPC_CMD_GET_BEARER_NAMES: -		rep_tlv_buf = tipc_bearer_get_names(); -		break; -	case TIPC_CMD_GET_MEDIA_NAMES: -		rep_tlv_buf = tipc_media_get_names(); -		break; -	case TIPC_CMD_SHOW_PORTS: -		rep_tlv_buf = tipc_sk_socks_show(); -		break; -	case TIPC_CMD_SHOW_STATS: -		rep_tlv_buf = tipc_show_stats(); -		break; -	case TIPC_CMD_SET_LINK_TOL: -	case TIPC_CMD_SET_LINK_PRI: -	case TIPC_CMD_SET_LINK_WINDOW: -		rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd); -		break; -	case TIPC_CMD_ENABLE_BEARER: -		rep_tlv_buf = cfg_enable_bearer(); -		break; -	case TIPC_CMD_DISABLE_BEARER: -		rep_tlv_buf = cfg_disable_bearer(); -		break; -	case TIPC_CMD_SET_NODE_ADDR: -		rep_tlv_buf = cfg_set_own_addr(); -		break; -	case TIPC_CMD_SET_MAX_PORTS: -		rep_tlv_buf = cfg_set_max_ports(); -		break; -	case TIPC_CMD_SET_NETID: -		rep_tlv_buf = cfg_set_netid(); -		break; -	case TIPC_CMD_GET_MAX_PORTS: -		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); -		break; -	case TIPC_CMD_GET_NETID: -		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); -		break; -	case TIPC_CMD_NOT_NET_ADMIN: -		rep_tlv_buf = -			tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); -		break; -	case TIPC_CMD_SET_MAX_ZONES: -	case TIPC_CMD_GET_MAX_ZONES: -	case TIPC_CMD_SET_MAX_SLAVES: -	case TIPC_CMD_GET_MAX_SLAVES: -	case TIPC_CMD_SET_MAX_CLUSTERS: -	case TIPC_CMD_GET_MAX_CLUSTERS: -	case TIPC_CMD_SET_MAX_NODES: -	case TIPC_CMD_GET_MAX_NODES: -	case TIPC_CMD_SET_MAX_SUBSCR: -	case TIPC_CMD_GET_MAX_SUBSCR: -	case TIPC_CMD_SET_MAX_PUBL: -	case TIPC_CMD_GET_MAX_PUBL: -	case TIPC_CMD_SET_LOG_SIZE: -	case TIPC_CMD_SET_REMOTE_MNG: -	case TIPC_CMD_GET_REMOTE_MNG: -	case TIPC_CMD_DUMP_LOG: -		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -							  " (obsolete command)"); -		break; -	default: -		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -							  " (unknown command)"); -		break; -	} - -	WARN_ON(rep_tlv_buf->len > TLV_SPACE(ULTRA_STRING_MAX_LEN)); - -	/* Append an error message if we cannot return all requested data */ -	if (rep_tlv_buf->len == TLV_SPACE(ULTRA_STRING_MAX_LEN)) { -		if (*(rep_tlv_buf->data + ULTRA_STRING_MAX_LEN) != '\0') -			sprintf(rep_tlv_buf->data + rep_tlv_buf->len - -				sizeof(REPLY_TRUNCATED) - 1, REPLY_TRUNCATED); -	} - -	/* Return reply buffer */ -exit: -	rtnl_unlock(); -	return rep_tlv_buf; -} diff --git a/net/tipc/config.h b/net/tipc/config.h deleted file mode 100644 index 47b1bf181612..000000000000 --- a/net/tipc/config.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * net/tipc/config.h: Include file for TIPC configuration service code - * - * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, 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. - */ - -#ifndef _TIPC_CONFIG_H -#define _TIPC_CONFIG_H - -/* ---------------------------------------------------------------------- */ - -#include "link.h" - -struct sk_buff *tipc_cfg_reply_alloc(int payload_size); -int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, -			void *tlv_data, int tlv_data_size); -struct sk_buff *tipc_cfg_reply_string_type(u16 tlv_type, char *string); - -static inline struct sk_buff *tipc_cfg_reply_none(void) -{ -	return tipc_cfg_reply_alloc(0); -} - -static inline struct sk_buff *tipc_cfg_reply_error_string(char *string) -{ -	return tipc_cfg_reply_string_type(TIPC_TLV_ERROR_STRING, string); -} - -static inline struct sk_buff *tipc_cfg_reply_ultra_string(char *string) -{ -	return tipc_cfg_reply_string_type(TIPC_TLV_ULTRA_STRING, string); -} - -struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, -				const void *req_tlv_area, int req_tlv_space, -				int headroom); -#endif diff --git a/net/tipc/core.c b/net/tipc/core.c index a5737b8407dd..935205e6bcfe 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -34,82 +34,88 @@   * POSSIBILITY OF SUCH DAMAGE.   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include "core.h"  #include "name_table.h"  #include "subscr.h" -#include "config.h" +#include "bearer.h" +#include "net.h"  #include "socket.h"  #include <linux/module.h> -/* global variables used by multiple sub-systems within TIPC */ -int tipc_random __read_mostly; -  /* configurable TIPC parameters */ -u32 tipc_own_addr __read_mostly; -int tipc_max_ports __read_mostly;  int tipc_net_id __read_mostly;  int sysctl_tipc_rmem[3] __read_mostly;	/* min/default/max */ -/** - * tipc_buf_acquire - creates a TIPC message buffer - * @size: message size (including TIPC header) - * - * Returns a new buffer with data pointers set to the specified size. - * - * NOTE: Headroom is reserved to allow prepending of a data link header. - *       There may also be unrequested tailroom present at the buffer's end. - */ -struct sk_buff *tipc_buf_acquire(u32 size) +static int __net_init tipc_init_net(struct net *net)  { -	struct sk_buff *skb; -	unsigned int buf_size = (BUF_HEADROOM + size + 3) & ~3u; - -	skb = alloc_skb_fclone(buf_size, GFP_ATOMIC); -	if (skb) { -		skb_reserve(skb, BUF_HEADROOM); -		skb_put(skb, size); -		skb->next = NULL; -	} -	return skb; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	int err; + +	tn->net_id = 4711; +	tn->own_addr = 0; +	get_random_bytes(&tn->random, sizeof(int)); +	INIT_LIST_HEAD(&tn->node_list); +	spin_lock_init(&tn->node_list_lock); + +	err = tipc_sk_rht_init(net); +	if (err) +		goto out_sk_rht; + +	err = tipc_nametbl_init(net); +	if (err) +		goto out_nametbl; + +	err = tipc_subscr_start(net); +	if (err) +		goto out_subscr; +	return 0; + +out_subscr: +	tipc_nametbl_stop(net); +out_nametbl: +	tipc_sk_rht_destroy(net); +out_sk_rht: +	return err;  } -/** - * tipc_core_stop - switch TIPC from SINGLE NODE to NOT RUNNING mode - */ -static void tipc_core_stop(void) +static void __net_exit tipc_exit_net(struct net *net)  { -	tipc_net_stop(); -	tipc_bearer_cleanup(); -	tipc_netlink_stop(); -	tipc_subscr_stop(); -	tipc_nametbl_stop(); -	tipc_sk_ref_table_stop(); -	tipc_socket_stop(); -	tipc_unregister_sysctl(); +	tipc_subscr_stop(net); +	tipc_net_stop(net); +	tipc_nametbl_stop(net); +	tipc_sk_rht_destroy(net);  } -/** - * tipc_core_start - switch TIPC from NOT RUNNING to SINGLE NODE mode - */ -static int tipc_core_start(void) +static struct pernet_operations tipc_net_ops = { +	.init = tipc_init_net, +	.exit = tipc_exit_net, +	.id   = &tipc_net_id, +	.size = sizeof(struct tipc_net), +}; + +static int __init tipc_init(void)  {  	int err; -	get_random_bytes(&tipc_random, sizeof(tipc_random)); - -	err = tipc_sk_ref_table_init(tipc_max_ports, tipc_random); -	if (err) -		goto out_reftbl; +	pr_info("Activated (version " TIPC_MOD_VER ")\n"); -	err = tipc_nametbl_init(); -	if (err) -		goto out_nametbl; +	sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << +			      TIPC_LOW_IMPORTANCE; +	sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << +			      TIPC_CRITICAL_IMPORTANCE; +	sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT;  	err = tipc_netlink_start();  	if (err)  		goto out_netlink; +	err = tipc_netlink_compat_start(); +	if (err) +		goto out_netlink_compat; +  	err = tipc_socket_init();  	if (err)  		goto out_socket; @@ -118,58 +124,40 @@ static int tipc_core_start(void)  	if (err)  		goto out_sysctl; -	err = tipc_subscr_start(); +	err = register_pernet_subsys(&tipc_net_ops);  	if (err) -		goto out_subscr; +		goto out_pernet;  	err = tipc_bearer_setup();  	if (err)  		goto out_bearer; +	pr_info("Started in single node mode\n");  	return 0;  out_bearer: -	tipc_subscr_stop(); -out_subscr: +	unregister_pernet_subsys(&tipc_net_ops); +out_pernet:  	tipc_unregister_sysctl();  out_sysctl:  	tipc_socket_stop();  out_socket: +	tipc_netlink_compat_stop(); +out_netlink_compat:  	tipc_netlink_stop();  out_netlink: -	tipc_nametbl_stop(); -out_nametbl: -	tipc_sk_ref_table_stop(); -out_reftbl: +	pr_err("Unable to start in single node mode\n");  	return err;  } -static int __init tipc_init(void) -{ -	int res; - -	pr_info("Activated (version " TIPC_MOD_VER ")\n"); - -	tipc_own_addr = 0; -	tipc_max_ports = CONFIG_TIPC_PORTS; -	tipc_net_id = 4711; - -	sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << -			      TIPC_LOW_IMPORTANCE; -	sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 << -			      TIPC_CRITICAL_IMPORTANCE; -	sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT; - -	res = tipc_core_start(); -	if (res) -		pr_err("Unable to start in single node mode\n"); -	else -		pr_info("Started in single node mode\n"); -	return res; -} -  static void __exit tipc_exit(void)  { -	tipc_core_stop(); +	tipc_bearer_cleanup(); +	tipc_netlink_stop(); +	tipc_netlink_compat_stop(); +	tipc_socket_stop(); +	tipc_unregister_sysctl(); +	unregister_pernet_subsys(&tipc_net_ops); +  	pr_info("Deactivated\n");  } diff --git a/net/tipc/core.h b/net/tipc/core.h index 84602137ce20..3dc68c7a966d 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -37,8 +37,6 @@  #ifndef _TIPC_CORE_H  #define _TIPC_CORE_H -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -  #include <linux/tipc.h>  #include <linux/tipc_config.h>  #include <linux/tipc_netlink.h> @@ -59,47 +57,54 @@  #include <linux/vmalloc.h>  #include <linux/rtnetlink.h>  #include <linux/etherdevice.h> +#include <net/netns/generic.h> +#include <linux/rhashtable.h> + +#include "node.h" +#include "bearer.h" +#include "bcast.h" +#include "netlink.h" +#include "link.h" +#include "node.h" +#include "msg.h"  #define TIPC_MOD_VER "2.0.0" -#define ULTRA_STRING_MAX_LEN	32768 -#define TIPC_MAX_SUBSCRIPTIONS	65535 -#define TIPC_MAX_PUBLICATIONS	65535 +extern int tipc_net_id __read_mostly; +extern int sysctl_tipc_rmem[3] __read_mostly; +extern int sysctl_tipc_named_timeout __read_mostly; -struct tipc_msg;	/* msg.h */ +struct tipc_net { +	u32 own_addr; +	int net_id; +	int random; -int tipc_snprintf(char *buf, int len, const char *fmt, ...); +	/* Node table and node list */ +	spinlock_t node_list_lock; +	struct hlist_head node_htable[NODE_HTABLE_SIZE]; +	struct list_head node_list; +	u32 num_nodes; +	u32 num_links; -/* - * TIPC-specific error codes - */ -#define ELINKCONG EAGAIN	/* link congestion <=> resource unavailable */ +	/* Bearer list */ +	struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; -/* - * Global configuration variables - */ -extern u32 tipc_own_addr __read_mostly; -extern int tipc_max_ports __read_mostly; -extern int tipc_net_id __read_mostly; -extern int sysctl_tipc_rmem[3] __read_mostly; -extern int sysctl_tipc_named_timeout __read_mostly; +	/* Broadcast link */ +	struct tipc_bcbearer *bcbearer; +	struct tipc_bclink *bclink; +	struct tipc_link *bcl; -/* - * Other global variables - */ -extern int tipc_random __read_mostly; +	/* Socket hash table */ +	struct rhashtable sk_rht; -/* - * Routines available to privileged subsystems - */ -int tipc_netlink_start(void); -void tipc_netlink_stop(void); -int tipc_socket_init(void); -void tipc_socket_stop(void); -int tipc_sock_create_local(int type, struct socket **res); -void tipc_sock_release_local(struct socket *sock); -int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, -			   int flags); +	/* Name table */ +	spinlock_t nametbl_lock; +	struct name_table *nametbl; + +	/* Topology subscription server */ +	struct tipc_server *topsrv; +	atomic_t subscription_count; +};  #ifdef CONFIG_SYSCTL  int tipc_register_sysctl(void); @@ -108,102 +113,4 @@ void tipc_unregister_sysctl(void);  #define tipc_register_sysctl() 0  #define tipc_unregister_sysctl()  #endif - -/* - * TIPC timer code - */ -typedef void (*Handler) (unsigned long); - -/** - * k_init_timer - initialize a timer - * @timer: pointer to timer structure - * @routine: pointer to routine to invoke when timer expires - * @argument: value to pass to routine when timer expires - * - * Timer must be initialized before use (and terminated when no longer needed). - */ -static inline void k_init_timer(struct timer_list *timer, Handler routine, -				unsigned long argument) -{ -	setup_timer(timer, routine, argument); -} - -/** - * k_start_timer - start a timer - * @timer: pointer to timer structure - * @msec: time to delay (in ms) - * - * Schedules a previously initialized timer for later execution. - * If timer is already running, the new timeout overrides the previous request. - * - * To ensure the timer doesn't expire before the specified delay elapses, - * the amount of delay is rounded up when converting to the jiffies - * then an additional jiffy is added to account for the fact that - * the starting time may be in the middle of the current jiffy. - */ -static inline void k_start_timer(struct timer_list *timer, unsigned long msec) -{ -	mod_timer(timer, jiffies + msecs_to_jiffies(msec) + 1); -} - -/** - * k_cancel_timer - cancel a timer - * @timer: pointer to timer structure - * - * Cancels a previously initialized timer. - * Can be called safely even if the timer is already inactive. - * - * WARNING: Must not be called when holding locks required by the timer's - *          timeout routine, otherwise deadlock can occur on SMP systems! - */ -static inline void k_cancel_timer(struct timer_list *timer) -{ -	del_timer_sync(timer); -} - -/** - * k_term_timer - terminate a timer - * @timer: pointer to timer structure - * - * Prevents further use of a previously initialized timer. - * - * WARNING: Caller must ensure timer isn't currently running. - * - * (Do not "enhance" this routine to automatically cancel an active timer, - * otherwise deadlock can arise when a timeout routine calls k_term_timer.) - */ -static inline void k_term_timer(struct timer_list *timer) -{ -} - -/* - * TIPC message buffer code - * - * TIPC message buffer headroom reserves space for the worst-case - * link-level device header (in case the message is sent off-node). - * - * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields - *       are word aligned for quicker access - */ -#define BUF_HEADROOM LL_MAX_HEADER - -struct tipc_skb_cb { -	void *handle; -	struct sk_buff *tail; -	bool deferred; -	bool wakeup_pending; -	bool bundling; -	u16 chain_sz; -	u16 chain_imp; -}; - -#define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0])) - -static inline struct tipc_msg *buf_msg(struct sk_buff *skb) -{ -	return (struct tipc_msg *)skb->data; -} - -struct sk_buff *tipc_buf_acquire(u32 size); -  #endif diff --git a/net/tipc/discover.c b/net/tipc/discover.c index aa722a42ef8b..feef3753615d 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.   * @@ -38,15 +38,19 @@  #include "link.h"  #include "discover.h" -#define TIPC_LINK_REQ_INIT	125	/* min delay during bearer start up */ -#define TIPC_LINK_REQ_FAST	1000	/* max delay if bearer has no links */ -#define TIPC_LINK_REQ_SLOW	60000	/* max delay if bearer has links */ -#define TIPC_LINK_REQ_INACTIVE	0xffffffff /* indicates no timer in use */ - +/* min delay during bearer start up */ +#define TIPC_LINK_REQ_INIT	msecs_to_jiffies(125) +/* max delay if bearer has no links */ +#define TIPC_LINK_REQ_FAST	msecs_to_jiffies(1000) +/* max delay if bearer has links */ +#define TIPC_LINK_REQ_SLOW	msecs_to_jiffies(60000) +/* 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 + * @net: network namespace instance   * @dest: destination address for request messages   * @domain: network domain to which links can be established   * @num_nodes: number of nodes currently discovered (i.e. with an active link) @@ -58,31 +62,35 @@  struct tipc_link_req {  	u32 bearer_id;  	struct tipc_media_addr dest; +	struct net *net;  	u32 domain;  	int num_nodes;  	spinlock_t lock;  	struct sk_buff *buf;  	struct timer_list timer; -	unsigned int timer_intv; +	unsigned long timer_intv;  };  /**   * tipc_disc_init_msg - initialize a link setup message + * @net: the applicable net namespace   * @type: message type (request or response)   * @b_ptr: ptr to bearer issuing message   */ -static void tipc_disc_init_msg(struct sk_buff *buf, u32 type, +static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,  			       struct tipc_bearer *b_ptr)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_msg *msg;  	u32 dest_domain = b_ptr->domain;  	msg = buf_msg(buf); -	tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain); +	tipc_msg_init(tn->own_addr, msg, LINK_CONFIG, type, +		      INT_H_SIZE, dest_domain);  	msg_set_non_seq(msg, 1); -	msg_set_node_sig(msg, tipc_random); +	msg_set_node_sig(msg, tn->random);  	msg_set_dest_domain(msg, dest_domain); -	msg_set_bc_netid(msg, tipc_net_id); +	msg_set_bc_netid(msg, tn->net_id);  	b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);  } @@ -107,11 +115,14 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,  /**   * tipc_disc_rcv - handle incoming discovery message (request or response) + * @net: the applicable net namespace   * @buf: buffer containing message   * @bearer: bearer that message arrived on   */ -void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer) +void tipc_disc_rcv(struct net *net, struct sk_buff *buf, +		   struct tipc_bearer *bearer)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_node *node;  	struct tipc_link *link;  	struct tipc_media_addr maddr; @@ -133,7 +144,7 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)  	kfree_skb(buf);  	/* Ensure message from node is valid and communication is permitted */ -	if (net_id != tipc_net_id) +	if (net_id != tn->net_id)  		return;  	if (maddr.broadcast)  		return; @@ -142,23 +153,19 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)  	if (!tipc_addr_node_valid(onode))  		return; -	if (in_own_node(onode)) { +	if (in_own_node(net, onode)) {  		if (memcmp(&maddr, &bearer->addr, sizeof(maddr))) -			disc_dupl_alert(bearer, tipc_own_addr, &maddr); +			disc_dupl_alert(bearer, tn->own_addr, &maddr);  		return;  	} -	if (!tipc_in_scope(ddom, tipc_own_addr)) +	if (!tipc_in_scope(ddom, tn->own_addr))  		return;  	if (!tipc_in_scope(bearer->domain, onode))  		return; -	/* Locate, or if necessary, create, node: */ -	node = tipc_node_find(onode); -	if (!node) -		node = tipc_node_create(onode); +	node = tipc_node_create(net, onode);  	if (!node)  		return; -  	tipc_node_lock(node);  	link = node->links[bearer->identity]; @@ -244,8 +251,8 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)  	if (respond && (mtyp == DSC_REQ_MSG)) {  		rbuf = tipc_buf_acquire(INT_H_SIZE);  		if (rbuf) { -			tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer); -			tipc_bearer_send(bearer->identity, rbuf, &maddr); +			tipc_disc_init_msg(net, rbuf, DSC_RESP_MSG, bearer); +			tipc_bearer_send(net, bearer->identity, rbuf, &maddr);  			kfree_skb(rbuf);  		}  	} @@ -265,7 +272,7 @@ static void disc_update(struct tipc_link_req *req)  		if ((req->timer_intv == TIPC_LINK_REQ_INACTIVE) ||  		    (req->timer_intv > TIPC_LINK_REQ_FAST)) {  			req->timer_intv = TIPC_LINK_REQ_INIT; -			k_start_timer(&req->timer, req->timer_intv); +			mod_timer(&req->timer, jiffies + req->timer_intv);  		}  	}  } @@ -295,12 +302,13 @@ void tipc_disc_remove_dest(struct tipc_link_req *req)  /**   * disc_timeout - send a periodic link setup request - * @req: ptr to link request structure + * @data: ptr to link request structure   *   * Called whenever a link setup request timer associated with a bearer expires.   */ -static void disc_timeout(struct tipc_link_req *req) +static void disc_timeout(unsigned long data)  { +	struct tipc_link_req *req = (struct tipc_link_req *)data;  	int max_delay;  	spin_lock_bh(&req->lock); @@ -318,7 +326,7 @@ static void disc_timeout(struct tipc_link_req *req)  	 * hold at fast polling rate if don't have any associated nodes,  	 * otherwise hold at slow polling rate  	 */ -	tipc_bearer_send(req->bearer_id, req->buf, &req->dest); +	tipc_bearer_send(req->net, req->bearer_id, req->buf, &req->dest);  	req->timer_intv *= 2; @@ -329,20 +337,22 @@ static void disc_timeout(struct tipc_link_req *req)  	if (req->timer_intv > max_delay)  		req->timer_intv = max_delay; -	k_start_timer(&req->timer, req->timer_intv); +	mod_timer(&req->timer, jiffies + req->timer_intv);  exit:  	spin_unlock_bh(&req->lock);  }  /**   * tipc_disc_create - create object to send periodic link setup requests + * @net: the applicable net namespace   * @b_ptr: ptr to bearer issuing requests   * @dest: destination address for request messages   * @dest_domain: network domain to which links can be established   *   * Returns 0 if successful, otherwise -errno.   */ -int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest) +int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, +		     struct tipc_media_addr *dest)  {  	struct tipc_link_req *req; @@ -356,17 +366,18 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)  		return -ENOMEM;  	} -	tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr); +	tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);  	memcpy(&req->dest, dest, sizeof(*dest)); +	req->net = net;  	req->bearer_id = b_ptr->identity;  	req->domain = b_ptr->domain;  	req->num_nodes = 0;  	req->timer_intv = TIPC_LINK_REQ_INIT;  	spin_lock_init(&req->lock); -	k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req); -	k_start_timer(&req->timer, req->timer_intv); +	setup_timer(&req->timer, disc_timeout, (unsigned long)req); +	mod_timer(&req->timer, jiffies + req->timer_intv);  	b_ptr->link_req = req; -	tipc_bearer_send(req->bearer_id, req->buf, &req->dest); +	tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest);  	return 0;  } @@ -376,28 +387,29 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)   */  void tipc_disc_delete(struct tipc_link_req *req)  { -	k_cancel_timer(&req->timer); -	k_term_timer(&req->timer); +	del_timer_sync(&req->timer);  	kfree_skb(req->buf);  	kfree(req);  }  /**   * tipc_disc_reset - reset object to send periodic link setup requests + * @net: the applicable net namespace   * @b_ptr: ptr to bearer issuing requests   * @dest_domain: network domain to which links can be established   */ -void tipc_disc_reset(struct tipc_bearer *b_ptr) +void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr)  {  	struct tipc_link_req *req = b_ptr->link_req;  	spin_lock_bh(&req->lock); -	tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr); +	tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr); +	req->net = net;  	req->bearer_id = b_ptr->identity;  	req->domain = b_ptr->domain;  	req->num_nodes = 0;  	req->timer_intv = TIPC_LINK_REQ_INIT; -	k_start_timer(&req->timer, req->timer_intv); -	tipc_bearer_send(req->bearer_id, req->buf, &req->dest); +	mod_timer(&req->timer, jiffies + req->timer_intv); +	tipc_bearer_send(net, req->bearer_id, req->buf, &req->dest);  	spin_unlock_bh(&req->lock);  } diff --git a/net/tipc/discover.h b/net/tipc/discover.h index 515b57392f4d..c9b12770c5ed 100644 --- a/net/tipc/discover.h +++ b/net/tipc/discover.h @@ -39,11 +39,13 @@  struct tipc_link_req; -int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); +int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr, +		     struct tipc_media_addr *dest);  void tipc_disc_delete(struct tipc_link_req *req); -void tipc_disc_reset(struct tipc_bearer *b_ptr); +void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr);  void tipc_disc_add_dest(struct tipc_link_req *req);  void tipc_disc_remove_dest(struct tipc_link_req *req); -void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr); +void tipc_disc_rcv(struct net *net, struct sk_buff *buf, +		   struct tipc_bearer *b_ptr);  #endif diff --git a/net/tipc/link.c b/net/tipc/link.c index 23bcc1132365..a4cf364316de 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -40,7 +40,6 @@  #include "socket.h"  #include "name_distr.h"  #include "discover.h" -#include "config.h"  #include "netlink.h"  #include <linux/pkt_sched.h> @@ -101,19 +100,20 @@ static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {   */  #define START_CHANGEOVER 100000u -static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, -				       struct sk_buff *buf); -static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf); -static int  tipc_link_tunnel_rcv(struct tipc_node *n_ptr, -				 struct sk_buff **buf); -static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance); +static void link_handle_out_of_seq_msg(struct tipc_link *link, +				       struct sk_buff *skb); +static void tipc_link_proto_rcv(struct tipc_link *link, +				struct sk_buff *skb); +static int  tipc_link_tunnel_rcv(struct tipc_node *node, +				 struct sk_buff **skb); +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol);  static void link_state_event(struct tipc_link *l_ptr, u32 event);  static void link_reset_statistics(struct tipc_link *l_ptr);  static void link_print(struct tipc_link *l_ptr, const char *str);  static void tipc_link_sync_xmit(struct tipc_link *l);  static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf); -static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf); -static int tipc_link_prepare_input(struct tipc_link *l, struct sk_buff **buf); +static void tipc_link_input(struct tipc_link *l, struct sk_buff *skb); +static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb);  /*   *  Simple link routines @@ -123,13 +123,30 @@ 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; +	struct tipc_net *tn = net_generic(node->net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	u32 max_pkt;  	rcu_read_lock(); -	b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]); +	b_ptr = rcu_dereference_rtnl(tn->bearer_list[l_ptr->bearer_id]);  	if (!b_ptr) {  		rcu_read_unlock();  		return; @@ -169,8 +186,9 @@ int tipc_link_is_active(struct tipc_link *l_ptr)   * link_timeout - handle expiration of link timer   * @l_ptr: pointer to link   */ -static void link_timeout(struct tipc_link *l_ptr) +static void link_timeout(unsigned long data)  { +	struct tipc_link *l_ptr = (struct tipc_link *)data;  	struct sk_buff *skb;  	tipc_node_lock(l_ptr->owner); @@ -215,11 +233,13 @@ static void link_timeout(struct tipc_link *l_ptr)  		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 *l_ptr, u32 time) +static void link_set_timer(struct tipc_link *link, unsigned long time)  { -	k_start_timer(&l_ptr->timer, time); +	if (!mod_timer(&link->timer, jiffies + time)) +		tipc_link_get(link);  }  /** @@ -234,6 +254,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,  				   struct tipc_bearer *b_ptr,  				   const struct tipc_media_addr *media_addr)  { +	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);  	struct tipc_link *l_ptr;  	struct tipc_msg *msg;  	char *if_name; @@ -259,12 +280,12 @@ 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", -		tipc_zone(tipc_own_addr), tipc_cluster(tipc_own_addr), -		tipc_node(tipc_own_addr), +		tipc_zone(tn->own_addr), tipc_cluster(tn->own_addr), +		tipc_node(tn->own_addr),  		if_name,  		tipc_zone(peer), tipc_cluster(peer), tipc_node(peer));  		/* note: peer i/f name is updated by reset/activate message */ @@ -278,9 +299,10 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,  	l_ptr->pmsg = (struct tipc_msg *)&l_ptr->proto_msg;  	msg = l_ptr->pmsg; -	tipc_msg_init(msg, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, l_ptr->addr); +	tipc_msg_init(tn->own_addr, msg, LINK_PROTOCOL, RESET_MSG, INT_H_SIZE, +		      l_ptr->addr);  	msg_set_size(msg, sizeof(l_ptr->proto_msg)); -	msg_set_session(msg, (tipc_random & 0xffff)); +	msg_set_session(msg, (tn->random & 0xffff));  	msg_set_bearer_id(msg, b_ptr->identity);  	strcpy((char *)msg_data(msg), if_name); @@ -293,48 +315,52 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,  	l_ptr->next_out_no = 1;  	__skb_queue_head_init(&l_ptr->outqueue);  	__skb_queue_head_init(&l_ptr->deferred_queue); -	skb_queue_head_init(&l_ptr->waiting_sks); - +	skb_queue_head_init(&l_ptr->wakeupq); +	skb_queue_head_init(&l_ptr->inputq); +	skb_queue_head_init(&l_ptr->namedq);  	link_reset_statistics(l_ptr); -  	tipc_node_attach_link(n_ptr, l_ptr); - -	k_init_timer(&l_ptr->timer, (Handler)link_timeout, -		     (unsigned long)l_ptr); - +	setup_timer(&l_ptr->timer, link_timeout, (unsigned long)l_ptr);  	link_state_event(l_ptr, STARTING_EVT);  	return l_ptr;  } -void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down) +/** + * 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_link *l_ptr; -	struct tipc_node *n_ptr; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_link *link; +	struct tipc_node *node;  	rcu_read_lock(); -	list_for_each_entry_rcu(n_ptr, &tipc_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();  } @@ -352,13 +378,14 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport,  {  	struct sk_buff *buf; -	buf = tipc_msg_create(SOCK_WAKEUP, 0, INT_H_SIZE, 0, tipc_own_addr, -			      tipc_own_addr, oport, 0, 0); +	buf = tipc_msg_create(SOCK_WAKEUP, 0, INT_H_SIZE, 0, +			      link_own_addr(link), link_own_addr(link), +			      oport, 0, 0);  	if (!buf)  		return false;  	TIPC_SKB_CB(buf)->chain_sz = chain_sz;  	TIPC_SKB_CB(buf)->chain_imp = imp; -	skb_queue_tail(&link->waiting_sks, buf); +	skb_queue_tail(&link->wakeupq, buf);  	link->stats.link_congs++;  	return true;  } @@ -369,17 +396,19 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport,   * Move a number of waiting users, as permitted by available space in   * the send queue, from link wait queue to node wait queue for wakeup   */ -static void link_prepare_wakeup(struct tipc_link *link) +void link_prepare_wakeup(struct tipc_link *link)  {  	uint pend_qsz = skb_queue_len(&link->outqueue);  	struct sk_buff *skb, *tmp; -	skb_queue_walk_safe(&link->waiting_sks, skb, tmp) { +	skb_queue_walk_safe(&link->wakeupq, skb, tmp) {  		if (pend_qsz >= link->queue_limit[TIPC_SKB_CB(skb)->chain_imp])  			break;  		pend_qsz += TIPC_SKB_CB(skb)->chain_sz; -		skb_unlink(skb, &link->waiting_sks); -		skb_queue_tail(&link->owner->waiting_sks, skb); +		skb_unlink(skb, &link->wakeupq); +		skb_queue_tail(&link->inputq, skb); +		link->owner->inputq = &link->inputq; +		link->owner->action_flags |= TIPC_MSG_EVT;  	}  } @@ -425,20 +454,20 @@ void tipc_link_reset(struct tipc_link *l_ptr)  		return;  	tipc_node_link_down(l_ptr->owner, l_ptr); -	tipc_bearer_remove_dest(l_ptr->bearer_id, l_ptr->addr); +	tipc_bearer_remove_dest(owner->net, l_ptr->bearer_id, l_ptr->addr);  	if (was_active_link && tipc_node_active_links(l_ptr->owner)) {  		l_ptr->reset_checkpoint = checkpoint;  		l_ptr->exp_msg_count = START_CHANGEOVER;  	} -	/* Clean up all queues: */ +	/* Clean up all queues, except inputq: */  	__skb_queue_purge(&l_ptr->outqueue);  	__skb_queue_purge(&l_ptr->deferred_queue); -	if (!skb_queue_empty(&l_ptr->waiting_sks)) { -		skb_queue_splice_init(&l_ptr->waiting_sks, &owner->waiting_sks); -		owner->action_flags |= TIPC_WAKEUP_USERS; -	} +	skb_queue_splice_init(&l_ptr->wakeupq, &l_ptr->inputq); +	if (!skb_queue_empty(&l_ptr->inputq)) +		owner->action_flags |= TIPC_MSG_EVT; +	owner->inputq = &l_ptr->inputq;  	l_ptr->next_out = NULL;  	l_ptr->unacked_window = 0;  	l_ptr->checkpoint = 1; @@ -448,13 +477,14 @@ void tipc_link_reset(struct tipc_link *l_ptr)  	link_reset_statistics(l_ptr);  } -void tipc_link_reset_list(unsigned int bearer_id) +void tipc_link_reset_list(struct net *net, unsigned int bearer_id)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_link *l_ptr;  	struct tipc_node *n_ptr;  	rcu_read_lock(); -	list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { +	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) @@ -464,11 +494,14 @@ void tipc_link_reset_list(unsigned int bearer_id)  	rcu_read_unlock();  } -static void link_activate(struct tipc_link *l_ptr) +static void link_activate(struct tipc_link *link)  { -	l_ptr->next_in_no = l_ptr->stats.recv_info = 1; -	tipc_node_link_up(l_ptr->owner, l_ptr); -	tipc_bearer_add_dest(l_ptr->bearer_id, l_ptr->addr); +	struct tipc_node *node = link->owner; + +	link->next_in_no = 1; +	link->stats.recv_info = 1; +	tipc_node_link_up(node, link); +	tipc_bearer_add_dest(node->net, link->bearer_id, link->addr);  }  /** @@ -479,7 +512,7 @@ static void link_activate(struct tipc_link *l_ptr)  static void link_state_event(struct tipc_link *l_ptr, unsigned int event)  {  	struct tipc_link *other; -	u32 cont_intv = l_ptr->continuity_interval; +	unsigned long cont_intv = l_ptr->cont_intv;  	if (l_ptr->flags & LINK_STOPPED)  		return; @@ -522,8 +555,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)  			link_set_timer(l_ptr, cont_intv / 4);  			break;  		case RESET_MSG: -			pr_info("%s<%s>, requested by peer\n", link_rst_msg, -				l_ptr->name); +			pr_debug("%s<%s>, requested by peer\n", +				 link_rst_msg, l_ptr->name);  			tipc_link_reset(l_ptr);  			l_ptr->state = RESET_RESET;  			l_ptr->fsm_msg_cnt = 0; @@ -533,7 +566,7 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)  			link_set_timer(l_ptr, cont_intv);  			break;  		default: -			pr_err("%s%u in WW state\n", link_unk_evt, event); +			pr_debug("%s%u in WW state\n", link_unk_evt, event);  		}  		break;  	case WORKING_UNKNOWN: @@ -545,8 +578,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)  			link_set_timer(l_ptr, cont_intv);  			break;  		case RESET_MSG: -			pr_info("%s<%s>, requested by peer while probing\n", -				link_rst_msg, l_ptr->name); +			pr_debug("%s<%s>, requested by peer while probing\n", +				 link_rst_msg, l_ptr->name);  			tipc_link_reset(l_ptr);  			l_ptr->state = RESET_RESET;  			l_ptr->fsm_msg_cnt = 0; @@ -572,8 +605,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)  				l_ptr->fsm_msg_cnt++;  				link_set_timer(l_ptr, cont_intv / 4);  			} else {	/* Link has failed */ -				pr_warn("%s<%s>, peer not responding\n", -					link_rst_msg, l_ptr->name); +				pr_debug("%s<%s>, peer not responding\n", +					 link_rst_msg, l_ptr->name);  				tipc_link_reset(l_ptr);  				l_ptr->state = RESET_UNKNOWN;  				l_ptr->fsm_msg_cnt = 0; @@ -614,7 +647,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++; @@ -700,7 +735,8 @@ drop:   * Only the socket functions tipc_send_stream() and tipc_send_packet() need   * to act on the return value, since they may need to do more send attempts.   */ -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list) +int __tipc_link_xmit(struct net *net, struct tipc_link *link, +		     struct sk_buff_head *list)  {  	struct tipc_msg *msg = buf_msg(skb_peek(list));  	uint psz = msg_size(msg); @@ -733,7 +769,8 @@ int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list)  		if (skb_queue_len(outqueue) < sndlim) {  			__skb_queue_tail(outqueue, skb); -			tipc_bearer_send(link->bearer_id, skb, addr); +			tipc_bearer_send(net, link->bearer_id, +					 skb, addr);  			link->next_out = NULL;  			link->unacked_window = 0;  		} else if (tipc_msg_bundle(outqueue, skb, mtu)) { @@ -758,7 +795,7 @@ int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list)  static void skb2list(struct sk_buff *skb, struct sk_buff_head *list)  { -	__skb_queue_head_init(list); +	skb_queue_head_init(list);  	__skb_queue_tail(list, skb);  } @@ -767,19 +804,21 @@ static int __tipc_link_xmit_skb(struct tipc_link *link, struct sk_buff *skb)  	struct sk_buff_head head;  	skb2list(skb, &head); -	return __tipc_link_xmit(link, &head); +	return __tipc_link_xmit(link->owner->net, link, &head);  } -int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector) +int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode, +		       u32 selector)  {  	struct sk_buff_head head;  	skb2list(skb, &head); -	return tipc_link_xmit(&head, dnode, selector); +	return tipc_link_xmit(net, &head, dnode, selector);  }  /**   * tipc_link_xmit() is the general link level function for message sending + * @net: the applicable net namespace   * @list: chain of buffers containing message   * @dsz: amount of user data to be sent   * @dnode: address of destination node @@ -787,33 +826,28 @@ int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector)   * Consumes the buffer chain, except when returning -ELINKCONG   * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE   */ -int tipc_link_xmit(struct sk_buff_head *list, u32 dnode, u32 selector) +int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode, +		   u32 selector)  {  	struct tipc_link *link = NULL;  	struct tipc_node *node;  	int rc = -EHOSTUNREACH; -	node = tipc_node_find(dnode); +	node = tipc_node_find(net, dnode);  	if (node) {  		tipc_node_lock(node);  		link = node->active_links[selector & 1];  		if (link) -			rc = __tipc_link_xmit(link, list); +			rc = __tipc_link_xmit(net, link, list);  		tipc_node_unlock(node);  	} -  	if (link)  		return rc; -	if (likely(in_own_node(dnode))) { -		/* As a node local message chain never contains more than one -		 * buffer, we just need to dequeue one SKB buffer from the -		 * head list. -		 */ -		return tipc_sk_rcv(__skb_dequeue(list)); -	} -	__skb_queue_purge(list); +	if (likely(in_own_node(net, dnode))) +		return tipc_sk_rcv(net, list); +	__skb_queue_purge(list);  	return rc;  } @@ -835,7 +869,8 @@ static void tipc_link_sync_xmit(struct tipc_link *link)  		return;  	msg = buf_msg(skb); -	tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, link->addr); +	tipc_msg_init(link_own_addr(link), msg, BCAST_PROTOCOL, STATE_MSG, +		      INT_H_SIZE, link->addr);  	msg_set_last_bcast(msg, link->owner->bclink.acked);  	__tipc_link_xmit_skb(link, skb);  } @@ -890,7 +925,8 @@ void tipc_link_push_packets(struct tipc_link *l_ptr)  			msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);  			if (msg_user(msg) == MSG_BUNDLER)  				TIPC_SKB_CB(skb)->bundling = false; -			tipc_bearer_send(l_ptr->bearer_id, skb, +			tipc_bearer_send(l_ptr->owner->net, +					 l_ptr->bearer_id, skb,  					 &l_ptr->media_addr);  			l_ptr->next_out = tipc_skb_queue_next(outqueue, skb);  		} else { @@ -923,6 +959,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,  				    struct sk_buff *buf)  {  	struct tipc_msg *msg = buf_msg(buf); +	struct net *net = l_ptr->owner->net;  	pr_warn("Retransmission failure on link <%s>\n", l_ptr->name); @@ -940,7 +977,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,  		pr_cont("Outstanding acks: %lu\n",  			(unsigned long) TIPC_SKB_CB(buf)->handle); -		n_ptr = tipc_bclink_retransmit_to(); +		n_ptr = tipc_bclink_retransmit_to(net);  		tipc_node_lock(n_ptr);  		tipc_addr_string_fill(addr_string, n_ptr->addr); @@ -955,7 +992,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,  		tipc_node_unlock(n_ptr); -		tipc_bclink_set_flags(TIPC_BCLINK_RESET); +		tipc_bclink_set_flags(net, TIPC_BCLINK_RESET);  		l_ptr->stale_count = 0;  	}  } @@ -987,7 +1024,8 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb,  		msg = buf_msg(skb);  		msg_set_ack(msg, mod(l_ptr->next_in_no - 1));  		msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); -		tipc_bearer_send(l_ptr->bearer_id, skb, &l_ptr->media_addr); +		tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, skb, +				 &l_ptr->media_addr);  		retransmits--;  		l_ptr->stats.retransmitted++;  	} @@ -1063,14 +1101,16 @@ static int link_recv_buf_validate(struct sk_buff *buf)  /**   * tipc_rcv - process TIPC packets/messages arriving from off-node + * @net: the applicable net namespace   * @skb: TIPC packet   * @b_ptr: pointer to bearer message arrived on   *   * Invoked with no locks held.  Bearer pointer must point to a valid bearer   * structure (i.e. cannot be NULL), but bearer can be inactive.   */ -void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) +void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sk_buff_head head;  	struct tipc_node *n_ptr;  	struct tipc_link *l_ptr; @@ -1096,19 +1136,19 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  		if (unlikely(msg_non_seq(msg))) {  			if (msg_user(msg) ==  LINK_CONFIG) -				tipc_disc_rcv(skb, b_ptr); +				tipc_disc_rcv(net, skb, b_ptr);  			else -				tipc_bclink_rcv(skb); +				tipc_bclink_rcv(net, skb);  			continue;  		}  		/* Discard unicast link messages destined for another node */  		if (unlikely(!msg_short(msg) && -			     (msg_destnode(msg) != tipc_own_addr))) +			     (msg_destnode(msg) != tn->own_addr)))  			goto discard;  		/* Locate neighboring node that sent message */ -		n_ptr = tipc_node_find(msg_prevnode(msg)); +		n_ptr = tipc_node_find(net, msg_prevnode(msg));  		if (unlikely(!n_ptr))  			goto discard;  		tipc_node_lock(n_ptr); @@ -1116,7 +1156,7 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  		/* Locate unicast link endpoint that should handle message */  		l_ptr = n_ptr->links[b_ptr->identity];  		if (unlikely(!l_ptr)) -			goto unlock_discard; +			goto unlock;  		/* Verify that communication with node is currently allowed */  		if ((n_ptr->action_flags & TIPC_WAIT_PEER_LINKS_DOWN) && @@ -1127,7 +1167,7 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  			n_ptr->action_flags &= ~TIPC_WAIT_PEER_LINKS_DOWN;  		if (tipc_node_blocked(n_ptr)) -			goto unlock_discard; +			goto unlock;  		/* Validate message sequence number info */  		seq_no = msg_seqno(msg); @@ -1151,18 +1191,16 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  		if (unlikely(l_ptr->next_out))  			tipc_link_push_packets(l_ptr); -		if (released && !skb_queue_empty(&l_ptr->waiting_sks)) { +		if (released && !skb_queue_empty(&l_ptr->wakeupq))  			link_prepare_wakeup(l_ptr); -			l_ptr->owner->action_flags |= TIPC_WAKEUP_USERS; -		}  		/* Process the incoming packet */  		if (unlikely(!link_working_working(l_ptr))) {  			if (msg_user(msg) == LINK_PROTOCOL) {  				tipc_link_proto_rcv(l_ptr, skb);  				link_retrieve_defq(l_ptr, &head); -				tipc_node_unlock(n_ptr); -				continue; +				skb = NULL; +				goto unlock;  			}  			/* Traffic message. Conditionally activate link */ @@ -1171,18 +1209,18 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  			if (link_working_working(l_ptr)) {  				/* Re-insert buffer in front of queue */  				__skb_queue_head(&head, skb); -				tipc_node_unlock(n_ptr); -				continue; +				skb = NULL; +				goto unlock;  			} -			goto unlock_discard; +			goto unlock;  		}  		/* Link is now in state WORKING_WORKING */  		if (unlikely(seq_no != mod(l_ptr->next_in_no))) {  			link_handle_out_of_seq_msg(l_ptr, skb);  			link_retrieve_defq(l_ptr, &head); -			tipc_node_unlock(n_ptr); -			continue; +			skb = NULL; +			goto unlock;  		}  		l_ptr->next_in_no++;  		if (unlikely(!skb_queue_empty(&l_ptr->deferred_queue))) @@ -1192,95 +1230,102 @@ void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)  			l_ptr->stats.sent_acks++;  			tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);  		} - -		if (tipc_link_prepare_input(l_ptr, &skb)) { -			tipc_node_unlock(n_ptr); -			continue; -		} -		tipc_node_unlock(n_ptr); - -		if (tipc_link_input(l_ptr, skb) != 0) -			goto discard; -		continue; -unlock_discard: +		tipc_link_input(l_ptr, skb); +		skb = NULL; +unlock:  		tipc_node_unlock(n_ptr);  discard: -		kfree_skb(skb); +		if (unlikely(skb)) +			kfree_skb(skb);  	}  } -/** - * tipc_link_prepare_input - process TIPC link messages - * - * returns nonzero if the message was consumed +/* tipc_data_input - deliver data and name distr msgs to upper layer   * + * Consumes buffer if message is of right type   * Node lock must be held   */ -static int tipc_link_prepare_input(struct tipc_link *l, struct sk_buff **buf) +static bool tipc_data_input(struct tipc_link *link, struct sk_buff *skb)  { -	struct tipc_node *n; -	struct tipc_msg *msg; -	int res = -EINVAL; +	struct tipc_node *node = link->owner; +	struct tipc_msg *msg = buf_msg(skb); +	u32 dport = msg_destport(msg); -	n = l->owner; -	msg = buf_msg(*buf);  	switch (msg_user(msg)) { -	case CHANGEOVER_PROTOCOL: -		if (tipc_link_tunnel_rcv(n, buf)) -			res = 0; -		break; -	case MSG_FRAGMENTER: -		l->stats.recv_fragments++; -		if (tipc_buf_append(&l->reasm_buf, buf)) { -			l->stats.recv_fragmented++; -			res = 0; -		} else if (!l->reasm_buf) { -			tipc_link_reset(l); +	case TIPC_LOW_IMPORTANCE: +	case TIPC_MEDIUM_IMPORTANCE: +	case TIPC_HIGH_IMPORTANCE: +	case TIPC_CRITICAL_IMPORTANCE: +	case CONN_MANAGER: +		if (tipc_skb_queue_tail(&link->inputq, skb, dport)) { +			node->inputq = &link->inputq; +			node->action_flags |= TIPC_MSG_EVT;  		} -		break; -	case MSG_BUNDLER: -		l->stats.recv_bundles++; -		l->stats.recv_bundled += msg_msgcnt(msg); -		res = 0; -		break; +		return true;  	case NAME_DISTRIBUTOR: -		n->bclink.recv_permitted = true; -		res = 0; -		break; +		node->bclink.recv_permitted = true; +		node->namedq = &link->namedq; +		skb_queue_tail(&link->namedq, skb); +		if (skb_queue_len(&link->namedq) == 1) +			node->action_flags |= TIPC_NAMED_MSG_EVT; +		return true; +	case MSG_BUNDLER: +	case CHANGEOVER_PROTOCOL: +	case MSG_FRAGMENTER:  	case BCAST_PROTOCOL: -		tipc_link_sync_rcv(n, *buf); -		break; +		return false;  	default: -		res = 0; -	} -	return res; +		pr_warn("Dropping received illegal msg type\n"); +		kfree_skb(skb); +		return false; +	};  } -/** - * tipc_link_input - Deliver message too higher layers + +/* tipc_link_input - process packet that has passed link protocol check + * + * Consumes buffer + * Node lock must be held   */ -static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf) +static void tipc_link_input(struct tipc_link *link, struct sk_buff *skb)  { -	struct tipc_msg *msg = buf_msg(buf); -	int res = 0; +	struct tipc_node *node = link->owner; +	struct tipc_msg *msg = buf_msg(skb); +	struct sk_buff *iskb; +	int pos = 0; + +	if (likely(tipc_data_input(link, skb))) +		return;  	switch (msg_user(msg)) { -	case TIPC_LOW_IMPORTANCE: -	case TIPC_MEDIUM_IMPORTANCE: -	case TIPC_HIGH_IMPORTANCE: -	case TIPC_CRITICAL_IMPORTANCE: -	case CONN_MANAGER: -		tipc_sk_rcv(buf); +	case CHANGEOVER_PROTOCOL: +		if (!tipc_link_tunnel_rcv(node, &skb)) +			break; +		if (msg_user(buf_msg(skb)) != MSG_BUNDLER) { +			tipc_data_input(link, skb); +			break; +		} +	case MSG_BUNDLER: +		link->stats.recv_bundles++; +		link->stats.recv_bundled += msg_msgcnt(msg); + +		while (tipc_msg_extract(skb, &iskb, &pos)) +			tipc_data_input(link, iskb);  		break; -	case NAME_DISTRIBUTOR: -		tipc_named_rcv(buf); +	case MSG_FRAGMENTER: +		link->stats.recv_fragments++; +		if (tipc_buf_append(&link->reasm_buf, &skb)) { +			link->stats.recv_fragmented++; +			tipc_data_input(link, skb); +		} else if (!link->reasm_buf) { +			tipc_link_reset(link); +		}  		break; -	case MSG_BUNDLER: -		tipc_link_bundle_rcv(buf); +	case BCAST_PROTOCOL: +		tipc_link_sync_rcv(node, skb);  		break;  	default: -		res = -EINVAL; -	} -	return res; +		break; +	};  }  /** @@ -1381,7 +1426,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,  	msg_set_type(msg, msg_typ);  	msg_set_net_plane(msg, l_ptr->net_plane);  	msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); -	msg_set_last_bcast(msg, tipc_bclink_get_last_sent()); +	msg_set_last_bcast(msg, tipc_bclink_get_last_sent(l_ptr->owner->net));  	if (msg_typ == STATE_MSG) {  		u32 next_sent = mod(l_ptr->next_out_no); @@ -1445,7 +1490,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,  	skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));  	buf->priority = TC_PRIO_CONTROL; -	tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr); +	tipc_bearer_send(l_ptr->owner->net, l_ptr->bearer_id, buf, +			 &l_ptr->media_addr);  	l_ptr->unacked_window = 0;  	kfree_skb(buf);  } @@ -1455,7 +1501,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,   * Note that network plane id propagates through the network, and may   * change at any time. The node with lowest address rules   */ -static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf) +static void tipc_link_proto_rcv(struct tipc_link *l_ptr, +				struct sk_buff *buf)  {  	u32 rec_gap = 0;  	u32 max_pkt_info; @@ -1468,7 +1515,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)  		goto exit;  	if (l_ptr->net_plane != msg_net_plane(msg)) -		if (tipc_own_addr > msg_prevnode(msg)) +		if (link_own_addr(l_ptr) > msg_prevnode(msg))  			l_ptr->net_plane = msg_net_plane(msg);  	switch (msg_type(msg)) { @@ -1535,9 +1582,9 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)  		if (msg_linkprio(msg) &&  		    (msg_linkprio(msg) != l_ptr->priority)) { -			pr_warn("%s<%s>, priority change %u->%u\n", -				link_rst_msg, l_ptr->name, l_ptr->priority, -				msg_linkprio(msg)); +			pr_debug("%s<%s>, priority change %u->%u\n", +				 link_rst_msg, l_ptr->name, +				 l_ptr->priority, msg_linkprio(msg));  			l_ptr->priority = msg_linkprio(msg);  			tipc_link_reset(l_ptr); /* Enforce change to take effect */  			break; @@ -1636,8 +1683,8 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)  	if (!tunnel)  		return; -	tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, -		 ORIGINAL_MSG, INT_H_SIZE, l_ptr->addr); +	tipc_msg_init(link_own_addr(l_ptr), &tunnel_hdr, CHANGEOVER_PROTOCOL, +		      ORIGINAL_MSG, INT_H_SIZE, l_ptr->addr);  	msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id);  	msg_set_msgcnt(&tunnel_hdr, msgcount); @@ -1694,8 +1741,8 @@ void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr,  	struct sk_buff *skb;  	struct tipc_msg tunnel_hdr; -	tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, -		 DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr); +	tipc_msg_init(link_own_addr(l_ptr), &tunnel_hdr, CHANGEOVER_PROTOCOL, +		      DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr);  	msg_set_msgcnt(&tunnel_hdr, skb_queue_len(&l_ptr->outqueue));  	msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id);  	skb_queue_walk(&l_ptr->outqueue, skb) { @@ -1729,7 +1776,7 @@ void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr,   * @from_pos: offset to extract from   *   * Returns a new message buffer containing an embedded message.  The - * encapsulating message itself is left unchanged. + * encapsulating buffer is left unchanged.   */  static struct sk_buff *buf_extract(struct sk_buff *skb, u32 from_pos)  { @@ -1743,8 +1790,6 @@ static struct sk_buff *buf_extract(struct sk_buff *skb, u32 from_pos)  	return eb;  } - -  /* tipc_link_dup_rcv(): Receive a tunnelled DUPLICATE_MSG packet.   * Owner node is locked.   */ @@ -1804,10 +1849,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;  } @@ -1845,50 +1888,16 @@ exit:  	return *buf != NULL;  } -/* - *  Bundler functionality: - */ -void tipc_link_bundle_rcv(struct sk_buff *buf) +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol)  { -	u32 msgcount = msg_msgcnt(buf_msg(buf)); -	u32 pos = INT_H_SIZE; -	struct sk_buff *obuf; -	struct tipc_msg *omsg; - -	while (msgcount--) { -		obuf = buf_extract(buf, pos); -		if (obuf == NULL) { -			pr_warn("Link unable to unbundle message(s)\n"); -			break; -		} -		omsg = buf_msg(obuf); -		pos += align(msg_size(omsg)); -		if (msg_isdata(omsg)) { -			if (unlikely(msg_type(omsg) == TIPC_MCAST_MSG)) -				tipc_sk_mcast_rcv(obuf); -			else -				tipc_sk_rcv(obuf); -		} else if (msg_user(omsg) == CONN_MANAGER) { -			tipc_sk_rcv(obuf); -		} else if (msg_user(omsg) == NAME_DISTRIBUTOR) { -			tipc_named_rcv(obuf); -		} else { -			pr_warn("Illegal bundled msg: %u\n", msg_user(omsg)); -			kfree_skb(obuf); -		} -	} -	kfree_skb(buf); -} +	unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4; -static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance) -{ -	if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL)) +	if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL))  		return; -	l_ptr->tolerance = tolerance; -	l_ptr->continuity_interval = -		((tolerance / 4) > 500) ? 500 : tolerance / 4; -	l_ptr->abort_limit = tolerance / (l_ptr->continuity_interval / 4); +	l_ptr->tolerance = tol; +	l_ptr->cont_intv = msecs_to_jiffies(intv); +	l_ptr->abort_limit = tol / (jiffies_to_msecs(l_ptr->cont_intv) / 4);  }  void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) @@ -1911,22 +1920,25 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window)  }  /* tipc_link_find_owner - locate owner node of link by link's name + * @net: the applicable net namespace   * @name: pointer to link name string   * @bearer_id: pointer to index in 'node->links' array where the link was found.   *   * Returns pointer to node owning the link, or 0 if no matching link is found.   */ -static struct tipc_node *tipc_link_find_owner(const char *link_name, +static struct tipc_node *tipc_link_find_owner(struct net *net, +					      const char *link_name,  					      unsigned int *bearer_id)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_link *l_ptr;  	struct tipc_node *n_ptr; -	struct tipc_node *found_node = 0; +	struct tipc_node *found_node = NULL;  	int i;  	*bearer_id = 0;  	rcu_read_lock(); -	list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { +	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {  		tipc_node_lock(n_ptr);  		for (i = 0; i < MAX_BEARERS; i++) {  			l_ptr = n_ptr->links[i]; @@ -1946,148 +1958,6 @@ static struct tipc_node *tipc_link_find_owner(const char *link_name,  }  /** - * link_value_is_valid -- validate proposed link tolerance/priority/window - * - * @cmd: value type (TIPC_CMD_SET_LINK_*) - * @new_value: the new value - * - * Returns 1 if value is within range, 0 if not. - */ -static int link_value_is_valid(u16 cmd, u32 new_value) -{ -	switch (cmd) { -	case TIPC_CMD_SET_LINK_TOL: -		return (new_value >= TIPC_MIN_LINK_TOL) && -			(new_value <= TIPC_MAX_LINK_TOL); -	case TIPC_CMD_SET_LINK_PRI: -		return (new_value <= TIPC_MAX_LINK_PRI); -	case TIPC_CMD_SET_LINK_WINDOW: -		return (new_value >= TIPC_MIN_LINK_WIN) && -			(new_value <= TIPC_MAX_LINK_WIN); -	} -	return 0; -} - -/** - * link_cmd_set_value - change priority/tolerance/window for link/bearer/media - * @name: ptr to link, bearer, or media name - * @new_value: new value of link, bearer, or media setting - * @cmd: which link, bearer, or media attribute to set (TIPC_CMD_SET_LINK_*) - * - * Caller must hold RTNL lock to ensure link/bearer/media is not deleted. - * - * Returns 0 if value updated and negative value on error. - */ -static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) -{ -	struct tipc_node *node; -	struct tipc_link *l_ptr; -	struct tipc_bearer *b_ptr; -	struct tipc_media *m_ptr; -	int bearer_id; -	int res = 0; - -	node = tipc_link_find_owner(name, &bearer_id); -	if (node) { -		tipc_node_lock(node); -		l_ptr = node->links[bearer_id]; - -		if (l_ptr) { -			switch (cmd) { -			case TIPC_CMD_SET_LINK_TOL: -				link_set_supervision_props(l_ptr, new_value); -				tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, -						     new_value, 0, 0); -				break; -			case TIPC_CMD_SET_LINK_PRI: -				l_ptr->priority = new_value; -				tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, -						     0, new_value, 0); -				break; -			case TIPC_CMD_SET_LINK_WINDOW: -				tipc_link_set_queue_limits(l_ptr, new_value); -				break; -			default: -				res = -EINVAL; -				break; -			} -		} -		tipc_node_unlock(node); -		return res; -	} - -	b_ptr = tipc_bearer_find(name); -	if (b_ptr) { -		switch (cmd) { -		case TIPC_CMD_SET_LINK_TOL: -			b_ptr->tolerance = new_value; -			break; -		case TIPC_CMD_SET_LINK_PRI: -			b_ptr->priority = new_value; -			break; -		case TIPC_CMD_SET_LINK_WINDOW: -			b_ptr->window = new_value; -			break; -		default: -			res = -EINVAL; -			break; -		} -		return res; -	} - -	m_ptr = tipc_media_find(name); -	if (!m_ptr) -		return -ENODEV; -	switch (cmd) { -	case TIPC_CMD_SET_LINK_TOL: -		m_ptr->tolerance = new_value; -		break; -	case TIPC_CMD_SET_LINK_PRI: -		m_ptr->priority = new_value; -		break; -	case TIPC_CMD_SET_LINK_WINDOW: -		m_ptr->window = new_value; -		break; -	default: -		res = -EINVAL; -		break; -	} -	return res; -} - -struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space, -				     u16 cmd) -{ -	struct tipc_link_config *args; -	u32 new_value; -	int res; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_CONFIG)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	args = (struct tipc_link_config *)TLV_DATA(req_tlv_area); -	new_value = ntohl(args->value); - -	if (!link_value_is_valid(cmd, new_value)) -		return tipc_cfg_reply_error_string( -			"cannot change, value invalid"); - -	if (!strcmp(args->name, tipc_bclink_name)) { -		if ((cmd == TIPC_CMD_SET_LINK_WINDOW) && -		    (tipc_bclink_set_queue_limits(new_value) == 0)) -			return tipc_cfg_reply_none(); -		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -						   " (cannot change setting on broadcast link)"); -	} - -	res = link_cmd_set_value(args->name, new_value, cmd); -	if (res) -		return tipc_cfg_reply_error_string("cannot change link setting"); - -	return tipc_cfg_reply_none(); -} - -/**   * link_reset_statistics - reset link statistics   * @l_ptr: pointer to link   */ @@ -2098,207 +1968,13 @@ static void link_reset_statistics(struct tipc_link *l_ptr)  	l_ptr->stats.recv_info = l_ptr->next_in_no;  } -struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_space) -{ -	char *link_name; -	struct tipc_link *l_ptr; -	struct tipc_node *node; -	unsigned int bearer_id; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_NAME)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	link_name = (char *)TLV_DATA(req_tlv_area); -	if (!strcmp(link_name, tipc_bclink_name)) { -		if (tipc_bclink_reset_stats()) -			return tipc_cfg_reply_error_string("link not found"); -		return tipc_cfg_reply_none(); -	} -	node = tipc_link_find_owner(link_name, &bearer_id); -	if (!node) -		return tipc_cfg_reply_error_string("link not found"); - -	tipc_node_lock(node); -	l_ptr = node->links[bearer_id]; -	if (!l_ptr) { -		tipc_node_unlock(node); -		return tipc_cfg_reply_error_string("link not found"); -	} -	link_reset_statistics(l_ptr); -	tipc_node_unlock(node); -	return tipc_cfg_reply_none(); -} - -/** - * percent - convert count to a percentage of total (rounding up or down) - */ -static u32 percent(u32 count, u32 total) -{ -	return (count * 100 + (total / 2)) / total; -} - -/** - * tipc_link_stats - print link statistics - * @name: link name - * @buf: print buffer area - * @buf_size: size of print buffer area - * - * Returns length of print buffer data string (or 0 if error) - */ -static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) -{ -	struct tipc_link *l; -	struct tipc_stats *s; -	struct tipc_node *node; -	char *status; -	u32 profile_total = 0; -	unsigned int bearer_id; -	int ret; - -	if (!strcmp(name, tipc_bclink_name)) -		return tipc_bclink_stats(buf, buf_size); - -	node = tipc_link_find_owner(name, &bearer_id); -	if (!node) -		return 0; - -	tipc_node_lock(node); - -	l = node->links[bearer_id]; -	if (!l) { -		tipc_node_unlock(node); -		return 0; -	} - -	s = &l->stats; - -	if (tipc_link_is_active(l)) -		status = "ACTIVE"; -	else if (tipc_link_is_up(l)) -		status = "STANDBY"; -	else -		status = "DEFUNCT"; - -	ret = tipc_snprintf(buf, buf_size, "Link <%s>\n" -			    "  %s  MTU:%u  Priority:%u  Tolerance:%u ms" -			    "  Window:%u packets\n", -			    l->name, status, l->max_pkt, l->priority, -			    l->tolerance, l->queue_limit[0]); - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  RX packets:%u fragments:%u/%u bundles:%u/%u\n", -			     l->next_in_no - s->recv_info, s->recv_fragments, -			     s->recv_fragmented, s->recv_bundles, -			     s->recv_bundled); - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  TX packets:%u fragments:%u/%u bundles:%u/%u\n", -			     l->next_out_no - s->sent_info, s->sent_fragments, -			     s->sent_fragmented, s->sent_bundles, -			     s->sent_bundled); - -	profile_total = s->msg_length_counts; -	if (!profile_total) -		profile_total = 1; - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  TX profile sample:%u packets  average:%u octets\n" -			     "  0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% " -			     "-16384:%u%% -32768:%u%% -66000:%u%%\n", -			     s->msg_length_counts, -			     s->msg_lengths_total / profile_total, -			     percent(s->msg_length_profile[0], profile_total), -			     percent(s->msg_length_profile[1], profile_total), -			     percent(s->msg_length_profile[2], profile_total), -			     percent(s->msg_length_profile[3], profile_total), -			     percent(s->msg_length_profile[4], profile_total), -			     percent(s->msg_length_profile[5], profile_total), -			     percent(s->msg_length_profile[6], profile_total)); - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  RX states:%u probes:%u naks:%u defs:%u" -			     " dups:%u\n", s->recv_states, s->recv_probes, -			     s->recv_nacks, s->deferred_recv, s->duplicates); - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  TX states:%u probes:%u naks:%u acks:%u" -			     " dups:%u\n", s->sent_states, s->sent_probes, -			     s->sent_nacks, s->sent_acks, s->retransmitted); - -	ret += tipc_snprintf(buf + ret, buf_size - ret, -			     "  Congestion link:%u  Send queue" -			     " max:%u avg:%u\n", s->link_congs, -			     s->max_queue_sz, s->queue_sz_counts ? -			     (s->accu_queue_sz / s->queue_sz_counts) : 0); - -	tipc_node_unlock(node); -	return ret; -} - -struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_space) -{ -	struct sk_buff *buf; -	struct tlv_desc *rep_tlv; -	int str_len; -	int pb_len; -	char *pb; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_NAME)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); -	if (!buf) -		return NULL; - -	rep_tlv = (struct tlv_desc *)buf->data; -	pb = TLV_DATA(rep_tlv); -	pb_len = ULTRA_STRING_MAX_LEN; -	str_len = tipc_link_stats((char *)TLV_DATA(req_tlv_area), -				  pb, pb_len); -	if (!str_len) { -		kfree_skb(buf); -		return tipc_cfg_reply_error_string("link not found"); -	} -	str_len += 1;	/* for "\0" */ -	skb_put(buf, TLV_SPACE(str_len)); -	TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); - -	return buf; -} - -/** - * tipc_link_get_max_pkt - get maximum packet size to use when sending to destination - * @dest: network address of destination node - * @selector: used to select from set of active links - * - * If no active link can be found, uses default maximum packet size. - */ -u32 tipc_link_get_max_pkt(u32 dest, u32 selector) -{ -	struct tipc_node *n_ptr; -	struct tipc_link *l_ptr; -	u32 res = MAX_PKT_DEFAULT; - -	if (dest == tipc_own_addr) -		return MAX_MSG_SIZE; - -	n_ptr = tipc_node_find(dest); -	if (n_ptr) { -		tipc_node_lock(n_ptr); -		l_ptr = n_ptr->active_links[selector & 1]; -		if (l_ptr) -			res = l_ptr->max_pkt; -		tipc_node_unlock(n_ptr); -	} -	return res; -} -  static void link_print(struct tipc_link *l_ptr, const char *str)  { +	struct tipc_net *tn = net_generic(l_ptr->owner->net, tipc_net_id);  	struct tipc_bearer *b_ptr;  	rcu_read_lock(); -	b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]); +	b_ptr = rcu_dereference_rtnl(tn->bearer_list[l_ptr->bearer_id]);  	if (b_ptr)  		pr_info("%s Link %x<%s>:", str, l_ptr->addr, b_ptr->name);  	rcu_read_unlock(); @@ -2362,6 +2038,7 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)  	struct tipc_link *link;  	struct tipc_node *node;  	struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; +	struct net *net = sock_net(skb->sk);  	if (!info->attrs[TIPC_NLA_LINK])  		return -EINVAL; @@ -2377,7 +2054,7 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)  	name = nla_data(attrs[TIPC_NLA_LINK_NAME]); -	node = tipc_link_find_owner(name, &bearer_id); +	node = tipc_link_find_owner(net, name, &bearer_id);  	if (!node)  		return -EINVAL; @@ -2493,14 +2170,16 @@ msg_full:  }  /* Caller should hold appropriate locks to protect the link */ -static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link) +static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg, +			      struct tipc_link *link)  {  	int err;  	void *hdr;  	struct nlattr *attrs;  	struct nlattr *prop; +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_LINK_GET);  	if (!hdr)  		return -EMSGSIZE; @@ -2512,7 +2191,7 @@ static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link)  	if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name))  		goto attr_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, -			tipc_cluster_mask(tipc_own_addr))) +			tipc_cluster_mask(tn->own_addr)))  		goto attr_msg_full;  	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt))  		goto attr_msg_full; @@ -2562,9 +2241,8 @@ msg_full:  }  /* Caller should hold node lock  */ -static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, -				    struct tipc_node *node, -				    u32 *prev_link) +static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg, +				    struct tipc_node *node, u32 *prev_link)  {  	u32 i;  	int err; @@ -2575,7 +2253,7 @@ static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg,  		if (!node->links[i])  			continue; -		err = __tipc_nl_add_link(msg, node->links[i]); +		err = __tipc_nl_add_link(net, msg, node->links[i]);  		if (err)  			return err;  	} @@ -2586,6 +2264,8 @@ static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg,  int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)  { +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_node *node;  	struct tipc_nl_msg msg;  	u32 prev_node = cb->args[0]; @@ -2603,7 +2283,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)  	rcu_read_lock();  	if (prev_node) { -		node = tipc_node_find(prev_node); +		node = tipc_node_find(net, prev_node);  		if (!node) {  			/* We never set seq or call nl_dump_check_consistent()  			 * this means that setting prev_seq here will cause the @@ -2615,9 +2295,11 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)  			goto out;  		} -		list_for_each_entry_continue_rcu(node, &tipc_node_list, list) { +		list_for_each_entry_continue_rcu(node, &tn->node_list, +						 list) {  			tipc_node_lock(node); -			err = __tipc_nl_add_node_links(&msg, node, &prev_link); +			err = __tipc_nl_add_node_links(net, &msg, node, +						       &prev_link);  			tipc_node_unlock(node);  			if (err)  				goto out; @@ -2625,13 +2307,14 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)  			prev_node = node->addr;  		}  	} else { -		err = tipc_nl_add_bc_link(&msg); +		err = tipc_nl_add_bc_link(net, &msg);  		if (err)  			goto out; -		list_for_each_entry_rcu(node, &tipc_node_list, list) { +		list_for_each_entry_rcu(node, &tn->node_list, list) {  			tipc_node_lock(node); -			err = __tipc_nl_add_node_links(&msg, node, &prev_link); +			err = __tipc_nl_add_node_links(net, &msg, node, +						       &prev_link);  			tipc_node_unlock(node);  			if (err)  				goto out; @@ -2652,6 +2335,7 @@ out:  int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)  { +	struct net *net = genl_info_net(info);  	struct sk_buff *ans_skb;  	struct tipc_nl_msg msg;  	struct tipc_link *link; @@ -2664,7 +2348,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)  		return -EINVAL;  	name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]); -	node = tipc_link_find_owner(name, &bearer_id); +	node = tipc_link_find_owner(net, name, &bearer_id);  	if (!node)  		return -EINVAL; @@ -2683,7 +2367,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)  		goto err_out;  	} -	err = __tipc_nl_add_link(&msg, link); +	err = __tipc_nl_add_link(net, &msg, link);  	if (err)  		goto err_out; @@ -2706,6 +2390,7 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)  	struct tipc_link *link;  	struct tipc_node *node;  	struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; +	struct net *net = sock_net(skb->sk);  	if (!info->attrs[TIPC_NLA_LINK])  		return -EINVAL; @@ -2722,13 +2407,13 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)  	link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);  	if (strcmp(link_name, tipc_bclink_name) == 0) { -		err = tipc_bclink_reset_stats(); +		err = tipc_bclink_reset_stats(net);  		if (err)  			return err;  		return 0;  	} -	node = tipc_link_find_owner(link_name, &bearer_id); +	node = tipc_link_find_owner(net, link_name, &bearer_id);  	if (!node)  		return -EINVAL; diff --git a/net/tipc/link.h b/net/tipc/link.h index 55812e87ca1e..7aeb52092bf3 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -41,6 +41,10 @@  #include "msg.h"  #include "node.h" +/* TIPC-specific error codes +*/ +#define ELINKCONG EAGAIN	/* link congestion <=> resource unavailable */ +  /* Out-of-range value for link sequence numbers   */  #define INVALID_LINK_SEQ 0x10000 @@ -99,13 +103,14 @@ 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   * @peer_bearer_id: bearer id used by link's peer endpoint   * @bearer_id: local bearer id used by link   * @tolerance: minimum link continuity loss needed to reset link [in ms] - * @continuity_interval: link continuity testing interval [in ms] + * @cont_intv: link continuity testing interval   * @abort_limit: # of unacknowledged continuity probes needed to reset link   * @state: current state of link FSM   * @fsm_msg_cnt: # of protocol messages link FSM has sent in current state @@ -126,8 +131,10 @@ struct tipc_stats {   * @next_in_no: next sequence number to expect for inbound messages   * @deferred_queue: deferred queue saved OOS b'cast message received from node   * @unacked_window: # of inbound messages rx'd without ack'ing back to peer + * @inputq: buffer queue for messages to be delivered upwards + * @namedq: buffer queue for name table messages to be delivered upwards   * @next_out: ptr to first unsent outbound message in queue - * @waiting_sks: linked list of sockets waiting for link congestion to abate + * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate   * @long_msg_seq_no: next identifier to use for outbound fragmented messages   * @reasm_buf: head of partially reassembled inbound message fragments   * @stats: collects statistics regarding link activity @@ -138,6 +145,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; @@ -146,7 +154,7 @@ struct tipc_link {  	u32 peer_bearer_id;  	u32 bearer_id;  	u32 tolerance; -	u32 continuity_interval; +	unsigned long cont_intv;  	u32 abort_limit;  	int state;  	u32 fsm_msg_cnt; @@ -178,10 +186,12 @@ struct tipc_link {  	u32 next_in_no;  	struct sk_buff_head deferred_queue;  	u32 unacked_window; +	struct sk_buff_head inputq; +	struct sk_buff_head namedq;  	/* Congestion handling */  	struct sk_buff *next_out; -	struct sk_buff_head waiting_sks; +	struct sk_buff_head wakeupq;  	/* Fragmentation/reassembly */  	u32 long_msg_seq_no; @@ -196,28 +206,24 @@ 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_list(unsigned int bearer_id, bool shutting_down); +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);  void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, struct tipc_link *dest);  void tipc_link_reset_fragments(struct tipc_link *l_ptr);  int tipc_link_is_up(struct tipc_link *l_ptr);  int tipc_link_is_active(struct tipc_link *l_ptr);  void tipc_link_purge_queues(struct tipc_link *l_ptr); -struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, -				     int req_tlv_space, -				     u16 cmd); -struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, -					 int req_tlv_space); -struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, -					  int req_tlv_space);  void tipc_link_reset_all(struct tipc_node *node);  void tipc_link_reset(struct tipc_link *l_ptr); -void tipc_link_reset_list(unsigned int bearer_id); -int tipc_link_xmit_skb(struct sk_buff *skb, u32 dest, u32 selector); -int tipc_link_xmit(struct sk_buff_head *list, u32 dest, u32 selector); -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list); -u32 tipc_link_get_max_pkt(u32 dest, u32 selector); -void tipc_link_bundle_rcv(struct sk_buff *buf); +void tipc_link_reset_list(struct net *net, unsigned int bearer_id); +int tipc_link_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest, +		       u32 selector); +int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dest, +		   u32 selector); +int __tipc_link_xmit(struct net *net, struct tipc_link *link, +		     struct sk_buff_head *list);  void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,  			  u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);  void tipc_link_push_packets(struct tipc_link *l_ptr); @@ -233,6 +239,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info);  int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info);  int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info);  int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]); +void link_prepare_wakeup(struct tipc_link *l);  /*   * Link sequence number manipulation routines (uses modulo 2**16 arithmetic) @@ -267,6 +274,10 @@ static inline u32 lesser(u32 left, u32 right)  	return less_eq(left, right) ? left : right;  } +static inline u32 link_own_addr(struct tipc_link *l) +{ +	return msg_prevnode(l->pmsg); +}  /*   * Link status checking routines diff --git a/net/tipc/log.c b/net/tipc/log.c deleted file mode 100644 index abef644f27d8..000000000000 --- a/net/tipc/log.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * net/tipc/log.c: TIPC print buffer routines for debugging - * - * Copyright (c) 1996-2006, Ericsson AB - * Copyright (c) 2005-2007, 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 "config.h" - -/** - * tipc_snprintf - append formatted output to print buffer - * @buf: pointer to print buffer - * @len: buffer length - * @fmt: formatted info to be printed - */ -int tipc_snprintf(char *buf, int len, const char *fmt, ...) -{ -	int i; -	va_list args; - -	va_start(args, fmt); -	i = vscnprintf(buf, len, fmt, args); -	va_end(args); -	return i; -} diff --git a/net/tipc/msg.c b/net/tipc/msg.c index a687b30a699c..b6eb90cd3ef7 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -34,6 +34,7 @@   * POSSIBILITY OF SUCH DAMAGE.   */ +#include <net/sock.h>  #include "core.h"  #include "msg.h"  #include "addr.h" @@ -46,25 +47,48 @@ static unsigned int align(unsigned int i)  	return (i + 3) & ~3u;  } -void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, -		   u32 destnode) +/** + * tipc_buf_acquire - creates a TIPC message buffer + * @size: message size (including TIPC header) + * + * Returns a new buffer with data pointers set to the specified size. + * + * NOTE: Headroom is reserved to allow prepending of a data link header. + *       There may also be unrequested tailroom present at the buffer's end. + */ +struct sk_buff *tipc_buf_acquire(u32 size) +{ +	struct sk_buff *skb; +	unsigned int buf_size = (BUF_HEADROOM + size + 3) & ~3u; + +	skb = alloc_skb_fclone(buf_size, GFP_ATOMIC); +	if (skb) { +		skb_reserve(skb, BUF_HEADROOM); +		skb_put(skb, size); +		skb->next = NULL; +	} +	return skb; +} + +void tipc_msg_init(u32 own_node, struct tipc_msg *m, u32 user, u32 type, +		   u32 hsize, u32 dnode)  {  	memset(m, 0, hsize);  	msg_set_version(m);  	msg_set_user(m, user);  	msg_set_hdr_sz(m, hsize);  	msg_set_size(m, hsize); -	msg_set_prevnode(m, tipc_own_addr); +	msg_set_prevnode(m, own_node);  	msg_set_type(m, type);  	if (hsize > SHORT_H_SIZE) { -		msg_set_orignode(m, tipc_own_addr); -		msg_set_destnode(m, destnode); +		msg_set_orignode(m, own_node); +		msg_set_destnode(m, dnode);  	}  } -struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, -				uint data_sz, u32 dnode, u32 onode, -				u32 dport, u32 oport, int errcode) +struct sk_buff *tipc_msg_create(uint user, uint type, +				uint hdr_sz, uint data_sz, u32 dnode, +				u32 onode, u32 dport, u32 oport, int errcode)  {  	struct tipc_msg *msg;  	struct sk_buff *buf; @@ -74,9 +98,8 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz,  		return NULL;  	msg = buf_msg(buf); -	tipc_msg_init(msg, user, type, hdr_sz, dnode); +	tipc_msg_init(onode, msg, user, type, hdr_sz, dnode);  	msg_set_size(msg, hdr_sz + data_sz); -	msg_set_prevnode(msg, onode);  	msg_set_origport(msg, oport);  	msg_set_destport(msg, dport);  	msg_set_errcode(msg, errcode); @@ -163,15 +186,14 @@ err:   * tipc_msg_build - create buffer chain containing specified header and data   * @mhdr: Message header, to be prepended to data   * @m: User message - * @offset: Posision in iov to start copying from   * @dsz: Total length of user data   * @pktmax: Max packet size that can be used   * @list: Buffer or chain of buffers to be returned to caller   *   * Returns message data size or errno: -ENOMEM, -EFAULT   */ -int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, -		   int dsz, int pktmax, struct sk_buff_head *list) +int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, +		   int offset, int dsz, int pktmax, struct sk_buff_head *list)  {  	int mhsz = msg_hdr_sz(mhdr);  	int msz = mhsz + dsz; @@ -191,19 +213,19 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,  		skb = tipc_buf_acquire(msz);  		if (unlikely(!skb))  			return -ENOMEM; +		skb_orphan(skb);  		__skb_queue_tail(list, skb);  		skb_copy_to_linear_data(skb, mhdr, mhsz);  		pktpos = skb->data + mhsz; -		if (!dsz || !memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, -						 dsz)) +		if (copy_from_iter(pktpos, dsz, &m->msg_iter) == dsz)  			return dsz;  		rc = -EFAULT;  		goto error;  	}  	/* Prepare reusable fragment header */ -	tipc_msg_init(&pkthdr, MSG_FRAGMENTER, FIRST_FRAGMENT, -		      INT_H_SIZE, msg_destnode(mhdr)); +	tipc_msg_init(msg_prevnode(mhdr), &pkthdr, MSG_FRAGMENTER, +		      FIRST_FRAGMENT, INT_H_SIZE, msg_destnode(mhdr));  	msg_set_size(&pkthdr, pktmax);  	msg_set_fragm_no(&pkthdr, pktno); @@ -211,6 +233,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,  	skb = tipc_buf_acquire(pktmax);  	if (!skb)  		return -ENOMEM; +	skb_orphan(skb);  	__skb_queue_tail(list, skb);  	pktpos = skb->data;  	skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); @@ -224,12 +247,11 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,  		if (drem < pktrem)  			pktrem = drem; -		if (memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, pktrem)) { +		if (copy_from_iter(pktpos, pktrem, &m->msg_iter) != pktrem) {  			rc = -EFAULT;  			goto error;  		}  		drem -= pktrem; -		offset += pktrem;  		if (!drem)  			break; @@ -244,6 +266,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,  			rc = -ENOMEM;  			goto error;  		} +		skb_orphan(skb);  		__skb_queue_tail(list, skb);  		msg_set_type(&pkthdr, FRAGMENT);  		msg_set_size(&pkthdr, pktsz); @@ -304,6 +327,40 @@ bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu)  }  /** + *  tipc_msg_extract(): extract bundled inner packet from buffer + *  @skb: linear outer buffer, to be extracted from. + *  @iskb: extracted inner buffer, to be returned + *  @pos: position of msg to be extracted. Returns with pointer of next msg + *  Consumes outer buffer when last packet extracted + *  Returns true when when there is an extracted buffer, otherwise false + */ +bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos) +{ +	struct tipc_msg *msg = buf_msg(skb); +	int imsz; +	struct tipc_msg *imsg = (struct tipc_msg *)(msg_data(msg) + *pos); + +	/* Is there space left for shortest possible message? */ +	if (*pos > (msg_data_sz(msg) - SHORT_H_SIZE)) +		goto none; +	imsz = msg_size(imsg); + +	/* Is there space left for current message ? */ +	if ((*pos + imsz) > msg_data_sz(msg)) +		goto none; +	*iskb = tipc_buf_acquire(imsz); +	if (!*iskb) +		goto none; +	skb_copy_to_linear_data(*iskb, imsg, imsz); +	*pos += align(imsz); +	return true; +none: +	kfree_skb(skb); +	*iskb = NULL; +	return false; +} + +/**   * tipc_msg_make_bundle(): Create bundle buf and append message to its tail   * @list: the buffer chain   * @skb: buffer to be appended and replaced @@ -312,8 +369,8 @@ bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu)   * Replaces buffer if successful   * Returns true if success, otherwise false   */ -bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, -			  u32 mtu, u32 dnode) +bool tipc_msg_make_bundle(struct sk_buff_head *list, +			  struct sk_buff *skb, u32 mtu, u32 dnode)  {  	struct sk_buff *bskb;  	struct tipc_msg *bmsg; @@ -336,7 +393,8 @@ bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb,  	skb_trim(bskb, INT_H_SIZE);  	bmsg = buf_msg(bskb); -	tipc_msg_init(bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode); +	tipc_msg_init(msg_prevnode(msg), bmsg, MSG_BUNDLER, 0, +		      INT_H_SIZE, dnode);  	msg_set_seqno(bmsg, msg_seqno(msg));  	msg_set_ack(bmsg, msg_ack(msg));  	msg_set_bcast_ack(bmsg, msg_bcast_ack(msg)); @@ -353,7 +411,8 @@ bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb,   * Consumes buffer if failure   * Returns true if success, otherwise false   */ -bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err) +bool tipc_msg_reverse(u32 own_addr,  struct sk_buff *buf, u32 *dnode, +		      int err)  {  	struct tipc_msg *msg = buf_msg(buf);  	uint imp = msg_importance(msg); @@ -374,7 +433,7 @@ bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err)  	msg_set_errcode(msg, err);  	msg_set_origport(msg, msg_destport(&ohdr));  	msg_set_destport(msg, msg_origport(&ohdr)); -	msg_set_prevnode(msg, tipc_own_addr); +	msg_set_prevnode(msg, own_addr);  	if (!msg_short(msg)) {  		msg_set_orignode(msg, msg_destnode(&ohdr));  		msg_set_destnode(msg, msg_orignode(&ohdr)); @@ -386,43 +445,43 @@ bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err)  	return true;  exit:  	kfree_skb(buf); +	*dnode = 0;  	return false;  }  /** - * tipc_msg_eval: determine fate of message that found no destination - * @buf: the buffer containing the message. - * @dnode: return value: next-hop node, if message to be forwarded - * @err: error code to use, if message to be rejected - * + * tipc_msg_lookup_dest(): try to find new destination for named message + * @skb: the buffer containing the message. + * @dnode: return value: next-hop node, if destination found + * @err: return value: error code to use, if message to be rejected   * Does not consume buffer - * Returns 0 (TIPC_OK) if message ok and we can try again, -TIPC error - * code if message to be rejected + * Returns true if a destination is found, false otherwise   */ -int tipc_msg_eval(struct sk_buff *buf, u32 *dnode) +bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, +			  u32 *dnode, int *err)  { -	struct tipc_msg *msg = buf_msg(buf); +	struct tipc_msg *msg = buf_msg(skb);  	u32 dport; -	if (msg_type(msg) != TIPC_NAMED_MSG) -		return -TIPC_ERR_NO_PORT; -	if (skb_linearize(buf)) -		return -TIPC_ERR_NO_NAME; -	if (msg_data_sz(msg) > MAX_FORWARD_SIZE) -		return -TIPC_ERR_NO_NAME; +	if (!msg_isdata(msg)) +		return false; +	if (!msg_named(msg)) +		return false; +	*err = -TIPC_ERR_NO_NAME; +	if (skb_linearize(skb)) +		return false;  	if (msg_reroute_cnt(msg) > 0) -		return -TIPC_ERR_NO_NAME; - -	*dnode = addr_domain(msg_lookup_scope(msg)); -	dport = tipc_nametbl_translate(msg_nametype(msg), -				       msg_nameinst(msg), -				       dnode); +		return false; +	*dnode = addr_domain(net, msg_lookup_scope(msg)); +	dport = tipc_nametbl_translate(net, msg_nametype(msg), +				       msg_nameinst(msg), dnode);  	if (!dport) -		return -TIPC_ERR_NO_NAME; +		return false;  	msg_incr_reroute_cnt(msg);  	msg_set_destnode(msg, *dnode);  	msg_set_destport(msg, dport); -	return TIPC_OK; +	*err = TIPC_OK; +	return true;  }  /* tipc_msg_reassemble() - clone a buffer chain of fragments and diff --git a/net/tipc/msg.h b/net/tipc/msg.h index d5c83d7ecb47..9ace47f44a69 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -37,7 +37,7 @@  #ifndef _TIPC_MSG_H  #define _TIPC_MSG_H -#include "bearer.h" +#include <linux/tipc.h>  /*   * Constants and routines used to read and write TIPC payload message headers @@ -45,6 +45,7 @@   * Note: Some items are also used with TIPC internal message headers   */  #define TIPC_VERSION              2 +struct plist;  /*   * Payload message users are defined in TIPC's public API: @@ -77,11 +78,37 @@  #define TIPC_MEDIA_ADDR_OFFSET	5 +/** + * TIPC message buffer code + * + * TIPC message buffer headroom reserves space for the worst-case + * link-level device header (in case the message is sent off-node). + * + * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields + *       are word aligned for quicker access + */ +#define BUF_HEADROOM LL_MAX_HEADER + +struct tipc_skb_cb { +	void *handle; +	struct sk_buff *tail; +	bool deferred; +	bool wakeup_pending; +	bool bundling; +	u16 chain_sz; +	u16 chain_imp; +}; + +#define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0]))  struct tipc_msg {  	__be32 hdr[15];  }; +static inline struct tipc_msg *buf_msg(struct sk_buff *skb) +{ +	return (struct tipc_msg *)skb->data; +}  static inline u32 msg_word(struct tipc_msg *m, u32 pos)  { @@ -721,27 +748,111 @@ static inline u32 msg_tot_origport(struct tipc_msg *m)  	return msg_origport(m);  } -bool tipc_msg_reverse(struct sk_buff *buf, u32 *dnode, int err); - -int tipc_msg_eval(struct sk_buff *buf, u32 *dnode); - -void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, -		   u32 destnode); - +struct sk_buff *tipc_buf_acquire(u32 size); +bool tipc_msg_reverse(u32 own_addr, struct sk_buff *buf, u32 *dnode, +		      int err); +void tipc_msg_init(u32 own_addr, struct tipc_msg *m, u32 user, u32 type, +		   u32 hsize, u32 destnode);  struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz,  				uint data_sz, u32 dnode, u32 onode,  				u32 dport, u32 oport, int errcode); -  int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf); -  bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu); +bool tipc_msg_make_bundle(struct sk_buff_head *list, +			  struct sk_buff *skb, u32 mtu, u32 dnode); +bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos); +int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, +		   int offset, int dsz, int mtu, struct sk_buff_head *list); +bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, u32 *dnode, +			  int *err); +struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); -bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, -			  u32 mtu, u32 dnode); - -int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, -		   int dsz, int mtu, struct sk_buff_head *list); +/* tipc_skb_peek(): peek and reserve first buffer in list + * @list: list to be peeked in + * Returns pointer to first buffer in list, if any + */ +static inline struct sk_buff *tipc_skb_peek(struct sk_buff_head *list, +					    spinlock_t *lock) +{ +	struct sk_buff *skb; + +	spin_lock_bh(lock); +	skb = skb_peek(list); +	if (skb) +		skb_get(skb); +	spin_unlock_bh(lock); +	return skb; +} + +/* tipc_skb_peek_port(): find a destination port, ignoring all destinations + *                       up to and including 'filter'. + * Note: ignoring previously tried destinations minimizes the risk of + *       contention on the socket lock + * @list: list to be peeked in + * @filter: last destination to be ignored from search + * Returns a destination port number, of applicable. + */ +static inline u32 tipc_skb_peek_port(struct sk_buff_head *list, u32 filter) +{ +	struct sk_buff *skb; +	u32 dport = 0; +	bool ignore = true; + +	spin_lock_bh(&list->lock); +	skb_queue_walk(list, skb) { +		dport = msg_destport(buf_msg(skb)); +		if (!filter || skb_queue_is_last(list, skb)) +			break; +		if (dport == filter) +			ignore = false; +		else if (!ignore) +			break; +	} +	spin_unlock_bh(&list->lock); +	return dport; +} + +/* tipc_skb_dequeue(): unlink first buffer with dest 'dport' from list + * @list: list to be unlinked from + * @dport: selection criteria for buffer to unlink + */ +static inline struct sk_buff *tipc_skb_dequeue(struct sk_buff_head *list, +					       u32 dport) +{ +	struct sk_buff *_skb, *tmp, *skb = NULL; + +	spin_lock_bh(&list->lock); +	skb_queue_walk_safe(list, _skb, tmp) { +		if (msg_destport(buf_msg(_skb)) == dport) { +			__skb_unlink(_skb, list); +			skb = _skb; +			break; +		} +	} +	spin_unlock_bh(&list->lock); +	return skb; +} + +/* tipc_skb_queue_tail(): add buffer to tail of list; + * @list: list to be appended to + * @skb: buffer to append. Always appended + * @dport: the destination port of the buffer + * returns true if dport differs from previous destination + */ +static inline bool tipc_skb_queue_tail(struct sk_buff_head *list, +				       struct sk_buff *skb, u32 dport) +{ +	struct sk_buff *_skb = NULL; +	bool rv = false; -struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); +	spin_lock_bh(&list->lock); +	_skb = skb_peek_tail(list); +	if (!_skb || (msg_destport(buf_msg(_skb)) != dport) || +	    (skb_queue_len(list) > 32)) +		rv = true; +	__skb_queue_tail(list, skb); +	spin_unlock_bh(&list->lock); +	return rv; +}  #endif diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index ba6083dca95b..fcb07915aaac 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -68,29 +68,33 @@ static void publ_to_item(struct distr_item *i, struct publication *p)  /**   * named_prepare_buf - allocate & initialize a publication message   */ -static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) +static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size, +					 u32 dest)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE + size);  	struct tipc_msg *msg;  	if (buf != NULL) {  		msg = buf_msg(buf); -		tipc_msg_init(msg, NAME_DISTRIBUTOR, type, INT_H_SIZE, dest); +		tipc_msg_init(tn->own_addr, msg, NAME_DISTRIBUTOR, type, +			      INT_H_SIZE, dest);  		msg_set_size(msg, INT_H_SIZE + size);  	}  	return buf;  } -void named_cluster_distribute(struct sk_buff *skb) +void named_cluster_distribute(struct net *net, struct sk_buff *skb)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sk_buff *oskb;  	struct tipc_node *node;  	u32 dnode;  	rcu_read_lock(); -	list_for_each_entry_rcu(node, &tipc_node_list, list) { +	list_for_each_entry_rcu(node, &tn->node_list, list) {  		dnode = node->addr; -		if (in_own_node(dnode)) +		if (in_own_node(net, dnode))  			continue;  		if (!tipc_node_active_links(node))  			continue; @@ -98,7 +102,7 @@ void named_cluster_distribute(struct sk_buff *skb)  		if (!oskb)  			break;  		msg_set_destnode(buf_msg(oskb), dnode); -		tipc_link_xmit_skb(oskb, dnode, dnode); +		tipc_link_xmit_skb(net, oskb, dnode, dnode);  	}  	rcu_read_unlock(); @@ -108,18 +112,19 @@ void named_cluster_distribute(struct sk_buff *skb)  /**   * tipc_named_publish - tell other nodes about a new publication by this node   */ -struct sk_buff *tipc_named_publish(struct publication *publ) +struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sk_buff *buf;  	struct distr_item *item;  	list_add_tail_rcu(&publ->local_list, -			  &tipc_nametbl->publ_list[publ->scope]); +			  &tn->nametbl->publ_list[publ->scope]);  	if (publ->scope == TIPC_NODE_SCOPE)  		return NULL; -	buf = named_prepare_buf(PUBLICATION, ITEM_SIZE, 0); +	buf = named_prepare_buf(net, PUBLICATION, ITEM_SIZE, 0);  	if (!buf) {  		pr_warn("Publication distribution failure\n");  		return NULL; @@ -133,7 +138,7 @@ struct sk_buff *tipc_named_publish(struct publication *publ)  /**   * tipc_named_withdraw - tell other nodes about a withdrawn publication by this node   */ -struct sk_buff *tipc_named_withdraw(struct publication *publ) +struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ)  {  	struct sk_buff *buf;  	struct distr_item *item; @@ -143,7 +148,7 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ)  	if (publ->scope == TIPC_NODE_SCOPE)  		return NULL; -	buf = named_prepare_buf(WITHDRAWAL, ITEM_SIZE, 0); +	buf = named_prepare_buf(net, WITHDRAWAL, ITEM_SIZE, 0);  	if (!buf) {  		pr_warn("Withdrawal distribution failure\n");  		return NULL; @@ -160,19 +165,21 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ)   * @dnode: node to be updated   * @pls: linked list of publication items to be packed into buffer chain   */ -static void named_distribute(struct sk_buff_head *list, u32 dnode, -			     struct list_head *pls) +static void named_distribute(struct net *net, struct sk_buff_head *list, +			     u32 dnode, struct list_head *pls)  {  	struct publication *publ;  	struct sk_buff *skb = NULL;  	struct distr_item *item = NULL; -	uint msg_dsz = (tipc_node_get_mtu(dnode, 0) / ITEM_SIZE) * ITEM_SIZE; +	uint msg_dsz = (tipc_node_get_mtu(net, dnode, 0) / ITEM_SIZE) * +			ITEM_SIZE;  	uint msg_rem = msg_dsz;  	list_for_each_entry(publ, pls, local_list) {  		/* Prepare next buffer: */  		if (!skb) { -			skb = named_prepare_buf(PUBLICATION, msg_rem, dnode); +			skb = named_prepare_buf(net, PUBLICATION, msg_rem, +						dnode);  			if (!skb) {  				pr_warn("Bulk publication failure\n");  				return; @@ -202,30 +209,32 @@ static void named_distribute(struct sk_buff_head *list, u32 dnode,  /**   * tipc_named_node_up - tell specified node about all publications by this node   */ -void tipc_named_node_up(u32 dnode) +void tipc_named_node_up(struct net *net, u32 dnode)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sk_buff_head head;  	__skb_queue_head_init(&head);  	rcu_read_lock(); -	named_distribute(&head, dnode, -			 &tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); -	named_distribute(&head, dnode, -			 &tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); +	named_distribute(net, &head, dnode, +			 &tn->nametbl->publ_list[TIPC_CLUSTER_SCOPE]); +	named_distribute(net, &head, dnode, +			 &tn->nametbl->publ_list[TIPC_ZONE_SCOPE]);  	rcu_read_unlock(); -	tipc_link_xmit(&head, dnode, dnode); +	tipc_link_xmit(net, &head, dnode, dnode);  } -static void tipc_publ_subscribe(struct publication *publ, u32 addr) +static void tipc_publ_subscribe(struct net *net, struct publication *publ, +				u32 addr)  {  	struct tipc_node *node; -	if (in_own_node(addr)) +	if (in_own_node(net, addr))  		return; -	node = tipc_node_find(addr); +	node = tipc_node_find(net, addr);  	if (!node) {  		pr_warn("Node subscription rejected, unknown node 0x%x\n",  			addr); @@ -237,11 +246,12 @@ static void tipc_publ_subscribe(struct publication *publ, u32 addr)  	tipc_node_unlock(node);  } -static void tipc_publ_unsubscribe(struct publication *publ, u32 addr) +static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, +				  u32 addr)  {  	struct tipc_node *node; -	node = tipc_node_find(addr); +	node = tipc_node_find(net, addr);  	if (!node)  		return; @@ -256,16 +266,17 @@ static void tipc_publ_unsubscribe(struct publication *publ, u32 addr)   * Invoked for each publication issued by a newly failed node.   * Removes publication structure from name table & deletes it.   */ -static void tipc_publ_purge(struct publication *publ, u32 addr) +static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct publication *p; -	spin_lock_bh(&tipc_nametbl_lock); -	p = tipc_nametbl_remove_publ(publ->type, publ->lower, +	spin_lock_bh(&tn->nametbl_lock); +	p = tipc_nametbl_remove_publ(net, publ->type, publ->lower,  				     publ->node, publ->ref, publ->key);  	if (p) -		tipc_publ_unsubscribe(p, addr); -	spin_unlock_bh(&tipc_nametbl_lock); +		tipc_publ_unsubscribe(net, p, addr); +	spin_unlock_bh(&tn->nametbl_lock);  	if (p != publ) {  		pr_err("Unable to remove publication from failed node\n" @@ -277,12 +288,12 @@ static void tipc_publ_purge(struct publication *publ, u32 addr)  	kfree_rcu(p, rcu);  } -void tipc_publ_notify(struct list_head *nsub_list, u32 addr) +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr)  {  	struct publication *publ, *tmp;  	list_for_each_entry_safe(publ, tmp, nsub_list, nodesub_list) -		tipc_publ_purge(publ, addr); +		tipc_publ_purge(net, publ, addr);  }  /** @@ -292,25 +303,28 @@ void tipc_publ_notify(struct list_head *nsub_list, u32 addr)   * tipc_nametbl_lock must be held.   * Returns the publication item if successful, otherwise NULL.   */ -static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) +static bool tipc_update_nametbl(struct net *net, struct distr_item *i, +				u32 node, u32 dtype)  {  	struct publication *publ = NULL;  	if (dtype == PUBLICATION) { -		publ = tipc_nametbl_insert_publ(ntohl(i->type), ntohl(i->lower), +		publ = tipc_nametbl_insert_publ(net, ntohl(i->type), +						ntohl(i->lower),  						ntohl(i->upper),  						TIPC_CLUSTER_SCOPE, node,  						ntohl(i->ref), ntohl(i->key));  		if (publ) { -			tipc_publ_subscribe(publ, node); +			tipc_publ_subscribe(net, publ, node);  			return true;  		}  	} else if (dtype == WITHDRAWAL) { -		publ = tipc_nametbl_remove_publ(ntohl(i->type), ntohl(i->lower), +		publ = tipc_nametbl_remove_publ(net, ntohl(i->type), +						ntohl(i->lower),  						node, ntohl(i->ref),  						ntohl(i->key));  		if (publ) { -			tipc_publ_unsubscribe(publ, node); +			tipc_publ_unsubscribe(net, publ, node);  			kfree_rcu(publ, rcu);  			return true;  		} @@ -343,7 +357,7 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node)   * tipc_named_process_backlog - try to process any pending name table updates   * from the network.   */ -void tipc_named_process_backlog(void) +void tipc_named_process_backlog(struct net *net)  {  	struct distr_queue_item *e, *tmp;  	char addr[16]; @@ -351,7 +365,7 @@ void tipc_named_process_backlog(void)  	list_for_each_entry_safe(e, tmp, &tipc_dist_queue, next) {  		if (time_after(e->expires, now)) { -			if (!tipc_update_nametbl(&e->i, e->node, e->dtype)) +			if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype))  				continue;  		} else {  			tipc_addr_string_fill(addr, e->node); @@ -367,24 +381,34 @@ void tipc_named_process_backlog(void)  }  /** - * tipc_named_rcv - process name table update message sent by another node + * tipc_named_rcv - process name table update messages sent by another node   */ -void tipc_named_rcv(struct sk_buff *buf) +void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq)  { -	struct tipc_msg *msg = buf_msg(buf); -	struct distr_item *item = (struct distr_item *)msg_data(msg); -	u32 count = msg_data_sz(msg) / ITEM_SIZE; -	u32 node = msg_orignode(msg); - -	spin_lock_bh(&tipc_nametbl_lock); -	while (count--) { -		if (!tipc_update_nametbl(item, node, msg_type(msg))) -			tipc_named_add_backlog(item, msg_type(msg), node); -		item++; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_msg *msg; +	struct distr_item *item; +	uint count; +	u32 node; +	struct sk_buff *skb; +	int mtype; + +	spin_lock_bh(&tn->nametbl_lock); +	for (skb = skb_dequeue(inputq); skb; skb = skb_dequeue(inputq)) { +		msg = buf_msg(skb); +		mtype = msg_type(msg); +		item = (struct distr_item *)msg_data(msg); +		count = msg_data_sz(msg) / ITEM_SIZE; +		node = msg_orignode(msg); +		while (count--) { +			if (!tipc_update_nametbl(net, item, node, mtype)) +				tipc_named_add_backlog(item, mtype, node); +			item++; +		} +		kfree_skb(skb); +		tipc_named_process_backlog(net);  	} -	tipc_named_process_backlog(); -	spin_unlock_bh(&tipc_nametbl_lock); -	kfree_skb(buf); +	spin_unlock_bh(&tn->nametbl_lock);  }  /** @@ -394,17 +418,18 @@ void tipc_named_rcv(struct sk_buff *buf)   * All name table entries published by this node are updated to reflect   * the node's new network address.   */ -void tipc_named_reinit(void) +void tipc_named_reinit(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct publication *publ;  	int scope; -	spin_lock_bh(&tipc_nametbl_lock); +	spin_lock_bh(&tn->nametbl_lock);  	for (scope = TIPC_ZONE_SCOPE; scope <= TIPC_NODE_SCOPE; scope++) -		list_for_each_entry_rcu(publ, &tipc_nametbl->publ_list[scope], +		list_for_each_entry_rcu(publ, &tn->nametbl->publ_list[scope],  					local_list) -			publ->node = tipc_own_addr; +			publ->node = tn->own_addr; -	spin_unlock_bh(&tipc_nametbl_lock); +	spin_unlock_bh(&tn->nametbl_lock);  } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index cef55cedcfb2..dd2d9fd80da2 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -67,13 +67,13 @@ struct distr_item {  	__be32 key;  }; -struct sk_buff *tipc_named_publish(struct publication *publ); -struct sk_buff *tipc_named_withdraw(struct publication *publ); -void named_cluster_distribute(struct sk_buff *buf); -void tipc_named_node_up(u32 dnode); -void tipc_named_rcv(struct sk_buff *buf); -void tipc_named_reinit(void); -void tipc_named_process_backlog(void); -void tipc_publ_notify(struct list_head *nsub_list, u32 addr); +struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ); +struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ); +void named_cluster_distribute(struct net *net, struct sk_buff *buf); +void tipc_named_node_up(struct net *net, u32 dnode); +void tipc_named_rcv(struct net *net, struct sk_buff_head *msg_queue); +void tipc_named_reinit(struct net *net); +void tipc_named_process_backlog(struct net *net); +void tipc_publ_notify(struct net *net, struct list_head *nsub_list, u32 addr);  #endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index c8df0223371a..105ba7adf06f 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -1,7 +1,7 @@  /*   * net/tipc/name_table.c: TIPC name table code   * - * Copyright (c) 2000-2006, 2014, Ericsson AB + * Copyright (c) 2000-2006, 2014-2015, Ericsson AB   * Copyright (c) 2004-2008, 2010-2014, Wind River Systems   * All rights reserved.   * @@ -34,11 +34,15 @@   * POSSIBILITY OF SUCH DAMAGE.   */ +#include <net/sock.h>  #include "core.h" -#include "config.h" +#include "netlink.h"  #include "name_table.h"  #include "name_distr.h"  #include "subscr.h" +#include "bcast.h" +#include "addr.h" +#include <net/genetlink.h>  #define TIPC_NAMETBL_SIZE 1024		/* must be a power of 2 */ @@ -105,9 +109,6 @@ struct name_seq {  	struct rcu_head rcu;  }; -struct name_table *tipc_nametbl; -DEFINE_SPINLOCK(tipc_nametbl_lock); -  static int hash(int x)  {  	return x & (TIPC_NAMETBL_SIZE - 1); @@ -228,9 +229,11 @@ static u32 nameseq_locate_subseq(struct name_seq *nseq, u32 instance)  /**   * tipc_nameseq_insert_publ   */ -static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, -						    u32 type, u32 lower, u32 upper, -						    u32 scope, u32 node, u32 port, u32 key) +static struct publication *tipc_nameseq_insert_publ(struct net *net, +						    struct name_seq *nseq, +						    u32 type, u32 lower, +						    u32 upper, u32 scope, +						    u32 node, u32 port, u32 key)  {  	struct tipc_subscription *s;  	struct tipc_subscription *st; @@ -315,12 +318,12 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq,  	list_add(&publ->zone_list, &info->zone_list);  	info->zone_list_size++; -	if (in_own_cluster(node)) { +	if (in_own_cluster(net, node)) {  		list_add(&publ->cluster_list, &info->cluster_list);  		info->cluster_list_size++;  	} -	if (in_own_node(node)) { +	if (in_own_node(net, node)) {  		list_add(&publ->node_list, &info->node_list);  		info->node_list_size++;  	} @@ -349,8 +352,10 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq,   * A failed withdraw request simply returns a failure indication and lets the   * caller issue any error or warning messages associated with such a problem.   */ -static struct publication *tipc_nameseq_remove_publ(struct name_seq *nseq, u32 inst, -						    u32 node, u32 ref, u32 key) +static struct publication *tipc_nameseq_remove_publ(struct net *net, +						    struct name_seq *nseq, +						    u32 inst, u32 node, +						    u32 ref, u32 key)  {  	struct publication *publ;  	struct sub_seq *sseq = nameseq_find_subseq(nseq, inst); @@ -378,13 +383,13 @@ found:  	info->zone_list_size--;  	/* Remove publication from cluster scope list, if present */ -	if (in_own_cluster(node)) { +	if (in_own_cluster(net, node)) {  		list_del(&publ->cluster_list);  		info->cluster_list_size--;  	}  	/* Remove publication from node scope list, if present */ -	if (in_own_node(node)) { +	if (in_own_node(net, node)) {  		list_del(&publ->node_list);  		info->node_list_size--;  	} @@ -447,12 +452,13 @@ static void tipc_nameseq_subscribe(struct name_seq *nseq,  	}  } -static struct name_seq *nametbl_find_seq(u32 type) +static struct name_seq *nametbl_find_seq(struct net *net, u32 type)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct hlist_head *seq_head;  	struct name_seq *ns; -	seq_head = &tipc_nametbl->seq_hlist[hash(type)]; +	seq_head = &tn->nametbl->seq_hlist[hash(type)];  	hlist_for_each_entry_rcu(ns, seq_head, ns_list) {  		if (ns->type == type)  			return ns; @@ -461,11 +467,13 @@ static struct name_seq *nametbl_find_seq(u32 type)  	return NULL;  }; -struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, -					     u32 scope, u32 node, u32 port, u32 key) +struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, +					     u32 lower, u32 upper, u32 scope, +					     u32 node, u32 port, u32 key)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct publication *publ; -	struct name_seq *seq = nametbl_find_seq(type); +	struct name_seq *seq = nametbl_find_seq(net, type);  	int index = hash(type);  	if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE) || @@ -476,29 +484,29 @@ struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper,  	}  	if (!seq) -		seq = tipc_nameseq_create(type, -					  &tipc_nametbl->seq_hlist[index]); +		seq = tipc_nameseq_create(type, &tn->nametbl->seq_hlist[index]);  	if (!seq)  		return NULL;  	spin_lock_bh(&seq->lock); -	publ = tipc_nameseq_insert_publ(seq, type, lower, upper, +	publ = tipc_nameseq_insert_publ(net, seq, type, lower, upper,  					scope, node, port, key);  	spin_unlock_bh(&seq->lock);  	return publ;  } -struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, -					     u32 node, u32 ref, u32 key) +struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, +					     u32 lower, u32 node, u32 ref, +					     u32 key)  {  	struct publication *publ; -	struct name_seq *seq = nametbl_find_seq(type); +	struct name_seq *seq = nametbl_find_seq(net, type);  	if (!seq)  		return NULL;  	spin_lock_bh(&seq->lock); -	publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key); +	publ = tipc_nameseq_remove_publ(net, seq, lower, node, ref, key);  	if (!seq->first_free && list_empty(&seq->subscriptions)) {  		hlist_del_init_rcu(&seq->ns_list);  		kfree(seq->sseqs); @@ -523,8 +531,10 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower,   * - if name translation is attempted and fails, sets 'destnode' to 0   *   and returns 0   */ -u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) +u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, +			   u32 *destnode)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct sub_seq *sseq;  	struct name_info *info;  	struct publication *publ; @@ -532,11 +542,11 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)  	u32 ref = 0;  	u32 node = 0; -	if (!tipc_in_scope(*destnode, tipc_own_addr)) +	if (!tipc_in_scope(*destnode, tn->own_addr))  		return 0;  	rcu_read_lock(); -	seq = nametbl_find_seq(type); +	seq = nametbl_find_seq(net, type);  	if (unlikely(!seq))  		goto not_found;  	spin_lock_bh(&seq->lock); @@ -569,13 +579,13 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)  	}  	/* Round-Robin Algorithm */ -	else if (*destnode == tipc_own_addr) { +	else if (*destnode == tn->own_addr) {  		if (list_empty(&info->node_list))  			goto no_match;  		publ = list_first_entry(&info->node_list, struct publication,  					node_list);  		list_move_tail(&publ->node_list, &info->node_list); -	} else if (in_own_cluster_exact(*destnode)) { +	} else if (in_own_cluster_exact(net, *destnode)) {  		if (list_empty(&info->cluster_list))  			goto no_match;  		publ = list_first_entry(&info->cluster_list, struct publication, @@ -609,8 +619,8 @@ not_found:   *   * Returns non-zero if any off-node ports overlap   */ -int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, -			      struct tipc_port_list *dports) +int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, +			      u32 limit, struct tipc_plist *dports)  {  	struct name_seq *seq;  	struct sub_seq *sseq; @@ -619,7 +629,7 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,  	int res = 0;  	rcu_read_lock(); -	seq = nametbl_find_seq(type); +	seq = nametbl_find_seq(net, type);  	if (!seq)  		goto exit; @@ -635,7 +645,7 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,  		info = sseq->info;  		list_for_each_entry(publ, &info->node_list, node_list) {  			if (publ->scope <= limit) -				tipc_port_list_add(dports, publ->ref); +				tipc_plist_push(dports, publ->ref);  		}  		if (info->cluster_list_size != info->node_list_size) @@ -650,50 +660,55 @@ exit:  /*   * tipc_nametbl_publish - add name publication to network name tables   */ -struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, -					 u32 scope, u32 port_ref, u32 key) +struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, +					 u32 upper, u32 scope, u32 port_ref, +					 u32 key)  {  	struct publication *publ;  	struct sk_buff *buf = NULL; +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	spin_lock_bh(&tipc_nametbl_lock); -	if (tipc_nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) { +	spin_lock_bh(&tn->nametbl_lock); +	if (tn->nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) {  		pr_warn("Publication failed, local publication limit reached (%u)\n",  			TIPC_MAX_PUBLICATIONS); -		spin_unlock_bh(&tipc_nametbl_lock); +		spin_unlock_bh(&tn->nametbl_lock);  		return NULL;  	} -	publ = tipc_nametbl_insert_publ(type, lower, upper, scope, -				   tipc_own_addr, port_ref, key); +	publ = tipc_nametbl_insert_publ(net, type, lower, upper, scope, +					tn->own_addr, port_ref, key);  	if (likely(publ)) { -		tipc_nametbl->local_publ_count++; -		buf = tipc_named_publish(publ); +		tn->nametbl->local_publ_count++; +		buf = tipc_named_publish(net, publ);  		/* Any pending external events? */ -		tipc_named_process_backlog(); +		tipc_named_process_backlog(net);  	} -	spin_unlock_bh(&tipc_nametbl_lock); +	spin_unlock_bh(&tn->nametbl_lock);  	if (buf) -		named_cluster_distribute(buf); +		named_cluster_distribute(net, buf);  	return publ;  }  /**   * tipc_nametbl_withdraw - withdraw name publication from network name tables   */ -int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) +int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, +			  u32 key)  {  	struct publication *publ;  	struct sk_buff *skb = NULL; +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	spin_lock_bh(&tipc_nametbl_lock); -	publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key); +	spin_lock_bh(&tn->nametbl_lock); +	publ = tipc_nametbl_remove_publ(net, type, lower, tn->own_addr, +					ref, key);  	if (likely(publ)) { -		tipc_nametbl->local_publ_count--; -		skb = tipc_named_withdraw(publ); +		tn->nametbl->local_publ_count--; +		skb = tipc_named_withdraw(net, publ);  		/* Any pending external events? */ -		tipc_named_process_backlog(); +		tipc_named_process_backlog(net);  		list_del_init(&publ->pport_list);  		kfree_rcu(publ, rcu);  	} else { @@ -701,10 +716,10 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)  		       "(type=%u, lower=%u, ref=%u, key=%u)\n",  		       type, lower, ref, key);  	} -	spin_unlock_bh(&tipc_nametbl_lock); +	spin_unlock_bh(&tn->nametbl_lock);  	if (skb) { -		named_cluster_distribute(skb); +		named_cluster_distribute(net, skb);  		return 1;  	}  	return 0; @@ -715,15 +730,15 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)   */  void tipc_nametbl_subscribe(struct tipc_subscription *s)  { +	struct tipc_net *tn = net_generic(s->net, tipc_net_id);  	u32 type = s->seq.type;  	int index = hash(type);  	struct name_seq *seq; -	spin_lock_bh(&tipc_nametbl_lock); -	seq = nametbl_find_seq(type); +	spin_lock_bh(&tn->nametbl_lock); +	seq = nametbl_find_seq(s->net, type);  	if (!seq) -		seq = tipc_nameseq_create(type, -					  &tipc_nametbl->seq_hlist[index]); +		seq = tipc_nameseq_create(type, &tn->nametbl->seq_hlist[index]);  	if (seq) {  		spin_lock_bh(&seq->lock);  		tipc_nameseq_subscribe(seq, s); @@ -732,7 +747,7 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s)  		pr_warn("Failed to create subscription for {%u,%u,%u}\n",  			s->seq.type, s->seq.lower, s->seq.upper);  	} -	spin_unlock_bh(&tipc_nametbl_lock); +	spin_unlock_bh(&tn->nametbl_lock);  }  /** @@ -740,10 +755,11 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s)   */  void tipc_nametbl_unsubscribe(struct tipc_subscription *s)  { +	struct tipc_net *tn = net_generic(s->net, tipc_net_id);  	struct name_seq *seq; -	spin_lock_bh(&tipc_nametbl_lock); -	seq = nametbl_find_seq(s->seq.type); +	spin_lock_bh(&tn->nametbl_lock); +	seq = nametbl_find_seq(s->net, s->seq.type);  	if (seq != NULL) {  		spin_lock_bh(&seq->lock);  		list_del_init(&s->nameseq_list); @@ -756,193 +772,13 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s)  			spin_unlock_bh(&seq->lock);  		}  	} -	spin_unlock_bh(&tipc_nametbl_lock); -} - -/** - * subseq_list - print specified sub-sequence contents into the given buffer - */ -static int subseq_list(struct sub_seq *sseq, char *buf, int len, u32 depth, -		       u32 index) -{ -	char portIdStr[27]; -	const char *scope_str[] = {"", " zone", " cluster", " node"}; -	struct publication *publ; -	struct name_info *info; -	int ret; - -	ret = tipc_snprintf(buf, len, "%-10u %-10u ", sseq->lower, sseq->upper); - -	if (depth == 2) { -		ret += tipc_snprintf(buf - ret, len + ret, "\n"); -		return ret; -	} - -	info = sseq->info; - -	list_for_each_entry(publ, &info->zone_list, zone_list) { -		sprintf(portIdStr, "<%u.%u.%u:%u>", -			 tipc_zone(publ->node), tipc_cluster(publ->node), -			 tipc_node(publ->node), publ->ref); -		ret += tipc_snprintf(buf + ret, len - ret, "%-26s ", portIdStr); -		if (depth > 3) { -			ret += tipc_snprintf(buf + ret, len - ret, "%-10u %s", -					     publ->key, scope_str[publ->scope]); -		} -		if (!list_is_last(&publ->zone_list, &info->zone_list)) -			ret += tipc_snprintf(buf + ret, len - ret, -					     "\n%33s", " "); -	} - -	ret += tipc_snprintf(buf + ret, len - ret, "\n"); -	return ret; -} - -/** - * nameseq_list - print specified name sequence contents into the given buffer - */ -static int nameseq_list(struct name_seq *seq, char *buf, int len, u32 depth, -			u32 type, u32 lowbound, u32 upbound, u32 index) -{ -	struct sub_seq *sseq; -	char typearea[11]; -	int ret = 0; - -	if (seq->first_free == 0) -		return 0; - -	sprintf(typearea, "%-10u", seq->type); - -	if (depth == 1) { -		ret += tipc_snprintf(buf, len, "%s\n", typearea); -		return ret; -	} - -	for (sseq = seq->sseqs; sseq != &seq->sseqs[seq->first_free]; sseq++) { -		if ((lowbound <= sseq->upper) && (upbound >= sseq->lower)) { -			ret += tipc_snprintf(buf + ret, len - ret, "%s ", -					    typearea); -			spin_lock_bh(&seq->lock); -			ret += subseq_list(sseq, buf + ret, len - ret, -					  depth, index); -			spin_unlock_bh(&seq->lock); -			sprintf(typearea, "%10s", " "); -		} -	} -	return ret; -} - -/** - * nametbl_header - print name table header into the given buffer - */ -static int nametbl_header(char *buf, int len, u32 depth) -{ -	const char *header[] = { -		"Type       ", -		"Lower      Upper      ", -		"Port Identity              ", -		"Publication Scope" -	}; - -	int i; -	int ret = 0; - -	if (depth > 4) -		depth = 4; -	for (i = 0; i < depth; i++) -		ret += tipc_snprintf(buf + ret, len - ret, header[i]); -	ret += tipc_snprintf(buf + ret, len - ret, "\n"); -	return ret; -} - -/** - * nametbl_list - print specified name table contents into the given buffer - */ -static int nametbl_list(char *buf, int len, u32 depth_info, -			u32 type, u32 lowbound, u32 upbound) -{ -	struct hlist_head *seq_head; -	struct name_seq *seq; -	int all_types; -	int ret = 0; -	u32 depth; -	u32 i; - -	all_types = (depth_info & TIPC_NTQ_ALLTYPES); -	depth = (depth_info & ~TIPC_NTQ_ALLTYPES); - -	if (depth == 0) -		return 0; - -	if (all_types) { -		/* display all entries in name table to specified depth */ -		ret += nametbl_header(buf, len, depth); -		lowbound = 0; -		upbound = ~0; -		for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { -			seq_head = &tipc_nametbl->seq_hlist[i]; -			hlist_for_each_entry_rcu(seq, seq_head, ns_list) { -				ret += nameseq_list(seq, buf + ret, len - ret, -						   depth, seq->type, -						   lowbound, upbound, i); -			} -		} -	} else { -		/* display only the sequence that matches the specified type */ -		if (upbound < lowbound) { -			ret += tipc_snprintf(buf + ret, len - ret, -					"invalid name sequence specified\n"); -			return ret; -		} -		ret += nametbl_header(buf + ret, len - ret, depth); -		i = hash(type); -		seq_head = &tipc_nametbl->seq_hlist[i]; -		hlist_for_each_entry_rcu(seq, seq_head, ns_list) { -			if (seq->type == type) { -				ret += nameseq_list(seq, buf + ret, len - ret, -						   depth, type, -						   lowbound, upbound, i); -				break; -			} -		} -	} -	return ret; +	spin_unlock_bh(&tn->nametbl_lock);  } -struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) -{ -	struct sk_buff *buf; -	struct tipc_name_table_query *argv; -	struct tlv_desc *rep_tlv; -	char *pb; -	int pb_len; -	int str_len; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NAME_TBL_QUERY)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); -	if (!buf) -		return NULL; - -	rep_tlv = (struct tlv_desc *)buf->data; -	pb = TLV_DATA(rep_tlv); -	pb_len = ULTRA_STRING_MAX_LEN; -	argv = (struct tipc_name_table_query *)TLV_DATA(req_tlv_area); -	rcu_read_lock(); -	str_len = nametbl_list(pb, pb_len, ntohl(argv->depth), -			       ntohl(argv->type), -			       ntohl(argv->lowbound), ntohl(argv->upbound)); -	rcu_read_unlock(); -	str_len += 1;	/* for "\0" */ -	skb_put(buf, TLV_SPACE(str_len)); -	TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); - -	return buf; -} - -int tipc_nametbl_init(void) +int tipc_nametbl_init(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct name_table *tipc_nametbl;  	int i;  	tipc_nametbl = kzalloc(sizeof(*tipc_nametbl), GFP_ATOMIC); @@ -955,6 +791,8 @@ int tipc_nametbl_init(void)  	INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]);  	INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]);  	INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_NODE_SCOPE]); +	tn->nametbl = tipc_nametbl; +	spin_lock_init(&tn->nametbl_lock);  	return 0;  } @@ -963,7 +801,7 @@ int tipc_nametbl_init(void)   *   * tipc_nametbl_lock must be held when calling this function   */ -static void tipc_purge_publications(struct name_seq *seq) +static void tipc_purge_publications(struct net *net, struct name_seq *seq)  {  	struct publication *publ, *safe;  	struct sub_seq *sseq; @@ -973,8 +811,8 @@ static void tipc_purge_publications(struct name_seq *seq)  	sseq = seq->sseqs;  	info = sseq->info;  	list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) { -		tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, -					 publ->ref, publ->key); +		tipc_nametbl_remove_publ(net, publ->type, publ->lower, +					 publ->node, publ->ref, publ->key);  		kfree_rcu(publ, rcu);  	}  	hlist_del_init_rcu(&seq->ns_list); @@ -984,25 +822,27 @@ static void tipc_purge_publications(struct name_seq *seq)  	kfree_rcu(seq, rcu);  } -void tipc_nametbl_stop(void) +void tipc_nametbl_stop(struct net *net)  {  	u32 i;  	struct name_seq *seq;  	struct hlist_head *seq_head; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct name_table *tipc_nametbl = tn->nametbl;  	/* Verify name table is empty and purge any lingering  	 * publications, then release the name table  	 */ -	spin_lock_bh(&tipc_nametbl_lock); +	spin_lock_bh(&tn->nametbl_lock);  	for (i = 0; i < TIPC_NAMETBL_SIZE; i++) {  		if (hlist_empty(&tipc_nametbl->seq_hlist[i]))  			continue;  		seq_head = &tipc_nametbl->seq_hlist[i];  		hlist_for_each_entry_rcu(seq, seq_head, ns_list) { -			tipc_purge_publications(seq); +			tipc_purge_publications(net, seq);  		}  	} -	spin_unlock_bh(&tipc_nametbl_lock); +	spin_unlock_bh(&tn->nametbl_lock);  	synchronize_net();  	kfree(tipc_nametbl); @@ -1033,7 +873,7 @@ static int __tipc_nl_add_nametable_publ(struct tipc_nl_msg *msg,  		*last_publ = p->key;  		hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, -				  &tipc_genl_v2_family, NLM_F_MULTI, +				  &tipc_genl_family, NLM_F_MULTI,  				  TIPC_NL_NAME_TABLE_GET);  		if (!hdr)  			return -EMSGSIZE; @@ -1106,9 +946,10 @@ static int __tipc_nl_subseq_list(struct tipc_nl_msg *msg, struct name_seq *seq,  	return 0;  } -static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type, -			      u32 *last_lower, u32 *last_publ) +static int tipc_nl_seq_list(struct net *net, struct tipc_nl_msg *msg, +			    u32 *last_type, u32 *last_lower, u32 *last_publ)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct hlist_head *seq_head;  	struct name_seq *seq = NULL;  	int err; @@ -1120,10 +961,10 @@ static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type,  		i = 0;  	for (; i < TIPC_NAMETBL_SIZE; i++) { -		seq_head = &tipc_nametbl->seq_hlist[i]; +		seq_head = &tn->nametbl->seq_hlist[i];  		if (*last_type) { -			seq = nametbl_find_seq(*last_type); +			seq = nametbl_find_seq(net, *last_type);  			if (!seq)  				return -EPIPE;  		} else { @@ -1157,6 +998,7 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb)  	u32 last_type = cb->args[0];  	u32 last_lower = cb->args[1];  	u32 last_publ = cb->args[2]; +	struct net *net = sock_net(skb->sk);  	struct tipc_nl_msg msg;  	if (done) @@ -1167,7 +1009,7 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb)  	msg.seq = cb->nlh->nlmsg_seq;  	rcu_read_lock(); -	err = __tipc_nl_seq_list(&msg, &last_type, &last_lower, &last_publ); +	err = tipc_nl_seq_list(net, &msg, &last_type, &last_lower, &last_publ);  	if (!err) {  		done = 1;  	} else if (err != -EMSGSIZE) { @@ -1188,3 +1030,41 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb)  	return skb->len;  } + +void tipc_plist_push(struct tipc_plist *pl, u32 port) +{ +	struct tipc_plist *nl; + +	if (likely(!pl->port)) { +		pl->port = port; +		return; +	} +	if (pl->port == port) +		return; +	list_for_each_entry(nl, &pl->list, list) { +		if (nl->port == port) +			return; +	} +	nl = kmalloc(sizeof(*nl), GFP_ATOMIC); +	if (nl) { +		nl->port = port; +		list_add(&nl->list, &pl->list); +	} +} + +u32 tipc_plist_pop(struct tipc_plist *pl) +{ +	struct tipc_plist *nl; +	u32 port = 0; + +	if (likely(list_empty(&pl->list))) { +		port = pl->port; +		pl->port = 0; +		return port; +	} +	nl = list_first_entry(&pl->list, typeof(*nl), list); +	port = nl->port; +	list_del(&nl->list); +	kfree(nl); +	return port; +} diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index 5f0dee92010d..1524a73830f7 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -1,7 +1,7 @@  /*   * net/tipc/name_table.h: Include file for TIPC name table code   * - * Copyright (c) 2000-2006, 2014, Ericsson AB + * Copyright (c) 2000-2006, 2014-2015, Ericsson AB   * Copyright (c) 2004-2005, 2010-2011, Wind River Systems   * All rights reserved.   * @@ -38,7 +38,7 @@  #define _TIPC_NAME_TABLE_H  struct tipc_subscription; -struct tipc_port_list; +struct tipc_plist;  /*   * TIPC name types reserved for internal TIPC use (both current and planned) @@ -95,26 +95,39 @@ struct name_table {  	u32 local_publ_count;  }; -extern spinlock_t tipc_nametbl_lock; -extern struct name_table *tipc_nametbl; -  int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); -struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space); -u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node); -int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, -			      struct tipc_port_list *dports); -struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, -					 u32 scope, u32 port_ref, u32 key); -int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key); -struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, -					     u32 scope, u32 node, u32 ref, +u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node); +int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, +			      u32 limit, struct tipc_plist *dports); +struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower, +					 u32 upper, u32 scope, u32 port_ref, +					 u32 key); +int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref, +			  u32 key); +struct publication *tipc_nametbl_insert_publ(struct net *net, u32 type, +					     u32 lower, u32 upper, u32 scope, +					     u32 node, u32 ref, u32 key); +struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, +					     u32 lower, u32 node, u32 ref,  					     u32 key); -struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, u32 node, -					     u32 ref, u32 key);  void tipc_nametbl_subscribe(struct tipc_subscription *s);  void tipc_nametbl_unsubscribe(struct tipc_subscription *s); -int tipc_nametbl_init(void); -void tipc_nametbl_stop(void); +int tipc_nametbl_init(struct net *net); +void tipc_nametbl_stop(struct net *net); + +struct tipc_plist { +	struct list_head list; +	u32 port; +}; + +static inline void tipc_plist_init(struct tipc_plist *pl) +{ +	INIT_LIST_HEAD(&pl->list); +	pl->port = 0; +} + +void tipc_plist_push(struct tipc_plist *pl, u32 port); +u32 tipc_plist_pop(struct tipc_plist *pl);  #endif diff --git a/net/tipc/net.c b/net/tipc/net.c index cf13df3cde8f..a54f3cbe2246 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -40,7 +40,6 @@  #include "subscr.h"  #include "socket.h"  #include "node.h" -#include "config.h"  static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = {  	[TIPC_NLA_NET_UNSPEC]	= { .type = NLA_UNSPEC }, @@ -108,48 +107,54 @@ static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = {   *     - A local spin_lock protecting the queue of subscriber events.  */ -int tipc_net_start(u32 addr) +int tipc_net_start(struct net *net, u32 addr)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	char addr_string[16];  	int res; -	tipc_own_addr = addr; -	tipc_named_reinit(); -	tipc_sk_reinit(); -	res = tipc_bclink_init(); +	tn->own_addr = addr; +	tipc_named_reinit(net); +	tipc_sk_reinit(net); +	res = tipc_bclink_init(net);  	if (res)  		return res; -	tipc_nametbl_publish(TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr, -			     TIPC_ZONE_SCOPE, 0, tipc_own_addr); +	tipc_nametbl_publish(net, TIPC_CFG_SRV, tn->own_addr, tn->own_addr, +			     TIPC_ZONE_SCOPE, 0, tn->own_addr);  	pr_info("Started in network mode\n");  	pr_info("Own node address %s, network identity %u\n", -		tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id); +		tipc_addr_string_fill(addr_string, tn->own_addr), +		tn->net_id);  	return 0;  } -void tipc_net_stop(void) +void tipc_net_stop(struct net *net)  { -	if (!tipc_own_addr) +	struct tipc_net *tn = net_generic(net, tipc_net_id); + +	if (!tn->own_addr)  		return; -	tipc_nametbl_withdraw(TIPC_CFG_SRV, tipc_own_addr, 0, tipc_own_addr); +	tipc_nametbl_withdraw(net, TIPC_CFG_SRV, tn->own_addr, 0, +			      tn->own_addr);  	rtnl_lock(); -	tipc_bearer_stop(); -	tipc_bclink_stop(); -	tipc_node_stop(); +	tipc_bearer_stop(net); +	tipc_bclink_stop(net); +	tipc_node_stop(net);  	rtnl_unlock();  	pr_info("Left network mode\n");  } -static int __tipc_nl_add_net(struct tipc_nl_msg *msg) +static int __tipc_nl_add_net(struct net *net, struct tipc_nl_msg *msg)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	void *hdr;  	struct nlattr *attrs; -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_NET_GET);  	if (!hdr)  		return -EMSGSIZE; @@ -158,7 +163,7 @@ static int __tipc_nl_add_net(struct tipc_nl_msg *msg)  	if (!attrs)  		goto msg_full; -	if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tipc_net_id)) +	if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tn->net_id))  		goto attr_msg_full;  	nla_nest_end(msg->skb, attrs); @@ -176,6 +181,7 @@ msg_full:  int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb)  { +	struct net *net = sock_net(skb->sk);  	int err;  	int done = cb->args[0];  	struct tipc_nl_msg msg; @@ -187,7 +193,7 @@ int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb)  	msg.portid = NETLINK_CB(cb->skb).portid;  	msg.seq = cb->nlh->nlmsg_seq; -	err = __tipc_nl_add_net(&msg); +	err = __tipc_nl_add_net(net, &msg);  	if (err)  		goto out; @@ -200,8 +206,10 @@ out:  int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)  { -	int err; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct nlattr *attrs[TIPC_NLA_NET_MAX + 1]; +	int err;  	if (!info->attrs[TIPC_NLA_NET])  		return -EINVAL; @@ -216,21 +224,21 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)  		u32 val;  		/* Can't change net id once TIPC has joined a network */ -		if (tipc_own_addr) +		if (tn->own_addr)  			return -EPERM;  		val = nla_get_u32(attrs[TIPC_NLA_NET_ID]);  		if (val < 1 || val > 9999)  			return -EINVAL; -		tipc_net_id = val; +		tn->net_id = val;  	}  	if (attrs[TIPC_NLA_NET_ADDR]) {  		u32 addr;  		/* Can't change net addr once TIPC has joined a network */ -		if (tipc_own_addr) +		if (tn->own_addr)  			return -EPERM;  		addr = nla_get_u32(attrs[TIPC_NLA_NET_ADDR]); @@ -238,7 +246,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)  			return -EINVAL;  		rtnl_lock(); -		tipc_net_start(addr); +		tipc_net_start(net, addr);  		rtnl_unlock();  	} diff --git a/net/tipc/net.h b/net/tipc/net.h index a81c1b9eb150..77a7a118911d 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -39,9 +39,9 @@  #include <net/genetlink.h> -int tipc_net_start(u32 addr); +int tipc_net_start(struct net *net, u32 addr); -void tipc_net_stop(void); +void tipc_net_stop(struct net *net);  int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb);  int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index b891e3905bc4..7f6475efc984 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -35,7 +35,6 @@   */  #include "core.h" -#include "config.h"  #include "socket.h"  #include "name_table.h"  #include "bearer.h" @@ -44,36 +43,6 @@  #include "net.h"  #include <net/genetlink.h> -static int handle_cmd(struct sk_buff *skb, struct genl_info *info) -{ -	struct sk_buff *rep_buf; -	struct nlmsghdr *rep_nlh; -	struct nlmsghdr *req_nlh = info->nlhdr; -	struct tipc_genlmsghdr *req_userhdr = info->userhdr; -	int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); -	u16 cmd; - -	if ((req_userhdr->cmd & 0xC000) && (!netlink_capable(skb, CAP_NET_ADMIN))) -		cmd = TIPC_CMD_NOT_NET_ADMIN; -	else -		cmd = req_userhdr->cmd; - -	rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd, -			nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, -			nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), -			hdr_space); - -	if (rep_buf) { -		skb_push(rep_buf, hdr_space); -		rep_nlh = nlmsg_hdr(rep_buf); -		memcpy(rep_nlh, req_nlh, hdr_space); -		rep_nlh->nlmsg_len = rep_buf->len; -		genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).portid); -	} - -	return 0; -} -  static const struct nla_policy tipc_nl_policy[TIPC_NLA_MAX + 1] = {  	[TIPC_NLA_UNSPEC]	= { .type = NLA_UNSPEC, },  	[TIPC_NLA_BEARER]	= { .type = NLA_NESTED, }, @@ -86,32 +55,16 @@ static const struct nla_policy tipc_nl_policy[TIPC_NLA_MAX + 1] = {  	[TIPC_NLA_NAME_TABLE]	= { .type = NLA_NESTED, }  }; -/* Legacy ASCII API */ -static struct genl_family tipc_genl_family = { -	.id		= GENL_ID_GENERATE, -	.name		= TIPC_GENL_NAME, -	.version	= TIPC_GENL_VERSION, -	.hdrsize	= TIPC_GENL_HDRLEN, -	.maxattr	= 0, -}; - -/* Legacy ASCII API */ -static struct genl_ops tipc_genl_ops[] = { -	{ -		.cmd		= TIPC_GENL_CMD, -		.doit		= handle_cmd, -	}, -}; -  /* Users of the legacy API (tipc-config) can't handle that we add operations,   * so we have a separate genl handling for the new API.   */ -struct genl_family tipc_genl_v2_family = { +struct genl_family tipc_genl_family = {  	.id		= GENL_ID_GENERATE,  	.name		= TIPC_GENL_V2_NAME,  	.version	= TIPC_GENL_V2_VERSION,  	.hdrsize	= 0,  	.maxattr	= TIPC_NLA_MAX, +	.netnsok	= true,  };  static const struct genl_ops tipc_genl_v2_ops[] = { @@ -197,9 +150,9 @@ static const struct genl_ops tipc_genl_v2_ops[] = {  int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr)  { -	u32 maxattr = tipc_genl_v2_family.maxattr; +	u32 maxattr = tipc_genl_family.maxattr; -	*attr = tipc_genl_v2_family.attrbuf; +	*attr = tipc_genl_family.attrbuf;  	if (!*attr)  		return -EOPNOTSUPP; @@ -210,13 +163,7 @@ int tipc_netlink_start(void)  {  	int res; -	res = genl_register_family_with_ops(&tipc_genl_family, tipc_genl_ops); -	if (res) { -		pr_err("Failed to register legacy interface\n"); -		return res; -	} - -	res = genl_register_family_with_ops(&tipc_genl_v2_family, +	res = genl_register_family_with_ops(&tipc_genl_family,  					    tipc_genl_v2_ops);  	if (res) {  		pr_err("Failed to register netlink interface\n"); @@ -228,5 +175,4 @@ int tipc_netlink_start(void)  void tipc_netlink_stop(void)  {  	genl_unregister_family(&tipc_genl_family); -	genl_unregister_family(&tipc_genl_v2_family);  } diff --git a/net/tipc/netlink.h b/net/tipc/netlink.h index 1425c6869de0..08a1db67b927 100644 --- a/net/tipc/netlink.h +++ b/net/tipc/netlink.h @@ -36,7 +36,7 @@  #ifndef _TIPC_NETLINK_H  #define _TIPC_NETLINK_H -extern struct genl_family tipc_genl_v2_family; +extern struct genl_family tipc_genl_family;  int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***buf);  struct tipc_nl_msg { @@ -45,4 +45,9 @@ struct tipc_nl_msg {  	u32 seq;  }; +int tipc_netlink_start(void); +int tipc_netlink_compat_start(void); +void tipc_netlink_stop(void); +void tipc_netlink_compat_stop(void); +  #endif diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c new file mode 100644 index 000000000000..ce9121e8e990 --- /dev/null +++ b/net/tipc/netlink_compat.c @@ -0,0 +1,1084 @@ +/* + * Copyright (c) 2014, Ericsson AB + * 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 "bearer.h" +#include "link.h" +#include "name_table.h" +#include "socket.h" +#include "node.h" +#include "net.h" +#include <net/genetlink.h> +#include <linux/tipc_config.h> + +/* The legacy API had an artificial message length limit called + * ULTRA_STRING_MAX_LEN. + */ +#define ULTRA_STRING_MAX_LEN 32768 + +#define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN) + +#define REPLY_TRUNCATED "<truncated>\n" + +struct tipc_nl_compat_msg { +	u16 cmd; +	int rep_type; +	int rep_size; +	int req_type; +	struct sk_buff *rep; +	struct tlv_desc *req; +	struct sock *dst_sk; +}; + +struct tipc_nl_compat_cmd_dump { +	int (*header)(struct tipc_nl_compat_msg *); +	int (*dumpit)(struct sk_buff *, struct netlink_callback *); +	int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs); +}; + +struct tipc_nl_compat_cmd_doit { +	int (*doit)(struct sk_buff *skb, struct genl_info *info); +	int (*transcode)(struct sk_buff *skb, struct tipc_nl_compat_msg *msg); +}; + +static int tipc_skb_tailroom(struct sk_buff *skb) +{ +	int tailroom; +	int limit; + +	tailroom = skb_tailroom(skb); +	limit = TIPC_SKB_MAX - skb->len; + +	if (tailroom < limit) +		return tailroom; + +	return limit; +} + +static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len) +{ +	struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb); + +	if (tipc_skb_tailroom(skb) < TLV_SPACE(len)) +		return -EMSGSIZE; + +	skb_put(skb, TLV_SPACE(len)); +	tlv->tlv_type = htons(type); +	tlv->tlv_len = htons(TLV_LENGTH(len)); +	if (len && data) +		memcpy(TLV_DATA(tlv), data, len); + +	return 0; +} + +static void tipc_tlv_init(struct sk_buff *skb, u16 type) +{ +	struct tlv_desc *tlv = (struct tlv_desc *)skb->data; + +	TLV_SET_LEN(tlv, 0); +	TLV_SET_TYPE(tlv, type); +	skb_put(skb, sizeof(struct tlv_desc)); +} + +static int tipc_tlv_sprintf(struct sk_buff *skb, const char *fmt, ...) +{ +	int n; +	u16 len; +	u32 rem; +	char *buf; +	struct tlv_desc *tlv; +	va_list args; + +	rem = tipc_skb_tailroom(skb); + +	tlv = (struct tlv_desc *)skb->data; +	len = TLV_GET_LEN(tlv); +	buf = TLV_DATA(tlv) + len; + +	va_start(args, fmt); +	n = vscnprintf(buf, rem, fmt, args); +	va_end(args); + +	TLV_SET_LEN(tlv, n + len); +	skb_put(skb, n); + +	return n; +} + +static struct sk_buff *tipc_tlv_alloc(int size) +{ +	int hdr_len; +	struct sk_buff *buf; + +	size = TLV_SPACE(size); +	hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); + +	buf = alloc_skb(hdr_len + size, GFP_KERNEL); +	if (!buf) +		return NULL; + +	skb_reserve(buf, hdr_len); + +	return buf; +} + +static struct sk_buff *tipc_get_err_tlv(char *str) +{ +	int str_len = strlen(str) + 1; +	struct sk_buff *buf; + +	buf = tipc_tlv_alloc(TLV_SPACE(str_len)); +	if (buf) +		tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len); + +	return buf; +} + +static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, +				   struct tipc_nl_compat_msg *msg, +				   struct sk_buff *arg) +{ +	int len = 0; +	int err; +	struct sk_buff *buf; +	struct nlmsghdr *nlmsg; +	struct netlink_callback cb; + +	memset(&cb, 0, sizeof(cb)); +	cb.nlh = (struct nlmsghdr *)arg->data; +	cb.skb = arg; + +	buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	buf->sk = msg->dst_sk; + +	do { +		int rem; + +		len = (*cmd->dumpit)(buf, &cb); + +		nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { +			struct nlattr **attrs; + +			err = tipc_nlmsg_parse(nlmsg, &attrs); +			if (err) +				goto err_out; + +			err = (*cmd->format)(msg, attrs); +			if (err) +				goto err_out; + +			if (tipc_skb_tailroom(msg->rep) <= 1) { +				err = -EMSGSIZE; +				goto err_out; +			} +		} + +		skb_reset_tail_pointer(buf); +		buf->len = 0; + +	} while (len); + +	err = 0; + +err_out: +	kfree_skb(buf); + +	if (err == -EMSGSIZE) { +		/* The legacy API only considered messages filling +		 * "ULTRA_STRING_MAX_LEN" to be truncated. +		 */ +		if ((TIPC_SKB_MAX - msg->rep->len) <= 1) { +			char *tail = skb_tail_pointer(msg->rep); + +			if (*tail != '\0') +				sprintf(tail - sizeof(REPLY_TRUNCATED) - 1, +					REPLY_TRUNCATED); +		} + +		return 0; +	} + +	return err; +} + +static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, +				 struct tipc_nl_compat_msg *msg) +{ +	int err; +	struct sk_buff *arg; + +	if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type)) +		return -EINVAL; + +	msg->rep = tipc_tlv_alloc(msg->rep_size); +	if (!msg->rep) +		return -ENOMEM; + +	if (msg->rep_type) +		tipc_tlv_init(msg->rep, msg->rep_type); + +	if (cmd->header) +		(*cmd->header)(msg); + +	arg = nlmsg_new(0, GFP_KERNEL); +	if (!arg) { +		kfree_skb(msg->rep); +		return -ENOMEM; +	} + +	err = __tipc_nl_compat_dumpit(cmd, msg, arg); +	if (err) +		kfree_skb(msg->rep); + +	kfree_skb(arg); + +	return err; +} + +static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd, +				 struct tipc_nl_compat_msg *msg) +{ +	int err; +	struct sk_buff *doit_buf; +	struct sk_buff *trans_buf; +	struct nlattr **attrbuf; +	struct genl_info info; + +	trans_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); +	if (!trans_buf) +		return -ENOMEM; + +	err = (*cmd->transcode)(trans_buf, msg); +	if (err) +		goto trans_out; + +	attrbuf = kmalloc((tipc_genl_family.maxattr + 1) * +			sizeof(struct nlattr *), GFP_KERNEL); +	if (!attrbuf) { +		err = -ENOMEM; +		goto trans_out; +	} + +	err = nla_parse(attrbuf, tipc_genl_family.maxattr, +			(const struct nlattr *)trans_buf->data, +			trans_buf->len, NULL); +	if (err) +		goto parse_out; + +	doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); +	if (!doit_buf) { +		err = -ENOMEM; +		goto parse_out; +	} + +	doit_buf->sk = msg->dst_sk; + +	memset(&info, 0, sizeof(info)); +	info.attrs = attrbuf; + +	err = (*cmd->doit)(doit_buf, &info); + +	kfree_skb(doit_buf); +parse_out: +	kfree(attrbuf); +trans_out: +	kfree_skb(trans_buf); + +	return err; +} + +static int tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd, +			       struct tipc_nl_compat_msg *msg) +{ +	int err; + +	if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type)) +		return -EINVAL; + +	err = __tipc_nl_compat_doit(cmd, msg); +	if (err) +		return err; + +	/* The legacy API considered an empty message a success message */ +	msg->rep = tipc_tlv_alloc(0); +	if (!msg->rep) +		return -ENOMEM; + +	return 0; +} + +static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg, +				      struct nlattr **attrs) +{ +	struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1]; + +	nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER], +			 NULL); + +	return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME, +			    nla_data(bearer[TIPC_NLA_BEARER_NAME]), +			    nla_len(bearer[TIPC_NLA_BEARER_NAME])); +} + +static int tipc_nl_compat_bearer_enable(struct sk_buff *skb, +					struct tipc_nl_compat_msg *msg) +{ +	struct nlattr *prop; +	struct nlattr *bearer; +	struct tipc_bearer_config *b; + +	b = (struct tipc_bearer_config *)TLV_DATA(msg->req); + +	bearer = nla_nest_start(skb, TIPC_NLA_BEARER); +	if (!bearer) +		return -EMSGSIZE; + +	if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, b->name)) +		return -EMSGSIZE; + +	if (nla_put_u32(skb, TIPC_NLA_BEARER_DOMAIN, ntohl(b->disc_domain))) +		return -EMSGSIZE; + +	if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) { +		prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP); +		if (!prop) +			return -EMSGSIZE; +		if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority))) +			return -EMSGSIZE; +		nla_nest_end(skb, prop); +	} +	nla_nest_end(skb, bearer); + +	return 0; +} + +static int tipc_nl_compat_bearer_disable(struct sk_buff *skb, +					 struct tipc_nl_compat_msg *msg) +{ +	char *name; +	struct nlattr *bearer; + +	name = (char *)TLV_DATA(msg->req); + +	bearer = nla_nest_start(skb, TIPC_NLA_BEARER); +	if (!bearer) +		return -EMSGSIZE; + +	if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, name)) +		return -EMSGSIZE; + +	nla_nest_end(skb, bearer); + +	return 0; +} + +static inline u32 perc(u32 count, u32 total) +{ +	return (count * 100 + (total / 2)) / total; +} + +static void __fill_bc_link_stat(struct tipc_nl_compat_msg *msg, +				struct nlattr *prop[], struct nlattr *stats[]) +{ +	tipc_tlv_sprintf(msg->rep, "  Window:%u packets\n", +			 nla_get_u32(prop[TIPC_NLA_PROP_WIN])); + +	tipc_tlv_sprintf(msg->rep, +			 "  RX packets:%u fragments:%u/%u bundles:%u/%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); + +	tipc_tlv_sprintf(msg->rep, +			 "  TX packets:%u fragments:%u/%u bundles:%u/%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); + +	tipc_tlv_sprintf(msg->rep, "  RX naks:%u defs:%u dups:%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); + +	tipc_tlv_sprintf(msg->rep, "  TX naks:%u acks:%u dups:%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); + +	tipc_tlv_sprintf(msg->rep, +			 "  Congestion link:%u  Send queue max:%u avg:%u", +			 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), +			 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); +} + +static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg, +					 struct nlattr **attrs) +{ +	char *name; +	struct nlattr *link[TIPC_NLA_LINK_MAX + 1]; +	struct nlattr *prop[TIPC_NLA_PROP_MAX + 1]; +	struct nlattr *stats[TIPC_NLA_STATS_MAX + 1]; + +	nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], NULL); + +	nla_parse_nested(prop, TIPC_NLA_PROP_MAX, link[TIPC_NLA_LINK_PROP], +			 NULL); + +	nla_parse_nested(stats, TIPC_NLA_STATS_MAX, link[TIPC_NLA_LINK_STATS], +			 NULL); + +	name = (char *)TLV_DATA(msg->req); +	if (strcmp(name, nla_data(link[TIPC_NLA_LINK_NAME])) != 0) +		return 0; + +	tipc_tlv_sprintf(msg->rep, "\nLink <%s>\n", +			 nla_data(link[TIPC_NLA_LINK_NAME])); + +	if (link[TIPC_NLA_LINK_BROADCAST]) { +		__fill_bc_link_stat(msg, prop, stats); +		return 0; +	} + +	if (link[TIPC_NLA_LINK_ACTIVE]) +		tipc_tlv_sprintf(msg->rep, "  ACTIVE"); +	else if (link[TIPC_NLA_LINK_UP]) +		tipc_tlv_sprintf(msg->rep, "  STANDBY"); +	else +		tipc_tlv_sprintf(msg->rep, "  DEFUNCT"); + +	tipc_tlv_sprintf(msg->rep, "  MTU:%u  Priority:%u", +			 nla_get_u32(link[TIPC_NLA_LINK_MTU]), +			 nla_get_u32(prop[TIPC_NLA_PROP_PRIO])); + +	tipc_tlv_sprintf(msg->rep, "  Tolerance:%u ms  Window:%u packets\n", +			 nla_get_u32(prop[TIPC_NLA_PROP_TOL]), +			 nla_get_u32(prop[TIPC_NLA_PROP_WIN])); + +	tipc_tlv_sprintf(msg->rep, +			 "  RX packets:%u fragments:%u/%u bundles:%u/%u\n", +			 nla_get_u32(link[TIPC_NLA_LINK_RX]) - +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); + +	tipc_tlv_sprintf(msg->rep, +			 "  TX packets:%u fragments:%u/%u bundles:%u/%u\n", +			 nla_get_u32(link[TIPC_NLA_LINK_TX]) - +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); + +	tipc_tlv_sprintf(msg->rep, +			 "  TX profile sample:%u packets  average:%u octets\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]), +			 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / +			 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])); + +	tipc_tlv_sprintf(msg->rep, +			 "  0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% ", +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]))); + +	tipc_tlv_sprintf(msg->rep, "-16384:%u%% -32768:%u%% -66000:%u%%\n", +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), +			 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]), +			      nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]))); + +	tipc_tlv_sprintf(msg->rep, +			 "  RX states:%u probes:%u naks:%u defs:%u dups:%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_STATES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), +			 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); + +	tipc_tlv_sprintf(msg->rep, +			 "  TX states:%u probes:%u naks:%u acks:%u dups:%u\n", +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_STATES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); + +	tipc_tlv_sprintf(msg->rep, +			 "  Congestion link:%u  Send queue max:%u avg:%u", +			 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), +			 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), +			 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); + +	return 0; +} + +static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg, +				    struct nlattr **attrs) +{ +	struct nlattr *link[TIPC_NLA_LINK_MAX + 1]; +	struct tipc_link_info link_info; + +	nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], NULL); + +	link_info.dest = nla_get_flag(link[TIPC_NLA_LINK_DEST]); +	link_info.up = htonl(nla_get_flag(link[TIPC_NLA_LINK_UP])); +	strcpy(link_info.str, nla_data(link[TIPC_NLA_LINK_NAME])); + +	return tipc_add_tlv(msg->rep, TIPC_TLV_LINK_INFO, +			    &link_info, sizeof(link_info)); +} + +static int tipc_nl_compat_link_set(struct sk_buff *skb, +				   struct tipc_nl_compat_msg *msg) +{ +	struct nlattr *link; +	struct nlattr *prop; +	struct tipc_link_config *lc; + +	lc = (struct tipc_link_config *)TLV_DATA(msg->req); + +	link = nla_nest_start(skb, TIPC_NLA_LINK); +	if (!link) +		return -EMSGSIZE; + +	if (nla_put_string(skb, TIPC_NLA_LINK_NAME, lc->name)) +		return -EMSGSIZE; + +	prop = nla_nest_start(skb, TIPC_NLA_LINK_PROP); +	if (!prop) +		return -EMSGSIZE; + +	if (msg->cmd == TIPC_CMD_SET_LINK_PRI) { +		if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(lc->value))) +			return -EMSGSIZE; +	} else if (msg->cmd == TIPC_CMD_SET_LINK_TOL) { +		if (nla_put_u32(skb, TIPC_NLA_PROP_TOL, ntohl(lc->value))) +			return -EMSGSIZE; +	} else if (msg->cmd == TIPC_CMD_SET_LINK_WINDOW) { +		if (nla_put_u32(skb, TIPC_NLA_PROP_WIN, ntohl(lc->value))) +			return -EMSGSIZE; +	} + +	nla_nest_end(skb, prop); +	nla_nest_end(skb, link); + +	return 0; +} + +static int tipc_nl_compat_link_reset_stats(struct sk_buff *skb, +					   struct tipc_nl_compat_msg *msg) +{ +	char *name; +	struct nlattr *link; + +	name = (char *)TLV_DATA(msg->req); + +	link = nla_nest_start(skb, TIPC_NLA_LINK); +	if (!link) +		return -EMSGSIZE; + +	if (nla_put_string(skb, TIPC_NLA_LINK_NAME, name)) +		return -EMSGSIZE; + +	nla_nest_end(skb, link); + +	return 0; +} + +static int tipc_nl_compat_name_table_dump_header(struct tipc_nl_compat_msg *msg) +{ +	int i; +	u32 depth; +	struct tipc_name_table_query *ntq; +	static const char * const header[] = { +		"Type       ", +		"Lower      Upper      ", +		"Port Identity              ", +		"Publication Scope" +	}; + +	ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req); + +	depth = ntohl(ntq->depth); + +	if (depth > 4) +		depth = 4; +	for (i = 0; i < depth; i++) +		tipc_tlv_sprintf(msg->rep, header[i]); +	tipc_tlv_sprintf(msg->rep, "\n"); + +	return 0; +} + +static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg, +					  struct nlattr **attrs) +{ +	char port_str[27]; +	struct tipc_name_table_query *ntq; +	struct nlattr *nt[TIPC_NLA_NAME_TABLE_MAX + 1]; +	struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1]; +	u32 node, depth, type, lowbound, upbound; +	static const char * const scope_str[] = {"", " zone", " cluster", +						 " node"}; + +	nla_parse_nested(nt, TIPC_NLA_NAME_TABLE_MAX, +			 attrs[TIPC_NLA_NAME_TABLE], NULL); + +	nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, nt[TIPC_NLA_NAME_TABLE_PUBL], +			 NULL); + +	ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req); + +	depth = ntohl(ntq->depth); +	type = ntohl(ntq->type); +	lowbound = ntohl(ntq->lowbound); +	upbound = ntohl(ntq->upbound); + +	if (!(depth & TIPC_NTQ_ALLTYPES) && +	    (type != nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]))) +		return 0; +	if (lowbound && (lowbound > nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]))) +		return 0; +	if (upbound && (upbound < nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]))) +		return 0; + +	tipc_tlv_sprintf(msg->rep, "%-10u ", +			 nla_get_u32(publ[TIPC_NLA_PUBL_TYPE])); + +	if (depth == 1) +		goto out; + +	tipc_tlv_sprintf(msg->rep, "%-10u %-10u ", +			 nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]), +			 nla_get_u32(publ[TIPC_NLA_PUBL_UPPER])); + +	if (depth == 2) +		goto out; + +	node = nla_get_u32(publ[TIPC_NLA_PUBL_NODE]); +	sprintf(port_str, "<%u.%u.%u:%u>", tipc_zone(node), tipc_cluster(node), +		tipc_node(node), nla_get_u32(publ[TIPC_NLA_PUBL_REF])); +	tipc_tlv_sprintf(msg->rep, "%-26s ", port_str); + +	if (depth == 3) +		goto out; + +	tipc_tlv_sprintf(msg->rep, "%-10u %s", +			 nla_get_u32(publ[TIPC_NLA_PUBL_REF]), +			 scope_str[nla_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]); +out: +	tipc_tlv_sprintf(msg->rep, "\n"); + +	return 0; +} + +static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, +				      struct nlattr **attrs) +{ +	u32 type, lower, upper; +	struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1]; + +	nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, attrs[TIPC_NLA_PUBL], NULL); + +	type = nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]); +	lower = nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]); +	upper = nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]); + +	if (lower == upper) +		tipc_tlv_sprintf(msg->rep, " {%u,%u}", type, lower); +	else +		tipc_tlv_sprintf(msg->rep, " {%u,%u,%u}", type, lower, upper); + +	return 0; +} + +static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock) +{ +	int err; +	void *hdr; +	struct nlattr *nest; +	struct sk_buff *args; +	struct tipc_nl_compat_cmd_dump dump; + +	args = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); +	if (!args) +		return -ENOMEM; + +	hdr = genlmsg_put(args, 0, 0, &tipc_genl_family, NLM_F_MULTI, +			  TIPC_NL_PUBL_GET); + +	nest = nla_nest_start(args, TIPC_NLA_SOCK); +	if (!nest) { +		kfree_skb(args); +		return -EMSGSIZE; +	} + +	if (nla_put_u32(args, TIPC_NLA_SOCK_REF, sock)) { +		kfree_skb(args); +		return -EMSGSIZE; +	} + +	nla_nest_end(args, nest); +	genlmsg_end(args, hdr); + +	dump.dumpit = tipc_nl_publ_dump; +	dump.format = __tipc_nl_compat_publ_dump; + +	err = __tipc_nl_compat_dumpit(&dump, msg, args); + +	kfree_skb(args); + +	return err; +} + +static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg, +				  struct nlattr **attrs) +{ +	int err; +	u32 sock_ref; +	struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1]; + +	nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], NULL); + +	sock_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); +	tipc_tlv_sprintf(msg->rep, "%u:", sock_ref); + +	if (sock[TIPC_NLA_SOCK_CON]) { +		u32 node; +		struct nlattr *con[TIPC_NLA_CON_MAX + 1]; + +		nla_parse_nested(con, TIPC_NLA_CON_MAX, sock[TIPC_NLA_SOCK_CON], +				 NULL); + +		node = nla_get_u32(con[TIPC_NLA_CON_NODE]); +		tipc_tlv_sprintf(msg->rep, "  connected to <%u.%u.%u:%u>", +				 tipc_zone(node), +				 tipc_cluster(node), +				 tipc_node(node), +				 nla_get_u32(con[TIPC_NLA_CON_SOCK])); + +		if (con[TIPC_NLA_CON_FLAG]) +			tipc_tlv_sprintf(msg->rep, " via {%u,%u}\n", +					 nla_get_u32(con[TIPC_NLA_CON_TYPE]), +					 nla_get_u32(con[TIPC_NLA_CON_INST])); +		else +			tipc_tlv_sprintf(msg->rep, "\n"); +	} else if (sock[TIPC_NLA_SOCK_HAS_PUBL]) { +		tipc_tlv_sprintf(msg->rep, " bound to"); + +		err = tipc_nl_compat_publ_dump(msg, sock_ref); +		if (err) +			return err; +	} +	tipc_tlv_sprintf(msg->rep, "\n"); + +	return 0; +} + +static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg, +				     struct nlattr **attrs) +{ +	struct nlattr *media[TIPC_NLA_MEDIA_MAX + 1]; + +	nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, attrs[TIPC_NLA_MEDIA], +			 NULL); + +	return tipc_add_tlv(msg->rep, TIPC_TLV_MEDIA_NAME, +			    nla_data(media[TIPC_NLA_MEDIA_NAME]), +			    nla_len(media[TIPC_NLA_MEDIA_NAME])); +} + +static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg, +				    struct nlattr **attrs) +{ +	struct tipc_node_info node_info; +	struct nlattr *node[TIPC_NLA_NODE_MAX + 1]; + +	nla_parse_nested(node, TIPC_NLA_NODE_MAX, attrs[TIPC_NLA_NODE], NULL); + +	node_info.addr = htonl(nla_get_u32(node[TIPC_NLA_NODE_ADDR])); +	node_info.up = htonl(nla_get_flag(node[TIPC_NLA_NODE_UP])); + +	return tipc_add_tlv(msg->rep, TIPC_TLV_NODE_INFO, &node_info, +			    sizeof(node_info)); +} + +static int tipc_nl_compat_net_set(struct sk_buff *skb, +				  struct tipc_nl_compat_msg *msg) +{ +	u32 val; +	struct nlattr *net; + +	val = ntohl(*(__be32 *)TLV_DATA(msg->req)); + +	net = nla_nest_start(skb, TIPC_NLA_NET); +	if (!net) +		return -EMSGSIZE; + +	if (msg->cmd == TIPC_CMD_SET_NODE_ADDR) { +		if (nla_put_u32(skb, TIPC_NLA_NET_ADDR, val)) +			return -EMSGSIZE; +	} else if (msg->cmd == TIPC_CMD_SET_NETID) { +		if (nla_put_u32(skb, TIPC_NLA_NET_ID, val)) +			return -EMSGSIZE; +	} +	nla_nest_end(skb, net); + +	return 0; +} + +static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg, +				   struct nlattr **attrs) +{ +	__be32 id; +	struct nlattr *net[TIPC_NLA_NET_MAX + 1]; + +	nla_parse_nested(net, TIPC_NLA_NET_MAX, attrs[TIPC_NLA_NET], NULL); +	id = htonl(nla_get_u32(net[TIPC_NLA_NET_ID])); + +	return tipc_add_tlv(msg->rep, TIPC_TLV_UNSIGNED, &id, sizeof(id)); +} + +static int tipc_cmd_show_stats_compat(struct tipc_nl_compat_msg *msg) +{ +	msg->rep = tipc_tlv_alloc(ULTRA_STRING_MAX_LEN); +	if (!msg->rep) +		return -ENOMEM; + +	tipc_tlv_init(msg->rep, TIPC_TLV_ULTRA_STRING); +	tipc_tlv_sprintf(msg->rep, "TIPC version " TIPC_MOD_VER "\n"); + +	return 0; +} + +static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg) +{ +	struct tipc_nl_compat_cmd_dump dump; +	struct tipc_nl_compat_cmd_doit doit; + +	memset(&dump, 0, sizeof(dump)); +	memset(&doit, 0, sizeof(doit)); + +	switch (msg->cmd) { +	case TIPC_CMD_NOOP: +		msg->rep = tipc_tlv_alloc(0); +		if (!msg->rep) +			return -ENOMEM; +		return 0; +	case TIPC_CMD_GET_BEARER_NAMES: +		msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME); +		dump.dumpit = tipc_nl_bearer_dump; +		dump.format = tipc_nl_compat_bearer_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_ENABLE_BEARER: +		msg->req_type = TIPC_TLV_BEARER_CONFIG; +		doit.doit = tipc_nl_bearer_enable; +		doit.transcode = tipc_nl_compat_bearer_enable; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_DISABLE_BEARER: +		msg->req_type = TIPC_TLV_BEARER_NAME; +		doit.doit = tipc_nl_bearer_disable; +		doit.transcode = tipc_nl_compat_bearer_disable; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_SHOW_LINK_STATS: +		msg->req_type = TIPC_TLV_LINK_NAME; +		msg->rep_size = ULTRA_STRING_MAX_LEN; +		msg->rep_type = TIPC_TLV_ULTRA_STRING; +		dump.dumpit = tipc_nl_link_dump; +		dump.format = tipc_nl_compat_link_stat_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_GET_LINKS: +		msg->req_type = TIPC_TLV_NET_ADDR; +		msg->rep_size = ULTRA_STRING_MAX_LEN; +		dump.dumpit = tipc_nl_link_dump; +		dump.format = tipc_nl_compat_link_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_SET_LINK_TOL: +	case TIPC_CMD_SET_LINK_PRI: +	case TIPC_CMD_SET_LINK_WINDOW: +		msg->req_type =  TIPC_TLV_LINK_CONFIG; +		doit.doit = tipc_nl_link_set; +		doit.transcode = tipc_nl_compat_link_set; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_RESET_LINK_STATS: +		msg->req_type = TIPC_TLV_LINK_NAME; +		doit.doit = tipc_nl_link_reset_stats; +		doit.transcode = tipc_nl_compat_link_reset_stats; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_SHOW_NAME_TABLE: +		msg->req_type = TIPC_TLV_NAME_TBL_QUERY; +		msg->rep_size = ULTRA_STRING_MAX_LEN; +		msg->rep_type = TIPC_TLV_ULTRA_STRING; +		dump.header = tipc_nl_compat_name_table_dump_header; +		dump.dumpit = tipc_nl_name_table_dump; +		dump.format = tipc_nl_compat_name_table_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_SHOW_PORTS: +		msg->rep_size = ULTRA_STRING_MAX_LEN; +		msg->rep_type = TIPC_TLV_ULTRA_STRING; +		dump.dumpit = tipc_nl_sk_dump; +		dump.format = tipc_nl_compat_sk_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_GET_MEDIA_NAMES: +		msg->rep_size = MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME); +		dump.dumpit = tipc_nl_media_dump; +		dump.format = tipc_nl_compat_media_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_GET_NODES: +		msg->rep_size = ULTRA_STRING_MAX_LEN; +		dump.dumpit = tipc_nl_node_dump; +		dump.format = tipc_nl_compat_node_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_SET_NODE_ADDR: +		msg->req_type = TIPC_TLV_NET_ADDR; +		doit.doit = tipc_nl_net_set; +		doit.transcode = tipc_nl_compat_net_set; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_SET_NETID: +		msg->req_type = TIPC_TLV_UNSIGNED; +		doit.doit = tipc_nl_net_set; +		doit.transcode = tipc_nl_compat_net_set; +		return tipc_nl_compat_doit(&doit, msg); +	case TIPC_CMD_GET_NETID: +		msg->rep_size = sizeof(u32); +		dump.dumpit = tipc_nl_net_dump; +		dump.format = tipc_nl_compat_net_dump; +		return tipc_nl_compat_dumpit(&dump, msg); +	case TIPC_CMD_SHOW_STATS: +		return tipc_cmd_show_stats_compat(msg); +	} + +	return -EOPNOTSUPP; +} + +static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info) +{ +	int err; +	int len; +	struct tipc_nl_compat_msg msg; +	struct nlmsghdr *req_nlh; +	struct nlmsghdr *rep_nlh; +	struct tipc_genlmsghdr *req_userhdr = info->userhdr; +	struct net *net = genl_info_net(info); + +	memset(&msg, 0, sizeof(msg)); + +	req_nlh = (struct nlmsghdr *)skb->data; +	msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN; +	msg.cmd = req_userhdr->cmd; +	msg.dst_sk = info->dst_sk; + +	if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) { +		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN); +		err = -EACCES; +		goto send; +	} + +	len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN); +	if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) { +		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); +		err = -EOPNOTSUPP; +		goto send; +	} + +	err = tipc_nl_compat_handle(&msg); +	if (err == -EOPNOTSUPP) +		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); +	else if (err == -EINVAL) +		msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR); +send: +	if (!msg.rep) +		return err; + +	len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); +	skb_push(msg.rep, len); +	rep_nlh = nlmsg_hdr(msg.rep); +	memcpy(rep_nlh, info->nlhdr, len); +	rep_nlh->nlmsg_len = msg.rep->len; +	genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid); + +	return err; +} + +static struct genl_family tipc_genl_compat_family = { +	.id		= GENL_ID_GENERATE, +	.name		= TIPC_GENL_NAME, +	.version	= TIPC_GENL_VERSION, +	.hdrsize	= TIPC_GENL_HDRLEN, +	.maxattr	= 0, +	.netnsok	= true, +}; + +static struct genl_ops tipc_genl_compat_ops[] = { +	{ +		.cmd		= TIPC_GENL_CMD, +		.doit		= tipc_nl_compat_recv, +	}, +}; + +int tipc_netlink_compat_start(void) +{ +	int res; + +	res = genl_register_family_with_ops(&tipc_genl_compat_family, +					    tipc_genl_compat_ops); +	if (res) { +		pr_err("Failed to register legacy compat interface\n"); +		return res; +	} + +	return 0; +} + +void tipc_netlink_compat_stop(void) +{ +	genl_unregister_family(&tipc_genl_compat_family); +} diff --git a/net/tipc/node.c b/net/tipc/node.c index 8d353ec77a66..86152de8248d 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -35,22 +35,14 @@   */  #include "core.h" -#include "config.h" +#include "link.h"  #include "node.h"  #include "name_distr.h"  #include "socket.h" -#define NODE_HTABLE_SIZE 512 -  static void node_lost_contact(struct tipc_node *n_ptr);  static void node_established_contact(struct tipc_node *n_ptr); -static struct hlist_head node_htable[NODE_HTABLE_SIZE]; -LIST_HEAD(tipc_node_list); -static u32 tipc_num_nodes; -static u32 tipc_num_links; -static DEFINE_SPINLOCK(node_list_lock); -  struct tipc_sock_conn {  	u32 port;  	u32 peer_port; @@ -78,15 +70,17 @@ static unsigned int tipc_hashfn(u32 addr)  /*   * tipc_node_find - locate specified node object, if it exists   */ -struct tipc_node *tipc_node_find(u32 addr) +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(addr))) +	if (unlikely(!in_own_cluster_exact(net, addr)))  		return NULL;  	rcu_read_lock(); -	hlist_for_each_entry_rcu(node, &node_htable[tipc_hashfn(addr)], hash) { +	hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], +				 hash) {  		if (node->addr == addr) {  			rcu_read_unlock();  			return node; @@ -96,72 +90,68 @@ struct tipc_node *tipc_node_find(u32 addr)  	return NULL;  } -struct tipc_node *tipc_node_create(u32 addr) +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(&node_list_lock); - +	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(&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);  	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->waiting_sks);  	__skb_queue_head_init(&n_ptr->bclink.deferred_queue); - -	hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); - -	list_for_each_entry_rcu(temp_node, &tipc_node_list, list) { +	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->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;  	n_ptr->signature = INVALID_NODE_SIG; - -	tipc_num_nodes++; - -	spin_unlock_bh(&node_list_lock); +exit: +	spin_unlock_bh(&tn->node_list_lock);  	return n_ptr;  } -static void tipc_node_delete(struct tipc_node *n_ptr) +static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr)  {  	list_del_rcu(&n_ptr->list);  	hlist_del_rcu(&n_ptr->hash);  	kfree_rcu(n_ptr, rcu); - -	tipc_num_nodes--;  } -void tipc_node_stop(void) +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(&node_list_lock); -	list_for_each_entry_safe(node, t_node, &tipc_node_list, list) -		tipc_node_delete(node); -	spin_unlock_bh(&node_list_lock); +	spin_lock_bh(&tn->node_list_lock); +	list_for_each_entry_safe(node, t_node, &tn->node_list, list) +		tipc_node_delete(tn, node); +	spin_unlock_bh(&tn->node_list_lock);  } -int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) +int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)  {  	struct tipc_node *node;  	struct tipc_sock_conn *conn; -	if (in_own_node(dnode)) +	if (in_own_node(net, dnode))  		return 0; -	node = tipc_node_find(dnode); +	node = tipc_node_find(net, dnode);  	if (!node) {  		pr_warn("Connecting sock to node 0x%x failed\n", dnode);  		return -EHOSTUNREACH; @@ -179,15 +169,15 @@ int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port)  	return 0;  } -void tipc_node_remove_conn(u32 dnode, u32 port) +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(dnode)) +	if (in_own_node(net, dnode))  		return; -	node = tipc_node_find(dnode); +	node = tipc_node_find(net, dnode);  	if (!node)  		return; @@ -201,23 +191,6 @@ void tipc_node_remove_conn(u32 dnode, u32 port)  	tipc_node_unlock(node);  } -void tipc_node_abort_sock_conns(struct list_head *conns) -{ -	struct tipc_sock_conn *conn, *safe; -	struct sk_buff *buf; - -	list_for_each_entry_safe(conn, safe, conns, list) { -		buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, -				      SHORT_H_SIZE, 0, tipc_own_addr, -				      conn->peer_node, conn->port, -				      conn->peer_port, TIPC_ERR_NO_NODE); -		if (likely(buf)) -			tipc_sk_rcv(buf); -		list_del(&conn->list); -		kfree(conn); -	} -} -  /**   * tipc_node_link_up - handle addition of link   * @@ -231,8 +204,8 @@ void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  	n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP;  	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; -	pr_info("Established link <%s> on network plane %c\n", -		l_ptr->name, l_ptr->net_plane); +	pr_debug("Established link <%s> on network plane %c\n", +		 l_ptr->name, l_ptr->net_plane);  	if (!active[0]) {  		active[0] = active[1] = l_ptr; @@ -240,7 +213,7 @@ void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  		goto exit;  	}  	if (l_ptr->priority < active[0]->priority) { -		pr_info("New link <%s> becomes standby\n", l_ptr->name); +		pr_debug("New link <%s> becomes standby\n", l_ptr->name);  		goto exit;  	}  	tipc_link_dup_queue_xmit(active[0], l_ptr); @@ -248,9 +221,9 @@ void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  		active[0] = l_ptr;  		goto exit;  	} -	pr_info("Old link <%s> becomes standby\n", active[0]->name); +	pr_debug("Old link <%s> becomes standby\n", active[0]->name);  	if (active[1] != active[0]) -		pr_info("Old link <%s> becomes standby\n", active[1]->name); +		pr_debug("Old link <%s> becomes standby\n", active[1]->name);  	active[0] = active[1] = l_ptr;  exit:  	/* Leave room for changeover header when returning 'mtu' to users: */ @@ -290,6 +263,7 @@ static void node_select_active_links(struct tipc_node *n_ptr)   */  void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  { +	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);  	struct tipc_link **active;  	n_ptr->working_links--; @@ -297,12 +271,12 @@ void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;  	if (!tipc_link_is_active(l_ptr)) { -		pr_info("Lost standby link <%s> on network plane %c\n", -			l_ptr->name, l_ptr->net_plane); +		pr_debug("Lost standby link <%s> on network plane %c\n", +			 l_ptr->name, l_ptr->net_plane);  		return;  	} -	pr_info("Lost link <%s> on network plane %c\n", -		l_ptr->name, l_ptr->net_plane); +	pr_debug("Lost link <%s> on network plane %c\n", +		 l_ptr->name, l_ptr->net_plane);  	active = &n_ptr->active_links[0];  	if (active[0] == l_ptr) @@ -324,7 +298,7 @@ void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  	}  	/* Loopback link went down? No fragmentation needed from now on. */ -	if (n_ptr->addr == tipc_own_addr) { +	if (n_ptr->addr == tn->own_addr) {  		n_ptr->act_mtus[0] = MAX_MSG_SIZE;  		n_ptr->act_mtus[1] = MAX_MSG_SIZE;  	} @@ -343,9 +317,6 @@ int tipc_node_is_up(struct tipc_node *n_ptr)  void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  {  	n_ptr->links[l_ptr->bearer_id] = l_ptr; -	spin_lock_bh(&node_list_lock); -	tipc_num_links++; -	spin_unlock_bh(&node_list_lock);  	n_ptr->link_cnt++;  } @@ -357,9 +328,6 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)  		if (l_ptr != n_ptr->links[i])  			continue;  		n_ptr->links[i] = NULL; -		spin_lock_bh(&node_list_lock); -		tipc_num_links--; -		spin_unlock_bh(&node_list_lock);  		n_ptr->link_cnt--;  	}  } @@ -368,17 +336,21 @@ static void node_established_contact(struct tipc_node *n_ptr)  {  	n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP;  	n_ptr->bclink.oos_state = 0; -	n_ptr->bclink.acked = tipc_bclink_get_last_sent(); -	tipc_bclink_add_node(n_ptr->addr); +	n_ptr->bclink.acked = tipc_bclink_get_last_sent(n_ptr->net); +	tipc_bclink_add_node(n_ptr->net, n_ptr->addr);  }  static void node_lost_contact(struct tipc_node *n_ptr)  {  	char addr_string[16]; -	u32 i; +	struct tipc_sock_conn *conn, *safe; +	struct list_head *conns = &n_ptr->conn_sks; +	struct sk_buff *skb; +	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id); +	uint i; -	pr_info("Lost contact with %s\n", -		tipc_addr_string_fill(addr_string, n_ptr->addr)); +	pr_debug("Lost contact with %s\n", +		 tipc_addr_string_fill(addr_string, n_ptr->addr));  	/* Flush broadcast link info associated with lost node */  	if (n_ptr->bclink.recv_permitted) { @@ -389,7 +361,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)  			n_ptr->bclink.reasm_buf = NULL;  		} -		tipc_bclink_remove_node(n_ptr->addr); +		tipc_bclink_remove_node(n_ptr->net, n_ptr->addr);  		tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);  		n_ptr->bclink.recv_permitted = false; @@ -403,126 +375,33 @@ 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); -	} - -	n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN; - -	/* Notify subscribers and prevent re-contact with node until -	 * cleanup is done. -	 */ -	n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN | -			       TIPC_NOTIFY_NODE_DOWN; -} -struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) -{ -	u32 domain; -	struct sk_buff *buf; -	struct tipc_node *n_ptr; -	struct tipc_node_info node_info; -	u32 payload_size; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); -	if (!tipc_addr_domain_valid(domain)) -		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE -						   " (network address)"); - -	spin_lock_bh(&node_list_lock); -	if (!tipc_num_nodes) { -		spin_unlock_bh(&node_list_lock); -		return tipc_cfg_reply_none(); +		/* Link marked for deletion after failover? => do it now */ +		if (l_ptr->flags & LINK_STOPPED) +			tipc_link_delete(l_ptr);  	} -	/* For now, get space for all other nodes */ -	payload_size = TLV_SPACE(sizeof(node_info)) * tipc_num_nodes; -	if (payload_size > 32768u) { -		spin_unlock_bh(&node_list_lock); -		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -						   " (too many nodes)"); -	} -	spin_unlock_bh(&node_list_lock); - -	buf = tipc_cfg_reply_alloc(payload_size); -	if (!buf) -		return NULL; - -	/* Add TLVs for all nodes in scope */ -	rcu_read_lock(); -	list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { -		if (!tipc_in_scope(domain, n_ptr->addr)) -			continue; -		node_info.addr = htonl(n_ptr->addr); -		node_info.up = htonl(tipc_node_is_up(n_ptr)); -		tipc_cfg_append_tlv(buf, TIPC_TLV_NODE_INFO, -				    &node_info, sizeof(node_info)); -	} -	rcu_read_unlock(); -	return buf; -} - -struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) -{ -	u32 domain; -	struct sk_buff *buf; -	struct tipc_node *n_ptr; -	struct tipc_link_info link_info; -	u32 payload_size; - -	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) -		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); - -	domain = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); -	if (!tipc_addr_domain_valid(domain)) -		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE -						   " (network address)"); - -	if (!tipc_own_addr) -		return tipc_cfg_reply_none(); - -	spin_lock_bh(&node_list_lock); -	/* Get space for all unicast links + broadcast link */ -	payload_size = TLV_SPACE((sizeof(link_info)) * (tipc_num_links + 1)); -	if (payload_size > 32768u) { -		spin_unlock_bh(&node_list_lock); -		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED -						   " (too many links)"); -	} -	spin_unlock_bh(&node_list_lock); - -	buf = tipc_cfg_reply_alloc(payload_size); -	if (!buf) -		return NULL; +	n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN; -	/* Add TLV for broadcast link */ -	link_info.dest = htonl(tipc_cluster_mask(tipc_own_addr)); -	link_info.up = htonl(1); -	strlcpy(link_info.str, tipc_bclink_name, TIPC_MAX_LINK_NAME); -	tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, &link_info, sizeof(link_info)); +	/* Prevent re-contact with node until cleanup is done */ +	n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN; -	/* Add TLVs for any other links in scope */ -	rcu_read_lock(); -	list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) { -		u32 i; +	/* Notify publications from this node */ +	n_ptr->action_flags |= TIPC_NOTIFY_NODE_DOWN; -		if (!tipc_in_scope(domain, n_ptr->addr)) -			continue; -		tipc_node_lock(n_ptr); -		for (i = 0; i < MAX_BEARERS; i++) { -			if (!n_ptr->links[i]) -				continue; -			link_info.dest = htonl(n_ptr->addr); -			link_info.up = htonl(tipc_link_is_up(n_ptr->links[i])); -			strcpy(link_info.str, n_ptr->links[i]->name); -			tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, -					    &link_info, sizeof(link_info)); +	/* Notify sockets connected to node */ +	list_for_each_entry_safe(conn, safe, conns, list) { +		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, +				      SHORT_H_SIZE, 0, tn->own_addr, +				      conn->peer_node, conn->port, +				      conn->peer_port, TIPC_ERR_NO_NODE); +		if (likely(skb)) { +			skb_queue_tail(n_ptr->inputq, skb); +			n_ptr->action_flags |= TIPC_MSG_EVT;  		} -		tipc_node_unlock(n_ptr); +		list_del(&conn->list); +		kfree(conn);  	} -	rcu_read_unlock(); -	return buf;  }  /** @@ -534,10 +413,11 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)   *   * Returns 0 on success   */ -int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) +int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr, +			   char *linkname, size_t len)  {  	struct tipc_link *link; -	struct tipc_node *node = tipc_node_find(addr); +	struct tipc_node *node = tipc_node_find(net, addr);  	if ((bearer_id >= MAX_BEARERS) || !node)  		return -EINVAL; @@ -554,58 +434,60 @@ int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len)  void tipc_node_unlock(struct tipc_node *node)  { -	LIST_HEAD(nsub_list); -	LIST_HEAD(conn_sks); -	struct sk_buff_head waiting_sks; +	struct net *net = node->net;  	u32 addr = 0; -	int flags = node->action_flags; +	u32 flags = node->action_flags;  	u32 link_id = 0; +	struct list_head *publ_list; +	struct sk_buff_head *inputq = node->inputq; +	struct sk_buff_head *namedq; -	if (likely(!flags)) { +	if (likely(!flags || (flags == TIPC_MSG_EVT))) { +		node->action_flags = 0;  		spin_unlock_bh(&node->lock); +		if (flags == TIPC_MSG_EVT) +			tipc_sk_rcv(net, inputq);  		return;  	}  	addr = node->addr;  	link_id = node->link_id; -	__skb_queue_head_init(&waiting_sks); +	namedq = node->namedq; +	publ_list = &node->publ_list; -	if (flags & TIPC_WAKEUP_USERS) -		skb_queue_splice_init(&node->waiting_sks, &waiting_sks); - -	if (flags & TIPC_NOTIFY_NODE_DOWN) { -		list_replace_init(&node->publ_list, &nsub_list); -		list_replace_init(&node->conn_sks, &conn_sks); -	} -	node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN | -				TIPC_NOTIFY_NODE_UP | TIPC_NOTIFY_LINK_UP | -				TIPC_NOTIFY_LINK_DOWN | -				TIPC_WAKEUP_BCAST_USERS); +	node->action_flags &= ~(TIPC_MSG_EVT | +				TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP | +				TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP | +				TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT | +				TIPC_NAMED_MSG_EVT);  	spin_unlock_bh(&node->lock); -	while (!skb_queue_empty(&waiting_sks)) -		tipc_sk_rcv(__skb_dequeue(&waiting_sks)); - -	if (!list_empty(&conn_sks)) -		tipc_node_abort_sock_conns(&conn_sks); - -	if (!list_empty(&nsub_list)) -		tipc_publ_notify(&nsub_list, addr); +	if (flags & TIPC_NOTIFY_NODE_DOWN) +		tipc_publ_notify(net, publ_list, addr);  	if (flags & TIPC_WAKEUP_BCAST_USERS) -		tipc_bclink_wakeup_users(); +		tipc_bclink_wakeup_users(net);  	if (flags & TIPC_NOTIFY_NODE_UP) -		tipc_named_node_up(addr); +		tipc_named_node_up(net, addr);  	if (flags & TIPC_NOTIFY_LINK_UP) -		tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, +		tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,  				     TIPC_NODE_SCOPE, link_id, addr);  	if (flags & TIPC_NOTIFY_LINK_DOWN) -		tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, +		tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,  				      link_id, addr); + +	if (flags & TIPC_MSG_EVT) +		tipc_sk_rcv(net, inputq); + +	if (flags & TIPC_NAMED_MSG_EVT) +		tipc_named_rcv(net, namedq); + +	if (flags & TIPC_BCAST_MSG_EVT) +		tipc_bclink_input(net);  }  /* Caller should hold node lock for the passed node */ @@ -614,7 +496,7 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)  	void *hdr;  	struct nlattr *attrs; -	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, +	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,  			  NLM_F_MULTI, TIPC_NL_NODE_GET);  	if (!hdr)  		return -EMSGSIZE; @@ -645,6 +527,8 @@ msg_full:  int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)  {  	int err; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	int done = cb->args[0];  	int last_addr = cb->args[1];  	struct tipc_node *node; @@ -659,7 +543,7 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)  	rcu_read_lock(); -	if (last_addr && !tipc_node_find(last_addr)) { +	if (last_addr && !tipc_node_find(net, last_addr)) {  		rcu_read_unlock();  		/* We never set seq or call nl_dump_check_consistent() this  		 * means that setting prev_seq here will cause the consistence @@ -671,7 +555,7 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)  		return -EPIPE;  	} -	list_for_each_entry_rcu(node, &tipc_node_list, list) { +	list_for_each_entry_rcu(node, &tn->node_list, list) {  		if (last_addr) {  			if (node->addr == last_addr)  				last_addr = 0; diff --git a/net/tipc/node.h b/net/tipc/node.h index cbe0e950f1cc..3d18c66b7f78 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -1,7 +1,7 @@  /*   * net/tipc/node.h: Include file for TIPC node management routines   * - * Copyright (c) 2000-2006, 2014, Ericsson AB + * Copyright (c) 2000-2006, 2014-2015, Ericsson AB   * Copyright (c) 2005, 2010-2014, Wind River Systems   * All rights reserved.   * @@ -42,10 +42,10 @@  #include "bearer.h"  #include "msg.h" -/* - * Out-of-range value for node signature - */ -#define INVALID_NODE_SIG 0x10000 +/* Out-of-range value for node signature */ +#define INVALID_NODE_SIG	0x10000 + +#define NODE_HTABLE_SIZE	512  /* Flags used to take different actions according to flag type   * TIPC_WAIT_PEER_LINKS_DOWN: wait to see that peer's links are down @@ -55,14 +55,16 @@   * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type   */  enum { +	TIPC_MSG_EVT                    = 1,  	TIPC_WAIT_PEER_LINKS_DOWN	= (1 << 1),  	TIPC_WAIT_OWN_LINKS_DOWN	= (1 << 2),  	TIPC_NOTIFY_NODE_DOWN		= (1 << 3),  	TIPC_NOTIFY_NODE_UP		= (1 << 4), -	TIPC_WAKEUP_USERS		= (1 << 5), -	TIPC_WAKEUP_BCAST_USERS		= (1 << 6), -	TIPC_NOTIFY_LINK_UP		= (1 << 7), -	TIPC_NOTIFY_LINK_DOWN		= (1 << 8) +	TIPC_WAKEUP_BCAST_USERS		= (1 << 5), +	TIPC_NOTIFY_LINK_UP		= (1 << 6), +	TIPC_NOTIFY_LINK_DOWN		= (1 << 7), +	TIPC_NAMED_MSG_EVT		= (1 << 8), +	TIPC_BCAST_MSG_EVT		= (1 << 9)  };  /** @@ -73,6 +75,7 @@ enum {   * @oos_state: state tracker for handling OOS b'cast messages   * @deferred_queue: deferred queue saved OOS b'cast message received from node   * @reasm_buf: broadcast reassembly queue head from node + * @inputq_map: bitmap indicating which inqueues should be kicked   * @recv_permitted: true if node is allowed to receive b'cast messages   */  struct tipc_node_bclink { @@ -83,6 +86,7 @@ struct tipc_node_bclink {  	u32 deferred_size;  	struct sk_buff_head deferred_queue;  	struct sk_buff *reasm_buf; +	int inputq_map;  	bool recv_permitted;  }; @@ -90,7 +94,11 @@ struct tipc_node_bclink {   * struct tipc_node - TIPC node structure   * @addr: network address of node   * @lock: spinlock 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 + * @curr_link: the link holding the node lock, if any   * @active_links: pointers to active links to node   * @links: pointers to all links to node   * @action_flags: bit mask of different types of node actions @@ -106,11 +114,14 @@ struct tipc_node_bclink {  struct tipc_node {  	u32 addr;  	spinlock_t lock; +	struct net *net;  	struct hlist_node hash; +	struct sk_buff_head *inputq; +	struct sk_buff_head *namedq;  	struct tipc_link *active_links[2];  	u32 act_mtus[2];  	struct tipc_link *links[MAX_BEARERS]; -	unsigned int action_flags; +	int action_flags;  	struct tipc_node_bclink bclink;  	struct list_head list;  	int link_cnt; @@ -118,28 +129,24 @@ struct tipc_node {  	u32 signature;  	u32 link_id;  	struct list_head publ_list; -	struct sk_buff_head waiting_sks;  	struct list_head conn_sks;  	struct rcu_head rcu;  }; -extern struct list_head tipc_node_list; - -struct tipc_node *tipc_node_find(u32 addr); -struct tipc_node *tipc_node_create(u32 addr); -void tipc_node_stop(void); +struct tipc_node *tipc_node_find(struct net *net, u32 addr); +struct tipc_node *tipc_node_create(struct net *net, u32 addr); +void tipc_node_stop(struct net *net);  void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);  void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);  void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr);  void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr);  int tipc_node_active_links(struct tipc_node *n_ptr);  int tipc_node_is_up(struct tipc_node *n_ptr); -struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space); -struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space); -int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len); +int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 node, +			   char *linkname, size_t len);  void tipc_node_unlock(struct tipc_node *node); -int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port); -void tipc_node_remove_conn(u32 dnode, u32 port); +int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port); +void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port);  int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb); @@ -154,12 +161,12 @@ static inline bool tipc_node_blocked(struct tipc_node *node)  		TIPC_NOTIFY_NODE_DOWN | TIPC_WAIT_OWN_LINKS_DOWN));  } -static inline uint tipc_node_get_mtu(u32 addr, u32 selector) +static inline uint tipc_node_get_mtu(struct net *net, u32 addr, u32 selector)  {  	struct tipc_node *node;  	u32 mtu; -	node = tipc_node_find(addr); +	node = tipc_node_find(net, addr);  	if (likely(node))  		mtu = node->act_mtus[selector & 1]; diff --git a/net/tipc/server.c b/net/tipc/server.c index a538a02f869b..eadd4ed45905 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c @@ -35,6 +35,7 @@  #include "server.h"  #include "core.h" +#include "socket.h"  #include <net/sock.h>  /* Number of messages to send before rescheduling */ @@ -255,7 +256,8 @@ static int tipc_receive_from_sock(struct tipc_conn *con)  		goto out_close;  	} -	s->tipc_conn_recvmsg(con->conid, &addr, con->usr_data, buf, ret); +	s->tipc_conn_recvmsg(sock_net(con->sock->sk), con->conid, &addr, +			     con->usr_data, buf, ret);  	kmem_cache_free(s->rcvbuf_cache, buf); @@ -307,7 +309,7 @@ static struct socket *tipc_create_listen_sock(struct tipc_conn *con)  	struct socket *sock = NULL;  	int ret; -	ret = tipc_sock_create_local(s->type, &sock); +	ret = tipc_sock_create_local(s->net, s->type, &sock);  	if (ret < 0)  		return NULL;  	ret = kernel_setsockopt(sock, SOL_TIPC, TIPC_IMPORTANCE, diff --git a/net/tipc/server.h b/net/tipc/server.h index be817b0b547e..9015faedb1b0 100644 --- a/net/tipc/server.h +++ b/net/tipc/server.h @@ -36,7 +36,9 @@  #ifndef _TIPC_SERVER_H  #define _TIPC_SERVER_H -#include "core.h" +#include <linux/idr.h> +#include <linux/tipc.h> +#include <net/net_namespace.h>  #define TIPC_SERVER_NAME_LEN	32 @@ -45,6 +47,7 @@   * @conn_idr: identifier set of connection   * @idr_lock: protect the connection identifier set   * @idr_in_use: amount of allocated identifier entry + * @net: network namspace instance   * @rcvbuf_cache: memory cache of server receive buffer   * @rcv_wq: receive workqueue   * @send_wq: send workqueue @@ -61,16 +64,18 @@ struct tipc_server {  	struct idr conn_idr;  	spinlock_t idr_lock;  	int idr_in_use; +	struct net *net;  	struct kmem_cache *rcvbuf_cache;  	struct workqueue_struct *rcv_wq;  	struct workqueue_struct *send_wq;  	int max_rcvbuf_size; -	void *(*tipc_conn_new) (int conid); -	void (*tipc_conn_shutdown) (int conid, void *usr_data); -	void (*tipc_conn_recvmsg) (int conid, struct sockaddr_tipc *addr, -				   void *usr_data, void *buf, size_t len); +	void *(*tipc_conn_new)(int conid); +	void (*tipc_conn_shutdown)(int conid, void *usr_data); +	void (*tipc_conn_recvmsg)(struct net *net, int conid, +				  struct sockaddr_tipc *addr, void *usr_data, +				  void *buf, size_t len);  	struct sockaddr_tipc *saddr; -	const char name[TIPC_SERVER_NAME_LEN]; +	char name[TIPC_SERVER_NAME_LEN];  	int imp;  	int type;  }; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 4731cad99d1c..f73e975af80b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1,7 +1,7 @@  /*   * net/tipc/socket.c: TIPC socket API   * - * Copyright (c) 2001-2007, 2012-2014, Ericsson AB + * Copyright (c) 2001-2007, 2012-2015, Ericsson AB   * Copyright (c) 2004-2008, 2010-2013, Wind River Systems   * All rights reserved.   * @@ -34,22 +34,25 @@   * POSSIBILITY OF SUCH DAMAGE.   */ +#include <linux/rhashtable.h> +#include <linux/jhash.h>  #include "core.h"  #include "name_table.h"  #include "node.h"  #include "link.h" -#include <linux/export.h> -#include "config.h" +#include "name_distr.h"  #include "socket.h" -#define SS_LISTENING	-1	/* socket is listening */ -#define SS_READY	-2	/* socket is connectionless */ +#define SS_LISTENING		-1	/* socket is listening */ +#define SS_READY		-2	/* socket is connectionless */ -#define CONN_TIMEOUT_DEFAULT  8000	/* default connect timeout = 8s */ -#define CONN_PROBING_INTERVAL 3600000	/* [ms] => 1 h */ -#define TIPC_FWD_MSG	      1 -#define TIPC_CONN_OK          0 -#define TIPC_CONN_PROBING     1 +#define CONN_TIMEOUT_DEFAULT	8000	/* default connect timeout = 8s */ +#define CONN_PROBING_INTERVAL	msecs_to_jiffies(3600000)  /* [ms] => 1 h */ +#define TIPC_FWD_MSG		1 +#define TIPC_CONN_OK		0 +#define TIPC_CONN_PROBING	1 +#define TIPC_MAX_PORT		0xffffffff +#define TIPC_MIN_PORT		1  /**   * struct tipc_sock - TIPC socket structure @@ -59,21 +62,20 @@   * @conn_instance: TIPC instance used when connection was established   * @published: non-zero if port has one or more associated names   * @max_pkt: maximum packet size "hint" used when building messages sent by port - * @ref: unique reference to port in TIPC object registry + * @portid: unique port identity in TIPC socket hash table   * @phdr: preformatted message header used when sending messages   * @port_list: adjacent ports in TIPC's global list of ports   * @publications: list of publications for port   * @pub_count: total # of publications port has made during its lifetime   * @probing_state: - * @probing_interval: - * @timer: - * @port: port - interacts with 'sk' and with the rest of the TIPC stack - * @peer_name: the peer of the connection, if any + * @probing_intv:   * @conn_timeout: the time we can wait for an unresponded setup request   * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue   * @link_cong: non-zero if owner must sleep because of link congestion   * @sent_unacked: # messages sent by socket, and not yet acked by peer   * @rcv_unacked: # messages read by user, but not yet acked back to peer + * @node: hash table node + * @rcu: rcu struct for tipc_sock   */  struct tipc_sock {  	struct sock sk; @@ -82,19 +84,20 @@ struct tipc_sock {  	u32 conn_instance;  	int published;  	u32 max_pkt; -	u32 ref; +	u32 portid;  	struct tipc_msg phdr;  	struct list_head sock_list;  	struct list_head publications;  	u32 pub_count;  	u32 probing_state; -	u32 probing_interval; -	struct timer_list timer; +	unsigned long probing_intv;  	uint conn_timeout;  	atomic_t dupl_rcvcnt;  	bool link_cong;  	uint sent_unacked;  	uint rcv_unacked; +	struct rhash_head node; +	struct rcu_head rcu;  };  static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb); @@ -103,16 +106,14 @@ static void tipc_write_space(struct sock *sk);  static int tipc_release(struct socket *sock);  static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags);  static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p); -static void tipc_sk_timeout(unsigned long ref); +static void tipc_sk_timeout(unsigned long data);  static int tipc_sk_publish(struct tipc_sock *tsk, uint scope,  			   struct tipc_name_seq const *seq);  static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope,  			    struct tipc_name_seq const *seq); -static u32 tipc_sk_ref_acquire(struct tipc_sock *tsk); -static void tipc_sk_ref_discard(u32 ref); -static struct tipc_sock *tipc_sk_get(u32 ref); -static struct tipc_sock *tipc_sk_get_next(u32 *ref); -static void tipc_sk_put(struct tipc_sock *tsk); +static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid); +static int tipc_sk_insert(struct tipc_sock *tsk); +static void tipc_sk_remove(struct tipc_sock *tsk);  static const struct proto_ops packet_ops;  static const struct proto_ops stream_ops; @@ -174,6 +175,11 @@ static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = {   *   - port reference   */ +static u32 tsk_own_node(struct tipc_sock *tsk) +{ +	return msg_prevnode(&tsk->phdr); +} +  static u32 tsk_peer_node(struct tipc_sock *tsk)  {  	return msg_destnode(&tsk->phdr); @@ -246,10 +252,11 @@ static void tsk_rej_rx_queue(struct sock *sk)  {  	struct sk_buff *skb;  	u32 dnode; +	u32 own_node = tsk_own_node(tipc_sk(sk));  	while ((skb = __skb_dequeue(&sk->sk_receive_queue))) { -		if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) -			tipc_link_xmit_skb(skb, dnode, 0); +		if (tipc_msg_reverse(own_node, skb, &dnode, TIPC_ERR_NO_PORT)) +			tipc_link_xmit_skb(sock_net(sk), skb, dnode, 0);  	}  } @@ -260,6 +267,7 @@ static void tsk_rej_rx_queue(struct sock *sk)   */  static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)  { +	struct tipc_net *tn = net_generic(sock_net(&tsk->sk), tipc_net_id);  	u32 peer_port = tsk_peer_port(tsk);  	u32 orig_node;  	u32 peer_node; @@ -276,10 +284,10 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)  	if (likely(orig_node == peer_node))  		return true; -	if (!orig_node && (peer_node == tipc_own_addr)) +	if (!orig_node && (peer_node == tn->own_addr))  		return true; -	if (!peer_node && (orig_node == tipc_own_addr)) +	if (!peer_node && (orig_node == tn->own_addr))  		return true;  	return false; @@ -300,12 +308,12 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)  static int tipc_sk_create(struct net *net, struct socket *sock,  			  int protocol, int kern)  { +	struct tipc_net *tn;  	const struct proto_ops *ops;  	socket_state state;  	struct sock *sk;  	struct tipc_sock *tsk;  	struct tipc_msg *msg; -	u32 ref;  	/* Validate arguments */  	if (unlikely(protocol != 0)) @@ -339,24 +347,23 @@ static int tipc_sk_create(struct net *net, struct socket *sock,  		return -ENOMEM;  	tsk = tipc_sk(sk); -	ref = tipc_sk_ref_acquire(tsk); -	if (!ref) { -		pr_warn("Socket create failed; reference table exhausted\n"); -		return -ENOMEM; -	}  	tsk->max_pkt = MAX_PKT_DEFAULT; -	tsk->ref = ref;  	INIT_LIST_HEAD(&tsk->publications);  	msg = &tsk->phdr; -	tipc_msg_init(msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, +	tn = net_generic(sock_net(sk), tipc_net_id); +	tipc_msg_init(tn->own_addr, msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG,  		      NAMED_H_SIZE, 0); -	msg_set_origport(msg, ref);  	/* Finish initializing socket data structures */  	sock->ops = ops;  	sock->state = state;  	sock_init_data(sock, sk); -	k_init_timer(&tsk->timer, (Handler)tipc_sk_timeout, ref); +	if (tipc_sk_insert(tsk)) { +		pr_warn("Socket create failed; port numbrer exhausted\n"); +		return -EINVAL; +	} +	msg_set_origport(msg, tsk->portid); +	setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk);  	sk->sk_backlog_rcv = tipc_backlog_rcv;  	sk->sk_rcvbuf = sysctl_tipc_rmem[1];  	sk->sk_data_ready = tipc_data_ready; @@ -384,7 +391,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,   *   * Returns 0 on success, errno otherwise   */ -int tipc_sock_create_local(int type, struct socket **res) +int tipc_sock_create_local(struct net *net, int type, struct socket **res)  {  	int rc; @@ -393,7 +400,7 @@ int tipc_sock_create_local(int type, struct socket **res)  		pr_err("Failed to create kernel socket\n");  		return rc;  	} -	tipc_sk_create(&init_net, *res, 0, 1); +	tipc_sk_create(net, *res, 0, 1);  	return 0;  } @@ -442,6 +449,13 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,  	return ret;  } +static void tipc_sk_callback(struct rcu_head *head) +{ +	struct tipc_sock *tsk = container_of(head, struct tipc_sock, rcu); + +	sock_put(&tsk->sk); +} +  /**   * tipc_release - destroy a TIPC socket   * @sock: socket to destroy @@ -461,9 +475,10 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,  static int tipc_release(struct socket *sock)  {  	struct sock *sk = sock->sk; +	struct net *net;  	struct tipc_sock *tsk;  	struct sk_buff *skb; -	u32 dnode; +	u32 dnode, probing_state;  	/*  	 * Exit if socket isn't fully initialized (occurs when a failed accept() @@ -472,6 +487,7 @@ static int tipc_release(struct socket *sock)  	if (sk == NULL)  		return 0; +	net = sock_net(sk);  	tsk = tipc_sk(sk);  	lock_sock(sk); @@ -491,26 +507,29 @@ static int tipc_release(struct socket *sock)  			    (sock->state == SS_CONNECTED)) {  				sock->state = SS_DISCONNECTING;  				tsk->connected = 0; -				tipc_node_remove_conn(dnode, tsk->ref); +				tipc_node_remove_conn(net, dnode, tsk->portid);  			} -			if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) -				tipc_link_xmit_skb(skb, dnode, 0); +			if (tipc_msg_reverse(tsk_own_node(tsk), skb, &dnode, +					     TIPC_ERR_NO_PORT)) +				tipc_link_xmit_skb(net, skb, dnode, 0);  		}  	}  	tipc_sk_withdraw(tsk, 0, NULL); -	tipc_sk_ref_discard(tsk->ref); -	k_cancel_timer(&tsk->timer); +	probing_state = tsk->probing_state; +	if (del_timer_sync(&sk->sk_timer) && +	    probing_state != TIPC_CONN_PROBING) +		sock_put(sk); +	tipc_sk_remove(tsk);  	if (tsk->connected) { -		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, -				      SHORT_H_SIZE, 0, dnode, tipc_own_addr, -				      tsk_peer_port(tsk), -				      tsk->ref, TIPC_ERR_NO_PORT); +		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, +				      TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, +				      tsk_own_node(tsk), tsk_peer_port(tsk), +				      tsk->portid, TIPC_ERR_NO_PORT);  		if (skb) -			tipc_link_xmit_skb(skb, dnode, tsk->ref); -		tipc_node_remove_conn(dnode, tsk->ref); +			tipc_link_xmit_skb(net, skb, dnode, tsk->portid); +		tipc_node_remove_conn(net, dnode, tsk->portid);  	} -	k_term_timer(&tsk->timer);  	/* Discard any remaining (connection-based) messages in receive queue */  	__skb_queue_purge(&sk->sk_receive_queue); @@ -518,7 +537,8 @@ static int tipc_release(struct socket *sock)  	/* Reject any messages that accumulated in backlog queue */  	sock->state = SS_DISCONNECTING;  	release_sock(sk); -	sock_put(sk); + +	call_rcu(&tsk->rcu, tipc_sk_callback);  	sock->sk = NULL;  	return 0; @@ -602,6 +622,7 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,  {  	struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;  	struct tipc_sock *tsk = tipc_sk(sock->sk); +	struct tipc_net *tn = net_generic(sock_net(sock->sk), tipc_net_id);  	memset(addr, 0, sizeof(*addr));  	if (peer) { @@ -611,8 +632,8 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,  		addr->addr.id.ref = tsk_peer_port(tsk);  		addr->addr.id.node = tsk_peer_node(tsk);  	} else { -		addr->addr.id.ref = tsk->ref; -		addr->addr.id.node = tipc_own_addr; +		addr->addr.id.ref = tsk->portid; +		addr->addr.id.node = tn->own_addr;  	}  	*uaddr_len = sizeof(*addr); @@ -711,8 +732,11 @@ static int tipc_sendmcast(struct  socket *sock, struct tipc_name_seq *seq,  			  struct msghdr *msg, size_t dsz, long timeo)  {  	struct sock *sk = sock->sk; -	struct tipc_msg *mhdr = &tipc_sk(sk)->phdr; -	struct sk_buff_head head; +	struct tipc_sock *tsk = tipc_sk(sk); +	struct net *net = sock_net(sk); +	struct tipc_msg *mhdr = &tsk->phdr; +	struct sk_buff_head *pktchain = &sk->sk_write_queue; +	struct iov_iter save = msg->msg_iter;  	uint mtu;  	int rc; @@ -727,83 +751,97 @@ static int tipc_sendmcast(struct  socket *sock, struct tipc_name_seq *seq,  new_mtu:  	mtu = tipc_bclink_get_mtu(); -	__skb_queue_head_init(&head); -	rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &head); +	rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, pktchain);  	if (unlikely(rc < 0))  		return rc;  	do { -		rc = tipc_bclink_xmit(&head); +		rc = tipc_bclink_xmit(net, pktchain);  		if (likely(rc >= 0)) {  			rc = dsz;  			break;  		} -		if (rc == -EMSGSIZE) +		if (rc == -EMSGSIZE) { +			msg->msg_iter = save;  			goto new_mtu; +		}  		if (rc != -ELINKCONG)  			break;  		tipc_sk(sk)->link_cong = 1;  		rc = tipc_wait_for_sndmsg(sock, &timeo);  		if (rc) -			__skb_queue_purge(&head); +			__skb_queue_purge(pktchain);  	} while (!rc);  	return rc;  } -/* tipc_sk_mcast_rcv - Deliver multicast message to all destination sockets +/** + * tipc_sk_mcast_rcv - Deliver multicast messages to all destination sockets + * @arrvq: queue with arriving messages, to be cloned after destination lookup + * @inputq: queue with cloned messages, delivered to socket after dest lookup + * + * Multi-threaded: parallel calls with reference to same queues may occur   */ -void tipc_sk_mcast_rcv(struct sk_buff *buf) +void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, +		       struct sk_buff_head *inputq)  { -	struct tipc_msg *msg = buf_msg(buf); -	struct tipc_port_list dports = {0, NULL, }; -	struct tipc_port_list *item; -	struct sk_buff *b; -	uint i, last, dst = 0; +	struct tipc_msg *msg; +	struct tipc_plist dports; +	u32 portid;  	u32 scope = TIPC_CLUSTER_SCOPE; - -	if (in_own_node(msg_orignode(msg))) -		scope = TIPC_NODE_SCOPE; - -	/* Create destination port list: */ -	tipc_nametbl_mc_translate(msg_nametype(msg), -				  msg_namelower(msg), -				  msg_nameupper(msg), -				  scope, -				  &dports); -	last = dports.count; -	if (!last) { -		kfree_skb(buf); -		return; -	} - -	for (item = &dports; item; item = item->next) { -		for (i = 0; i < PLSIZE && ++dst <= last; i++) { -			b = (dst != last) ? skb_clone(buf, GFP_ATOMIC) : buf; -			if (!b) { -				pr_warn("Failed do clone mcast rcv buffer\n"); +	struct sk_buff_head tmpq; +	uint hsz; +	struct sk_buff *skb, *_skb; + +	__skb_queue_head_init(&tmpq); +	tipc_plist_init(&dports); + +	skb = tipc_skb_peek(arrvq, &inputq->lock); +	for (; skb; skb = tipc_skb_peek(arrvq, &inputq->lock)) { +		msg = buf_msg(skb); +		hsz = skb_headroom(skb) + msg_hdr_sz(msg); + +		if (in_own_node(net, msg_orignode(msg))) +			scope = TIPC_NODE_SCOPE; + +		/* Create destination port list and message clones: */ +		tipc_nametbl_mc_translate(net, +					  msg_nametype(msg), msg_namelower(msg), +					  msg_nameupper(msg), scope, &dports); +		portid = tipc_plist_pop(&dports); +		for (; portid; portid = tipc_plist_pop(&dports)) { +			_skb = __pskb_copy(skb, hsz, GFP_ATOMIC); +			if (_skb) { +				msg_set_destport(buf_msg(_skb), portid); +				__skb_queue_tail(&tmpq, _skb);  				continue;  			} -			msg_set_destport(msg, item->ports[i]); -			tipc_sk_rcv(b); +			pr_warn("Failed to clone mcast rcv buffer\n");  		} +		/* Append to inputq if not already done by other thread */ +		spin_lock_bh(&inputq->lock); +		if (skb_peek(arrvq) == skb) { +			skb_queue_splice_tail_init(&tmpq, inputq); +			kfree_skb(__skb_dequeue(arrvq)); +		} +		spin_unlock_bh(&inputq->lock); +		__skb_queue_purge(&tmpq); +		kfree_skb(skb);  	} -	tipc_port_list_free(&dports); +	tipc_sk_rcv(net, inputq);  }  /**   * tipc_sk_proto_rcv - receive a connection mng protocol message   * @tsk: receiving socket - * @dnode: node to send response message to, if any - * @buf: buffer containing protocol message - * Returns 0 (TIPC_OK) if message was consumed, 1 (TIPC_FWD_MSG) if - * (CONN_PROBE_REPLY) message should be forwarded. + * @skb: pointer to message buffer. Set to NULL if buffer is consumed.   */ -static int tipc_sk_proto_rcv(struct tipc_sock *tsk, u32 *dnode, -			     struct sk_buff *buf) +static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff **skb)  { -	struct tipc_msg *msg = buf_msg(buf); +	struct tipc_msg *msg = buf_msg(*skb);  	int conn_cong; - +	u32 dnode; +	u32 own_node = tsk_own_node(tsk);  	/* Ignore if connection cannot be validated: */  	if (!tsk_peer_msg(tsk, msg))  		goto exit; @@ -816,15 +854,15 @@ static int tipc_sk_proto_rcv(struct tipc_sock *tsk, u32 *dnode,  		if (conn_cong)  			tsk->sk.sk_write_space(&tsk->sk);  	} else if (msg_type(msg) == CONN_PROBE) { -		if (!tipc_msg_reverse(buf, dnode, TIPC_OK)) -			return TIPC_OK; -		msg_set_type(msg, CONN_PROBE_REPLY); -		return TIPC_FWD_MSG; +		if (tipc_msg_reverse(own_node, *skb, &dnode, TIPC_OK)) { +			msg_set_type(msg, CONN_PROBE_REPLY); +			return; +		}  	}  	/* Do nothing if msg_type() == CONN_PROBE_REPLY */  exit: -	kfree_skb(buf); -	return TIPC_OK; +	kfree_skb(*skb); +	*skb = NULL;  }  static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) @@ -872,11 +910,13 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,  	DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);  	struct sock *sk = sock->sk;  	struct tipc_sock *tsk = tipc_sk(sk); +	struct net *net = sock_net(sk);  	struct tipc_msg *mhdr = &tsk->phdr;  	u32 dnode, dport; -	struct sk_buff_head head; +	struct sk_buff_head *pktchain = &sk->sk_write_queue;  	struct sk_buff *skb;  	struct tipc_name_seq *seq = &dest->addr.nameseq; +	struct iov_iter save;  	u32 mtu;  	long timeo;  	int rc; @@ -929,7 +969,7 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,  		msg_set_nametype(mhdr, type);  		msg_set_nameinst(mhdr, inst);  		msg_set_lookup_scope(mhdr, tipc_addr_scope(domain)); -		dport = tipc_nametbl_translate(type, inst, &dnode); +		dport = tipc_nametbl_translate(net, type, inst, &dnode);  		msg_set_destnode(mhdr, dnode);  		msg_set_destport(mhdr, dport);  		if (unlikely(!dport && !dnode)) { @@ -945,31 +985,33 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,  		msg_set_hdr_sz(mhdr, BASIC_H_SIZE);  	} +	save = m->msg_iter;  new_mtu: -	mtu = tipc_node_get_mtu(dnode, tsk->ref); -	__skb_queue_head_init(&head); -	rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head); +	mtu = tipc_node_get_mtu(net, dnode, tsk->portid); +	rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, pktchain);  	if (rc < 0)  		goto exit;  	do { -		skb = skb_peek(&head); +		skb = skb_peek(pktchain);  		TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; -		rc = tipc_link_xmit(&head, dnode, tsk->ref); +		rc = tipc_link_xmit(net, pktchain, dnode, tsk->portid);  		if (likely(rc >= 0)) {  			if (sock->state != SS_READY)  				sock->state = SS_CONNECTING;  			rc = dsz;  			break;  		} -		if (rc == -EMSGSIZE) +		if (rc == -EMSGSIZE) { +			m->msg_iter = save;  			goto new_mtu; +		}  		if (rc != -ELINKCONG)  			break;  		tsk->link_cong = 1;  		rc = tipc_wait_for_sndmsg(sock, &timeo);  		if (rc) -			__skb_queue_purge(&head); +			__skb_queue_purge(pktchain);  	} while (!rc);  exit:  	if (iocb) @@ -1024,15 +1066,17 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,  			    struct msghdr *m, size_t dsz)  {  	struct sock *sk = sock->sk; +	struct net *net = sock_net(sk);  	struct tipc_sock *tsk = tipc_sk(sk);  	struct tipc_msg *mhdr = &tsk->phdr; -	struct sk_buff_head head; +	struct sk_buff_head *pktchain = &sk->sk_write_queue;  	DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); -	u32 ref = tsk->ref; +	u32 portid = tsk->portid;  	int rc = -EINVAL;  	long timeo;  	u32 dnode;  	uint mtu, send, sent = 0; +	struct iov_iter save;  	/* Handle implied connection establishment */  	if (unlikely(dest)) { @@ -1059,15 +1103,15 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,  	dnode = tsk_peer_node(tsk);  next: +	save = m->msg_iter;  	mtu = tsk->max_pkt;  	send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE); -	__skb_queue_head_init(&head); -	rc = tipc_msg_build(mhdr, m, sent, send, mtu, &head); +	rc = tipc_msg_build(mhdr, m, sent, send, mtu, pktchain);  	if (unlikely(rc < 0))  		goto exit;  	do {  		if (likely(!tsk_conn_cong(tsk))) { -			rc = tipc_link_xmit(&head, dnode, ref); +			rc = tipc_link_xmit(net, pktchain, dnode, portid);  			if (likely(!rc)) {  				tsk->sent_unacked++;  				sent += send; @@ -1076,7 +1120,9 @@ next:  				goto next;  			}  			if (rc == -EMSGSIZE) { -				tsk->max_pkt = tipc_node_get_mtu(dnode, ref); +				tsk->max_pkt = tipc_node_get_mtu(net, dnode, +								 portid); +				m->msg_iter = save;  				goto next;  			}  			if (rc != -ELINKCONG) @@ -1085,7 +1131,7 @@ next:  		}  		rc = tipc_wait_for_sndpkt(sock, &timeo);  		if (rc) -			__skb_queue_purge(&head); +			__skb_queue_purge(pktchain);  	} while (!rc);  exit:  	if (iocb) @@ -1118,6 +1164,8 @@ static int tipc_send_packet(struct kiocb *iocb, struct socket *sock,  static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port,  				u32 peer_node)  { +	struct sock *sk = &tsk->sk; +	struct net *net = sock_net(sk);  	struct tipc_msg *msg = &tsk->phdr;  	msg_set_destnode(msg, peer_node); @@ -1126,12 +1174,12 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port,  	msg_set_lookup_scope(msg, 0);  	msg_set_hdr_sz(msg, SHORT_H_SIZE); -	tsk->probing_interval = CONN_PROBING_INTERVAL; +	tsk->probing_intv = CONN_PROBING_INTERVAL;  	tsk->probing_state = TIPC_CONN_OK;  	tsk->connected = 1; -	k_start_timer(&tsk->timer, tsk->probing_interval); -	tipc_node_add_conn(peer_node, tsk->ref, peer_port); -	tsk->max_pkt = tipc_node_get_mtu(peer_node, tsk->ref); +	sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv); +	tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); +	tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid);  }  /** @@ -1230,6 +1278,7 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,  static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack)  { +	struct net *net = sock_net(&tsk->sk);  	struct sk_buff *skb = NULL;  	struct tipc_msg *msg;  	u32 peer_port = tsk_peer_port(tsk); @@ -1237,13 +1286,14 @@ static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack)  	if (!tsk->connected)  		return; -	skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, -			      tipc_own_addr, peer_port, tsk->ref, TIPC_OK); +	skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, +			      dnode, tsk_own_node(tsk), peer_port, +			      tsk->portid, TIPC_OK);  	if (!skb)  		return;  	msg = buf_msg(skb);  	msg_set_msgcnt(msg, ack); -	tipc_link_xmit_skb(skb, dnode, msg_link_selector(msg)); +	tipc_link_xmit_skb(net, skb, dnode, msg_link_selector(msg));  }  static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) @@ -1529,15 +1579,16 @@ static void tipc_data_ready(struct sock *sk)  /**   * filter_connect - Handle all incoming messages for a connection-based socket   * @tsk: TIPC socket - * @msg: message + * @skb: pointer to message buffer. Set to NULL if buffer is consumed   *   * Returns 0 (TIPC_OK) if everything ok, -TIPC_ERR_NO_PORT otherwise   */ -static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) +static int filter_connect(struct tipc_sock *tsk, struct sk_buff **skb)  {  	struct sock *sk = &tsk->sk; +	struct net *net = sock_net(sk);  	struct socket *sock = sk->sk_socket; -	struct tipc_msg *msg = buf_msg(*buf); +	struct tipc_msg *msg = buf_msg(*skb);  	int retval = -TIPC_ERR_NO_PORT;  	if (msg_mcast(msg)) @@ -1551,8 +1602,8 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)  				sock->state = SS_DISCONNECTING;  				tsk->connected = 0;  				/* let timer expire on it's own */ -				tipc_node_remove_conn(tsk_peer_node(tsk), -						      tsk->ref); +				tipc_node_remove_conn(net, tsk_peer_node(tsk), +						      tsk->portid);  			}  			retval = TIPC_OK;  		} @@ -1587,8 +1638,8 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)  		 * connect() routine if sleeping.  		 */  		if (msg_data_sz(msg) == 0) { -			kfree_skb(*buf); -			*buf = NULL; +			kfree_skb(*skb); +			*skb = NULL;  			if (waitqueue_active(sk_sleep(sk)))  				wake_up_interruptible(sk_sleep(sk));  		} @@ -1640,32 +1691,33 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)  /**   * filter_rcv - validate incoming message   * @sk: socket - * @buf: message + * @skb: pointer to message. Set to NULL if buffer is consumed.   *   * Enqueues message on receive queue if acceptable; optionally handles   * disconnect indication for a connected socket.   * - * Called with socket lock already taken; port lock may also be taken. + * Called with socket lock already taken   * - * Returns 0 (TIPC_OK) if message was consumed, -TIPC error code if message - * to be rejected, 1 (TIPC_FWD_MSG) if (CONN_MANAGER) message to be forwarded + * Returns 0 (TIPC_OK) if message was ok, -TIPC error code if rejected   */ -static int filter_rcv(struct sock *sk, struct sk_buff *buf) +static int filter_rcv(struct sock *sk, struct sk_buff **skb)  {  	struct socket *sock = sk->sk_socket;  	struct tipc_sock *tsk = tipc_sk(sk); -	struct tipc_msg *msg = buf_msg(buf); -	unsigned int limit = rcvbuf_limit(sk, buf); -	u32 onode; +	struct tipc_msg *msg = buf_msg(*skb); +	unsigned int limit = rcvbuf_limit(sk, *skb);  	int rc = TIPC_OK; -	if (unlikely(msg_user(msg) == CONN_MANAGER)) -		return tipc_sk_proto_rcv(tsk, &onode, buf); +	if (unlikely(msg_user(msg) == CONN_MANAGER)) { +		tipc_sk_proto_rcv(tsk, skb); +		return TIPC_OK; +	}  	if (unlikely(msg_user(msg) == SOCK_WAKEUP)) { -		kfree_skb(buf); +		kfree_skb(*skb);  		tsk->link_cong = 0;  		sk->sk_write_space(sk); +		*skb = NULL;  		return TIPC_OK;  	} @@ -1677,21 +1729,22 @@ static int filter_rcv(struct sock *sk, struct sk_buff *buf)  		if (msg_connected(msg))  			return -TIPC_ERR_NO_PORT;  	} else { -		rc = filter_connect(tsk, &buf); -		if (rc != TIPC_OK || buf == NULL) +		rc = filter_connect(tsk, skb); +		if (rc != TIPC_OK || !*skb)  			return rc;  	}  	/* Reject message if there isn't room to queue it */ -	if (sk_rmem_alloc_get(sk) + buf->truesize >= limit) +	if (sk_rmem_alloc_get(sk) + (*skb)->truesize >= limit)  		return -TIPC_ERR_OVERLOAD;  	/* Enqueue message */ -	TIPC_SKB_CB(buf)->handle = NULL; -	__skb_queue_tail(&sk->sk_receive_queue, buf); -	skb_set_owner_r(buf, sk); +	TIPC_SKB_CB(*skb)->handle = NULL; +	__skb_queue_tail(&sk->sk_receive_queue, *skb); +	skb_set_owner_r(*skb, sk);  	sk->sk_data_ready(sk); +	*skb = NULL;  	return TIPC_OK;  } @@ -1700,78 +1753,125 @@ static int filter_rcv(struct sock *sk, struct sk_buff *buf)   * @sk: socket   * @skb: message   * - * Caller must hold socket lock, but not port lock. + * Caller must hold socket lock   *   * Returns 0   */  static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb)  { -	int rc; -	u32 onode; +	int err; +	atomic_t *dcnt; +	u32 dnode;  	struct tipc_sock *tsk = tipc_sk(sk); +	struct net *net = sock_net(sk);  	uint truesize = skb->truesize; -	rc = filter_rcv(sk, skb); - -	if (likely(!rc)) { -		if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT) -			atomic_add(truesize, &tsk->dupl_rcvcnt); +	err = filter_rcv(sk, &skb); +	if (likely(!skb)) { +		dcnt = &tsk->dupl_rcvcnt; +		if (atomic_read(dcnt) < TIPC_CONN_OVERLOAD_LIMIT) +			atomic_add(truesize, dcnt);  		return 0;  	} +	if (!err || tipc_msg_reverse(tsk_own_node(tsk), skb, &dnode, -err)) +		tipc_link_xmit_skb(net, skb, dnode, tsk->portid); +	return 0; +} -	if ((rc < 0) && !tipc_msg_reverse(skb, &onode, -rc)) -		return 0; - -	tipc_link_xmit_skb(skb, onode, 0); +/** + * tipc_sk_enqueue - extract all buffers with destination 'dport' from + *                   inputq and try adding them to socket or backlog queue + * @inputq: list of incoming buffers with potentially different destinations + * @sk: socket where the buffers should be enqueued + * @dport: port number for the socket + * @_skb: returned buffer to be forwarded or rejected, if applicable + * + * Caller must hold socket lock + * + * Returns TIPC_OK if all buffers enqueued, otherwise -TIPC_ERR_OVERLOAD + * or -TIPC_ERR_NO_PORT + */ +static int tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, +			   u32 dport, struct sk_buff **_skb) +{ +	unsigned int lim; +	atomic_t *dcnt; +	int err; +	struct sk_buff *skb; +	unsigned long time_limit = jiffies + 2; -	return 0; +	while (skb_queue_len(inputq)) { +		if (unlikely(time_after_eq(jiffies, time_limit))) +			return TIPC_OK; +		skb = tipc_skb_dequeue(inputq, dport); +		if (unlikely(!skb)) +			return TIPC_OK; +		if (!sock_owned_by_user(sk)) { +			err = filter_rcv(sk, &skb); +			if (likely(!skb)) +				continue; +			*_skb = skb; +			return err; +		} +		dcnt = &tipc_sk(sk)->dupl_rcvcnt; +		if (sk->sk_backlog.len) +			atomic_set(dcnt, 0); +		lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); +		if (likely(!sk_add_backlog(sk, skb, lim))) +			continue; +		*_skb = skb; +		return -TIPC_ERR_OVERLOAD; +	} +	return TIPC_OK;  }  /** - * tipc_sk_rcv - handle incoming message - * @skb: buffer containing arriving message - * Consumes buffer - * Returns 0 if success, or errno: -EHOSTUNREACH + * tipc_sk_rcv - handle a chain of incoming buffers + * @inputq: buffer list containing the buffers + * Consumes all buffers in list until inputq is empty + * Note: may be called in multiple threads referring to the same queue + * Returns 0 if last buffer was accepted, otherwise -EHOSTUNREACH + * Only node local calls check the return value, sending single-buffer queues   */ -int tipc_sk_rcv(struct sk_buff *skb) +int tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)  { +	u32 dnode, dport = 0; +	int err = -TIPC_ERR_NO_PORT; +	struct sk_buff *skb;  	struct tipc_sock *tsk; +	struct tipc_net *tn;  	struct sock *sk; -	u32 dport = msg_destport(buf_msg(skb)); -	int rc = TIPC_OK; -	uint limit; -	u32 dnode; -	/* Validate destination and message */ -	tsk = tipc_sk_get(dport); -	if (unlikely(!tsk)) { -		rc = tipc_msg_eval(skb, &dnode); -		goto exit; +	while (skb_queue_len(inputq)) { +		skb = NULL; +		dport = tipc_skb_peek_port(inputq, dport); +		tsk = tipc_sk_lookup(net, dport); +		if (likely(tsk)) { +			sk = &tsk->sk; +			if (likely(spin_trylock_bh(&sk->sk_lock.slock))) { +				err = tipc_sk_enqueue(inputq, sk, dport, &skb); +				spin_unlock_bh(&sk->sk_lock.slock); +				dport = 0; +			} +			sock_put(sk); +		} else { +			skb = tipc_skb_dequeue(inputq, dport); +		} +		if (likely(!skb)) +			continue; +		if (tipc_msg_lookup_dest(net, skb, &dnode, &err)) +			goto xmit; +		if (!err) { +			dnode = msg_destnode(buf_msg(skb)); +			goto xmit; +		} +		tn = net_generic(net, tipc_net_id); +		if (!tipc_msg_reverse(tn->own_addr, skb, &dnode, -err)) +			continue; +xmit: +		tipc_link_xmit_skb(net, skb, dnode, dport);  	} -	sk = &tsk->sk; - -	/* Queue message */ -	spin_lock_bh(&sk->sk_lock.slock); - -	if (!sock_owned_by_user(sk)) { -		rc = filter_rcv(sk, skb); -	} else { -		if (sk->sk_backlog.len == 0) -			atomic_set(&tsk->dupl_rcvcnt, 0); -		limit = rcvbuf_limit(sk, skb) + atomic_read(&tsk->dupl_rcvcnt); -		if (sk_add_backlog(sk, skb, limit)) -			rc = -TIPC_ERR_OVERLOAD; -	} -	spin_unlock_bh(&sk->sk_lock.slock); -	tipc_sk_put(tsk); -	if (likely(!rc)) -		return 0; -exit: -	if ((rc < 0) && !tipc_msg_reverse(skb, &dnode, -rc)) -		return -EHOSTUNREACH; - -	tipc_link_xmit_skb(skb, dnode, 0); -	return (rc < 0) ? -EHOSTUNREACH : 0; +	return err ? -EHOSTUNREACH : 0;  }  static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) @@ -2027,6 +2127,7 @@ exit:  static int tipc_shutdown(struct socket *sock, int how)  {  	struct sock *sk = sock->sk; +	struct net *net = sock_net(sk);  	struct tipc_sock *tsk = tipc_sk(sk);  	struct sk_buff *skb;  	u32 dnode; @@ -2049,21 +2150,24 @@ restart:  				kfree_skb(skb);  				goto restart;  			} -			if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN)) -				tipc_link_xmit_skb(skb, dnode, tsk->ref); -			tipc_node_remove_conn(dnode, tsk->ref); +			if (tipc_msg_reverse(tsk_own_node(tsk), skb, &dnode, +					     TIPC_CONN_SHUTDOWN)) +				tipc_link_xmit_skb(net, skb, dnode, +						   tsk->portid); +			tipc_node_remove_conn(net, dnode, tsk->portid);  		} else {  			dnode = tsk_peer_node(tsk); +  			skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,  					      TIPC_CONN_MSG, SHORT_H_SIZE, -					      0, dnode, tipc_own_addr, +					      0, dnode, tsk_own_node(tsk),  					      tsk_peer_port(tsk), -					      tsk->ref, TIPC_CONN_SHUTDOWN); -			tipc_link_xmit_skb(skb, dnode, tsk->ref); +					      tsk->portid, TIPC_CONN_SHUTDOWN); +			tipc_link_xmit_skb(net, skb, dnode, tsk->portid);  		}  		tsk->connected = 0;  		sock->state = SS_DISCONNECTING; -		tipc_node_remove_conn(dnode, tsk->ref); +		tipc_node_remove_conn(net, dnode, tsk->portid);  		/* fall through */  	case SS_DISCONNECTING: @@ -2084,18 +2188,14 @@ restart:  	return res;  } -static void tipc_sk_timeout(unsigned long ref) +static void tipc_sk_timeout(unsigned long data)  { -	struct tipc_sock *tsk; -	struct sock *sk; +	struct tipc_sock *tsk = (struct tipc_sock *)data; +	struct sock *sk = &tsk->sk;  	struct sk_buff *skb = NULL;  	u32 peer_port, peer_node; +	u32 own_node = tsk_own_node(tsk); -	tsk = tipc_sk_get(ref); -	if (!tsk) -		return; - -	sk = &tsk->sk;  	bh_lock_sock(sk);  	if (!tsk->connected) {  		bh_unlock_sock(sk); @@ -2106,38 +2206,39 @@ static void tipc_sk_timeout(unsigned long ref)  	if (tsk->probing_state == TIPC_CONN_PROBING) {  		/* Previous probe not answered -> self abort */ -		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, -				      SHORT_H_SIZE, 0, tipc_own_addr, -				      peer_node, ref, peer_port, -				      TIPC_ERR_NO_PORT); +		skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, +				      TIPC_CONN_MSG, SHORT_H_SIZE, 0, +				      own_node, peer_node, tsk->portid, +				      peer_port, TIPC_ERR_NO_PORT);  	} else { -		skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, -				      0, peer_node, tipc_own_addr, -				      peer_port, ref, TIPC_OK); +		skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, +				      INT_H_SIZE, 0, peer_node, own_node, +				      peer_port, tsk->portid, TIPC_OK);  		tsk->probing_state = TIPC_CONN_PROBING; -		k_start_timer(&tsk->timer, tsk->probing_interval); +		sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv);  	}  	bh_unlock_sock(sk);  	if (skb) -		tipc_link_xmit_skb(skb, peer_node, ref); +		tipc_link_xmit_skb(sock_net(sk), skb, peer_node, tsk->portid);  exit: -	tipc_sk_put(tsk); +	sock_put(sk);  }  static int tipc_sk_publish(struct tipc_sock *tsk, uint scope,  			   struct tipc_name_seq const *seq)  { +	struct net *net = sock_net(&tsk->sk);  	struct publication *publ;  	u32 key;  	if (tsk->connected)  		return -EINVAL; -	key = tsk->ref + tsk->pub_count + 1; -	if (key == tsk->ref) +	key = tsk->portid + tsk->pub_count + 1; +	if (key == tsk->portid)  		return -EADDRINUSE; -	publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, -				    scope, tsk->ref, key); +	publ = tipc_nametbl_publish(net, seq->type, seq->lower, seq->upper, +				    scope, tsk->portid, key);  	if (unlikely(!publ))  		return -EINVAL; @@ -2150,6 +2251,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope,  static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope,  			    struct tipc_name_seq const *seq)  { +	struct net *net = sock_net(&tsk->sk);  	struct publication *publ;  	struct publication *safe;  	int rc = -EINVAL; @@ -2164,12 +2266,12 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope,  				continue;  			if (publ->upper != seq->upper)  				break; -			tipc_nametbl_withdraw(publ->type, publ->lower, +			tipc_nametbl_withdraw(net, publ->type, publ->lower,  					      publ->ref, publ->key);  			rc = 0;  			break;  		} -		tipc_nametbl_withdraw(publ->type, publ->lower, +		tipc_nametbl_withdraw(net, publ->type, publ->lower,  				      publ->ref, publ->key);  		rc = 0;  	} @@ -2178,336 +2280,105 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope,  	return rc;  } -static int tipc_sk_show(struct tipc_sock *tsk, char *buf, -			int len, int full_id) -{ -	struct publication *publ; -	int ret; - -	if (full_id) -		ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", -				    tipc_zone(tipc_own_addr), -				    tipc_cluster(tipc_own_addr), -				    tipc_node(tipc_own_addr), tsk->ref); -	else -		ret = tipc_snprintf(buf, len, "%-10u:", tsk->ref); - -	if (tsk->connected) { -		u32 dport = tsk_peer_port(tsk); -		u32 destnode = tsk_peer_node(tsk); - -		ret += tipc_snprintf(buf + ret, len - ret, -				     " connected to <%u.%u.%u:%u>", -				     tipc_zone(destnode), -				     tipc_cluster(destnode), -				     tipc_node(destnode), dport); -		if (tsk->conn_type != 0) -			ret += tipc_snprintf(buf + ret, len - ret, -					     " via {%u,%u}", tsk->conn_type, -					     tsk->conn_instance); -	} else if (tsk->published) { -		ret += tipc_snprintf(buf + ret, len - ret, " bound to"); -		list_for_each_entry(publ, &tsk->publications, pport_list) { -			if (publ->lower == publ->upper) -				ret += tipc_snprintf(buf + ret, len - ret, -						     " {%u,%u}", publ->type, -						     publ->lower); -			else -				ret += tipc_snprintf(buf + ret, len - ret, -						     " {%u,%u,%u}", publ->type, -						     publ->lower, publ->upper); -		} -	} -	ret += tipc_snprintf(buf + ret, len - ret, "\n"); -	return ret; -} - -struct sk_buff *tipc_sk_socks_show(void) -{ -	struct sk_buff *buf; -	struct tlv_desc *rep_tlv; -	char *pb; -	int pb_len; -	struct tipc_sock *tsk; -	int str_len = 0; -	u32 ref = 0; - -	buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); -	if (!buf) -		return NULL; -	rep_tlv = (struct tlv_desc *)buf->data; -	pb = TLV_DATA(rep_tlv); -	pb_len = ULTRA_STRING_MAX_LEN; - -	tsk = tipc_sk_get_next(&ref); -	for (; tsk; tsk = tipc_sk_get_next(&ref)) { -		lock_sock(&tsk->sk); -		str_len += tipc_sk_show(tsk, pb + str_len, -					pb_len - str_len, 0); -		release_sock(&tsk->sk); -		tipc_sk_put(tsk); -	} -	str_len += 1;	/* for "\0" */ -	skb_put(buf, TLV_SPACE(str_len)); -	TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); - -	return buf; -} -  /* tipc_sk_reinit: set non-zero address in all existing sockets   *                 when we go from standalone to network mode.   */ -void tipc_sk_reinit(void) +void tipc_sk_reinit(struct net *net)  { +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	const struct bucket_table *tbl; +	struct rhash_head *pos; +	struct tipc_sock *tsk;  	struct tipc_msg *msg; -	u32 ref = 0; -	struct tipc_sock *tsk = tipc_sk_get_next(&ref); +	int i; -	for (; tsk; tsk = tipc_sk_get_next(&ref)) { -		lock_sock(&tsk->sk); -		msg = &tsk->phdr; -		msg_set_prevnode(msg, tipc_own_addr); -		msg_set_orignode(msg, tipc_own_addr); -		release_sock(&tsk->sk); -		tipc_sk_put(tsk); +	rcu_read_lock(); +	tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); +	for (i = 0; i < tbl->size; i++) { +		rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { +			spin_lock_bh(&tsk->sk.sk_lock.slock); +			msg = &tsk->phdr; +			msg_set_prevnode(msg, tn->own_addr); +			msg_set_orignode(msg, tn->own_addr); +			spin_unlock_bh(&tsk->sk.sk_lock.slock); +		}  	} +	rcu_read_unlock();  } -/** - * struct reference - TIPC socket reference entry - * @tsk: pointer to socket associated with reference entry - * @ref: reference value for socket (combines instance & array index info) - */ -struct reference { -	struct tipc_sock *tsk; -	u32 ref; -}; - -/** - * struct tipc_ref_table - table of TIPC socket reference entries - * @entries: pointer to array of reference entries - * @capacity: array index of first unusable entry - * @init_point: array index of first uninitialized entry - * @first_free: array index of first unused socket reference entry - * @last_free: array index of last unused socket reference entry - * @index_mask: bitmask for array index portion of reference values - * @start_mask: initial value for instance value portion of reference values - */ -struct ref_table { -	struct reference *entries; -	u32 capacity; -	u32 init_point; -	u32 first_free; -	u32 last_free; -	u32 index_mask; -	u32 start_mask; -}; - -/* Socket reference table consists of 2**N entries. - * - * State	Socket ptr	Reference - * -----        ----------      --------- - * In use        non-NULL       XXXX|own index - *				(XXXX changes each time entry is acquired) - * Free            NULL         YYYY|next free index - *				(YYYY is one more than last used XXXX) - * Uninitialized   NULL         0 - * - * Entry 0 is not used; this allows index 0 to denote the end of the free list. - * - * Note that a reference value of 0 does not necessarily indicate that an - * entry is uninitialized, since the last entry in the free list could also - * have a reference value of 0 (although this is unlikely). - */ - -static struct ref_table tipc_ref_table; - -static DEFINE_RWLOCK(ref_table_lock); - -/** - * tipc_ref_table_init - create reference table for sockets - */ -int tipc_sk_ref_table_init(u32 req_sz, u32 start) +static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid)  { -	struct reference *table; -	u32 actual_sz; - -	/* account for unused entry, then round up size to a power of 2 */ - -	req_sz++; -	for (actual_sz = 16; actual_sz < req_sz; actual_sz <<= 1) { -		/* do nothing */ -	}; - -	/* allocate table & mark all entries as uninitialized */ -	table = vzalloc(actual_sz * sizeof(struct reference)); -	if (table == NULL) -		return -ENOMEM; - -	tipc_ref_table.entries = table; -	tipc_ref_table.capacity = req_sz; -	tipc_ref_table.init_point = 1; -	tipc_ref_table.first_free = 0; -	tipc_ref_table.last_free = 0; -	tipc_ref_table.index_mask = actual_sz - 1; -	tipc_ref_table.start_mask = start & ~tipc_ref_table.index_mask; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_sock *tsk; -	return 0; -} +	rcu_read_lock(); +	tsk = rhashtable_lookup(&tn->sk_rht, &portid); +	if (tsk) +		sock_hold(&tsk->sk); +	rcu_read_unlock(); -/** - * tipc_ref_table_stop - destroy reference table for sockets - */ -void tipc_sk_ref_table_stop(void) -{ -	if (!tipc_ref_table.entries) -		return; -	vfree(tipc_ref_table.entries); -	tipc_ref_table.entries = NULL; +	return tsk;  } -/* tipc_ref_acquire - create reference to a socket - * - * Register an socket pointer in the reference table. - * Returns a unique reference value that is used from then on to retrieve the - * socket pointer, or to determine if the socket has been deregistered. - */ -u32 tipc_sk_ref_acquire(struct tipc_sock *tsk) +static int tipc_sk_insert(struct tipc_sock *tsk)  { -	u32 index; -	u32 index_mask; -	u32 next_plus_upper; -	u32 ref = 0; -	struct reference *entry; - -	if (unlikely(!tsk)) { -		pr_err("Attempt to acquire ref. to non-existent obj\n"); -		return 0; -	} -	if (unlikely(!tipc_ref_table.entries)) { -		pr_err("Ref. table not found in acquisition attempt\n"); -		return 0; -	} - -	/* Take a free entry, if available; otherwise initialize a new one */ -	write_lock_bh(&ref_table_lock); -	index = tipc_ref_table.first_free; -	entry = &tipc_ref_table.entries[index]; - -	if (likely(index)) { -		index = tipc_ref_table.first_free; -		entry = &tipc_ref_table.entries[index]; -		index_mask = tipc_ref_table.index_mask; -		next_plus_upper = entry->ref; -		tipc_ref_table.first_free = next_plus_upper & index_mask; -		ref = (next_plus_upper & ~index_mask) + index; -		entry->tsk = tsk; -	} else if (tipc_ref_table.init_point < tipc_ref_table.capacity) { -		index = tipc_ref_table.init_point++; -		entry = &tipc_ref_table.entries[index]; -		ref = tipc_ref_table.start_mask + index; +	struct sock *sk = &tsk->sk; +	struct net *net = sock_net(sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	u32 remaining = (TIPC_MAX_PORT - TIPC_MIN_PORT) + 1; +	u32 portid = prandom_u32() % remaining + TIPC_MIN_PORT; + +	while (remaining--) { +		portid++; +		if ((portid < TIPC_MIN_PORT) || (portid > TIPC_MAX_PORT)) +			portid = TIPC_MIN_PORT; +		tsk->portid = portid; +		sock_hold(&tsk->sk); +		if (rhashtable_lookup_insert(&tn->sk_rht, &tsk->node)) +			return 0; +		sock_put(&tsk->sk);  	} -	if (ref) { -		entry->ref = ref; -		entry->tsk = tsk; -	} -	write_unlock_bh(&ref_table_lock); -	return ref; +	return -1;  } -/* tipc_sk_ref_discard - invalidate reference to an socket - * - * Disallow future references to an socket and free up the entry for re-use. - */ -void tipc_sk_ref_discard(u32 ref) +static void tipc_sk_remove(struct tipc_sock *tsk)  { -	struct reference *entry; -	u32 index; -	u32 index_mask; - -	if (unlikely(!tipc_ref_table.entries)) { -		pr_err("Ref. table not found during discard attempt\n"); -		return; -	} - -	index_mask = tipc_ref_table.index_mask; -	index = ref & index_mask; -	entry = &tipc_ref_table.entries[index]; - -	write_lock_bh(&ref_table_lock); +	struct sock *sk = &tsk->sk; +	struct tipc_net *tn = net_generic(sock_net(sk), tipc_net_id); -	if (unlikely(!entry->tsk)) { -		pr_err("Attempt to discard ref. to non-existent socket\n"); -		goto exit; +	if (rhashtable_remove(&tn->sk_rht, &tsk->node)) { +		WARN_ON(atomic_read(&sk->sk_refcnt) == 1); +		__sock_put(sk);  	} -	if (unlikely(entry->ref != ref)) { -		pr_err("Attempt to discard non-existent reference\n"); -		goto exit; -	} - -	/* Mark entry as unused; increment instance part of entry's -	 *   reference to invalidate any subsequent references -	 */ - -	entry->tsk = NULL; -	entry->ref = (ref & ~index_mask) + (index_mask + 1); - -	/* Append entry to free entry list */ -	if (unlikely(tipc_ref_table.first_free == 0)) -		tipc_ref_table.first_free = index; -	else -		tipc_ref_table.entries[tipc_ref_table.last_free].ref |= index; -	tipc_ref_table.last_free = index; -exit: -	write_unlock_bh(&ref_table_lock);  } -/* tipc_sk_get - find referenced socket and return pointer to it - */ -struct tipc_sock *tipc_sk_get(u32 ref) +int tipc_sk_rht_init(struct net *net)  { -	struct reference *entry; -	struct tipc_sock *tsk; +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct rhashtable_params rht_params = { +		.nelem_hint = 192, +		.head_offset = offsetof(struct tipc_sock, node), +		.key_offset = offsetof(struct tipc_sock, portid), +		.key_len = sizeof(u32), /* portid */ +		.hashfn = jhash, +		.max_shift = 20, /* 1M */ +		.min_shift = 8,  /* 256 */ +		.grow_decision = rht_grow_above_75, +		.shrink_decision = rht_shrink_below_30, +	}; -	if (unlikely(!tipc_ref_table.entries)) -		return NULL; -	read_lock_bh(&ref_table_lock); -	entry = &tipc_ref_table.entries[ref & tipc_ref_table.index_mask]; -	tsk = entry->tsk; -	if (likely(tsk && (entry->ref == ref))) -		sock_hold(&tsk->sk); -	else -		tsk = NULL; -	read_unlock_bh(&ref_table_lock); -	return tsk; +	return rhashtable_init(&tn->sk_rht, &rht_params);  } -/* tipc_sk_get_next - lock & return next socket after referenced one -*/ -struct tipc_sock *tipc_sk_get_next(u32 *ref) +void tipc_sk_rht_destroy(struct net *net)  { -	struct reference *entry; -	struct tipc_sock *tsk = NULL; -	uint index = *ref & tipc_ref_table.index_mask; +	struct tipc_net *tn = net_generic(net, tipc_net_id); -	read_lock_bh(&ref_table_lock); -	while (++index < tipc_ref_table.capacity) { -		entry = &tipc_ref_table.entries[index]; -		if (!entry->tsk) -			continue; -		tsk = entry->tsk; -		sock_hold(&tsk->sk); -		*ref = entry->ref; -		break; -	} -	read_unlock_bh(&ref_table_lock); -	return tsk; -} +	/* Wait for socket readers to complete */ +	synchronize_net(); -static void tipc_sk_put(struct tipc_sock *tsk) -{ -	sock_put(&tsk->sk); +	rhashtable_destroy(&tn->sk_rht);  }  /** @@ -2639,8 +2510,9 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt,  	return put_user(sizeof(value), ol);  } -static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg) +static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)  { +	struct sock *sk = sock->sk;  	struct tipc_sioc_ln_req lnr;  	void __user *argp = (void __user *)arg; @@ -2648,7 +2520,8 @@ static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg)  	case SIOCGETLINKNAME:  		if (copy_from_user(&lnr, argp, sizeof(lnr)))  			return -EFAULT; -		if (!tipc_node_get_linkname(lnr.bearer_id & 0xffff, lnr.peer, +		if (!tipc_node_get_linkname(sock_net(sk), +					    lnr.bearer_id & 0xffff, lnr.peer,  					    lnr.linkname, TIPC_MAX_LINK_NAME)) {  			if (copy_to_user(argp, &lnr, sizeof(lnr)))  				return -EFAULT; @@ -2820,18 +2693,20 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,  	int err;  	void *hdr;  	struct nlattr *attrs; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, -			  &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_SOCK_GET); +			  &tipc_genl_family, NLM_F_MULTI, TIPC_NL_SOCK_GET);  	if (!hdr)  		goto msg_cancel;  	attrs = nla_nest_start(skb, TIPC_NLA_SOCK);  	if (!attrs)  		goto genlmsg_cancel; -	if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->ref)) +	if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->portid))  		goto attr_msg_cancel; -	if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr)) +	if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tn->own_addr))  		goto attr_msg_cancel;  	if (tsk->connected) { @@ -2859,22 +2734,37 @@ int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb)  {  	int err;  	struct tipc_sock *tsk; -	u32 prev_ref = cb->args[0]; -	u32 ref = prev_ref; - -	tsk = tipc_sk_get_next(&ref); -	for (; tsk; tsk = tipc_sk_get_next(&ref)) { -		lock_sock(&tsk->sk); -		err = __tipc_nl_add_sk(skb, cb, tsk); -		release_sock(&tsk->sk); -		tipc_sk_put(tsk); -		if (err) -			break; +	const struct bucket_table *tbl; +	struct rhash_head *pos; +	struct net *net = sock_net(skb->sk); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	u32 tbl_id = cb->args[0]; +	u32 prev_portid = cb->args[1]; -		prev_ref = ref; -	} +	rcu_read_lock(); +	tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); +	for (; tbl_id < tbl->size; tbl_id++) { +		rht_for_each_entry_rcu(tsk, pos, tbl, tbl_id, node) { +			spin_lock_bh(&tsk->sk.sk_lock.slock); +			if (prev_portid && prev_portid != tsk->portid) { +				spin_unlock_bh(&tsk->sk.sk_lock.slock); +				continue; +			} -	cb->args[0] = prev_ref; +			err = __tipc_nl_add_sk(skb, cb, tsk); +			if (err) { +				prev_portid = tsk->portid; +				spin_unlock_bh(&tsk->sk.sk_lock.slock); +				goto out; +			} +			prev_portid = 0; +			spin_unlock_bh(&tsk->sk.sk_lock.slock); +		} +	} +out: +	rcu_read_unlock(); +	cb->args[0] = tbl_id; +	cb->args[1] = prev_portid;  	return skb->len;  } @@ -2888,7 +2778,7 @@ static int __tipc_nl_add_sk_publ(struct sk_buff *skb,  	struct nlattr *attrs;  	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, -			  &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_PUBL_GET); +			  &tipc_genl_family, NLM_F_MULTI, TIPC_NL_PUBL_GET);  	if (!hdr)  		goto msg_cancel; @@ -2962,12 +2852,13 @@ static int __tipc_nl_list_sk_publ(struct sk_buff *skb,  int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)  {  	int err; -	u32 tsk_ref = cb->args[0]; +	u32 tsk_portid = cb->args[0];  	u32 last_publ = cb->args[1];  	u32 done = cb->args[2]; +	struct net *net = sock_net(skb->sk);  	struct tipc_sock *tsk; -	if (!tsk_ref) { +	if (!tsk_portid) {  		struct nlattr **attrs;  		struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1]; @@ -2984,13 +2875,13 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)  		if (!sock[TIPC_NLA_SOCK_REF])  			return -EINVAL; -		tsk_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); +		tsk_portid = nla_get_u32(sock[TIPC_NLA_SOCK_REF]);  	}  	if (done)  		return 0; -	tsk = tipc_sk_get(tsk_ref); +	tsk = tipc_sk_lookup(net, tsk_portid);  	if (!tsk)  		return -EINVAL; @@ -2999,9 +2890,9 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)  	if (!err)  		done = 1;  	release_sock(&tsk->sk); -	tipc_sk_put(tsk); +	sock_put(&tsk->sk); -	cb->args[0] = tsk_ref; +	cb->args[0] = tsk_portid;  	cb->args[1] = last_publ;  	cb->args[2] = done; diff --git a/net/tipc/socket.h b/net/tipc/socket.h index d34089387006..238f1b7bd9bd 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -1,6 +1,6 @@  /* net/tipc/socket.h: Include file for TIPC socket code   * - * Copyright (c) 2014, Ericsson AB + * Copyright (c) 2014-2015, Ericsson AB   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -42,12 +42,18 @@  #define TIPC_FLOWCTRL_WIN        (TIPC_CONNACK_INTV * 2)  #define TIPC_CONN_OVERLOAD_LIMIT ((TIPC_FLOWCTRL_WIN * 2 + 1) * \  				  SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE)) -int tipc_sk_rcv(struct sk_buff *buf); -struct sk_buff *tipc_sk_socks_show(void); -void tipc_sk_mcast_rcv(struct sk_buff *buf); -void tipc_sk_reinit(void); -int tipc_sk_ref_table_init(u32 requested_size, u32 start); -void tipc_sk_ref_table_stop(void); +int tipc_socket_init(void); +void tipc_socket_stop(void); +int tipc_sock_create_local(struct net *net, int type, struct socket **res); +void tipc_sock_release_local(struct socket *sock); +int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, +			   int flags); +int tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq); +void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, +		       struct sk_buff_head *inputq); +void tipc_sk_reinit(struct net *net); +int tipc_sk_rht_init(struct net *net); +void tipc_sk_rht_destroy(struct net *net);  int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb);  int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb); diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 0344206b984f..72c339e432aa 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -50,33 +50,6 @@ struct tipc_subscriber {  	struct list_head subscription_list;  }; -static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr, -				  void *usr_data, void *buf, size_t len); -static void *subscr_named_msg_event(int conid); -static void subscr_conn_shutdown_event(int conid, void *usr_data); - -static atomic_t subscription_count = ATOMIC_INIT(0); - -static struct sockaddr_tipc topsrv_addr __read_mostly = { -	.family			= AF_TIPC, -	.addrtype		= TIPC_ADDR_NAMESEQ, -	.addr.nameseq.type	= TIPC_TOP_SRV, -	.addr.nameseq.lower	= TIPC_TOP_SRV, -	.addr.nameseq.upper	= TIPC_TOP_SRV, -	.scope			= TIPC_NODE_SCOPE -}; - -static struct tipc_server topsrv __read_mostly = { -	.saddr			= &topsrv_addr, -	.imp			= TIPC_CRITICAL_IMPORTANCE, -	.type			= SOCK_SEQPACKET, -	.max_rcvbuf_size	= sizeof(struct tipc_subscr), -	.name			= "topology_server", -	.tipc_conn_recvmsg	= subscr_conn_msg_event, -	.tipc_conn_new		= subscr_named_msg_event, -	.tipc_conn_shutdown	= subscr_conn_shutdown_event, -}; -  /**   * htohl - convert value to endianness used by destination   * @in: value to convert @@ -93,6 +66,7 @@ static void subscr_send_event(struct tipc_subscription *sub, u32 found_lower,  			      u32 found_upper, u32 event, u32 port_ref,  			      u32 node)  { +	struct tipc_net *tn = net_generic(sub->net, tipc_net_id);  	struct tipc_subscriber *subscriber = sub->subscriber;  	struct kvec msg_sect; @@ -103,8 +77,8 @@ static void subscr_send_event(struct tipc_subscription *sub, u32 found_lower,  	sub->evt.found_upper = htohl(found_upper, sub->swap);  	sub->evt.port.ref = htohl(port_ref, sub->swap);  	sub->evt.port.node = htohl(node, sub->swap); -	tipc_conn_sendmsg(&topsrv, subscriber->conid, NULL, msg_sect.iov_base, -			  msg_sect.iov_len); +	tipc_conn_sendmsg(tn->topsrv, subscriber->conid, NULL, +			  msg_sect.iov_base, msg_sect.iov_len);  }  /** @@ -141,9 +115,11 @@ void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower,  	subscr_send_event(sub, found_lower, found_upper, event, port_ref, node);  } -static void subscr_timeout(struct tipc_subscription *sub) +static void subscr_timeout(unsigned long data)  { +	struct tipc_subscription *sub = (struct tipc_subscription *)data;  	struct tipc_subscriber *subscriber = sub->subscriber; +	struct tipc_net *tn = net_generic(sub->net, tipc_net_id);  	/* The spin lock per subscriber is used to protect its members */  	spin_lock_bh(&subscriber->lock); @@ -167,9 +143,8 @@ static void subscr_timeout(struct tipc_subscription *sub)  			  TIPC_SUBSCR_TIMEOUT, 0, 0);  	/* Now destroy subscription */ -	k_term_timer(&sub->timer);  	kfree(sub); -	atomic_dec(&subscription_count); +	atomic_dec(&tn->subscription_count);  }  /** @@ -179,10 +154,12 @@ static void subscr_timeout(struct tipc_subscription *sub)   */  static void subscr_del(struct tipc_subscription *sub)  { +	struct tipc_net *tn = net_generic(sub->net, tipc_net_id); +  	tipc_nametbl_unsubscribe(sub);  	list_del(&sub->subscription_list);  	kfree(sub); -	atomic_dec(&subscription_count); +	atomic_dec(&tn->subscription_count);  }  /** @@ -190,9 +167,12 @@ static void subscr_del(struct tipc_subscription *sub)   *   * Note: Must call it in process context since it might sleep.   */ -static void subscr_terminate(struct tipc_subscriber *subscriber) +static void subscr_terminate(struct tipc_subscription *sub)  { -	tipc_conn_terminate(&topsrv, subscriber->conid); +	struct tipc_subscriber *subscriber = sub->subscriber; +	struct tipc_net *tn = net_generic(sub->net, tipc_net_id); + +	tipc_conn_terminate(tn->topsrv, subscriber->conid);  }  static void subscr_release(struct tipc_subscriber *subscriber) @@ -207,8 +187,7 @@ static void subscr_release(struct tipc_subscriber *subscriber)  				 subscription_list) {  		if (sub->timeout != TIPC_WAIT_FOREVER) {  			spin_unlock_bh(&subscriber->lock); -			k_cancel_timer(&sub->timer); -			k_term_timer(&sub->timer); +			del_timer_sync(&sub->timer);  			spin_lock_bh(&subscriber->lock);  		}  		subscr_del(sub); @@ -250,8 +229,7 @@ static void subscr_cancel(struct tipc_subscr *s,  	if (sub->timeout != TIPC_WAIT_FOREVER) {  		sub->timeout = TIPC_WAIT_FOREVER;  		spin_unlock_bh(&subscriber->lock); -		k_cancel_timer(&sub->timer); -		k_term_timer(&sub->timer); +		del_timer_sync(&sub->timer);  		spin_lock_bh(&subscriber->lock);  	}  	subscr_del(sub); @@ -262,9 +240,11 @@ static void subscr_cancel(struct tipc_subscr *s,   *   * Called with subscriber lock held.   */ -static int subscr_subscribe(struct tipc_subscr *s, +static int subscr_subscribe(struct net *net, struct tipc_subscr *s,  			    struct tipc_subscriber *subscriber, -			    struct tipc_subscription **sub_p) { +			    struct tipc_subscription **sub_p) +{ +	struct tipc_net *tn = net_generic(net, tipc_net_id);  	struct tipc_subscription *sub;  	int swap; @@ -279,7 +259,7 @@ static int subscr_subscribe(struct tipc_subscr *s,  	}  	/* Refuse subscription if global limit exceeded */ -	if (atomic_read(&subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) { +	if (atomic_read(&tn->subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) {  		pr_warn("Subscription rejected, limit reached (%u)\n",  			TIPC_MAX_SUBSCRIPTIONS);  		return -EINVAL; @@ -293,10 +273,11 @@ static int subscr_subscribe(struct tipc_subscr *s,  	}  	/* Initialize subscription object */ +	sub->net = net;  	sub->seq.type = htohl(s->seq.type, swap);  	sub->seq.lower = htohl(s->seq.lower, swap);  	sub->seq.upper = htohl(s->seq.upper, swap); -	sub->timeout = htohl(s->timeout, swap); +	sub->timeout = msecs_to_jiffies(htohl(s->timeout, swap));  	sub->filter = htohl(s->filter, swap);  	if ((!(sub->filter & TIPC_SUB_PORTS) ==  	     !(sub->filter & TIPC_SUB_SERVICE)) || @@ -309,11 +290,10 @@ static int subscr_subscribe(struct tipc_subscr *s,  	sub->subscriber = subscriber;  	sub->swap = swap;  	memcpy(&sub->evt.s, s, sizeof(struct tipc_subscr)); -	atomic_inc(&subscription_count); +	atomic_inc(&tn->subscription_count);  	if (sub->timeout != TIPC_WAIT_FOREVER) { -		k_init_timer(&sub->timer, -			     (Handler)subscr_timeout, (unsigned long)sub); -		k_start_timer(&sub->timer, sub->timeout); +		setup_timer(&sub->timer, subscr_timeout, (unsigned long)sub); +		mod_timer(&sub->timer, jiffies + sub->timeout);  	}  	*sub_p = sub;  	return 0; @@ -326,16 +306,18 @@ static void subscr_conn_shutdown_event(int conid, void *usr_data)  }  /* Handle one request to create a new subscription for the subscriber */ -static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr, -				  void *usr_data, void *buf, size_t len) +static void subscr_conn_msg_event(struct net *net, int conid, +				  struct sockaddr_tipc *addr, void *usr_data, +				  void *buf, size_t len)  {  	struct tipc_subscriber *subscriber = usr_data;  	struct tipc_subscription *sub = NULL;  	spin_lock_bh(&subscriber->lock); -	if (subscr_subscribe((struct tipc_subscr *)buf, subscriber, &sub) < 0) { +	if (subscr_subscribe(net, (struct tipc_subscr *)buf, subscriber, +			     &sub) < 0) {  		spin_unlock_bh(&subscriber->lock); -		subscr_terminate(subscriber); +		subscr_terminate(sub);  		return;  	}  	if (sub) @@ -343,7 +325,6 @@ static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr,  	spin_unlock_bh(&subscriber->lock);  } -  /* Handle one request to establish a new subscriber */  static void *subscr_named_msg_event(int conid)  { @@ -362,12 +343,50 @@ static void *subscr_named_msg_event(int conid)  	return (void *)subscriber;  } -int tipc_subscr_start(void) +int tipc_subscr_start(struct net *net)  { -	return tipc_server_start(&topsrv); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	const char name[] = "topology_server"; +	struct tipc_server *topsrv; +	struct sockaddr_tipc *saddr; + +	saddr = kzalloc(sizeof(*saddr), GFP_ATOMIC); +	if (!saddr) +		return -ENOMEM; +	saddr->family			= AF_TIPC; +	saddr->addrtype			= TIPC_ADDR_NAMESEQ; +	saddr->addr.nameseq.type	= TIPC_TOP_SRV; +	saddr->addr.nameseq.lower	= TIPC_TOP_SRV; +	saddr->addr.nameseq.upper	= TIPC_TOP_SRV; +	saddr->scope			= TIPC_NODE_SCOPE; + +	topsrv = kzalloc(sizeof(*topsrv), GFP_ATOMIC); +	if (!topsrv) { +		kfree(saddr); +		return -ENOMEM; +	} +	topsrv->net			= net; +	topsrv->saddr			= saddr; +	topsrv->imp			= TIPC_CRITICAL_IMPORTANCE; +	topsrv->type			= SOCK_SEQPACKET; +	topsrv->max_rcvbuf_size		= sizeof(struct tipc_subscr); +	topsrv->tipc_conn_recvmsg	= subscr_conn_msg_event; +	topsrv->tipc_conn_new		= subscr_named_msg_event; +	topsrv->tipc_conn_shutdown	= subscr_conn_shutdown_event; + +	strncpy(topsrv->name, name, strlen(name) + 1); +	tn->topsrv = topsrv; +	atomic_set(&tn->subscription_count, 0); + +	return tipc_server_start(topsrv);  } -void tipc_subscr_stop(void) +void tipc_subscr_stop(struct net *net)  { -	tipc_server_stop(&topsrv); +	struct tipc_net *tn = net_generic(net, tipc_net_id); +	struct tipc_server *topsrv = tn->topsrv; + +	tipc_server_stop(topsrv); +	kfree(topsrv->saddr); +	kfree(topsrv);  } diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 393e417bee3f..33488bd9fe3c 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -39,6 +39,9 @@  #include "server.h" +#define TIPC_MAX_SUBSCRIPTIONS	65535 +#define TIPC_MAX_PUBLICATIONS	65535 +  struct tipc_subscription;  struct tipc_subscriber; @@ -46,6 +49,7 @@ struct tipc_subscriber;   * struct tipc_subscription - TIPC network topology subscription object   * @subscriber: pointer to its subscriber   * @seq: name sequence associated with subscription + * @net: point to network namespace   * @timeout: duration of subscription (in ms)   * @filter: event filtering to be done for subscription   * @timer: timer governing subscription duration (optional) @@ -58,7 +62,8 @@ struct tipc_subscriber;  struct tipc_subscription {  	struct tipc_subscriber *subscriber;  	struct tipc_name_seq seq; -	u32 timeout; +	struct net *net; +	unsigned long timeout;  	u32 filter;  	struct timer_list timer;  	struct list_head nameseq_list; @@ -69,13 +74,10 @@ struct tipc_subscription {  int tipc_subscr_overlap(struct tipc_subscription *sub, u32 found_lower,  			u32 found_upper); -  void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower,  				u32 found_upper, u32 event, u32 port_ref,  				u32 node, int must); - -int tipc_subscr_start(void); - -void tipc_subscr_stop(void); +int tipc_subscr_start(struct net *net); +void tipc_subscr_stop(struct net *net);  #endif |