diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /net/tipc | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'net/tipc')
40 files changed, 16345 insertions, 0 deletions
diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig new file mode 100644 index 00000000..2c5954b8 --- /dev/null +++ b/net/tipc/Kconfig @@ -0,0 +1,69 @@ +# +# TIPC configuration +# + +menuconfig TIPC + tristate "The TIPC Protocol (EXPERIMENTAL)" + depends on INET && EXPERIMENTAL + ---help--- + The Transparent Inter Process Communication (TIPC) protocol is + specially designed for intra cluster communication. This protocol + originates from Ericsson where it has been used in carrier grade + cluster applications for many years. + + For more information about TIPC, see http://tipc.sourceforge.net. + + This protocol support is also available as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). The module will be called tipc. If you want to compile it + as a module, say M here and read <file:Documentation/kbuild/modules.txt>. + + If in doubt, say N. + +if TIPC + +config TIPC_ADVANCED + bool "Advanced TIPC configuration" + default n + help + Saying Y here will open some advanced configuration for TIPC. + Most users do not need to bother; if unsure, just say N. + +config TIPC_PORTS + int "Maximum number of ports in a node" + depends on TIPC_ADVANCED + 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_LOG + int "Size of log buffer" + depends on TIPC_ADVANCED + range 0 32768 + default "0" + help + Size (in bytes) of TIPC's internal log buffer, which records the + occurrence of significant events. Can range from 0 to 32768 bytes; + default is 0. + + There is no need to enable the log buffer unless the node will be + managed remotely via TIPC. + +config TIPC_DEBUG + bool "Enable debugging support" + default n + help + Saying Y here enables TIPC debugging capabilities used by developers. + Most users do not need to bother; if unsure, just say N. + + Enabling debugging support causes TIPC to display data about its + internal state when certain abnormal conditions occur. It also + makes it easy for developers to capture additional information of + interest using the dbg() or msg_dbg() macros. + +endif # TIPC diff --git a/net/tipc/Makefile b/net/tipc/Makefile new file mode 100644 index 00000000..521d24d0 --- /dev/null +++ b/net/tipc/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the Linux TIPC layer +# + +obj-$(CONFIG_TIPC) := tipc.o + +tipc-y += addr.o bcast.o bearer.o config.o \ + core.o handler.o link.o discover.o msg.o \ + name_distr.o subscr.o name_table.o net.o \ + netlink.o node.o node_subscr.o port.o ref.o \ + socket.o log.o eth_media.o + +# End of file diff --git a/net/tipc/addr.c b/net/tipc/addr.c new file mode 100644 index 00000000..a6fdab33 --- /dev/null +++ b/net/tipc/addr.c @@ -0,0 +1,106 @@ +/* + * net/tipc/addr.c: TIPC address utility routines + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2004-2005, 2010-2011, 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 "addr.h" + +/** + * tipc_addr_domain_valid - validates a network domain address + * + * Accepts <Z.C.N>, <Z.C.0>, <Z.0.0>, and <0.0.0>, + * where Z, C, and N are non-zero. + * + * Returns 1 if domain address is valid, otherwise 0 + */ + +int tipc_addr_domain_valid(u32 addr) +{ + u32 n = tipc_node(addr); + u32 c = tipc_cluster(addr); + u32 z = tipc_zone(addr); + + if (n && (!z || !c)) + return 0; + if (c && !z) + return 0; + return 1; +} + +/** + * tipc_addr_node_valid - validates a proposed network address for this node + * + * Accepts <Z.C.N>, where Z, C, and N are non-zero. + * + * Returns 1 if address can be used, otherwise 0 + */ + +int tipc_addr_node_valid(u32 addr) +{ + return tipc_addr_domain_valid(addr) && tipc_node(addr); +} + +int tipc_in_scope(u32 domain, u32 addr) +{ + if (!domain || (domain == addr)) + return 1; + if (domain == tipc_cluster_mask(addr)) /* domain <Z.C.0> */ + return 1; + if (domain == tipc_zone_mask(addr)) /* domain <Z.0.0> */ + return 1; + return 0; +} + +/** + * tipc_addr_scope - convert message lookup domain to a 2-bit scope value + */ + +int tipc_addr_scope(u32 domain) +{ + if (likely(!domain)) + return TIPC_ZONE_SCOPE; + if (tipc_node(domain)) + return TIPC_NODE_SCOPE; + if (tipc_cluster(domain)) + return TIPC_CLUSTER_SCOPE; + return TIPC_ZONE_SCOPE; +} + +char *tipc_addr_string_fill(char *string, u32 addr) +{ + snprintf(string, 16, "<%u.%u.%u>", + tipc_zone(addr), tipc_cluster(addr), tipc_node(addr)); + return string; +} diff --git a/net/tipc/addr.h b/net/tipc/addr.h new file mode 100644 index 00000000..e4f35afe --- /dev/null +++ b/net/tipc/addr.h @@ -0,0 +1,79 @@ +/* + * net/tipc/addr.h: Include file for TIPC address utility routines + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2004-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_ADDR_H +#define _TIPC_ADDR_H + +#define TIPC_ZONE_MASK 0xff000000u +#define TIPC_CLUSTER_MASK 0xfffff000u + +static inline u32 tipc_zone_mask(u32 addr) +{ + return addr & TIPC_ZONE_MASK; +} + +static inline u32 tipc_cluster_mask(u32 addr) +{ + return addr & TIPC_CLUSTER_MASK; +} + +static inline int in_own_cluster(u32 addr) +{ + return !((addr ^ tipc_own_addr) >> 12); +} + +/** + * 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 tipc_addr_domain_valid(u32); +int tipc_addr_node_valid(u32 addr); +int tipc_in_scope(u32 domain, u32 addr); +int tipc_addr_scope(u32 domain); +char *tipc_addr_string_fill(char *string, u32 addr); +#endif diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c new file mode 100644 index 00000000..e00441a2 --- /dev/null +++ b/net/tipc/bcast.c @@ -0,0 +1,926 @@ +/* + * net/tipc/bcast.c: TIPC broadcast code + * + * Copyright (c) 2004-2006, Ericsson AB + * Copyright (c) 2004, Intel Corporation. + * Copyright (c) 2005, 2010-2011, Wind River Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "link.h" +#include "port.h" +#include "bcast.h" +#include "name_distr.h" + +#define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */ + +#define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */ + +/** + * 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 "bc_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 + * @link: (non-standard) broadcast link structure + * @node: (non-standard) node structure representing b'cast link's peer node + * @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 { + struct tipc_link link; + struct tipc_node node; + struct tipc_node_map bcast_nodes; + struct tipc_node *retransmit_to; +}; + +static struct tipc_bcbearer bcast_bearer; +static struct tipc_bclink bcast_link; + +static struct tipc_bcbearer *bcbearer = &bcast_bearer; +static struct tipc_bclink *bclink = &bcast_link; +static struct tipc_link *bcl = &bcast_link.link; + +static DEFINE_SPINLOCK(bc_lock); + +const char tipc_bclink_name[] = "broadcast-link"; + +static void tipc_nmap_diff(struct tipc_node_map *nm_a, + struct tipc_node_map *nm_b, + struct tipc_node_map *nm_diff); + +static u32 bcbuf_acks(struct sk_buff *buf) +{ + return (u32)(unsigned long)TIPC_SKB_CB(buf)->handle; +} + +static void bcbuf_set_acks(struct sk_buff *buf, u32 acks) +{ + TIPC_SKB_CB(buf)->handle = (void *)(unsigned long)acks; +} + +static void bcbuf_decr_acks(struct sk_buff *buf) +{ + bcbuf_set_acks(buf, bcbuf_acks(buf) - 1); +} + +void tipc_bclink_add_node(u32 addr) +{ + spin_lock_bh(&bc_lock); + tipc_nmap_add(&bclink->bcast_nodes, addr); + spin_unlock_bh(&bc_lock); +} + +void tipc_bclink_remove_node(u32 addr) +{ + spin_lock_bh(&bc_lock); + tipc_nmap_remove(&bclink->bcast_nodes, addr); + spin_unlock_bh(&bc_lock); +} + +static void bclink_set_last_sent(void) +{ + 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) +{ + return bcl->fsm_msg_cnt; +} + +static void bclink_update_last_sent(struct tipc_node *node, u32 seqno) +{ + node->bclink.last_sent = less_eq(node->bclink.last_sent, seqno) ? + seqno : node->bclink.last_sent; +} + + +/* + * tipc_bclink_retransmit_to - get most recent node to request retransmission + * + * Called with bc_lock locked + */ + +struct tipc_node *tipc_bclink_retransmit_to(void) +{ + return bclink->retransmit_to; +} + +/** + * bclink_retransmit_pkt - retransmit broadcast packets + * @after: sequence number of last packet to *not* retransmit + * @to: sequence number of last packet to retransmit + * + * Called with bc_lock locked + */ + +static void bclink_retransmit_pkt(u32 after, u32 to) +{ + struct sk_buff *buf; + + buf = bcl->first_out; + while (buf && less_eq(buf_seqno(buf), after)) + buf = buf->next; + tipc_link_retransmit(bcl, buf, mod(to - after)); +} + +/** + * tipc_bclink_acknowledge - handle acknowledgement of broadcast packets + * @n_ptr: node that sent acknowledgement info + * @acked: broadcast sequence # that has been acknowledged + * + * Node is locked, bc_lock unlocked. + */ + +void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) +{ + struct sk_buff *crs; + struct sk_buff *next; + unsigned int released = 0; + + spin_lock_bh(&bc_lock); + + /* Bail out if tx queue is empty (no clean up is required) */ + crs = bcl->first_out; + if (!crs) + goto exit; + + /* Determine which messages need to be acknowledged */ + if (acked == INVALID_LINK_SEQ) { + /* + * Contact with specified node has been lost, so need to + * 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; + else + acked = 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(crs)) || + less(bcl->fsm_msg_cnt, acked) || + less_eq(acked, n_ptr->bclink.acked)) + goto exit; + } + + /* Skip over packets that node has previously acknowledged */ + while (crs && less_eq(buf_seqno(crs), n_ptr->bclink.acked)) + crs = crs->next; + + /* Update packets that node is now acknowledging */ + + while (crs && less_eq(buf_seqno(crs), acked)) { + next = crs->next; + + if (crs != bcl->next_out) + bcbuf_decr_acks(crs); + else { + bcbuf_set_acks(crs, 0); + bcl->next_out = next; + bclink_set_last_sent(); + } + + if (bcbuf_acks(crs) == 0) { + bcl->first_out = next; + bcl->out_queue_size--; + kfree_skb(crs); + released = 1; + } + crs = next; + } + n_ptr->bclink.acked = acked; + + /* Try resolving broadcast link congestion, if necessary */ + + if (unlikely(bcl->next_out)) { + tipc_link_push_queue(bcl); + bclink_set_last_sent(); + } + if (unlikely(released && !list_empty(&bcl->waiting_ports))) + tipc_link_wakeup_ports(bcl, 0); +exit: + spin_unlock_bh(&bc_lock); +} + +/* + * tipc_bclink_update_link_state - update broadcast link state + * + * tipc_net_lock and node lock set + */ + +void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) +{ + struct sk_buff *buf; + + /* Ignore "stale" link state info */ + + if (less_eq(last_sent, n_ptr->bclink.last_in)) + return; + + /* Update link synchronization state; quit if in sync */ + + bclink_update_last_sent(n_ptr, last_sent); + + if (n_ptr->bclink.last_sent == n_ptr->bclink.last_in) + return; + + /* Update out-of-sync state; quit if loss is still unconfirmed */ + + if ((++n_ptr->bclink.oos_state) == 1) { + if (n_ptr->bclink.deferred_size < (TIPC_MIN_LINK_WIN / 2)) + return; + n_ptr->bclink.oos_state++; + } + + /* Don't NACK if one has been recently sent (or seen) */ + + if (n_ptr->bclink.oos_state & 0x1) + return; + + /* Send NACK */ + + buf = tipc_buf_acquire(INT_H_SIZE); + if (buf) { + struct tipc_msg *msg = buf_msg(buf); + + tipc_msg_init(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_bcast_ack(msg, n_ptr->bclink.last_in); + msg_set_bcgap_after(msg, n_ptr->bclink.last_in); + msg_set_bcgap_to(msg, n_ptr->bclink.deferred_head + ? buf_seqno(n_ptr->bclink.deferred_head) - 1 + : n_ptr->bclink.last_sent); + + spin_lock_bh(&bc_lock); + tipc_bearer_send(&bcbearer->bearer, buf, NULL); + bcl->stats.sent_nacks++; + spin_unlock_bh(&bc_lock); + kfree_skb(buf); + + n_ptr->bclink.oos_state++; + } +} + +/* + * bclink_peek_nack - monitor retransmission requests sent by other nodes + * + * Delay any upcoming NACK by this node if another node has already + * requested the first message this node is going to ask for. + * + * Only tipc_net_lock set. + */ + +static void bclink_peek_nack(struct tipc_msg *msg) +{ + struct tipc_node *n_ptr = tipc_node_find(msg_destnode(msg)); + + if (unlikely(!n_ptr)) + return; + + tipc_node_lock(n_ptr); + + if (n_ptr->bclink.supported && + (n_ptr->bclink.last_in != n_ptr->bclink.last_sent) && + (n_ptr->bclink.last_in == msg_bcgap_after(msg))) + n_ptr->bclink.oos_state = 2; + + tipc_node_unlock(n_ptr); +} + +/* + * tipc_bclink_send_msg - broadcast a packet to all nodes in cluster + */ + +int tipc_bclink_send_msg(struct sk_buff *buf) +{ + int res; + + spin_lock_bh(&bc_lock); + + if (!bclink->bcast_nodes.count) { + res = msg_data_sz(buf_msg(buf)); + kfree_skb(buf); + goto exit; + } + + res = tipc_link_send_buf(bcl, buf); + if (likely(res >= 0)) { + bclink_set_last_sent(); + bcl->stats.queue_sz_counts++; + bcl->stats.accu_queue_sz += bcl->out_queue_size; + } +exit: + spin_unlock_bh(&bc_lock); + return res; +} + +/* + * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet + * + * Called with both sending node's lock and bc_lock taken. + */ + +static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) +{ + bclink_update_last_sent(node, seqno); + node->bclink.last_in = seqno; + node->bclink.oos_state = 0; + 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) { + tipc_link_send_proto_msg( + node->active_links[node->addr & 1], + STATE_MSG, 0, 0, 0, 0, 0); + bcl->stats.sent_acks++; + } +} + +/* + * tipc_bclink_recv_pkt - receive a broadcast packet, and deliver upwards + * + * tipc_net_lock is read_locked, no other locks set + */ + +void tipc_bclink_recv_pkt(struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + struct tipc_node *node; + u32 next_in; + u32 seqno; + int deferred; + + /* Screen out unwanted broadcast messages */ + + if (msg_mc_netid(msg) != tipc_net_id) + goto exit; + + node = tipc_node_find(msg_prevnode(msg)); + if (unlikely(!node)) + goto exit; + + tipc_node_lock(node); + if (unlikely(!node->bclink.supported)) + goto unlock; + + /* Handle broadcast protocol message */ + + if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { + if (msg_type(msg) != STATE_MSG) + goto unlock; + if (msg_destnode(msg) == tipc_own_addr) { + tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); + tipc_node_unlock(node); + spin_lock_bh(&bc_lock); + bcl->stats.recv_nacks++; + bclink->retransmit_to = node; + bclink_retransmit_pkt(msg_bcgap_after(msg), + msg_bcgap_to(msg)); + spin_unlock_bh(&bc_lock); + } else { + tipc_node_unlock(node); + bclink_peek_nack(msg); + } + goto exit; + } + + /* Handle in-sequence broadcast message */ + + seqno = msg_seqno(msg); + next_in = mod(node->bclink.last_in + 1); + + if (likely(seqno == next_in)) { +receive: + /* Deliver message to destination */ + + if (likely(msg_isdata(msg))) { + spin_lock_bh(&bc_lock); + bclink_accept_pkt(node, seqno); + spin_unlock_bh(&bc_lock); + tipc_node_unlock(node); + if (likely(msg_mcast(msg))) + tipc_port_recv_mcast(buf, NULL); + else + kfree_skb(buf); + } else if (msg_user(msg) == MSG_BUNDLER) { + spin_lock_bh(&bc_lock); + bclink_accept_pkt(node, seqno); + bcl->stats.recv_bundles++; + bcl->stats.recv_bundled += msg_msgcnt(msg); + spin_unlock_bh(&bc_lock); + tipc_node_unlock(node); + tipc_link_recv_bundle(buf); + } else if (msg_user(msg) == MSG_FRAGMENTER) { + int ret = tipc_link_recv_fragment(&node->bclink.defragm, + &buf, &msg); + if (ret < 0) + goto unlock; + spin_lock_bh(&bc_lock); + bclink_accept_pkt(node, seqno); + bcl->stats.recv_fragments++; + if (ret > 0) + bcl->stats.recv_fragmented++; + spin_unlock_bh(&bc_lock); + tipc_node_unlock(node); + tipc_net_route_msg(buf); + } else if (msg_user(msg) == NAME_DISTRIBUTOR) { + spin_lock_bh(&bc_lock); + bclink_accept_pkt(node, seqno); + spin_unlock_bh(&bc_lock); + tipc_node_unlock(node); + tipc_named_recv(buf); + } else { + spin_lock_bh(&bc_lock); + bclink_accept_pkt(node, seqno); + spin_unlock_bh(&bc_lock); + tipc_node_unlock(node); + kfree_skb(buf); + } + buf = NULL; + + /* Determine new synchronization state */ + + tipc_node_lock(node); + if (unlikely(!tipc_node_is_up(node))) + goto unlock; + + if (node->bclink.last_in == node->bclink.last_sent) + goto unlock; + + if (!node->bclink.deferred_head) { + node->bclink.oos_state = 1; + goto unlock; + } + + msg = buf_msg(node->bclink.deferred_head); + seqno = msg_seqno(msg); + next_in = mod(next_in + 1); + if (seqno != next_in) + goto unlock; + + /* Take in-sequence message from deferred queue & deliver it */ + + buf = node->bclink.deferred_head; + node->bclink.deferred_head = buf->next; + node->bclink.deferred_size--; + goto receive; + } + + /* Handle out-of-sequence broadcast message */ + + if (less(next_in, seqno)) { + deferred = tipc_link_defer_pkt(&node->bclink.deferred_head, + &node->bclink.deferred_tail, + buf); + node->bclink.deferred_size += deferred; + bclink_update_last_sent(node, seqno); + buf = NULL; + } else + deferred = 0; + + spin_lock_bh(&bc_lock); + + if (deferred) + bcl->stats.deferred_recv++; + else + bcl->stats.duplicates++; + + spin_unlock_bh(&bc_lock); + +unlock: + tipc_node_unlock(node); +exit: + kfree_skb(buf); +} + +u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr) +{ + return (n_ptr->bclink.supported && + (tipc_bclink_get_last_sent() != n_ptr->bclink.acked)); +} + + +/** + * tipc_bcbearer_send - send a packet through the broadcast pseudo-bearer + * + * Send packet over as many bearers as necessary to reach all nodes + * that have joined the broadcast link. + * + * 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, + struct tipc_media_addr *unused2) +{ + int bp_index; + + /* + * Prepare broadcast link message for reliable transmission, + * if first time trying to send it; + * preparation is skipped for broadcast link protocol messages + * since they are sent in an unreliable manner and don't need it + */ + + if (likely(!msg_non_seq(buf_msg(buf)))) { + struct tipc_msg *msg; + + bcbuf_set_acks(buf, bclink->bcast_nodes.count); + msg = buf_msg(buf); + msg_set_non_seq(msg, 1); + msg_set_mc_netid(msg, tipc_net_id); + bcl->stats.sent_info++; + + if (WARN_ON(!bclink->bcast_nodes.count)) { + dump_stack(); + return 0; + } + } + + /* Send buffer over bearers until all targets reached */ + + bcbearer->remains = bclink->bcast_nodes; + + for (bp_index = 0; bp_index < MAX_BEARERS; bp_index++) { + struct tipc_bearer *p = bcbearer->bpairs[bp_index].primary; + struct tipc_bearer *s = bcbearer->bpairs[bp_index].secondary; + + if (!p) + break; /* no more bearers to try */ + + tipc_nmap_diff(&bcbearer->remains, &p->nodes, &bcbearer->remains_new); + if (bcbearer->remains_new.count == bcbearer->remains.count) + continue; /* bearer pair doesn't add anything */ + + if (p->blocked || + p->media->send_msg(buf, p, &p->media->bcast_addr)) { + /* unable to send on primary bearer */ + if (!s || s->blocked || + s->media->send_msg(buf, s, + &s->media->bcast_addr)) { + /* unable to send on either bearer */ + continue; + } + } + + if (s) { + bcbearer->bpairs[bp_index].primary = s; + bcbearer->bpairs[bp_index].secondary = p; + } + + if (bcbearer->remains_new.count == 0) + break; /* all targets reached */ + + bcbearer->remains = bcbearer->remains_new; + } + + return 0; +} + +/** + * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer + */ + +void tipc_bcbearer_sort(void) +{ + struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp; + struct tipc_bcbearer_pair *bp_curr; + int b_index; + int pri; + + spin_lock_bh(&bc_lock); + + /* Group bearers by priority (can assume max of two per priority) */ + + memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp)); + + for (b_index = 0; b_index < MAX_BEARERS; b_index++) { + struct tipc_bearer *b = &tipc_bearers[b_index]; + + if (!b->active || !b->nodes.count) + continue; + + if (!bp_temp[b->priority].primary) + bp_temp[b->priority].primary = b; + else + bp_temp[b->priority].secondary = b; + } + + /* Create array of bearer pairs for broadcasting */ + + bp_curr = bcbearer->bpairs; + memset(bcbearer->bpairs, 0, sizeof(bcbearer->bpairs)); + + for (pri = TIPC_MAX_LINK_PRI; pri >= 0; pri--) { + + if (!bp_temp[pri].primary) + continue; + + bp_curr->primary = bp_temp[pri].primary; + + if (bp_temp[pri].secondary) { + if (tipc_nmap_equal(&bp_temp[pri].primary->nodes, + &bp_temp[pri].secondary->nodes)) { + bp_curr->secondary = bp_temp[pri].secondary; + } else { + bp_curr++; + bp_curr->primary = bp_temp[pri].secondary; + } + } + + bp_curr++; + } + + spin_unlock_bh(&bc_lock); +} + + +int tipc_bclink_stats(char *buf, const u32 buf_size) +{ + struct print_buf pb; + + if (!bcl) + return 0; + + tipc_printbuf_init(&pb, buf, buf_size); + + spin_lock_bh(&bc_lock); + + tipc_printf(&pb, "Link <%s>\n" + " Window:%u packets\n", + bcl->name, bcl->queue_limit[0]); + tipc_printf(&pb, " RX packets:%u fragments:%u/%u bundles:%u/%u\n", + bcl->stats.recv_info, + bcl->stats.recv_fragments, + bcl->stats.recv_fragmented, + bcl->stats.recv_bundles, + bcl->stats.recv_bundled); + tipc_printf(&pb, " TX packets:%u fragments:%u/%u bundles:%u/%u\n", + bcl->stats.sent_info, + bcl->stats.sent_fragments, + bcl->stats.sent_fragmented, + bcl->stats.sent_bundles, + bcl->stats.sent_bundled); + tipc_printf(&pb, " RX naks:%u defs:%u dups:%u\n", + bcl->stats.recv_nacks, + bcl->stats.deferred_recv, + bcl->stats.duplicates); + tipc_printf(&pb, " TX naks:%u acks:%u dups:%u\n", + bcl->stats.sent_nacks, + bcl->stats.sent_acks, + bcl->stats.retransmitted); + tipc_printf(&pb, " Congestion bearer:%u link:%u Send queue max:%u avg:%u\n", + bcl->stats.bearer_congs, + bcl->stats.link_congs, + bcl->stats.max_queue_sz, + bcl->stats.queue_sz_counts + ? (bcl->stats.accu_queue_sz / bcl->stats.queue_sz_counts) + : 0); + + spin_unlock_bh(&bc_lock); + return tipc_printbuf_validate(&pb); +} + +int tipc_bclink_reset_stats(void) +{ + if (!bcl) + return -ENOPROTOOPT; + + spin_lock_bh(&bc_lock); + memset(&bcl->stats, 0, sizeof(bcl->stats)); + spin_unlock_bh(&bc_lock); + return 0; +} + +int tipc_bclink_set_queue_limits(u32 limit) +{ + if (!bcl) + return -ENOPROTOOPT; + if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN)) + return -EINVAL; + + spin_lock_bh(&bc_lock); + tipc_link_set_queue_limits(bcl, limit); + spin_unlock_bh(&bc_lock); + return 0; +} + +void tipc_bclink_init(void) +{ + INIT_LIST_HEAD(&bcbearer->bearer.cong_links); + bcbearer->bearer.media = &bcbearer->media; + bcbearer->media.send_msg = tipc_bcbearer_send; + sprintf(bcbearer->media.name, "tipc-broadcast"); + + INIT_LIST_HEAD(&bcl->waiting_ports); + bcl->next_out_no = 1; + spin_lock_init(&bclink->node.lock); + bcl->owner = &bclink->node; + bcl->max_pkt = MAX_PKT_DEFAULT_MCAST; + tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT); + bcl->b_ptr = &bcbearer->bearer; + bcl->state = WORKING_WORKING; + strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME); +} + +void tipc_bclink_stop(void) +{ + spin_lock_bh(&bc_lock); + tipc_link_stop(bcl); + spin_unlock_bh(&bc_lock); + + memset(bclink, 0, sizeof(*bclink)); + memset(bcbearer, 0, sizeof(*bcbearer)); +} + + +/** + * tipc_nmap_add - add a node to a node map + */ + +void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node) +{ + int n = tipc_node(node); + int w = n / WSIZE; + u32 mask = (1 << (n % WSIZE)); + + if ((nm_ptr->map[w] & mask) == 0) { + nm_ptr->count++; + nm_ptr->map[w] |= mask; + } +} + +/** + * tipc_nmap_remove - remove a node from a node map + */ + +void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node) +{ + int n = tipc_node(node); + int w = n / WSIZE; + u32 mask = (1 << (n % WSIZE)); + + if ((nm_ptr->map[w] & mask) != 0) { + nm_ptr->map[w] &= ~mask; + nm_ptr->count--; + } +} + +/** + * tipc_nmap_diff - find differences between node maps + * @nm_a: input node map A + * @nm_b: input node map B + * @nm_diff: output node map A-B (i.e. nodes of A that are not in B) + */ + +static void tipc_nmap_diff(struct tipc_node_map *nm_a, + struct tipc_node_map *nm_b, + struct tipc_node_map *nm_diff) +{ + int stop = ARRAY_SIZE(nm_a->map); + int w; + int b; + u32 map; + + memset(nm_diff, 0, sizeof(*nm_diff)); + for (w = 0; w < stop; w++) { + map = nm_a->map[w] ^ (nm_a->map[w] & nm_b->map[w]); + nm_diff->map[w] = map; + if (map != 0) { + for (b = 0 ; b < WSIZE; b++) { + if (map & (1 << b)) + nm_diff->count++; + } + } + } +} + +/** + * 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) { + 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 new file mode 100644 index 00000000..55713940 --- /dev/null +++ b/net/tipc/bcast.h @@ -0,0 +1,105 @@ +/* + * net/tipc/bcast.h: Include file for TIPC broadcast code + * + * Copyright (c) 2003-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_BCAST_H +#define _TIPC_BCAST_H + +#define MAX_NODES 4096 +#define WSIZE 32 + +/** + * 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]; +}; + +#define PLSIZE 32 + +/** + * 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_port_list { + int count; + struct tipc_port_list *next; + u32 ports[PLSIZE]; +}; + + +struct tipc_node; + +extern const char tipc_bclink_name[]; + +void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node); +void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node); + +/** + * tipc_nmap_equal - test for equality of node maps + */ + +static inline int tipc_nmap_equal(struct tipc_node_map *nm_a, struct tipc_node_map *nm_b) +{ + 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); + +void tipc_bclink_init(void); +void tipc_bclink_stop(void); +void tipc_bclink_add_node(u32 addr); +void tipc_bclink_remove_node(u32 addr); +struct tipc_node *tipc_bclink_retransmit_to(void); +void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked); +int tipc_bclink_send_msg(struct sk_buff *buf); +void tipc_bclink_recv_pkt(struct sk_buff *buf); +u32 tipc_bclink_get_last_sent(void); +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(void); + +#endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c new file mode 100644 index 00000000..5dfd89c4 --- /dev/null +++ b/net/tipc/bearer.c @@ -0,0 +1,629 @@ +/* + * net/tipc/bearer.c: TIPC bearer code + * + * Copyright (c) 1996-2006, Ericsson AB + * Copyright (c) 2004-2006, 2010-2011, 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" +#include "bearer.h" +#include "discover.h" + +#define MAX_ADDR_STR 32 + +static struct tipc_media *media_list[MAX_MEDIA]; +static u32 media_count; + +struct tipc_bearer tipc_bearers[MAX_BEARERS]; + +static void bearer_disable(struct tipc_bearer *b_ptr); + +/** + * media_name_valid - validate media name + * + * Returns 1 if media name is valid, otherwise 0. + */ + +static int media_name_valid(const char *name) +{ + u32 len; + + len = strlen(name); + if ((len + 1) > TIPC_MAX_MEDIA_NAME) + return 0; + return strspn(name, tipc_alphabet) == len; +} + +/** + * tipc_media_find - locates specified media object by name + */ + +struct tipc_media *tipc_media_find(const char *name) +{ + u32 i; + + for (i = 0; i < media_count; i++) { + if (!strcmp(media_list[i]->name, name)) + return media_list[i]; + } + return NULL; +} + +/** + * media_find_id - locates specified media object by type identifier + */ + +static struct tipc_media *media_find_id(u8 type) +{ + u32 i; + + for (i = 0; i < media_count; i++) { + if (media_list[i]->type_id == type) + return media_list[i]; + } + return NULL; +} + +/** + * tipc_register_media - register a media type + * + * Bearers for this media type must be activated separately at a later stage. + */ + +int tipc_register_media(struct tipc_media *m_ptr) +{ + int res = -EINVAL; + + write_lock_bh(&tipc_net_lock); + + if (!media_name_valid(m_ptr->name)) + goto exit; + if ((m_ptr->bcast_addr.media_id != m_ptr->type_id) || + !m_ptr->bcast_addr.broadcast) + goto exit; + if (m_ptr->priority > TIPC_MAX_LINK_PRI) + goto exit; + if ((m_ptr->tolerance < TIPC_MIN_LINK_TOL) || + (m_ptr->tolerance > TIPC_MAX_LINK_TOL)) + goto exit; + if (media_count >= MAX_MEDIA) + goto exit; + if (tipc_media_find(m_ptr->name) || media_find_id(m_ptr->type_id)) + goto exit; + + media_list[media_count] = m_ptr; + media_count++; + res = 0; +exit: + write_unlock_bh(&tipc_net_lock); + if (res) + warn("Media <%s> registration error\n", m_ptr->name); + return res; +} + +/** + * tipc_media_addr_printf - record media address in print buffer + */ + +void tipc_media_addr_printf(struct print_buf *pb, struct tipc_media_addr *a) +{ + char addr_str[MAX_ADDR_STR]; + struct tipc_media *m_ptr; + + m_ptr = media_find_id(a->media_id); + + if (m_ptr && !m_ptr->addr2str(a, addr_str, sizeof(addr_str))) + tipc_printf(pb, "%s(%s)", m_ptr->name, addr_str); + else { + u32 i; + + tipc_printf(pb, "UNKNOWN(%u)", a->media_id); + for (i = 0; i < sizeof(a->value); i++) + tipc_printf(pb, "-%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; + + read_lock_bh(&tipc_net_lock); + for (i = 0; i < media_count; i++) { + tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME, + media_list[i]->name, + strlen(media_list[i]->name) + 1); + } + read_unlock_bh(&tipc_net_lock); + 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) + * + * Returns 1 if bearer name is valid, otherwise 0. + */ + +static int bearer_name_validate(const char *name, + struct tipc_bearer_names *name_parts) +{ + char name_copy[TIPC_MAX_BEARER_NAME]; + char *media_name; + char *if_name; + u32 media_len; + u32 if_len; + + /* copy bearer name & ensure length is OK */ + + name_copy[TIPC_MAX_BEARER_NAME - 1] = 0; + /* need above in case non-Posix strncpy() doesn't pad with nulls */ + strncpy(name_copy, name, TIPC_MAX_BEARER_NAME); + if (name_copy[TIPC_MAX_BEARER_NAME - 1] != 0) + return 0; + + /* ensure all component parts of bearer name are present */ + + media_name = name_copy; + if_name = strchr(media_name, ':'); + if (if_name == NULL) + return 0; + *(if_name++) = 0; + media_len = if_name - media_name; + if_len = strlen(if_name) + 1; + + /* validate component parts of bearer name */ + + if ((media_len <= 1) || (media_len > TIPC_MAX_MEDIA_NAME) || + (if_len <= 1) || (if_len > TIPC_MAX_IF_NAME) || + (strspn(media_name, tipc_alphabet) != (media_len - 1)) || + (strspn(if_name, tipc_alphabet) != (if_len - 1))) + return 0; + + /* return bearer name components, if necessary */ + + if (name_parts) { + strcpy(name_parts->media_name, media_name); + strcpy(name_parts->if_name, if_name); + } + return 1; +} + +/** + * tipc_bearer_find - locates bearer object with matching bearer name + */ + +struct tipc_bearer *tipc_bearer_find(const char *name) +{ + struct tipc_bearer *b_ptr; + u32 i; + + for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) { + if (b_ptr->active && (!strcmp(b_ptr->name, name))) + return b_ptr; + } + return NULL; +} + +/** + * tipc_bearer_find_interface - locates bearer object with matching interface name + */ + +struct tipc_bearer *tipc_bearer_find_interface(const char *if_name) +{ + struct tipc_bearer *b_ptr; + char *b_if_name; + u32 i; + + for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) { + if (!b_ptr->active) + continue; + b_if_name = strchr(b_ptr->name, ':') + 1; + if (!strcmp(b_if_name, if_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_ptr; + int i, j; + + buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME)); + if (!buf) + return NULL; + + read_lock_bh(&tipc_net_lock); + for (i = 0; i < media_count; i++) { + for (j = 0; j < MAX_BEARERS; j++) { + b_ptr = &tipc_bearers[j]; + if (b_ptr->active && (b_ptr->media == media_list[i])) { + tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME, + b_ptr->name, + strlen(b_ptr->name) + 1); + } + } + } + read_unlock_bh(&tipc_net_lock); + return buf; +} + +void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest) +{ + tipc_nmap_add(&b_ptr->nodes, dest); + tipc_bcbearer_sort(); + tipc_disc_add_dest(b_ptr->link_req); +} + +void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest) +{ + tipc_nmap_remove(&b_ptr->nodes, dest); + tipc_bcbearer_sort(); + tipc_disc_remove_dest(b_ptr->link_req); +} + +/* + * bearer_push(): Resolve bearer congestion. Force the waiting + * links to push out their unsent packets, one packet per link + * per iteration, until all packets are gone or congestion reoccurs. + * 'tipc_net_lock' is read_locked when this function is called + * bearer.lock must be taken before calling + * Returns binary true(1) ore false(0) + */ +static int bearer_push(struct tipc_bearer *b_ptr) +{ + u32 res = 0; + struct tipc_link *ln, *tln; + + if (b_ptr->blocked) + return 0; + + while (!list_empty(&b_ptr->cong_links) && (res != PUSH_FAILED)) { + list_for_each_entry_safe(ln, tln, &b_ptr->cong_links, link_list) { + res = tipc_link_push_packet(ln); + if (res == PUSH_FAILED) + break; + if (res == PUSH_FINISHED) + list_move_tail(&ln->link_list, &b_ptr->links); + } + } + return list_empty(&b_ptr->cong_links); +} + +void tipc_bearer_lock_push(struct tipc_bearer *b_ptr) +{ + spin_lock_bh(&b_ptr->lock); + bearer_push(b_ptr); + spin_unlock_bh(&b_ptr->lock); +} + + +/* + * Interrupt enabling new requests after bearer congestion or blocking: + * See bearer_send(). + */ +void tipc_continue(struct tipc_bearer *b_ptr) +{ + spin_lock_bh(&b_ptr->lock); + if (!list_empty(&b_ptr->cong_links)) + tipc_k_signal((Handler)tipc_bearer_lock_push, (unsigned long)b_ptr); + b_ptr->blocked = 0; + spin_unlock_bh(&b_ptr->lock); +} + +/* + * Schedule link for sending of messages after the bearer + * has been deblocked by 'continue()'. This method is called + * when somebody tries to send a message via this link while + * the bearer is congested. 'tipc_net_lock' is in read_lock here + * bearer.lock is busy + */ + +static void tipc_bearer_schedule_unlocked(struct tipc_bearer *b_ptr, + struct tipc_link *l_ptr) +{ + list_move_tail(&l_ptr->link_list, &b_ptr->cong_links); +} + +/* + * Schedule link for sending of messages after the bearer + * has been deblocked by 'continue()'. This method is called + * when somebody tries to send a message via this link while + * the bearer is congested. 'tipc_net_lock' is in read_lock here, + * bearer.lock is free + */ + +void tipc_bearer_schedule(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr) +{ + spin_lock_bh(&b_ptr->lock); + tipc_bearer_schedule_unlocked(b_ptr, l_ptr); + spin_unlock_bh(&b_ptr->lock); +} + + +/* + * tipc_bearer_resolve_congestion(): Check if there is bearer congestion, + * and if there is, try to resolve it before returning. + * 'tipc_net_lock' is read_locked when this function is called + */ +int tipc_bearer_resolve_congestion(struct tipc_bearer *b_ptr, + struct tipc_link *l_ptr) +{ + int res = 1; + + if (list_empty(&b_ptr->cong_links)) + return 1; + spin_lock_bh(&b_ptr->lock); + if (!bearer_push(b_ptr)) { + tipc_bearer_schedule_unlocked(b_ptr, l_ptr); + res = 0; + } + spin_unlock_bh(&b_ptr->lock); + return res; +} + +/** + * tipc_bearer_congested - determines if bearer is currently congested + */ + +int tipc_bearer_congested(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr) +{ + if (unlikely(b_ptr->blocked)) + return 1; + if (likely(list_empty(&b_ptr->cong_links))) + return 0; + return !tipc_bearer_resolve_congestion(b_ptr, l_ptr); +} + +/** + * tipc_enable_bearer - enable bearer with the given name + */ + +int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority) +{ + struct tipc_bearer *b_ptr; + struct tipc_media *m_ptr; + struct tipc_bearer_names b_names; + char addr_string[16]; + u32 bearer_id; + u32 with_this_prio; + u32 i; + int res = -EINVAL; + + if (!tipc_own_addr) { + warn("Bearer <%s> rejected, not supported in standalone mode\n", + name); + return -ENOPROTOOPT; + } + if (!bearer_name_validate(name, &b_names)) { + warn("Bearer <%s> rejected, illegal name\n", name); + 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; + res = 0; /* accept any node in own cluster */ + } else if (in_own_cluster(disc_domain)) + res = 0; /* accept specified node in own cluster */ + } + if (res) { + warn("Bearer <%s> rejected, illegal discovery domain\n", name); + return -EINVAL; + } + if ((priority > TIPC_MAX_LINK_PRI) && + (priority != TIPC_MEDIA_LINK_PRI)) { + warn("Bearer <%s> rejected, illegal priority\n", name); + return -EINVAL; + } + + write_lock_bh(&tipc_net_lock); + + m_ptr = tipc_media_find(b_names.media_name); + if (!m_ptr) { + warn("Bearer <%s> rejected, media <%s> not registered\n", name, + b_names.media_name); + goto exit; + } + + if (priority == TIPC_MEDIA_LINK_PRI) + priority = m_ptr->priority; + +restart: + bearer_id = MAX_BEARERS; + with_this_prio = 1; + for (i = MAX_BEARERS; i-- != 0; ) { + if (!tipc_bearers[i].active) { + bearer_id = i; + continue; + } + if (!strcmp(name, tipc_bearers[i].name)) { + warn("Bearer <%s> rejected, already enabled\n", name); + goto exit; + } + if ((tipc_bearers[i].priority == priority) && + (++with_this_prio > 2)) { + if (priority-- == 0) { + warn("Bearer <%s> rejected, duplicate priority\n", + name); + goto exit; + } + warn("Bearer <%s> priority adjustment required %u->%u\n", + name, priority + 1, priority); + goto restart; + } + } + if (bearer_id >= MAX_BEARERS) { + warn("Bearer <%s> rejected, bearer limit reached (%u)\n", + name, MAX_BEARERS); + goto exit; + } + + b_ptr = &tipc_bearers[bearer_id]; + strcpy(b_ptr->name, name); + res = m_ptr->enable_bearer(b_ptr); + if (res) { + warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); + goto exit; + } + + b_ptr->identity = bearer_id; + b_ptr->media = m_ptr; + b_ptr->tolerance = m_ptr->tolerance; + b_ptr->window = m_ptr->window; + b_ptr->net_plane = bearer_id + 'A'; + b_ptr->active = 1; + b_ptr->priority = priority; + INIT_LIST_HEAD(&b_ptr->cong_links); + INIT_LIST_HEAD(&b_ptr->links); + spin_lock_init(&b_ptr->lock); + + res = tipc_disc_create(b_ptr, &m_ptr->bcast_addr, disc_domain); + if (res) { + bearer_disable(b_ptr); + warn("Bearer <%s> rejected, discovery object creation failed\n", + name); + goto exit; + } + info("Enabled bearer <%s>, discovery domain %s, priority %u\n", + name, tipc_addr_string_fill(addr_string, disc_domain), priority); +exit: + write_unlock_bh(&tipc_net_lock); + return res; +} + +/** + * tipc_block_bearer(): Block the bearer with the given name, + * and reset all its links + */ + +int tipc_block_bearer(const char *name) +{ + struct tipc_bearer *b_ptr = NULL; + struct tipc_link *l_ptr; + struct tipc_link *temp_l_ptr; + + read_lock_bh(&tipc_net_lock); + b_ptr = tipc_bearer_find(name); + if (!b_ptr) { + warn("Attempt to block unknown bearer <%s>\n", name); + read_unlock_bh(&tipc_net_lock); + return -EINVAL; + } + + info("Blocking bearer <%s>\n", name); + spin_lock_bh(&b_ptr->lock); + b_ptr->blocked = 1; + list_splice_init(&b_ptr->cong_links, &b_ptr->links); + list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { + struct tipc_node *n_ptr = l_ptr->owner; + + spin_lock_bh(&n_ptr->lock); + tipc_link_reset(l_ptr); + spin_unlock_bh(&n_ptr->lock); + } + spin_unlock_bh(&b_ptr->lock); + read_unlock_bh(&tipc_net_lock); + return 0; +} + +/** + * bearer_disable - + * + * Note: This routine assumes caller holds tipc_net_lock. + */ + +static void bearer_disable(struct tipc_bearer *b_ptr) +{ + struct tipc_link *l_ptr; + struct tipc_link *temp_l_ptr; + + info("Disabling bearer <%s>\n", b_ptr->name); + spin_lock_bh(&b_ptr->lock); + b_ptr->blocked = 1; + b_ptr->media->disable_bearer(b_ptr); + list_splice_init(&b_ptr->cong_links, &b_ptr->links); + list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { + tipc_link_delete(l_ptr); + } + if (b_ptr->link_req) + tipc_disc_delete(b_ptr->link_req); + spin_unlock_bh(&b_ptr->lock); + memset(b_ptr, 0, sizeof(struct tipc_bearer)); +} + +int tipc_disable_bearer(const char *name) +{ + struct tipc_bearer *b_ptr; + int res; + + write_lock_bh(&tipc_net_lock); + b_ptr = tipc_bearer_find(name); + if (b_ptr == NULL) { + warn("Attempt to disable unknown bearer <%s>\n", name); + res = -EINVAL; + } else { + bearer_disable(b_ptr); + res = 0; + } + write_unlock_bh(&tipc_net_lock); + return res; +} + + + +void tipc_bearer_stop(void) +{ + u32 i; + + for (i = 0; i < MAX_BEARERS; i++) { + if (tipc_bearers[i].active) + bearer_disable(&tipc_bearers[i]); + } + media_count = 0; +} diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h new file mode 100644 index 00000000..d3eac56b --- /dev/null +++ b/net/tipc/bearer.h @@ -0,0 +1,227 @@ +/* + * net/tipc/bearer.h: Include file for TIPC bearer code + * + * Copyright (c) 1996-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_BEARER_H +#define _TIPC_BEARER_H + +#include "bcast.h" + +#define MAX_BEARERS 2 +#define MAX_MEDIA 2 + +/* + * Identifiers associated with TIPC message header media address info + * + * - address info field is 20 bytes long + * - media type identifier located at offset 3 + * - remaining bytes vary according to media type + */ + +#define TIPC_MEDIA_ADDR_SIZE 20 +#define TIPC_MEDIA_TYPE_OFFSET 3 + +/* + * Identifiers of supported TIPC media types + */ +#define TIPC_MEDIA_TYPE_ETH 1 + +/* + * struct tipc_media_addr - destination address used by TIPC bearers + * @value: address info (format defined by media) + * @media_id: TIPC media type identifier + * @broadcast: non-zero if address is a broadcast address + */ + +struct tipc_media_addr { + u8 value[TIPC_MEDIA_ADDR_SIZE]; + u8 media_id; + u8 broadcast; +}; + +struct tipc_bearer; + +/** + * struct tipc_media - TIPC media information available to internal users + * @send_msg: routine which handles buffer transmission + * @enable_bearer: routine which enables a bearer + * @disable_bearer: routine which disables a bearer + * @addr2str: routine which converts media address to string + * @str2addr: routine which converts media address from string + * @addr2msg: routine which converts media address to protocol message area + * @msg2addr: routine which converts media address from protocol message area + * @bcast_addr: media address used in broadcasting + * @priority: default link (and bearer) priority + * @tolerance: default time (in ms) before declaring link failure + * @window: default window (in packets) before declaring link congestion + * @type_id: TIPC media identifier + * @name: media name + */ + +struct tipc_media { + int (*send_msg)(struct sk_buff *buf, + struct tipc_bearer *b_ptr, + struct tipc_media_addr *dest); + int (*enable_bearer)(struct tipc_bearer *b_ptr); + void (*disable_bearer)(struct tipc_bearer *b_ptr); + int (*addr2str)(struct tipc_media_addr *a, char *str_buf, int str_size); + int (*str2addr)(struct tipc_media_addr *a, char *str_buf); + int (*addr2msg)(struct tipc_media_addr *a, char *msg_area); + int (*msg2addr)(struct tipc_media_addr *a, char *msg_area); + struct tipc_media_addr bcast_addr; + u32 priority; + u32 tolerance; + u32 window; + u32 type_id; + char name[TIPC_MAX_MEDIA_NAME]; +}; + +/** + * struct tipc_bearer - TIPC bearer structure + * @usr_handle: pointer to additional media-specific information about bearer + * @mtu: max packet size bearer can support + * @blocked: non-zero if bearer is blocked + * @lock: spinlock for controlling access to bearer + * @addr: media-specific address associated with bearer + * @name: bearer name (format = media:interface) + * @media: ptr to media structure associated with bearer + * @priority: default link priority for bearer + * @window: default window size for bearer + * @tolerance: default link tolerance for bearer + * @identity: array index of this bearer within TIPC bearer array + * @link_req: ptr to (optional) structure making periodic link setup requests + * @links: list of non-congested links associated with bearer + * @cong_links: list of congested links associated with bearer + * @active: non-zero if bearer structure is represents a bearer + * @net_plane: network plane ('A' through 'H') currently associated with bearer + * @nodes: indicates which nodes in cluster can be reached through bearer + * + * Note: media-specific code is responsible for initialization of the fields + * indicated below when a bearer is enabled; TIPC's generic bearer code takes + * care of initializing all other fields. + */ +struct tipc_bearer { + void *usr_handle; /* initalized by media */ + u32 mtu; /* initalized by media */ + int blocked; /* initalized by media */ + struct tipc_media_addr addr; /* initalized by media */ + char name[TIPC_MAX_BEARER_NAME]; + spinlock_t lock; + struct tipc_media *media; + u32 priority; + u32 window; + u32 tolerance; + u32 identity; + struct tipc_link_req *link_req; + struct list_head links; + struct list_head cong_links; + int active; + char net_plane; + struct tipc_node_map nodes; +}; + +struct tipc_bearer_names { + char media_name[TIPC_MAX_MEDIA_NAME]; + char if_name[TIPC_MAX_IF_NAME]; +}; + +struct tipc_link; + +extern struct tipc_bearer tipc_bearers[]; + +/* + * TIPC routines available to supported media types + */ +int tipc_register_media(struct tipc_media *m_ptr); + +void tipc_recv_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr); + +int tipc_block_bearer(const char *name); +void tipc_continue(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); + +/* + * Routines made available to TIPC by supported media types + */ +int tipc_eth_media_start(void); +void tipc_eth_media_stop(void); + +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(struct print_buf *pb, struct tipc_media_addr *a); +struct sk_buff *tipc_media_get_names(void); + +struct sk_buff *tipc_bearer_get_names(void); +void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest); +void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest); +void tipc_bearer_schedule(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr); +struct tipc_bearer *tipc_bearer_find(const char *name); +struct tipc_bearer *tipc_bearer_find_interface(const char *if_name); +struct tipc_media *tipc_media_find(const char *name); +int tipc_bearer_resolve_congestion(struct tipc_bearer *b_ptr, + struct tipc_link *l_ptr); +int tipc_bearer_congested(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr); +void tipc_bearer_stop(void); +void tipc_bearer_lock_push(struct tipc_bearer *b_ptr); + + +/** + * tipc_bearer_send- sends buffer to destination over bearer + * + * Returns true (1) if successful, or false (0) if unable to send + * + * IMPORTANT: + * The media send routine must not alter the buffer being passed in + * as it may be needed for later retransmission! + * + * If the media send routine returns a non-zero value (indicating that + * it was unable to send the buffer), it must: + * 1) mark the bearer as blocked, + * 2) call tipc_continue() once the bearer is able to send again. + * Media types that are unable to meet these two critera must ensure their + * send routine always returns success -- even if the buffer was not sent -- + * and let TIPC's link code deal with the undelivered message. + */ + +static inline int tipc_bearer_send(struct tipc_bearer *b_ptr, + struct sk_buff *buf, + struct tipc_media_addr *dest) +{ + return !b_ptr->media->send_msg(buf, b_ptr, dest); +} + +#endif /* _TIPC_BEARER_H */ diff --git a/net/tipc/config.c b/net/tipc/config.c new file mode 100644 index 00000000..f76d3b15 --- /dev/null +++ b/net/tipc/config.c @@ -0,0 +1,498 @@ +/* + * net/tipc/config.c: TIPC configuration management code + * + * Copyright (c) 2002-2006, Ericsson AB + * Copyright (c) 2004-2007, 2010-2011, 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 "port.h" +#include "name_table.h" +#include "config.h" + +static u32 config_port_ref; + +static DEFINE_SPINLOCK(config_lock); + +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; +} + +#define MAX_STATS_INFO 2000 + +static struct sk_buff *tipc_show_stats(void) +{ + struct sk_buff *buf; + struct tlv_desc *rep_tlv; + struct print_buf pb; + 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(MAX_STATS_INFO)); + if (buf == NULL) + return NULL; + + rep_tlv = (struct tlv_desc *)buf->data; + tipc_printbuf_init(&pb, (char *)TLV_DATA(rep_tlv), MAX_STATS_INFO); + + tipc_printf(&pb, "TIPC version " TIPC_MOD_VER "\n"); + + /* Use additional tipc_printf()'s to return more info ... */ + + str_len = tipc_printbuf_validate(&pb); + 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)"); + + /* + * Must temporarily release configuration spinlock while switching into + * networking mode as it calls tipc_eth_media_start(), which may sleep. + * Releasing the lock is harmless as other locally-issued configuration + * commands won't occur until this one completes, and remotely-issued + * configuration commands can't be received until a local configuration + * command to enable the first bearer is received and processed. + */ + + spin_unlock_bh(&config_lock); + tipc_core_start_net(addr); + spin_lock_bh(&config_lock); + return tipc_cfg_reply_none(); +} + +static struct sk_buff *cfg_set_remote_mng(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)); + tipc_remote_management = (value != 0); + return tipc_cfg_reply_none(); +} + +static struct sk_buff *cfg_set_max_publications(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 < 1 || value > 65535) + return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE + " (max publications must be 1-65535)"); + tipc_max_publications = value; + return tipc_cfg_reply_none(); +} + +static struct sk_buff *cfg_set_max_subscriptions(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 < 1 || value > 65535) + return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE + " (max subscriptions must be 1-65535"); + tipc_max_subscriptions = value; + return tipc_cfg_reply_none(); +} + +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; + + spin_lock_bh(&config_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(orig_node == tipc_own_addr)) { + /* command is permitted */ + } else if (cmd >= 0x8000) { + rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (cannot be done remotely)"); + goto exit; + } else if (!tipc_remote_management) { + rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NO_REMOTE); + goto exit; + } else if (cmd >= 0x4000) { + u32 domain = 0; + + if ((tipc_nametbl_translate(TIPC_ZM_SRV, 0, &domain) == 0) || + (domain != orig_node)) { + rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_ZONE_MSTR); + 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_port_get_ports(); + break; + case TIPC_CMD_SET_LOG_SIZE: + rep_tlv_buf = tipc_log_resize_cmd(req_tlv_area, req_tlv_space); + break; + case TIPC_CMD_DUMP_LOG: + rep_tlv_buf = tipc_log_dump(); + 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_REMOTE_MNG: + rep_tlv_buf = cfg_set_remote_mng(); + break; + case TIPC_CMD_SET_MAX_PORTS: + rep_tlv_buf = cfg_set_max_ports(); + break; + case TIPC_CMD_SET_MAX_PUBL: + rep_tlv_buf = cfg_set_max_publications(); + break; + case TIPC_CMD_SET_MAX_SUBSCR: + rep_tlv_buf = cfg_set_max_subscriptions(); + break; + case TIPC_CMD_SET_NETID: + rep_tlv_buf = cfg_set_netid(); + break; + case TIPC_CMD_GET_REMOTE_MNG: + rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_remote_management); + break; + case TIPC_CMD_GET_MAX_PORTS: + rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); + break; + case TIPC_CMD_GET_MAX_PUBL: + rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_publications); + break; + case TIPC_CMD_GET_MAX_SUBSCR: + rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_subscriptions); + 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: + 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; + } + + /* Return reply buffer */ +exit: + spin_unlock_bh(&config_lock); + return rep_tlv_buf; +} + +static void cfg_named_msg_event(void *userdata, + u32 port_ref, + struct sk_buff **buf, + const unchar *msg, + u32 size, + u32 importance, + struct tipc_portid const *orig, + struct tipc_name_seq const *dest) +{ + struct tipc_cfg_msg_hdr *req_hdr; + struct tipc_cfg_msg_hdr *rep_hdr; + struct sk_buff *rep_buf; + + /* Validate configuration message header (ignore invalid message) */ + + req_hdr = (struct tipc_cfg_msg_hdr *)msg; + if ((size < sizeof(*req_hdr)) || + (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) || + (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) { + warn("Invalid configuration message discarded\n"); + return; + } + + /* Generate reply for request (if can't, return request) */ + + rep_buf = tipc_cfg_do_cmd(orig->node, + ntohs(req_hdr->tcm_type), + msg + sizeof(*req_hdr), + size - sizeof(*req_hdr), + BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr)); + if (rep_buf) { + skb_push(rep_buf, sizeof(*rep_hdr)); + rep_hdr = (struct tipc_cfg_msg_hdr *)rep_buf->data; + memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr)); + rep_hdr->tcm_len = htonl(rep_buf->len); + rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST); + } else { + rep_buf = *buf; + *buf = NULL; + } + + /* NEED TO ADD CODE TO HANDLE FAILED SEND (SUCH AS CONGESTION) */ + tipc_send_buf2port(port_ref, orig, rep_buf, rep_buf->len); +} + +int tipc_cfg_init(void) +{ + struct tipc_name_seq seq; + int res; + + res = tipc_createport(NULL, TIPC_CRITICAL_IMPORTANCE, + NULL, NULL, NULL, + NULL, cfg_named_msg_event, NULL, + NULL, &config_port_ref); + if (res) + goto failed; + + seq.type = TIPC_CFG_SRV; + seq.lower = seq.upper = tipc_own_addr; + res = tipc_publish(config_port_ref, TIPC_ZONE_SCOPE, &seq); + if (res) + goto failed; + + return 0; + +failed: + err("Unable to create configuration service\n"); + return res; +} + +void tipc_cfg_stop(void) +{ + if (config_port_ref) { + tipc_deleteport(config_port_ref); + config_port_ref = 0; + } +} diff --git a/net/tipc/config.h b/net/tipc/config.h new file mode 100644 index 00000000..80da6ebc --- /dev/null +++ b/net/tipc/config.h @@ -0,0 +1,71 @@ +/* + * 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); + +int tipc_cfg_init(void); +void tipc_cfg_stop(void); + +#endif diff --git a/net/tipc/core.c b/net/tipc/core.c new file mode 100644 index 00000000..68eba03e --- /dev/null +++ b/net/tipc/core.c @@ -0,0 +1,203 @@ +/* + * net/tipc/core.c: TIPC module code + * + * Copyright (c) 2003-2006, Ericsson AB + * Copyright (c) 2005-2006, 2010-2011, 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 <linux/module.h> + +#include "core.h" +#include "ref.h" +#include "name_table.h" +#include "subscr.h" +#include "config.h" + + +#ifndef CONFIG_TIPC_PORTS +#define CONFIG_TIPC_PORTS 8191 +#endif + +#ifndef CONFIG_TIPC_LOG +#define CONFIG_TIPC_LOG 0 +#endif + +/* global variables used by multiple sub-systems within TIPC */ + +int tipc_random; + +const char tipc_alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."; + +/* configurable TIPC parameters */ + +u32 tipc_own_addr; +int tipc_max_ports; +int tipc_max_subscriptions; +int tipc_max_publications; +int tipc_net_id; +int tipc_remote_management; + + +/** + * 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; +} + +/** + * tipc_core_stop_net - shut down TIPC networking sub-systems + */ + +static void tipc_core_stop_net(void) +{ + tipc_net_stop(); + tipc_eth_media_stop(); +} + +/** + * start_net - start TIPC networking sub-systems + */ + +int tipc_core_start_net(unsigned long addr) +{ + int res; + + res = tipc_net_start(addr); + if (!res) + res = tipc_eth_media_start(); + if (res) + tipc_core_stop_net(); + return res; +} + +/** + * tipc_core_stop - switch TIPC from SINGLE NODE to NOT RUNNING mode + */ + +static void tipc_core_stop(void) +{ + tipc_netlink_stop(); + tipc_handler_stop(); + tipc_cfg_stop(); + tipc_subscr_stop(); + tipc_nametbl_stop(); + tipc_ref_table_stop(); + tipc_socket_stop(); + tipc_log_resize(0); +} + +/** + * tipc_core_start - switch TIPC from NOT RUNNING to SINGLE NODE mode + */ + +static int tipc_core_start(void) +{ + int res; + + get_random_bytes(&tipc_random, sizeof(tipc_random)); + + res = tipc_handler_start(); + if (!res) + res = tipc_ref_table_init(tipc_max_ports, tipc_random); + if (!res) + res = tipc_nametbl_init(); + if (!res) + res = tipc_k_signal((Handler)tipc_subscr_start, 0); + if (!res) + res = tipc_k_signal((Handler)tipc_cfg_init, 0); + if (!res) + res = tipc_netlink_start(); + if (!res) + res = tipc_socket_init(); + if (res) + tipc_core_stop(); + + return res; +} + + +static int __init tipc_init(void) +{ + int res; + + if (tipc_log_resize(CONFIG_TIPC_LOG) != 0) + warn("Unable to create log buffer\n"); + + info("Activated (version " TIPC_MOD_VER ")\n"); + + tipc_own_addr = 0; + tipc_remote_management = 1; + tipc_max_publications = 10000; + tipc_max_subscriptions = 2000; + tipc_max_ports = CONFIG_TIPC_PORTS; + tipc_net_id = 4711; + + res = tipc_core_start(); + if (res) + err("Unable to start in single node mode\n"); + else + info("Started in single node mode\n"); + return res; +} + +static void __exit tipc_exit(void) +{ + tipc_core_stop_net(); + tipc_core_stop(); + info("Deactivated\n"); +} + +module_init(tipc_init); +module_exit(tipc_exit); + +MODULE_DESCRIPTION("TIPC: Transparent Inter Process Communication"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(TIPC_MOD_VER); diff --git a/net/tipc/core.h b/net/tipc/core.h new file mode 100644 index 00000000..13837e0e --- /dev/null +++ b/net/tipc/core.h @@ -0,0 +1,264 @@ +/* + * net/tipc/core.h: Include file for TIPC global declarations + * + * Copyright (c) 2005-2006, Ericsson AB + * Copyright (c) 2005-2007, 2010-2011, 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_CORE_H +#define _TIPC_CORE_H + +#include <linux/tipc.h> +#include <linux/tipc_config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <asm/uaccess.h> +#include <linux/interrupt.h> +#include <linux/atomic.h> +#include <asm/hardirq.h> +#include <linux/netdevice.h> +#include <linux/in.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + + +#define TIPC_MOD_VER "2.0.0" + +struct tipc_msg; /* msg.h */ +struct print_buf; /* log.h */ + +/* + * TIPC system monitoring code + */ + +/* + * TIPC's print buffer subsystem supports the following print buffers: + * + * TIPC_NULL : null buffer (i.e. print nowhere) + * TIPC_CONS : system console + * TIPC_LOG : TIPC log buffer + * &buf : user-defined buffer (struct print_buf *) + * + * Note: TIPC_LOG is configured to echo its output to the system console; + * user-defined buffers can be configured to do the same thing. + */ +extern struct print_buf *const TIPC_NULL; +extern struct print_buf *const TIPC_CONS; +extern struct print_buf *const TIPC_LOG; + +void tipc_printf(struct print_buf *, const char *fmt, ...); + +/* + * TIPC_OUTPUT is the destination print buffer for system messages. + */ + +#ifndef TIPC_OUTPUT +#define TIPC_OUTPUT TIPC_LOG +#endif + +#define err(fmt, arg...) tipc_printf(TIPC_OUTPUT, \ + KERN_ERR "TIPC: " fmt, ## arg) +#define warn(fmt, arg...) tipc_printf(TIPC_OUTPUT, \ + KERN_WARNING "TIPC: " fmt, ## arg) +#define info(fmt, arg...) tipc_printf(TIPC_OUTPUT, \ + KERN_NOTICE "TIPC: " fmt, ## arg) + +#ifdef CONFIG_TIPC_DEBUG + +/* + * DBG_OUTPUT is the destination print buffer for debug messages. + */ + +#ifndef DBG_OUTPUT +#define DBG_OUTPUT TIPC_LOG +#endif + +#define dbg(fmt, arg...) tipc_printf(DBG_OUTPUT, KERN_DEBUG fmt, ## arg); + +#define msg_dbg(msg, txt) tipc_msg_dbg(DBG_OUTPUT, msg, txt); + +void tipc_msg_dbg(struct print_buf *, struct tipc_msg *, const char *); + +#else + +#define dbg(fmt, arg...) do {} while (0) +#define msg_dbg(msg, txt) do {} while (0) + +#define tipc_msg_dbg(buf, msg, txt) do {} while (0) + +#endif + + +/* + * TIPC-specific error codes + */ + +#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */ + +/* + * Global configuration variables + */ + +extern u32 tipc_own_addr; +extern int tipc_max_ports; +extern int tipc_max_subscriptions; +extern int tipc_max_publications; +extern int tipc_net_id; +extern int tipc_remote_management; + +/* + * Other global variables + */ + +extern int tipc_random; +extern const char tipc_alphabet[]; + + +/* + * Routines available to privileged subsystems + */ + +extern int tipc_core_start_net(unsigned long); +extern int tipc_handler_start(void); +extern void tipc_handler_stop(void); +extern int tipc_netlink_start(void); +extern void tipc_netlink_stop(void); +extern int tipc_socket_init(void); +extern void tipc_socket_stop(void); + +/* + * TIPC timer and signal code + */ + +typedef void (*Handler) (unsigned long); + +u32 tipc_k_signal(Handler routine, unsigned long argument); + +/** + * 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; +}; + +#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; +} + +extern struct sk_buff *tipc_buf_acquire(u32 size); + +#endif diff --git a/net/tipc/discover.c b/net/tipc/discover.c new file mode 100644 index 00000000..c630a21b --- /dev/null +++ b/net/tipc/discover.c @@ -0,0 +1,402 @@ +/* + * net/tipc/discover.c + * + * Copyright (c) 2003-2006, Ericsson AB + * Copyright (c) 2005-2006, 2010-2011, Wind River Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "link.h" +#include "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 */ + + +/** + * struct tipc_link_req - information about an ongoing link setup request + * @bearer: bearer issuing requests + * @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) + * @buf: request message to be (repeatedly) sent + * @timer: timer governing period between requests + * @timer_intv: current interval between requests (in ms) + */ +struct tipc_link_req { + struct tipc_bearer *bearer; + struct tipc_media_addr dest; + u32 domain; + int num_nodes; + struct sk_buff *buf; + struct timer_list timer; + unsigned int timer_intv; +}; + +/** + * tipc_disc_init_msg - initialize a link setup message + * @type: message type (request or response) + * @dest_domain: network domain of node(s) which should respond to message + * @b_ptr: ptr to bearer issuing message + */ + +static struct sk_buff *tipc_disc_init_msg(u32 type, + u32 dest_domain, + struct tipc_bearer *b_ptr) +{ + struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE); + struct tipc_msg *msg; + + if (buf) { + msg = buf_msg(buf); + tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain); + msg_set_non_seq(msg, 1); + msg_set_node_sig(msg, tipc_random); + msg_set_dest_domain(msg, dest_domain); + msg_set_bc_netid(msg, tipc_net_id); + b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg)); + } + return buf; +} + +/** + * disc_dupl_alert - issue node address duplication alert + * @b_ptr: pointer to bearer detecting duplication + * @node_addr: duplicated node address + * @media_addr: media address advertised by duplicated node + */ + +static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr, + struct tipc_media_addr *media_addr) +{ + char node_addr_str[16]; + char media_addr_str[64]; + struct print_buf pb; + + tipc_addr_string_fill(node_addr_str, node_addr); + tipc_printbuf_init(&pb, media_addr_str, sizeof(media_addr_str)); + tipc_media_addr_printf(&pb, media_addr); + tipc_printbuf_validate(&pb); + warn("Duplicate %s using %s seen on <%s>\n", + node_addr_str, media_addr_str, b_ptr->name); +} + +/** + * tipc_disc_recv_msg - handle incoming link setup message (request or response) + * @buf: buffer containing message + * @b_ptr: bearer that message arrived on + */ + +void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr) +{ + struct tipc_node *n_ptr; + struct tipc_link *link; + struct tipc_media_addr media_addr; + struct sk_buff *rbuf; + struct tipc_msg *msg = buf_msg(buf); + u32 dest = msg_dest_domain(msg); + u32 orig = msg_prevnode(msg); + u32 net_id = msg_bc_netid(msg); + u32 type = msg_type(msg); + u32 signature = msg_node_sig(msg); + int addr_mismatch; + int link_fully_up; + + media_addr.broadcast = 1; + b_ptr->media->msg2addr(&media_addr, msg_media_addr(msg)); + kfree_skb(buf); + + /* Ensure message from node is valid and communication is permitted */ + if (net_id != tipc_net_id) + return; + if (media_addr.broadcast) + return; + if (!tipc_addr_domain_valid(dest)) + return; + if (!tipc_addr_node_valid(orig)) + return; + if (orig == tipc_own_addr) { + if (memcmp(&media_addr, &b_ptr->addr, sizeof(media_addr))) + disc_dupl_alert(b_ptr, tipc_own_addr, &media_addr); + return; + } + if (!tipc_in_scope(dest, tipc_own_addr)) + return; + if (!tipc_in_scope(b_ptr->link_req->domain, orig)) + return; + + /* Locate structure corresponding to requesting node */ + n_ptr = tipc_node_find(orig); + if (!n_ptr) { + n_ptr = tipc_node_create(orig); + if (!n_ptr) + return; + } + tipc_node_lock(n_ptr); + + /* Prepare to validate requesting node's signature and media address */ + link = n_ptr->links[b_ptr->identity]; + addr_mismatch = (link != NULL) && + memcmp(&link->media_addr, &media_addr, sizeof(media_addr)); + + /* + * Ensure discovery message's signature is correct + * + * If signature is incorrect and there is no working link to the node, + * accept the new signature but invalidate all existing links to the + * node so they won't re-activate without a new discovery message. + * + * If signature is incorrect and the requested link to the node is + * working, accept the new signature. (This is an instance of delayed + * rediscovery, where a link endpoint was able to re-establish contact + * with its peer endpoint on a node that rebooted before receiving a + * discovery message from that node.) + * + * If signature is incorrect and there is a working link to the node + * that is not the requested link, reject the request (must be from + * a duplicate node). + */ + if (signature != n_ptr->signature) { + if (n_ptr->working_links == 0) { + struct tipc_link *curr_link; + int i; + + for (i = 0; i < MAX_BEARERS; i++) { + curr_link = n_ptr->links[i]; + if (curr_link) { + memset(&curr_link->media_addr, 0, + sizeof(media_addr)); + tipc_link_reset(curr_link); + } + } + addr_mismatch = (link != NULL); + } else if (tipc_link_is_up(link) && !addr_mismatch) { + /* delayed rediscovery */ + } else { + disc_dupl_alert(b_ptr, orig, &media_addr); + tipc_node_unlock(n_ptr); + return; + } + n_ptr->signature = signature; + } + + /* + * Ensure requesting node's media address is correct + * + * If media address doesn't match and the link is working, reject the + * request (must be from a duplicate node). + * + * If media address doesn't match and the link is not working, accept + * the new media address and reset the link to ensure it starts up + * cleanly. + */ + + if (addr_mismatch) { + if (tipc_link_is_up(link)) { + disc_dupl_alert(b_ptr, orig, &media_addr); + tipc_node_unlock(n_ptr); + return; + } else { + memcpy(&link->media_addr, &media_addr, + sizeof(media_addr)); + tipc_link_reset(link); + } + } + + /* Create a link endpoint for this bearer, if necessary */ + if (!link) { + link = tipc_link_create(n_ptr, b_ptr, &media_addr); + if (!link) { + tipc_node_unlock(n_ptr); + return; + } + } + + /* Accept discovery message & send response, if necessary */ + link_fully_up = link_working_working(link); + + if ((type == DSC_REQ_MSG) && !link_fully_up && !b_ptr->blocked) { + rbuf = tipc_disc_init_msg(DSC_RESP_MSG, orig, b_ptr); + if (rbuf) { + b_ptr->media->send_msg(rbuf, b_ptr, &media_addr); + kfree_skb(rbuf); + } + } + + tipc_node_unlock(n_ptr); +} + +/** + * disc_update - update frequency of periodic link setup requests + * @req: ptr to link request structure + * + * Reinitiates discovery process if discovery object has no associated nodes + * and is either not currently searching or is searching at a slow rate + */ + +static void disc_update(struct tipc_link_req *req) +{ + if (!req->num_nodes) { + 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); + } + } +} + +/** + * tipc_disc_add_dest - increment set of discovered nodes + * @req: ptr to link request structure + */ + +void tipc_disc_add_dest(struct tipc_link_req *req) +{ + req->num_nodes++; +} + +/** + * tipc_disc_remove_dest - decrement set of discovered nodes + * @req: ptr to link request structure + */ + +void tipc_disc_remove_dest(struct tipc_link_req *req) +{ + req->num_nodes--; + disc_update(req); +} + +/** + * disc_send_msg - send link setup request message + * @req: ptr to link request structure + */ + +static void disc_send_msg(struct tipc_link_req *req) +{ + if (!req->bearer->blocked) + tipc_bearer_send(req->bearer, req->buf, &req->dest); +} + +/** + * disc_timeout - send a periodic link setup request + * @req: 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) +{ + int max_delay; + + spin_lock_bh(&req->bearer->lock); + + /* Stop searching if only desired node has been found */ + + if (tipc_node(req->domain) && req->num_nodes) { + req->timer_intv = TIPC_LINK_REQ_INACTIVE; + goto exit; + } + + /* + * Send discovery message, then update discovery timer + * + * Keep doubling time between requests until limit is reached; + * hold at fast polling rate if don't have any associated nodes, + * otherwise hold at slow polling rate + */ + + disc_send_msg(req); + + req->timer_intv *= 2; + if (req->num_nodes) + max_delay = TIPC_LINK_REQ_SLOW; + else + max_delay = TIPC_LINK_REQ_FAST; + if (req->timer_intv > max_delay) + req->timer_intv = max_delay; + + k_start_timer(&req->timer, req->timer_intv); +exit: + spin_unlock_bh(&req->bearer->lock); +} + +/** + * tipc_disc_create - create object to send periodic link setup requests + * @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, u32 dest_domain) +{ + struct tipc_link_req *req; + + req = kmalloc(sizeof(*req), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + req->buf = tipc_disc_init_msg(DSC_REQ_MSG, dest_domain, b_ptr); + if (!req->buf) { + kfree(req); + return -ENOMSG; + } + + memcpy(&req->dest, dest, sizeof(*dest)); + req->bearer = b_ptr; + req->domain = dest_domain; + req->num_nodes = 0; + req->timer_intv = TIPC_LINK_REQ_INIT; + k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req); + k_start_timer(&req->timer, req->timer_intv); + b_ptr->link_req = req; + disc_send_msg(req); + return 0; +} + +/** + * tipc_disc_delete - destroy object sending periodic link setup requests + * @req: ptr to link request structure + */ + +void tipc_disc_delete(struct tipc_link_req *req) +{ + k_cancel_timer(&req->timer); + k_term_timer(&req->timer); + kfree_skb(req->buf); + kfree(req); +} + diff --git a/net/tipc/discover.h b/net/tipc/discover.h new file mode 100644 index 00000000..75b67c40 --- /dev/null +++ b/net/tipc/discover.h @@ -0,0 +1,49 @@ +/* + * net/tipc/discover.h + * + * Copyright (c) 2003-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_DISCOVER_H +#define _TIPC_DISCOVER_H + +struct tipc_link_req; + +int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest, + u32 dest_domain); +void tipc_disc_delete(struct tipc_link_req *req); +void tipc_disc_add_dest(struct tipc_link_req *req); +void tipc_disc_remove_dest(struct tipc_link_req *req); +void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr); + +#endif diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c new file mode 100644 index 00000000..527e3f0e --- /dev/null +++ b/net/tipc/eth_media.c @@ -0,0 +1,398 @@ +/* + * net/tipc/eth_media.c: Ethernet bearer support for TIPC + * + * Copyright (c) 2001-2007, Ericsson AB + * Copyright (c) 2005-2008, 2011, 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 "bearer.h" + +#define MAX_ETH_BEARERS MAX_BEARERS + +#define ETH_ADDR_OFFSET 4 /* message header offset of MAC address */ + +/** + * struct eth_bearer - Ethernet bearer data structure + * @bearer: ptr to associated "generic" bearer structure + * @dev: ptr to associated Ethernet network device + * @tipc_packet_type: used in binding TIPC to Ethernet driver + * @cleanup: work item used when disabling bearer + */ + +struct eth_bearer { + struct tipc_bearer *bearer; + struct net_device *dev; + struct packet_type tipc_packet_type; + struct work_struct cleanup; +}; + +static struct tipc_media eth_media_info; +static struct eth_bearer eth_bearers[MAX_ETH_BEARERS]; +static int eth_started; +static struct notifier_block notifier; + +/** + * eth_media_addr_set - initialize Ethernet media address structure + * + * Media-dependent "value" field stores MAC address in first 6 bytes + * and zeroes out the remaining bytes. + */ + +static void eth_media_addr_set(struct tipc_media_addr *a, char *mac) +{ + memcpy(a->value, mac, ETH_ALEN); + memset(a->value + ETH_ALEN, 0, sizeof(a->value) - ETH_ALEN); + a->media_id = TIPC_MEDIA_TYPE_ETH; + a->broadcast = !memcmp(mac, eth_media_info.bcast_addr.value, ETH_ALEN); +} + +/** + * send_msg - send a TIPC message out over an Ethernet interface + */ + +static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr, + struct tipc_media_addr *dest) +{ + struct sk_buff *clone; + struct net_device *dev; + int delta; + + clone = skb_clone(buf, GFP_ATOMIC); + if (!clone) + return 0; + + dev = ((struct eth_bearer *)(tb_ptr->usr_handle))->dev; + delta = dev->hard_header_len - skb_headroom(buf); + + if ((delta > 0) && + pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) { + kfree_skb(clone); + return 0; + } + + skb_reset_network_header(clone); + clone->dev = dev; + dev_hard_header(clone, dev, ETH_P_TIPC, dest->value, + dev->dev_addr, clone->len); + dev_queue_xmit(clone); + return 0; +} + +/** + * recv_msg - handle incoming TIPC message from an Ethernet interface + * + * Accept only packets explicitly sent to this node, or broadcast packets; + * ignores packets sent using Ethernet multicast, and traffic sent to other + * nodes (which can happen if interface is running in promiscuous mode). + */ + +static int recv_msg(struct sk_buff *buf, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct eth_bearer *eb_ptr = (struct eth_bearer *)pt->af_packet_priv; + + if (!net_eq(dev_net(dev), &init_net)) { + kfree_skb(buf); + return 0; + } + + if (likely(eb_ptr->bearer)) { + if (likely(buf->pkt_type <= PACKET_BROADCAST)) { + buf->next = NULL; + tipc_recv_msg(buf, eb_ptr->bearer); + return 0; + } + } + kfree_skb(buf); + return 0; +} + +/** + * enable_bearer - attach TIPC bearer to an Ethernet interface + */ + +static int enable_bearer(struct tipc_bearer *tb_ptr) +{ + struct net_device *dev = NULL; + struct net_device *pdev = NULL; + struct eth_bearer *eb_ptr = ð_bearers[0]; + struct eth_bearer *stop = ð_bearers[MAX_ETH_BEARERS]; + char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1; + int pending_dev = 0; + + /* Find unused Ethernet bearer structure */ + + while (eb_ptr->dev) { + if (!eb_ptr->bearer) + pending_dev++; + if (++eb_ptr == stop) + return pending_dev ? -EAGAIN : -EDQUOT; + } + + /* Find device with specified name */ + + read_lock(&dev_base_lock); + for_each_netdev(&init_net, pdev) { + if (!strncmp(pdev->name, driver_name, IFNAMSIZ)) { + dev = pdev; + dev_hold(dev); + break; + } + } + read_unlock(&dev_base_lock); + if (!dev) + return -ENODEV; + + /* Create Ethernet bearer for device */ + + eb_ptr->dev = dev; + eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC); + eb_ptr->tipc_packet_type.dev = dev; + eb_ptr->tipc_packet_type.func = recv_msg; + eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr; + INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list)); + dev_add_pack(&eb_ptr->tipc_packet_type); + + /* Associate TIPC bearer with Ethernet bearer */ + + eb_ptr->bearer = tb_ptr; + tb_ptr->usr_handle = (void *)eb_ptr; + tb_ptr->mtu = dev->mtu; + tb_ptr->blocked = 0; + eth_media_addr_set(&tb_ptr->addr, (char *)dev->dev_addr); + return 0; +} + +/** + * cleanup_bearer - break association between Ethernet bearer and interface + * + * This routine must be invoked from a work queue because it can sleep. + */ + +static void cleanup_bearer(struct work_struct *work) +{ + struct eth_bearer *eb_ptr = + container_of(work, struct eth_bearer, cleanup); + + dev_remove_pack(&eb_ptr->tipc_packet_type); + dev_put(eb_ptr->dev); + eb_ptr->dev = NULL; +} + +/** + * disable_bearer - detach TIPC bearer from an Ethernet interface + * + * Mark Ethernet bearer as inactive so that incoming buffers are thrown away, + * then get worker thread to complete bearer cleanup. (Can't do cleanup + * here because cleanup code needs to sleep and caller holds spinlocks.) + */ + +static void disable_bearer(struct tipc_bearer *tb_ptr) +{ + struct eth_bearer *eb_ptr = (struct eth_bearer *)tb_ptr->usr_handle; + + eb_ptr->bearer = NULL; + INIT_WORK(&eb_ptr->cleanup, cleanup_bearer); + schedule_work(&eb_ptr->cleanup); +} + +/** + * recv_notification - handle device updates from OS + * + * Change the state of the Ethernet bearer (if any) associated with the + * specified device. + */ + +static int recv_notification(struct notifier_block *nb, unsigned long evt, + void *dv) +{ + struct net_device *dev = (struct net_device *)dv; + struct eth_bearer *eb_ptr = ð_bearers[0]; + struct eth_bearer *stop = ð_bearers[MAX_ETH_BEARERS]; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + while ((eb_ptr->dev != dev)) { + if (++eb_ptr == stop) + return NOTIFY_DONE; /* couldn't find device */ + } + if (!eb_ptr->bearer) + return NOTIFY_DONE; /* bearer had been disabled */ + + eb_ptr->bearer->mtu = dev->mtu; + + switch (evt) { + case NETDEV_CHANGE: + if (netif_carrier_ok(dev)) + tipc_continue(eb_ptr->bearer); + else + tipc_block_bearer(eb_ptr->bearer->name); + break; + case NETDEV_UP: + tipc_continue(eb_ptr->bearer); + break; + case NETDEV_DOWN: + tipc_block_bearer(eb_ptr->bearer->name); + break; + case NETDEV_CHANGEMTU: + case NETDEV_CHANGEADDR: + tipc_block_bearer(eb_ptr->bearer->name); + tipc_continue(eb_ptr->bearer); + break; + case NETDEV_UNREGISTER: + case NETDEV_CHANGENAME: + tipc_disable_bearer(eb_ptr->bearer->name); + break; + } + return NOTIFY_OK; +} + +/** + * eth_addr2str - convert Ethernet address to string + */ + +static int eth_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size) +{ + if (str_size < 18) /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */ + return 1; + + sprintf(str_buf, "%pM", a->value); + return 0; +} + +/** + * eth_str2addr - convert string to Ethernet address + */ + +static int eth_str2addr(struct tipc_media_addr *a, char *str_buf) +{ + char mac[ETH_ALEN]; + int r; + + r = sscanf(str_buf, "%02x:%02x:%02x:%02x:%02x:%02x", + (u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2], + (u32 *)&mac[3], (u32 *)&mac[4], (u32 *)&mac[5]); + + if (r != ETH_ALEN) + return 1; + + eth_media_addr_set(a, mac); + return 0; +} + +/** + * eth_str2addr - convert Ethernet address format to message header format + */ + +static int eth_addr2msg(struct tipc_media_addr *a, char *msg_area) +{ + memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE); + msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH; + memcpy(msg_area + ETH_ADDR_OFFSET, a->value, ETH_ALEN); + return 0; +} + +/** + * eth_str2addr - convert message header address format to Ethernet format + */ + +static int eth_msg2addr(struct tipc_media_addr *a, char *msg_area) +{ + if (msg_area[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_ETH) + return 1; + + eth_media_addr_set(a, msg_area + ETH_ADDR_OFFSET); + return 0; +} + +/* + * Ethernet media registration info + */ + +static struct tipc_media eth_media_info = { + .send_msg = send_msg, + .enable_bearer = enable_bearer, + .disable_bearer = disable_bearer, + .addr2str = eth_addr2str, + .str2addr = eth_str2addr, + .addr2msg = eth_addr2msg, + .msg2addr = eth_msg2addr, + .bcast_addr = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + TIPC_MEDIA_TYPE_ETH, 1 }, + .priority = TIPC_DEF_LINK_PRI, + .tolerance = TIPC_DEF_LINK_TOL, + .window = TIPC_DEF_LINK_WIN, + .type_id = TIPC_MEDIA_TYPE_ETH, + .name = "eth" +}; + +/** + * tipc_eth_media_start - activate Ethernet bearer support + * + * Register Ethernet media type with TIPC bearer code. Also register + * with OS for notifications about device state changes. + */ + +int tipc_eth_media_start(void) +{ + int res; + + if (eth_started) + return -EINVAL; + + res = tipc_register_media(ð_media_info); + if (res) + return res; + + notifier.notifier_call = &recv_notification; + notifier.priority = 0; + res = register_netdevice_notifier(¬ifier); + if (!res) + eth_started = 1; + return res; +} + +/** + * tipc_eth_media_stop - deactivate Ethernet bearer support + */ + +void tipc_eth_media_stop(void) +{ + if (!eth_started) + return; + + flush_scheduled_work(); + unregister_netdevice_notifier(¬ifier); + eth_started = 0; +} diff --git a/net/tipc/handler.c b/net/tipc/handler.c new file mode 100644 index 00000000..274c98e1 --- /dev/null +++ b/net/tipc/handler.c @@ -0,0 +1,132 @@ +/* + * net/tipc/handler.c: TIPC signal handling + * + * Copyright (c) 2000-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. + */ + +#include "core.h" + +struct queue_item { + struct list_head next_signal; + void (*handler) (unsigned long); + unsigned long data; +}; + +static struct kmem_cache *tipc_queue_item_cache; +static struct list_head signal_queue_head; +static DEFINE_SPINLOCK(qitem_lock); +static int handler_enabled; + +static void process_signal_queue(unsigned long dummy); + +static DECLARE_TASKLET_DISABLED(tipc_tasklet, process_signal_queue, 0); + + +unsigned int tipc_k_signal(Handler routine, unsigned long argument) +{ + struct queue_item *item; + + if (!handler_enabled) { + err("Signal request ignored by handler\n"); + return -ENOPROTOOPT; + } + + spin_lock_bh(&qitem_lock); + item = kmem_cache_alloc(tipc_queue_item_cache, GFP_ATOMIC); + if (!item) { + err("Signal queue out of memory\n"); + spin_unlock_bh(&qitem_lock); + return -ENOMEM; + } + item->handler = routine; + item->data = argument; + list_add_tail(&item->next_signal, &signal_queue_head); + spin_unlock_bh(&qitem_lock); + tasklet_schedule(&tipc_tasklet); + return 0; +} + +static void process_signal_queue(unsigned long dummy) +{ + struct queue_item *__volatile__ item; + struct list_head *l, *n; + + spin_lock_bh(&qitem_lock); + list_for_each_safe(l, n, &signal_queue_head) { + item = list_entry(l, struct queue_item, next_signal); + list_del(&item->next_signal); + spin_unlock_bh(&qitem_lock); + item->handler(item->data); + spin_lock_bh(&qitem_lock); + kmem_cache_free(tipc_queue_item_cache, item); + } + spin_unlock_bh(&qitem_lock); +} + +int tipc_handler_start(void) +{ + tipc_queue_item_cache = + kmem_cache_create("tipc_queue_items", sizeof(struct queue_item), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!tipc_queue_item_cache) + return -ENOMEM; + + INIT_LIST_HEAD(&signal_queue_head); + tasklet_enable(&tipc_tasklet); + handler_enabled = 1; + return 0; +} + +void tipc_handler_stop(void) +{ + struct list_head *l, *n; + struct queue_item *item; + + if (!handler_enabled) + return; + + handler_enabled = 0; + tasklet_disable(&tipc_tasklet); + tasklet_kill(&tipc_tasklet); + + spin_lock_bh(&qitem_lock); + list_for_each_safe(l, n, &signal_queue_head) { + item = list_entry(l, struct queue_item, next_signal); + list_del(&item->next_signal); + kmem_cache_free(tipc_queue_item_cache, item); + } + spin_unlock_bh(&qitem_lock); + + kmem_cache_destroy(tipc_queue_item_cache); +} + diff --git a/net/tipc/link.c b/net/tipc/link.c new file mode 100644 index 00000000..b4b9b301 --- /dev/null +++ b/net/tipc/link.c @@ -0,0 +1,3174 @@ +/* + * net/tipc/link.c: TIPC link code + * + * Copyright (c) 1996-2007, Ericsson AB + * Copyright (c) 2004-2007, 2010-2011, Wind River Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "link.h" +#include "port.h" +#include "name_distr.h" +#include "discover.h" +#include "config.h" + + +/* + * Out-of-range value for link session numbers + */ + +#define INVALID_SESSION 0x10000 + +/* + * Link state events: + */ + +#define STARTING_EVT 856384768 /* link processing trigger */ +#define TRAFFIC_MSG_EVT 560815u /* rx'd ??? */ +#define TIMEOUT_EVT 560817u /* link timer expired */ + +/* + * The following two 'message types' is really just implementation + * data conveniently stored in the message header. + * They must not be considered part of the protocol + */ +#define OPEN_MSG 0 +#define CLOSED_MSG 1 + +/* + * State value stored in 'exp_msg_count' + */ + +#define START_CHANGEOVER 100000u + +/** + * struct tipc_link_name - deconstructed link name + * @addr_local: network address of node at this end + * @if_local: name of interface at this end + * @addr_peer: network address of node at far end + * @if_peer: name of interface at far end + */ + +struct tipc_link_name { + u32 addr_local; + char if_local[TIPC_MAX_IF_NAME]; + u32 addr_peer; + char if_peer[TIPC_MAX_IF_NAME]; +}; + +static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, + struct sk_buff *buf); +static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf); +static int link_recv_changeover_msg(struct tipc_link **l_ptr, + struct sk_buff **buf); +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance); +static int link_send_sections_long(struct tipc_port *sender, + struct iovec const *msg_sect, + u32 num_sect, unsigned int total_len, + u32 destnode); +static void link_check_defragm_bufs(struct tipc_link *l_ptr); +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 link_start(struct tipc_link *l_ptr); +static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf); + +/* + * Simple link routines + */ + +static unsigned int align(unsigned int i) +{ + return (i + 3) & ~3u; +} + +static void link_init_max_pkt(struct tipc_link *l_ptr) +{ + u32 max_pkt; + + max_pkt = (l_ptr->b_ptr->mtu & ~3); + if (max_pkt > MAX_MSG_SIZE) + max_pkt = MAX_MSG_SIZE; + + l_ptr->max_pkt_target = max_pkt; + if (l_ptr->max_pkt_target < MAX_PKT_DEFAULT) + l_ptr->max_pkt = l_ptr->max_pkt_target; + else + l_ptr->max_pkt = MAX_PKT_DEFAULT; + + l_ptr->max_pkt_probes = 0; +} + +static u32 link_next_sent(struct tipc_link *l_ptr) +{ + if (l_ptr->next_out) + return buf_seqno(l_ptr->next_out); + return mod(l_ptr->next_out_no); +} + +static u32 link_last_sent(struct tipc_link *l_ptr) +{ + return mod(link_next_sent(l_ptr) - 1); +} + +/* + * Simple non-static link routines (i.e. referenced outside this file) + */ + +int tipc_link_is_up(struct tipc_link *l_ptr) +{ + if (!l_ptr) + return 0; + return link_working_working(l_ptr) || link_working_unknown(l_ptr); +} + +int tipc_link_is_active(struct tipc_link *l_ptr) +{ + return (l_ptr->owner->active_links[0] == l_ptr) || + (l_ptr->owner->active_links[1] == l_ptr); +} + +/** + * link_name_validate - validate & (optionally) deconstruct tipc_link name + * @name - ptr to link name string + * @name_parts - ptr to area for link name components (or NULL if not needed) + * + * Returns 1 if link name is valid, otherwise 0. + */ + +static int link_name_validate(const char *name, + struct tipc_link_name *name_parts) +{ + char name_copy[TIPC_MAX_LINK_NAME]; + char *addr_local; + char *if_local; + char *addr_peer; + char *if_peer; + char dummy; + u32 z_local, c_local, n_local; + u32 z_peer, c_peer, n_peer; + u32 if_local_len; + u32 if_peer_len; + + /* copy link name & ensure length is OK */ + + name_copy[TIPC_MAX_LINK_NAME - 1] = 0; + /* need above in case non-Posix strncpy() doesn't pad with nulls */ + strncpy(name_copy, name, TIPC_MAX_LINK_NAME); + if (name_copy[TIPC_MAX_LINK_NAME - 1] != 0) + return 0; + + /* ensure all component parts of link name are present */ + + addr_local = name_copy; + if_local = strchr(addr_local, ':'); + if (if_local == NULL) + return 0; + *(if_local++) = 0; + addr_peer = strchr(if_local, '-'); + if (addr_peer == NULL) + return 0; + *(addr_peer++) = 0; + if_local_len = addr_peer - if_local; + if_peer = strchr(addr_peer, ':'); + if (if_peer == NULL) + return 0; + *(if_peer++) = 0; + if_peer_len = strlen(if_peer) + 1; + + /* validate component parts of link name */ + + if ((sscanf(addr_local, "%u.%u.%u%c", + &z_local, &c_local, &n_local, &dummy) != 3) || + (sscanf(addr_peer, "%u.%u.%u%c", + &z_peer, &c_peer, &n_peer, &dummy) != 3) || + (z_local > 255) || (c_local > 4095) || (n_local > 4095) || + (z_peer > 255) || (c_peer > 4095) || (n_peer > 4095) || + (if_local_len <= 1) || (if_local_len > TIPC_MAX_IF_NAME) || + (if_peer_len <= 1) || (if_peer_len > TIPC_MAX_IF_NAME) || + (strspn(if_local, tipc_alphabet) != (if_local_len - 1)) || + (strspn(if_peer, tipc_alphabet) != (if_peer_len - 1))) + return 0; + + /* return link name components, if necessary */ + + if (name_parts) { + name_parts->addr_local = tipc_addr(z_local, c_local, n_local); + strcpy(name_parts->if_local, if_local); + name_parts->addr_peer = tipc_addr(z_peer, c_peer, n_peer); + strcpy(name_parts->if_peer, if_peer); + } + return 1; +} + +/** + * link_timeout - handle expiration of link timer + * @l_ptr: pointer to link + * + * This routine must not grab "tipc_net_lock" to avoid a potential deadlock conflict + * with tipc_link_delete(). (There is no risk that the node will be deleted by + * another thread because tipc_link_delete() always cancels the link timer before + * tipc_node_delete() is called.) + */ + +static void link_timeout(struct tipc_link *l_ptr) +{ + tipc_node_lock(l_ptr->owner); + + /* update counters used in statistical profiling of send traffic */ + + l_ptr->stats.accu_queue_sz += l_ptr->out_queue_size; + l_ptr->stats.queue_sz_counts++; + + if (l_ptr->first_out) { + struct tipc_msg *msg = buf_msg(l_ptr->first_out); + u32 length = msg_size(msg); + + if ((msg_user(msg) == MSG_FRAGMENTER) && + (msg_type(msg) == FIRST_FRAGMENT)) { + length = msg_size(msg_get_wrapped(msg)); + } + if (length) { + l_ptr->stats.msg_lengths_total += length; + l_ptr->stats.msg_length_counts++; + if (length <= 64) + l_ptr->stats.msg_length_profile[0]++; + else if (length <= 256) + l_ptr->stats.msg_length_profile[1]++; + else if (length <= 1024) + l_ptr->stats.msg_length_profile[2]++; + else if (length <= 4096) + l_ptr->stats.msg_length_profile[3]++; + else if (length <= 16384) + l_ptr->stats.msg_length_profile[4]++; + else if (length <= 32768) + l_ptr->stats.msg_length_profile[5]++; + else + l_ptr->stats.msg_length_profile[6]++; + } + } + + /* do all other link processing performed on a periodic basis */ + + link_check_defragm_bufs(l_ptr); + + link_state_event(l_ptr, TIMEOUT_EVT); + + if (l_ptr->next_out) + tipc_link_push_queue(l_ptr); + + tipc_node_unlock(l_ptr->owner); +} + +static void link_set_timer(struct tipc_link *l_ptr, u32 time) +{ + k_start_timer(&l_ptr->timer, time); +} + +/** + * tipc_link_create - create a new link + * @n_ptr: pointer to associated node + * @b_ptr: pointer to associated bearer + * @media_addr: media address to use when sending messages over link + * + * Returns pointer to link. + */ + +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_link *l_ptr; + struct tipc_msg *msg; + char *if_name; + char addr_string[16]; + u32 peer = n_ptr->addr; + + if (n_ptr->link_cnt >= 2) { + tipc_addr_string_fill(addr_string, n_ptr->addr); + err("Attempt to establish third link to %s\n", addr_string); + return NULL; + } + + if (n_ptr->links[b_ptr->identity]) { + tipc_addr_string_fill(addr_string, n_ptr->addr); + err("Attempt to establish second link on <%s> to %s\n", + b_ptr->name, addr_string); + return NULL; + } + + l_ptr = kzalloc(sizeof(*l_ptr), GFP_ATOMIC); + if (!l_ptr) { + warn("Link creation failed, no memory\n"); + return NULL; + } + + 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), + if_name, + tipc_zone(peer), tipc_cluster(peer), tipc_node(peer)); + /* note: peer i/f name is updated by reset/activate message */ + memcpy(&l_ptr->media_addr, media_addr, sizeof(*media_addr)); + l_ptr->owner = n_ptr; + l_ptr->checkpoint = 1; + l_ptr->peer_session = INVALID_SESSION; + l_ptr->b_ptr = b_ptr; + link_set_supervision_props(l_ptr, b_ptr->tolerance); + l_ptr->state = RESET_UNKNOWN; + + 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); + msg_set_size(msg, sizeof(l_ptr->proto_msg)); + msg_set_session(msg, (tipc_random & 0xffff)); + msg_set_bearer_id(msg, b_ptr->identity); + strcpy((char *)msg_data(msg), if_name); + + l_ptr->priority = b_ptr->priority; + tipc_link_set_queue_limits(l_ptr, b_ptr->window); + + link_init_max_pkt(l_ptr); + + l_ptr->next_out_no = 1; + INIT_LIST_HEAD(&l_ptr->waiting_ports); + + 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); + list_add_tail(&l_ptr->link_list, &b_ptr->links); + tipc_k_signal((Handler)link_start, (unsigned long)l_ptr); + + return l_ptr; +} + +/** + * tipc_link_delete - delete a link + * @l_ptr: pointer to link + * + * Note: 'tipc_net_lock' is write_locked, bearer is locked. + * This routine must not grab the node lock until after link timer cancellation + * to avoid a potential deadlock situation. + */ + +void tipc_link_delete(struct tipc_link *l_ptr) +{ + if (!l_ptr) { + err("Attempt to delete non-existent link\n"); + return; + } + + k_cancel_timer(&l_ptr->timer); + + tipc_node_lock(l_ptr->owner); + tipc_link_reset(l_ptr); + tipc_node_detach_link(l_ptr->owner, l_ptr); + tipc_link_stop(l_ptr); + list_del_init(&l_ptr->link_list); + tipc_node_unlock(l_ptr->owner); + k_term_timer(&l_ptr->timer); + kfree(l_ptr); +} + +static void link_start(struct tipc_link *l_ptr) +{ + tipc_node_lock(l_ptr->owner); + link_state_event(l_ptr, STARTING_EVT); + tipc_node_unlock(l_ptr->owner); +} + +/** + * link_schedule_port - schedule port for deferred sending + * @l_ptr: pointer to link + * @origport: reference to sending port + * @sz: amount of data to be sent + * + * Schedules port for renewed sending of messages after link congestion + * has abated. + */ + +static int link_schedule_port(struct tipc_link *l_ptr, u32 origport, u32 sz) +{ + struct tipc_port *p_ptr; + + spin_lock_bh(&tipc_port_list_lock); + p_ptr = tipc_port_lock(origport); + if (p_ptr) { + if (!p_ptr->wakeup) + goto exit; + if (!list_empty(&p_ptr->wait_list)) + goto exit; + p_ptr->congested = 1; + p_ptr->waiting_pkts = 1 + ((sz - 1) / l_ptr->max_pkt); + list_add_tail(&p_ptr->wait_list, &l_ptr->waiting_ports); + l_ptr->stats.link_congs++; +exit: + tipc_port_unlock(p_ptr); + } + spin_unlock_bh(&tipc_port_list_lock); + return -ELINKCONG; +} + +void tipc_link_wakeup_ports(struct tipc_link *l_ptr, int all) +{ + struct tipc_port *p_ptr; + struct tipc_port *temp_p_ptr; + int win = l_ptr->queue_limit[0] - l_ptr->out_queue_size; + + if (all) + win = 100000; + if (win <= 0) + return; + if (!spin_trylock_bh(&tipc_port_list_lock)) + return; + if (link_congested(l_ptr)) + goto exit; + list_for_each_entry_safe(p_ptr, temp_p_ptr, &l_ptr->waiting_ports, + wait_list) { + if (win <= 0) + break; + list_del_init(&p_ptr->wait_list); + spin_lock_bh(p_ptr->lock); + p_ptr->congested = 0; + p_ptr->wakeup(p_ptr); + win -= p_ptr->waiting_pkts; + spin_unlock_bh(p_ptr->lock); + } + +exit: + spin_unlock_bh(&tipc_port_list_lock); +} + +/** + * link_release_outqueue - purge link's outbound message queue + * @l_ptr: pointer to link + */ + +static void link_release_outqueue(struct tipc_link *l_ptr) +{ + struct sk_buff *buf = l_ptr->first_out; + struct sk_buff *next; + + while (buf) { + next = buf->next; + kfree_skb(buf); + buf = next; + } + l_ptr->first_out = NULL; + l_ptr->out_queue_size = 0; +} + +/** + * tipc_link_reset_fragments - purge link's inbound message fragments queue + * @l_ptr: pointer to link + */ + +void tipc_link_reset_fragments(struct tipc_link *l_ptr) +{ + struct sk_buff *buf = l_ptr->defragm_buf; + struct sk_buff *next; + + while (buf) { + next = buf->next; + kfree_skb(buf); + buf = next; + } + l_ptr->defragm_buf = NULL; +} + +/** + * tipc_link_stop - purge all inbound and outbound messages associated with link + * @l_ptr: pointer to link + */ + +void tipc_link_stop(struct tipc_link *l_ptr) +{ + struct sk_buff *buf; + struct sk_buff *next; + + buf = l_ptr->oldest_deferred_in; + while (buf) { + next = buf->next; + kfree_skb(buf); + buf = next; + } + + buf = l_ptr->first_out; + while (buf) { + next = buf->next; + kfree_skb(buf); + buf = next; + } + + tipc_link_reset_fragments(l_ptr); + + kfree_skb(l_ptr->proto_msg_queue); + l_ptr->proto_msg_queue = NULL; +} + +void tipc_link_reset(struct tipc_link *l_ptr) +{ + struct sk_buff *buf; + u32 prev_state = l_ptr->state; + u32 checkpoint = l_ptr->next_in_no; + int was_active_link = tipc_link_is_active(l_ptr); + + msg_set_session(l_ptr->pmsg, ((msg_session(l_ptr->pmsg) + 1) & 0xffff)); + + /* Link is down, accept any session */ + l_ptr->peer_session = INVALID_SESSION; + + /* Prepare for max packet size negotiation */ + link_init_max_pkt(l_ptr); + + l_ptr->state = RESET_UNKNOWN; + + if ((prev_state == RESET_UNKNOWN) || (prev_state == RESET_RESET)) + return; + + tipc_node_link_down(l_ptr->owner, l_ptr); + tipc_bearer_remove_dest(l_ptr->b_ptr, l_ptr->addr); + + if (was_active_link && tipc_node_active_links(l_ptr->owner) && + l_ptr->owner->permit_changeover) { + l_ptr->reset_checkpoint = checkpoint; + l_ptr->exp_msg_count = START_CHANGEOVER; + } + + /* Clean up all queues: */ + + link_release_outqueue(l_ptr); + kfree_skb(l_ptr->proto_msg_queue); + l_ptr->proto_msg_queue = NULL; + buf = l_ptr->oldest_deferred_in; + while (buf) { + struct sk_buff *next = buf->next; + kfree_skb(buf); + buf = next; + } + if (!list_empty(&l_ptr->waiting_ports)) + tipc_link_wakeup_ports(l_ptr, 1); + + l_ptr->retransm_queue_head = 0; + l_ptr->retransm_queue_size = 0; + l_ptr->last_out = NULL; + l_ptr->first_out = NULL; + l_ptr->next_out = NULL; + l_ptr->unacked_window = 0; + l_ptr->checkpoint = 1; + l_ptr->next_out_no = 1; + l_ptr->deferred_inqueue_sz = 0; + l_ptr->oldest_deferred_in = NULL; + l_ptr->newest_deferred_in = NULL; + l_ptr->fsm_msg_cnt = 0; + l_ptr->stale_count = 0; + link_reset_statistics(l_ptr); +} + + +static void link_activate(struct tipc_link *l_ptr) +{ + 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->b_ptr, l_ptr->addr); +} + +/** + * link_state_event - link finite state machine + * @l_ptr: pointer to link + * @event: state machine event to process + */ + +static void link_state_event(struct tipc_link *l_ptr, unsigned event) +{ + struct tipc_link *other; + u32 cont_intv = l_ptr->continuity_interval; + + if (!l_ptr->started && (event != STARTING_EVT)) + return; /* Not yet. */ + + if (link_blocked(l_ptr)) { + if (event == TIMEOUT_EVT) + link_set_timer(l_ptr, cont_intv); + return; /* Changeover going on */ + } + + switch (l_ptr->state) { + case WORKING_WORKING: + switch (event) { + case TRAFFIC_MSG_EVT: + case ACTIVATE_MSG: + break; + case TIMEOUT_EVT: + if (l_ptr->next_in_no != l_ptr->checkpoint) { + l_ptr->checkpoint = l_ptr->next_in_no; + if (tipc_bclink_acks_missing(l_ptr->owner)) { + tipc_link_send_proto_msg(l_ptr, STATE_MSG, + 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + } else if (l_ptr->max_pkt < l_ptr->max_pkt_target) { + tipc_link_send_proto_msg(l_ptr, STATE_MSG, + 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + } + link_set_timer(l_ptr, cont_intv); + break; + } + l_ptr->state = WORKING_UNKNOWN; + l_ptr->fsm_msg_cnt = 0; + tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv / 4); + break; + case RESET_MSG: + info("Resetting link <%s>, requested by peer\n", + l_ptr->name); + tipc_link_reset(l_ptr); + l_ptr->state = RESET_RESET; + l_ptr->fsm_msg_cnt = 0; + tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + default: + err("Unknown link event %u in WW state\n", event); + } + break; + case WORKING_UNKNOWN: + switch (event) { + case TRAFFIC_MSG_EVT: + case ACTIVATE_MSG: + l_ptr->state = WORKING_WORKING; + l_ptr->fsm_msg_cnt = 0; + link_set_timer(l_ptr, cont_intv); + break; + case RESET_MSG: + info("Resetting link <%s>, requested by peer " + "while probing\n", l_ptr->name); + tipc_link_reset(l_ptr); + l_ptr->state = RESET_RESET; + l_ptr->fsm_msg_cnt = 0; + tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + case TIMEOUT_EVT: + if (l_ptr->next_in_no != l_ptr->checkpoint) { + l_ptr->state = WORKING_WORKING; + l_ptr->fsm_msg_cnt = 0; + l_ptr->checkpoint = l_ptr->next_in_no; + if (tipc_bclink_acks_missing(l_ptr->owner)) { + tipc_link_send_proto_msg(l_ptr, STATE_MSG, + 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + } + link_set_timer(l_ptr, cont_intv); + } else if (l_ptr->fsm_msg_cnt < l_ptr->abort_limit) { + tipc_link_send_proto_msg(l_ptr, STATE_MSG, + 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv / 4); + } else { /* Link has failed */ + warn("Resetting link <%s>, peer not responding\n", + l_ptr->name); + tipc_link_reset(l_ptr); + l_ptr->state = RESET_UNKNOWN; + l_ptr->fsm_msg_cnt = 0; + tipc_link_send_proto_msg(l_ptr, RESET_MSG, + 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + } + break; + default: + err("Unknown link event %u in WU state\n", event); + } + break; + case RESET_UNKNOWN: + switch (event) { + case TRAFFIC_MSG_EVT: + break; + case ACTIVATE_MSG: + other = l_ptr->owner->active_links[0]; + if (other && link_working_unknown(other)) + break; + l_ptr->state = WORKING_WORKING; + l_ptr->fsm_msg_cnt = 0; + link_activate(l_ptr); + tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + case RESET_MSG: + l_ptr->state = RESET_RESET; + l_ptr->fsm_msg_cnt = 0; + tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + case STARTING_EVT: + l_ptr->started = 1; + /* fall through */ + case TIMEOUT_EVT: + tipc_link_send_proto_msg(l_ptr, RESET_MSG, 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + default: + err("Unknown link event %u in RU state\n", event); + } + break; + case RESET_RESET: + switch (event) { + case TRAFFIC_MSG_EVT: + case ACTIVATE_MSG: + other = l_ptr->owner->active_links[0]; + if (other && link_working_unknown(other)) + break; + l_ptr->state = WORKING_WORKING; + l_ptr->fsm_msg_cnt = 0; + link_activate(l_ptr); + tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + case RESET_MSG: + break; + case TIMEOUT_EVT: + tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0); + l_ptr->fsm_msg_cnt++; + link_set_timer(l_ptr, cont_intv); + break; + default: + err("Unknown link event %u in RR state\n", event); + } + break; + default: + err("Unknown link state %u/%u\n", l_ptr->state, event); + } +} + +/* + * link_bundle_buf(): Append contents of a buffer to + * the tail of an existing one. + */ + +static int link_bundle_buf(struct tipc_link *l_ptr, + struct sk_buff *bundler, + struct sk_buff *buf) +{ + struct tipc_msg *bundler_msg = buf_msg(bundler); + struct tipc_msg *msg = buf_msg(buf); + u32 size = msg_size(msg); + u32 bundle_size = msg_size(bundler_msg); + u32 to_pos = align(bundle_size); + u32 pad = to_pos - bundle_size; + + if (msg_user(bundler_msg) != MSG_BUNDLER) + return 0; + if (msg_type(bundler_msg) != OPEN_MSG) + return 0; + if (skb_tailroom(bundler) < (pad + size)) + return 0; + if (l_ptr->max_pkt < (to_pos + size)) + return 0; + + skb_put(bundler, pad + size); + skb_copy_to_linear_data_offset(bundler, to_pos, buf->data, size); + msg_set_size(bundler_msg, to_pos + size); + msg_set_msgcnt(bundler_msg, msg_msgcnt(bundler_msg) + 1); + kfree_skb(buf); + l_ptr->stats.sent_bundled++; + return 1; +} + +static void link_add_to_outqueue(struct tipc_link *l_ptr, + struct sk_buff *buf, + struct tipc_msg *msg) +{ + u32 ack = mod(l_ptr->next_in_no - 1); + u32 seqno = mod(l_ptr->next_out_no++); + + msg_set_word(msg, 2, ((ack << 16) | seqno)); + msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); + buf->next = NULL; + if (l_ptr->first_out) { + l_ptr->last_out->next = buf; + l_ptr->last_out = buf; + } else + l_ptr->first_out = l_ptr->last_out = buf; + + l_ptr->out_queue_size++; + if (l_ptr->out_queue_size > l_ptr->stats.max_queue_sz) + l_ptr->stats.max_queue_sz = l_ptr->out_queue_size; +} + +static void link_add_chain_to_outqueue(struct tipc_link *l_ptr, + struct sk_buff *buf_chain, + u32 long_msgno) +{ + struct sk_buff *buf; + struct tipc_msg *msg; + + if (!l_ptr->next_out) + l_ptr->next_out = buf_chain; + while (buf_chain) { + buf = buf_chain; + buf_chain = buf_chain->next; + + msg = buf_msg(buf); + msg_set_long_msgno(msg, long_msgno); + link_add_to_outqueue(l_ptr, buf, msg); + } +} + +/* + * tipc_link_send_buf() is the 'full path' for messages, called from + * inside TIPC when the 'fast path' in tipc_send_buf + * has failed, and from link_send() + */ + +int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + u32 size = msg_size(msg); + u32 dsz = msg_data_sz(msg); + u32 queue_size = l_ptr->out_queue_size; + u32 imp = tipc_msg_tot_importance(msg); + u32 queue_limit = l_ptr->queue_limit[imp]; + u32 max_packet = l_ptr->max_pkt; + + /* Match msg importance against queue limits: */ + + if (unlikely(queue_size >= queue_limit)) { + if (imp <= TIPC_CRITICAL_IMPORTANCE) { + link_schedule_port(l_ptr, msg_origport(msg), size); + kfree_skb(buf); + return -ELINKCONG; + } + kfree_skb(buf); + if (imp > CONN_MANAGER) { + warn("Resetting link <%s>, send queue full", l_ptr->name); + tipc_link_reset(l_ptr); + } + return dsz; + } + + /* Fragmentation needed ? */ + + if (size > max_packet) + return link_send_long_buf(l_ptr, buf); + + /* Packet can be queued or sent: */ + + if (likely(!tipc_bearer_congested(l_ptr->b_ptr, l_ptr) && + !link_congested(l_ptr))) { + link_add_to_outqueue(l_ptr, buf, msg); + + if (likely(tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr))) { + l_ptr->unacked_window = 0; + } else { + tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); + l_ptr->stats.bearer_congs++; + l_ptr->next_out = buf; + } + return dsz; + } + /* Congestion: can message be bundled ?: */ + + if ((msg_user(msg) != CHANGEOVER_PROTOCOL) && + (msg_user(msg) != MSG_FRAGMENTER)) { + + /* Try adding message to an existing bundle */ + + if (l_ptr->next_out && + link_bundle_buf(l_ptr, l_ptr->last_out, buf)) { + tipc_bearer_resolve_congestion(l_ptr->b_ptr, l_ptr); + return dsz; + } + + /* Try creating a new bundle */ + + if (size <= max_packet * 2 / 3) { + struct sk_buff *bundler = tipc_buf_acquire(max_packet); + struct tipc_msg bundler_hdr; + + if (bundler) { + tipc_msg_init(&bundler_hdr, MSG_BUNDLER, OPEN_MSG, + INT_H_SIZE, l_ptr->addr); + skb_copy_to_linear_data(bundler, &bundler_hdr, + INT_H_SIZE); + skb_trim(bundler, INT_H_SIZE); + link_bundle_buf(l_ptr, bundler, buf); + buf = bundler; + msg = buf_msg(buf); + l_ptr->stats.sent_bundles++; + } + } + } + if (!l_ptr->next_out) + l_ptr->next_out = buf; + link_add_to_outqueue(l_ptr, buf, msg); + tipc_bearer_resolve_congestion(l_ptr->b_ptr, l_ptr); + return dsz; +} + +/* + * tipc_link_send(): same as tipc_link_send_buf(), but the link to use has + * not been selected yet, and the the owner node is not locked + * Called by TIPC internal users, e.g. the name distributor + */ + +int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector) +{ + struct tipc_link *l_ptr; + struct tipc_node *n_ptr; + int res = -ELINKCONG; + + read_lock_bh(&tipc_net_lock); + 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 = tipc_link_send_buf(l_ptr, buf); + else + kfree_skb(buf); + tipc_node_unlock(n_ptr); + } else { + kfree_skb(buf); + } + read_unlock_bh(&tipc_net_lock); + return res; +} + +/* + * tipc_link_send_names - send name table entries to new neighbor + * + * Send routine for bulk delivery of name table messages when contact + * with a new neighbor occurs. No link congestion checking is performed + * because name table messages *must* be delivered. The messages must be + * small enough not to require fragmentation. + * Called without any locks held. + */ + +void tipc_link_send_names(struct list_head *message_list, u32 dest) +{ + struct tipc_node *n_ptr; + struct tipc_link *l_ptr; + struct sk_buff *buf; + struct sk_buff *temp_buf; + + if (list_empty(message_list)) + return; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(dest); + if (n_ptr) { + tipc_node_lock(n_ptr); + l_ptr = n_ptr->active_links[0]; + if (l_ptr) { + /* convert circular list to linear list */ + ((struct sk_buff *)message_list->prev)->next = NULL; + link_add_chain_to_outqueue(l_ptr, + (struct sk_buff *)message_list->next, 0); + tipc_link_push_queue(l_ptr); + INIT_LIST_HEAD(message_list); + } + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); + + /* discard the messages if they couldn't be sent */ + + list_for_each_safe(buf, temp_buf, ((struct sk_buff *)message_list)) { + list_del((struct list_head *)buf); + kfree_skb(buf); + } +} + +/* + * link_send_buf_fast: Entry for data messages where the + * destination link is known and the header is complete, + * inclusive total message length. Very time critical. + * Link is locked. Returns user data length. + */ + +static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf, + u32 *used_max_pkt) +{ + struct tipc_msg *msg = buf_msg(buf); + int res = msg_data_sz(msg); + + if (likely(!link_congested(l_ptr))) { + if (likely(msg_size(msg) <= l_ptr->max_pkt)) { + if (likely(list_empty(&l_ptr->b_ptr->cong_links))) { + link_add_to_outqueue(l_ptr, buf, msg); + if (likely(tipc_bearer_send(l_ptr->b_ptr, buf, + &l_ptr->media_addr))) { + l_ptr->unacked_window = 0; + return res; + } + tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); + l_ptr->stats.bearer_congs++; + l_ptr->next_out = buf; + return res; + } + } else + *used_max_pkt = l_ptr->max_pkt; + } + return tipc_link_send_buf(l_ptr, buf); /* All other cases */ +} + +/* + * tipc_send_buf_fast: Entry for data messages where the + * destination node is known and the header is complete, + * inclusive total message length. + * Returns user data length. + */ +int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode) +{ + struct tipc_link *l_ptr; + struct tipc_node *n_ptr; + int res; + u32 selector = msg_origport(buf_msg(buf)) & 1; + u32 dummy; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(destnode); + if (likely(n_ptr)) { + tipc_node_lock(n_ptr); + l_ptr = n_ptr->active_links[selector]; + if (likely(l_ptr)) { + res = link_send_buf_fast(l_ptr, buf, &dummy); + tipc_node_unlock(n_ptr); + read_unlock_bh(&tipc_net_lock); + return res; + } + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); + res = msg_data_sz(buf_msg(buf)); + tipc_reject_msg(buf, TIPC_ERR_NO_NODE); + return res; +} + + +/* + * tipc_link_send_sections_fast: Entry for messages where the + * destination processor is known and the header is complete, + * except for total message length. + * Returns user data length or errno. + */ +int tipc_link_send_sections_fast(struct tipc_port *sender, + struct iovec const *msg_sect, + const u32 num_sect, + unsigned int total_len, + u32 destaddr) +{ + struct tipc_msg *hdr = &sender->phdr; + struct tipc_link *l_ptr; + struct sk_buff *buf; + struct tipc_node *node; + int res; + u32 selector = msg_origport(hdr) & 1; + +again: + /* + * Try building message using port's max_pkt hint. + * (Must not hold any locks while building message.) + */ + + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, + sender->max_pkt, !sender->user_port, &buf); + + read_lock_bh(&tipc_net_lock); + node = tipc_node_find(destaddr); + if (likely(node)) { + tipc_node_lock(node); + l_ptr = node->active_links[selector]; + if (likely(l_ptr)) { + if (likely(buf)) { + res = link_send_buf_fast(l_ptr, buf, + &sender->max_pkt); +exit: + tipc_node_unlock(node); + read_unlock_bh(&tipc_net_lock); + return res; + } + + /* Exit if build request was invalid */ + + if (unlikely(res < 0)) + goto exit; + + /* Exit if link (or bearer) is congested */ + + if (link_congested(l_ptr) || + !list_empty(&l_ptr->b_ptr->cong_links)) { + res = link_schedule_port(l_ptr, + sender->ref, res); + goto exit; + } + + /* + * Message size exceeds max_pkt hint; update hint, + * then re-try fast path or fragment the message + */ + + sender->max_pkt = l_ptr->max_pkt; + tipc_node_unlock(node); + read_unlock_bh(&tipc_net_lock); + + + if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt) + goto again; + + return link_send_sections_long(sender, msg_sect, + num_sect, total_len, + destaddr); + } + tipc_node_unlock(node); + } + read_unlock_bh(&tipc_net_lock); + + /* Couldn't find a link to the destination node */ + + if (buf) + return tipc_reject_msg(buf, TIPC_ERR_NO_NODE); + if (res >= 0) + return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, + total_len, TIPC_ERR_NO_NODE); + return res; +} + +/* + * link_send_sections_long(): Entry for long messages where the + * destination node is known and the header is complete, + * inclusive total message length. + * Link and bearer congestion status have been checked to be ok, + * and are ignored if they change. + * + * Note that fragments do not use the full link MTU so that they won't have + * to undergo refragmentation if link changeover causes them to be sent + * over another link with an additional tunnel header added as prefix. + * (Refragmentation will still occur if the other link has a smaller MTU.) + * + * Returns user data length or errno. + */ +static int link_send_sections_long(struct tipc_port *sender, + struct iovec const *msg_sect, + u32 num_sect, + unsigned int total_len, + u32 destaddr) +{ + struct tipc_link *l_ptr; + struct tipc_node *node; + struct tipc_msg *hdr = &sender->phdr; + u32 dsz = total_len; + u32 max_pkt, fragm_sz, rest; + struct tipc_msg fragm_hdr; + struct sk_buff *buf, *buf_chain, *prev; + u32 fragm_crs, fragm_rest, hsz, sect_rest; + const unchar *sect_crs; + int curr_sect; + u32 fragm_no; + +again: + fragm_no = 1; + max_pkt = sender->max_pkt - INT_H_SIZE; + /* leave room for tunnel header in case of link changeover */ + fragm_sz = max_pkt - INT_H_SIZE; + /* leave room for fragmentation header in each fragment */ + rest = dsz; + fragm_crs = 0; + fragm_rest = 0; + sect_rest = 0; + sect_crs = NULL; + curr_sect = -1; + + /* Prepare reusable fragment header: */ + + tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT, + INT_H_SIZE, msg_destnode(hdr)); + msg_set_size(&fragm_hdr, max_pkt); + msg_set_fragm_no(&fragm_hdr, 1); + + /* Prepare header of first fragment: */ + + buf_chain = buf = tipc_buf_acquire(max_pkt); + if (!buf) + return -ENOMEM; + buf->next = NULL; + skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE); + hsz = msg_hdr_sz(hdr); + skb_copy_to_linear_data_offset(buf, INT_H_SIZE, hdr, hsz); + + /* Chop up message: */ + + fragm_crs = INT_H_SIZE + hsz; + fragm_rest = fragm_sz - hsz; + + do { /* For all sections */ + u32 sz; + + if (!sect_rest) { + sect_rest = msg_sect[++curr_sect].iov_len; + sect_crs = (const unchar *)msg_sect[curr_sect].iov_base; + } + + if (sect_rest < fragm_rest) + sz = sect_rest; + else + sz = fragm_rest; + + if (likely(!sender->user_port)) { + if (copy_from_user(buf->data + fragm_crs, sect_crs, sz)) { +error: + for (; buf_chain; buf_chain = buf) { + buf = buf_chain->next; + kfree_skb(buf_chain); + } + return -EFAULT; + } + } else + skb_copy_to_linear_data_offset(buf, fragm_crs, + sect_crs, sz); + sect_crs += sz; + sect_rest -= sz; + fragm_crs += sz; + fragm_rest -= sz; + rest -= sz; + + if (!fragm_rest && rest) { + + /* Initiate new fragment: */ + if (rest <= fragm_sz) { + fragm_sz = rest; + msg_set_type(&fragm_hdr, LAST_FRAGMENT); + } else { + msg_set_type(&fragm_hdr, FRAGMENT); + } + msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE); + msg_set_fragm_no(&fragm_hdr, ++fragm_no); + prev = buf; + buf = tipc_buf_acquire(fragm_sz + INT_H_SIZE); + if (!buf) + goto error; + + buf->next = NULL; + prev->next = buf; + skb_copy_to_linear_data(buf, &fragm_hdr, INT_H_SIZE); + fragm_crs = INT_H_SIZE; + fragm_rest = fragm_sz; + } + } while (rest > 0); + + /* + * Now we have a buffer chain. Select a link and check + * that packet size is still OK + */ + node = tipc_node_find(destaddr); + if (likely(node)) { + tipc_node_lock(node); + l_ptr = node->active_links[sender->ref & 1]; + if (!l_ptr) { + tipc_node_unlock(node); + goto reject; + } + if (l_ptr->max_pkt < max_pkt) { + sender->max_pkt = l_ptr->max_pkt; + tipc_node_unlock(node); + for (; buf_chain; buf_chain = buf) { + buf = buf_chain->next; + kfree_skb(buf_chain); + } + goto again; + } + } else { +reject: + for (; buf_chain; buf_chain = buf) { + buf = buf_chain->next; + kfree_skb(buf_chain); + } + return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, + total_len, TIPC_ERR_NO_NODE); + } + + /* Append chain of fragments to send queue & send them */ + + l_ptr->long_msg_seq_no++; + link_add_chain_to_outqueue(l_ptr, buf_chain, l_ptr->long_msg_seq_no); + l_ptr->stats.sent_fragments += fragm_no; + l_ptr->stats.sent_fragmented++; + tipc_link_push_queue(l_ptr); + tipc_node_unlock(node); + return dsz; +} + +/* + * tipc_link_push_packet: Push one unsent packet to the media + */ +u32 tipc_link_push_packet(struct tipc_link *l_ptr) +{ + struct sk_buff *buf = l_ptr->first_out; + u32 r_q_size = l_ptr->retransm_queue_size; + u32 r_q_head = l_ptr->retransm_queue_head; + + /* Step to position where retransmission failed, if any, */ + /* consider that buffers may have been released in meantime */ + + if (r_q_size && buf) { + u32 last = lesser(mod(r_q_head + r_q_size), + link_last_sent(l_ptr)); + u32 first = buf_seqno(buf); + + while (buf && less(first, r_q_head)) { + first = mod(first + 1); + buf = buf->next; + } + l_ptr->retransm_queue_head = r_q_head = first; + l_ptr->retransm_queue_size = r_q_size = mod(last - first); + } + + /* Continue retransmission now, if there is anything: */ + + if (r_q_size && buf) { + msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1)); + msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in); + if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { + l_ptr->retransm_queue_head = mod(++r_q_head); + l_ptr->retransm_queue_size = --r_q_size; + l_ptr->stats.retransmitted++; + return 0; + } else { + l_ptr->stats.bearer_congs++; + return PUSH_FAILED; + } + } + + /* Send deferred protocol message, if any: */ + + buf = l_ptr->proto_msg_queue; + if (buf) { + msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1)); + msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in); + if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { + l_ptr->unacked_window = 0; + kfree_skb(buf); + l_ptr->proto_msg_queue = NULL; + return 0; + } else { + l_ptr->stats.bearer_congs++; + return PUSH_FAILED; + } + } + + /* Send one deferred data message, if send window not full: */ + + buf = l_ptr->next_out; + if (buf) { + struct tipc_msg *msg = buf_msg(buf); + u32 next = msg_seqno(msg); + u32 first = buf_seqno(l_ptr->first_out); + + if (mod(next - first) < l_ptr->queue_limit[0]) { + msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); + msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); + if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { + if (msg_user(msg) == MSG_BUNDLER) + msg_set_type(msg, CLOSED_MSG); + l_ptr->next_out = buf->next; + return 0; + } else { + l_ptr->stats.bearer_congs++; + return PUSH_FAILED; + } + } + } + return PUSH_FINISHED; +} + +/* + * push_queue(): push out the unsent messages of a link where + * congestion has abated. Node is locked + */ +void tipc_link_push_queue(struct tipc_link *l_ptr) +{ + u32 res; + + if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) + return; + + do { + res = tipc_link_push_packet(l_ptr); + } while (!res); + + if (res == PUSH_FAILED) + tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); +} + +static void link_reset_all(unsigned long addr) +{ + struct tipc_node *n_ptr; + char addr_string[16]; + u32 i; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find((u32)addr); + if (!n_ptr) { + read_unlock_bh(&tipc_net_lock); + return; /* node no longer exists */ + } + + tipc_node_lock(n_ptr); + + warn("Resetting all links to %s\n", + tipc_addr_string_fill(addr_string, n_ptr->addr)); + + for (i = 0; i < MAX_BEARERS; i++) { + if (n_ptr->links[i]) { + link_print(n_ptr->links[i], "Resetting link\n"); + tipc_link_reset(n_ptr->links[i]); + } + } + + tipc_node_unlock(n_ptr); + read_unlock_bh(&tipc_net_lock); +} + +static void link_retransmit_failure(struct tipc_link *l_ptr, + struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + + warn("Retransmission failure on link <%s>\n", l_ptr->name); + + if (l_ptr->addr) { + + /* Handle failure on standard link */ + + link_print(l_ptr, "Resetting link\n"); + tipc_link_reset(l_ptr); + + } else { + + /* Handle failure on broadcast link */ + + struct tipc_node *n_ptr; + char addr_string[16]; + + info("Msg seq number: %u, ", msg_seqno(msg)); + info("Outstanding acks: %lu\n", + (unsigned long) TIPC_SKB_CB(buf)->handle); + + n_ptr = tipc_bclink_retransmit_to(); + tipc_node_lock(n_ptr); + + tipc_addr_string_fill(addr_string, n_ptr->addr); + info("Broadcast link info for %s\n", addr_string); + info("Supportable: %d, ", n_ptr->bclink.supportable); + info("Supported: %d, ", n_ptr->bclink.supported); + info("Acked: %u\n", n_ptr->bclink.acked); + info("Last in: %u, ", n_ptr->bclink.last_in); + info("Oos state: %u, ", n_ptr->bclink.oos_state); + info("Last sent: %u\n", n_ptr->bclink.last_sent); + + tipc_k_signal((Handler)link_reset_all, (unsigned long)n_ptr->addr); + + tipc_node_unlock(n_ptr); + + l_ptr->stale_count = 0; + } +} + +void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf, + u32 retransmits) +{ + struct tipc_msg *msg; + + if (!buf) + return; + + msg = buf_msg(buf); + + if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) { + if (l_ptr->retransm_queue_size == 0) { + l_ptr->retransm_queue_head = msg_seqno(msg); + l_ptr->retransm_queue_size = retransmits; + } else { + err("Unexpected retransmit on link %s (qsize=%d)\n", + l_ptr->name, l_ptr->retransm_queue_size); + } + return; + } else { + /* Detect repeated retransmit failures on uncongested bearer */ + + if (l_ptr->last_retransmitted == msg_seqno(msg)) { + if (++l_ptr->stale_count > 100) { + link_retransmit_failure(l_ptr, buf); + return; + } + } else { + l_ptr->last_retransmitted = msg_seqno(msg); + l_ptr->stale_count = 1; + } + } + + while (retransmits && (buf != l_ptr->next_out) && buf) { + msg = buf_msg(buf); + msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); + msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); + if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { + buf = buf->next; + retransmits--; + l_ptr->stats.retransmitted++; + } else { + tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); + l_ptr->stats.bearer_congs++; + l_ptr->retransm_queue_head = buf_seqno(buf); + l_ptr->retransm_queue_size = retransmits; + return; + } + } + + l_ptr->retransm_queue_head = l_ptr->retransm_queue_size = 0; +} + +/** + * link_insert_deferred_queue - insert deferred messages back into receive chain + */ + +static struct sk_buff *link_insert_deferred_queue(struct tipc_link *l_ptr, + struct sk_buff *buf) +{ + u32 seq_no; + + if (l_ptr->oldest_deferred_in == NULL) + return buf; + + seq_no = buf_seqno(l_ptr->oldest_deferred_in); + if (seq_no == mod(l_ptr->next_in_no)) { + l_ptr->newest_deferred_in->next = buf; + buf = l_ptr->oldest_deferred_in; + l_ptr->oldest_deferred_in = NULL; + l_ptr->deferred_inqueue_sz = 0; + } + return buf; +} + +/** + * link_recv_buf_validate - validate basic format of received message + * + * This routine ensures a TIPC message has an acceptable header, and at least + * as much data as the header indicates it should. The routine also ensures + * that the entire message header is stored in the main fragment of the message + * buffer, to simplify future access to message header fields. + * + * Note: Having extra info present in the message header or data areas is OK. + * TIPC will ignore the excess, under the assumption that it is optional info + * introduced by a later release of the protocol. + */ + +static int link_recv_buf_validate(struct sk_buff *buf) +{ + static u32 min_data_hdr_size[8] = { + SHORT_H_SIZE, MCAST_H_SIZE, NAMED_H_SIZE, BASIC_H_SIZE, + MAX_H_SIZE, MAX_H_SIZE, MAX_H_SIZE, MAX_H_SIZE + }; + + struct tipc_msg *msg; + u32 tipc_hdr[2]; + u32 size; + u32 hdr_size; + u32 min_hdr_size; + + if (unlikely(buf->len < MIN_H_SIZE)) + return 0; + + msg = skb_header_pointer(buf, 0, sizeof(tipc_hdr), tipc_hdr); + if (msg == NULL) + return 0; + + if (unlikely(msg_version(msg) != TIPC_VERSION)) + return 0; + + size = msg_size(msg); + hdr_size = msg_hdr_sz(msg); + min_hdr_size = msg_isdata(msg) ? + min_data_hdr_size[msg_type(msg)] : INT_H_SIZE; + + if (unlikely((hdr_size < min_hdr_size) || + (size < hdr_size) || + (buf->len < size) || + (size - hdr_size > TIPC_MAX_USER_MSG_SIZE))) + return 0; + + return pskb_may_pull(buf, hdr_size); +} + +/** + * tipc_recv_msg - process TIPC messages arriving from off-node + * @head: pointer to message buffer chain + * @tb_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_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) +{ + read_lock_bh(&tipc_net_lock); + while (head) { + struct tipc_node *n_ptr; + struct tipc_link *l_ptr; + struct sk_buff *crs; + struct sk_buff *buf = head; + struct tipc_msg *msg; + u32 seq_no; + u32 ackd; + u32 released = 0; + int type; + + head = head->next; + + /* Ensure bearer is still enabled */ + + if (unlikely(!b_ptr->active)) + goto cont; + + /* Ensure message is well-formed */ + + if (unlikely(!link_recv_buf_validate(buf))) + goto cont; + + /* Ensure message data is a single contiguous unit */ + + if (unlikely(skb_linearize(buf))) + goto cont; + + /* Handle arrival of a non-unicast link message */ + + msg = buf_msg(buf); + + if (unlikely(msg_non_seq(msg))) { + if (msg_user(msg) == LINK_CONFIG) + tipc_disc_recv_msg(buf, b_ptr); + else + tipc_bclink_recv_pkt(buf); + continue; + } + + /* Discard unicast link messages destined for another node */ + + if (unlikely(!msg_short(msg) && + (msg_destnode(msg) != tipc_own_addr))) + goto cont; + + /* Locate neighboring node that sent message */ + + n_ptr = tipc_node_find(msg_prevnode(msg)); + if (unlikely(!n_ptr)) + goto cont; + tipc_node_lock(n_ptr); + + /* Locate unicast link endpoint that should handle message */ + + l_ptr = n_ptr->links[b_ptr->identity]; + if (unlikely(!l_ptr)) { + tipc_node_unlock(n_ptr); + goto cont; + } + + /* Verify that communication with node is currently allowed */ + + if ((n_ptr->block_setup & WAIT_PEER_DOWN) && + msg_user(msg) == LINK_PROTOCOL && + (msg_type(msg) == RESET_MSG || + msg_type(msg) == ACTIVATE_MSG) && + !msg_redundant_link(msg)) + n_ptr->block_setup &= ~WAIT_PEER_DOWN; + + if (n_ptr->block_setup) { + tipc_node_unlock(n_ptr); + goto cont; + } + + /* Validate message sequence number info */ + + seq_no = msg_seqno(msg); + ackd = msg_ack(msg); + + /* Release acked messages */ + + if (n_ptr->bclink.supported) + tipc_bclink_acknowledge(n_ptr, msg_bcast_ack(msg)); + + crs = l_ptr->first_out; + while ((crs != l_ptr->next_out) && + less_eq(buf_seqno(crs), ackd)) { + struct sk_buff *next = crs->next; + + kfree_skb(crs); + crs = next; + released++; + } + if (released) { + l_ptr->first_out = crs; + l_ptr->out_queue_size -= released; + } + + /* Try sending any messages link endpoint has pending */ + + if (unlikely(l_ptr->next_out)) + tipc_link_push_queue(l_ptr); + if (unlikely(!list_empty(&l_ptr->waiting_ports))) + tipc_link_wakeup_ports(l_ptr, 0); + if (unlikely(++l_ptr->unacked_window >= TIPC_MIN_LINK_WIN)) { + l_ptr->stats.sent_acks++; + tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, 0, 0, 0, 0); + } + + /* Now (finally!) process the incoming message */ + +protocol_check: + if (likely(link_working_working(l_ptr))) { + if (likely(seq_no == mod(l_ptr->next_in_no))) { + l_ptr->next_in_no++; + if (unlikely(l_ptr->oldest_deferred_in)) + head = link_insert_deferred_queue(l_ptr, + head); +deliver: + if (likely(msg_isdata(msg))) { + tipc_node_unlock(n_ptr); + tipc_port_recv_msg(buf); + continue; + } + switch (msg_user(msg)) { + int ret; + case MSG_BUNDLER: + l_ptr->stats.recv_bundles++; + l_ptr->stats.recv_bundled += + msg_msgcnt(msg); + tipc_node_unlock(n_ptr); + tipc_link_recv_bundle(buf); + continue; + case NAME_DISTRIBUTOR: + tipc_node_unlock(n_ptr); + tipc_named_recv(buf); + continue; + case CONN_MANAGER: + tipc_node_unlock(n_ptr); + tipc_port_recv_proto_msg(buf); + continue; + case MSG_FRAGMENTER: + l_ptr->stats.recv_fragments++; + ret = tipc_link_recv_fragment( + &l_ptr->defragm_buf, + &buf, &msg); + if (ret == 1) { + l_ptr->stats.recv_fragmented++; + goto deliver; + } + if (ret == -1) + l_ptr->next_in_no--; + break; + case CHANGEOVER_PROTOCOL: + type = msg_type(msg); + if (link_recv_changeover_msg(&l_ptr, + &buf)) { + msg = buf_msg(buf); + seq_no = msg_seqno(msg); + if (type == ORIGINAL_MSG) + goto deliver; + goto protocol_check; + } + break; + default: + kfree_skb(buf); + buf = NULL; + break; + } + tipc_node_unlock(n_ptr); + tipc_net_route_msg(buf); + continue; + } + link_handle_out_of_seq_msg(l_ptr, buf); + head = link_insert_deferred_queue(l_ptr, head); + tipc_node_unlock(n_ptr); + continue; + } + + if (msg_user(msg) == LINK_PROTOCOL) { + link_recv_proto_msg(l_ptr, buf); + head = link_insert_deferred_queue(l_ptr, head); + tipc_node_unlock(n_ptr); + continue; + } + link_state_event(l_ptr, TRAFFIC_MSG_EVT); + + if (link_working_working(l_ptr)) { + /* Re-insert in front of queue */ + buf->next = head; + head = buf; + tipc_node_unlock(n_ptr); + continue; + } + tipc_node_unlock(n_ptr); +cont: + kfree_skb(buf); + } + read_unlock_bh(&tipc_net_lock); +} + +/* + * tipc_link_defer_pkt - Add out-of-sequence message to deferred reception queue + * + * Returns increase in queue length (i.e. 0 or 1) + */ + +u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail, + struct sk_buff *buf) +{ + struct sk_buff *queue_buf; + struct sk_buff **prev; + u32 seq_no = buf_seqno(buf); + + buf->next = NULL; + + /* Empty queue ? */ + if (*head == NULL) { + *head = *tail = buf; + return 1; + } + + /* Last ? */ + if (less(buf_seqno(*tail), seq_no)) { + (*tail)->next = buf; + *tail = buf; + return 1; + } + + /* Locate insertion point in queue, then insert; discard if duplicate */ + prev = head; + queue_buf = *head; + for (;;) { + u32 curr_seqno = buf_seqno(queue_buf); + + if (seq_no == curr_seqno) { + kfree_skb(buf); + return 0; + } + + if (less(seq_no, curr_seqno)) + break; + + prev = &queue_buf->next; + queue_buf = queue_buf->next; + } + + buf->next = queue_buf; + *prev = buf; + return 1; +} + +/* + * link_handle_out_of_seq_msg - handle arrival of out-of-sequence packet + */ + +static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, + struct sk_buff *buf) +{ + u32 seq_no = buf_seqno(buf); + + if (likely(msg_user(buf_msg(buf)) == LINK_PROTOCOL)) { + link_recv_proto_msg(l_ptr, buf); + return; + } + + /* Record OOS packet arrival (force mismatch on next timeout) */ + + l_ptr->checkpoint--; + + /* + * Discard packet if a duplicate; otherwise add it to deferred queue + * and notify peer of gap as per protocol specification + */ + + if (less(seq_no, mod(l_ptr->next_in_no))) { + l_ptr->stats.duplicates++; + kfree_skb(buf); + return; + } + + if (tipc_link_defer_pkt(&l_ptr->oldest_deferred_in, + &l_ptr->newest_deferred_in, buf)) { + l_ptr->deferred_inqueue_sz++; + l_ptr->stats.deferred_recv++; + if ((l_ptr->deferred_inqueue_sz % 16) == 1) + tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, 0, 0, 0, 0); + } else + l_ptr->stats.duplicates++; +} + +/* + * Send protocol message to the other endpoint. + */ +void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ, + int probe_msg, u32 gap, u32 tolerance, + u32 priority, u32 ack_mtu) +{ + struct sk_buff *buf = NULL; + struct tipc_msg *msg = l_ptr->pmsg; + u32 msg_size = sizeof(l_ptr->proto_msg); + int r_flag; + + /* Discard any previous message that was deferred due to congestion */ + + if (l_ptr->proto_msg_queue) { + kfree_skb(l_ptr->proto_msg_queue); + l_ptr->proto_msg_queue = NULL; + } + + if (link_blocked(l_ptr)) + return; + + /* Abort non-RESET send if communication with node is prohibited */ + + if ((l_ptr->owner->block_setup) && (msg_typ != RESET_MSG)) + return; + + /* Create protocol message with "out-of-sequence" sequence number */ + + msg_set_type(msg, msg_typ); + msg_set_net_plane(msg, l_ptr->b_ptr->net_plane); + msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); + msg_set_last_bcast(msg, tipc_bclink_get_last_sent()); + + if (msg_typ == STATE_MSG) { + u32 next_sent = mod(l_ptr->next_out_no); + + if (!tipc_link_is_up(l_ptr)) + return; + if (l_ptr->next_out) + next_sent = buf_seqno(l_ptr->next_out); + msg_set_next_sent(msg, next_sent); + if (l_ptr->oldest_deferred_in) { + u32 rec = buf_seqno(l_ptr->oldest_deferred_in); + gap = mod(rec - mod(l_ptr->next_in_no)); + } + msg_set_seq_gap(msg, gap); + if (gap) + l_ptr->stats.sent_nacks++; + msg_set_link_tolerance(msg, tolerance); + msg_set_linkprio(msg, priority); + msg_set_max_pkt(msg, ack_mtu); + msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); + msg_set_probe(msg, probe_msg != 0); + if (probe_msg) { + u32 mtu = l_ptr->max_pkt; + + if ((mtu < l_ptr->max_pkt_target) && + link_working_working(l_ptr) && + l_ptr->fsm_msg_cnt) { + msg_size = (mtu + (l_ptr->max_pkt_target - mtu)/2 + 2) & ~3; + if (l_ptr->max_pkt_probes == 10) { + l_ptr->max_pkt_target = (msg_size - 4); + l_ptr->max_pkt_probes = 0; + msg_size = (mtu + (l_ptr->max_pkt_target - mtu)/2 + 2) & ~3; + } + l_ptr->max_pkt_probes++; + } + + l_ptr->stats.sent_probes++; + } + l_ptr->stats.sent_states++; + } else { /* RESET_MSG or ACTIVATE_MSG */ + msg_set_ack(msg, mod(l_ptr->reset_checkpoint - 1)); + msg_set_seq_gap(msg, 0); + msg_set_next_sent(msg, 1); + msg_set_probe(msg, 0); + msg_set_link_tolerance(msg, l_ptr->tolerance); + msg_set_linkprio(msg, l_ptr->priority); + msg_set_max_pkt(msg, l_ptr->max_pkt_target); + } + + r_flag = (l_ptr->owner->working_links > tipc_link_is_up(l_ptr)); + msg_set_redundant_link(msg, r_flag); + msg_set_linkprio(msg, l_ptr->priority); + msg_set_size(msg, msg_size); + + msg_set_seqno(msg, mod(l_ptr->next_out_no + (0xffff/2))); + + buf = tipc_buf_acquire(msg_size); + if (!buf) + return; + + skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg)); + + /* Defer message if bearer is already congested */ + + if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) { + l_ptr->proto_msg_queue = buf; + return; + } + + /* Defer message if attempting to send results in bearer congestion */ + + if (!tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { + tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); + l_ptr->proto_msg_queue = buf; + l_ptr->stats.bearer_congs++; + return; + } + + /* Discard message if it was sent successfully */ + + l_ptr->unacked_window = 0; + kfree_skb(buf); +} + +/* + * Receive protocol message : + * Note that network plane id propagates through the network, and may + * change at any time. The node with lowest address rules + */ + +static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf) +{ + u32 rec_gap = 0; + u32 max_pkt_info; + u32 max_pkt_ack; + u32 msg_tol; + struct tipc_msg *msg = buf_msg(buf); + + if (link_blocked(l_ptr)) + goto exit; + + /* record unnumbered packet arrival (force mismatch on next timeout) */ + + l_ptr->checkpoint--; + + if (l_ptr->b_ptr->net_plane != msg_net_plane(msg)) + if (tipc_own_addr > msg_prevnode(msg)) + l_ptr->b_ptr->net_plane = msg_net_plane(msg); + + l_ptr->owner->permit_changeover = msg_redundant_link(msg); + + switch (msg_type(msg)) { + + case RESET_MSG: + if (!link_working_unknown(l_ptr) && + (l_ptr->peer_session != INVALID_SESSION)) { + if (less_eq(msg_session(msg), l_ptr->peer_session)) + break; /* duplicate or old reset: ignore */ + } + + if (!msg_redundant_link(msg) && (link_working_working(l_ptr) || + link_working_unknown(l_ptr))) { + /* + * peer has lost contact -- don't allow peer's links + * to reactivate before we recognize loss & clean up + */ + l_ptr->owner->block_setup = WAIT_NODE_DOWN; + } + + link_state_event(l_ptr, RESET_MSG); + + /* fall thru' */ + case ACTIVATE_MSG: + /* Update link settings according other endpoint's values */ + + strcpy((strrchr(l_ptr->name, ':') + 1), (char *)msg_data(msg)); + + msg_tol = msg_link_tolerance(msg); + if (msg_tol > l_ptr->tolerance) + link_set_supervision_props(l_ptr, msg_tol); + + if (msg_linkprio(msg) > l_ptr->priority) + l_ptr->priority = msg_linkprio(msg); + + max_pkt_info = msg_max_pkt(msg); + if (max_pkt_info) { + if (max_pkt_info < l_ptr->max_pkt_target) + l_ptr->max_pkt_target = max_pkt_info; + if (l_ptr->max_pkt > l_ptr->max_pkt_target) + l_ptr->max_pkt = l_ptr->max_pkt_target; + } else { + l_ptr->max_pkt = l_ptr->max_pkt_target; + } + l_ptr->owner->bclink.supportable = (max_pkt_info != 0); + + /* Synchronize broadcast link info, if not done previously */ + + if (!tipc_node_is_up(l_ptr->owner)) { + l_ptr->owner->bclink.last_sent = + l_ptr->owner->bclink.last_in = + msg_last_bcast(msg); + l_ptr->owner->bclink.oos_state = 0; + } + + l_ptr->peer_session = msg_session(msg); + l_ptr->peer_bearer_id = msg_bearer_id(msg); + + if (msg_type(msg) == ACTIVATE_MSG) + link_state_event(l_ptr, ACTIVATE_MSG); + break; + case STATE_MSG: + + msg_tol = msg_link_tolerance(msg); + if (msg_tol) + link_set_supervision_props(l_ptr, msg_tol); + + if (msg_linkprio(msg) && + (msg_linkprio(msg) != l_ptr->priority)) { + warn("Resetting link <%s>, priority change %u->%u\n", + 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; + } + link_state_event(l_ptr, TRAFFIC_MSG_EVT); + l_ptr->stats.recv_states++; + if (link_reset_unknown(l_ptr)) + break; + + if (less_eq(mod(l_ptr->next_in_no), msg_next_sent(msg))) { + rec_gap = mod(msg_next_sent(msg) - + mod(l_ptr->next_in_no)); + } + + max_pkt_ack = msg_max_pkt(msg); + if (max_pkt_ack > l_ptr->max_pkt) { + l_ptr->max_pkt = max_pkt_ack; + l_ptr->max_pkt_probes = 0; + } + + max_pkt_ack = 0; + if (msg_probe(msg)) { + l_ptr->stats.recv_probes++; + if (msg_size(msg) > sizeof(l_ptr->proto_msg)) + max_pkt_ack = msg_size(msg); + } + + /* Protocol message before retransmits, reduce loss risk */ + + if (l_ptr->owner->bclink.supported) + tipc_bclink_update_link_state(l_ptr->owner, + msg_last_bcast(msg)); + + if (rec_gap || (msg_probe(msg))) { + tipc_link_send_proto_msg(l_ptr, STATE_MSG, + 0, rec_gap, 0, 0, max_pkt_ack); + } + if (msg_seq_gap(msg)) { + l_ptr->stats.recv_nacks++; + tipc_link_retransmit(l_ptr, l_ptr->first_out, + msg_seq_gap(msg)); + } + break; + } +exit: + kfree_skb(buf); +} + + +/* + * tipc_link_tunnel(): Send one message via a link belonging to + * another bearer. Owner node is locked. + */ +static void tipc_link_tunnel(struct tipc_link *l_ptr, + struct tipc_msg *tunnel_hdr, + struct tipc_msg *msg, + u32 selector) +{ + struct tipc_link *tunnel; + struct sk_buff *buf; + u32 length = msg_size(msg); + + tunnel = l_ptr->owner->active_links[selector & 1]; + if (!tipc_link_is_up(tunnel)) { + warn("Link changeover error, " + "tunnel link no longer available\n"); + return; + } + msg_set_size(tunnel_hdr, length + INT_H_SIZE); + buf = tipc_buf_acquire(length + INT_H_SIZE); + if (!buf) { + warn("Link changeover error, " + "unable to send tunnel msg\n"); + return; + } + skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length); + tipc_link_send_buf(tunnel, buf); +} + + + +/* + * changeover(): Send whole message queue via the remaining link + * Owner node is locked. + */ + +void tipc_link_changeover(struct tipc_link *l_ptr) +{ + u32 msgcount = l_ptr->out_queue_size; + struct sk_buff *crs = l_ptr->first_out; + struct tipc_link *tunnel = l_ptr->owner->active_links[0]; + struct tipc_msg tunnel_hdr; + int split_bundles; + + if (!tunnel) + return; + + if (!l_ptr->owner->permit_changeover) { + warn("Link changeover error, " + "peer did not permit changeover\n"); + return; + } + + tipc_msg_init(&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); + + if (!l_ptr->first_out) { + struct sk_buff *buf; + + buf = tipc_buf_acquire(INT_H_SIZE); + if (buf) { + skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE); + msg_set_size(&tunnel_hdr, INT_H_SIZE); + tipc_link_send_buf(tunnel, buf); + } else { + warn("Link changeover error, " + "unable to send changeover msg\n"); + } + return; + } + + split_bundles = (l_ptr->owner->active_links[0] != + l_ptr->owner->active_links[1]); + + while (crs) { + struct tipc_msg *msg = buf_msg(crs); + + if ((msg_user(msg) == MSG_BUNDLER) && split_bundles) { + struct tipc_msg *m = msg_get_wrapped(msg); + unchar *pos = (unchar *)m; + + msgcount = msg_msgcnt(msg); + while (msgcount--) { + msg_set_seqno(m, msg_seqno(msg)); + tipc_link_tunnel(l_ptr, &tunnel_hdr, m, + msg_link_selector(m)); + pos += align(msg_size(m)); + m = (struct tipc_msg *)pos; + } + } else { + tipc_link_tunnel(l_ptr, &tunnel_hdr, msg, + msg_link_selector(msg)); + } + crs = crs->next; + } +} + +void tipc_link_send_duplicate(struct tipc_link *l_ptr, struct tipc_link *tunnel) +{ + struct sk_buff *iter; + struct tipc_msg tunnel_hdr; + + tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, + DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr); + msg_set_msgcnt(&tunnel_hdr, l_ptr->out_queue_size); + msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); + iter = l_ptr->first_out; + while (iter) { + struct sk_buff *outbuf; + struct tipc_msg *msg = buf_msg(iter); + u32 length = msg_size(msg); + + if (msg_user(msg) == MSG_BUNDLER) + msg_set_type(msg, CLOSED_MSG); + msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); /* Update */ + msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); + msg_set_size(&tunnel_hdr, length + INT_H_SIZE); + outbuf = tipc_buf_acquire(length + INT_H_SIZE); + if (outbuf == NULL) { + warn("Link changeover error, " + "unable to send duplicate msg\n"); + return; + } + skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data, + length); + tipc_link_send_buf(tunnel, outbuf); + if (!tipc_link_is_up(l_ptr)) + return; + iter = iter->next; + } +} + + + +/** + * buf_extract - extracts embedded TIPC message from another message + * @skb: encapsulating message buffer + * @from_pos: offset to extract from + * + * Returns a new message buffer containing an embedded message. The + * encapsulating message itself is left unchanged. + */ + +static struct sk_buff *buf_extract(struct sk_buff *skb, u32 from_pos) +{ + struct tipc_msg *msg = (struct tipc_msg *)(skb->data + from_pos); + u32 size = msg_size(msg); + struct sk_buff *eb; + + eb = tipc_buf_acquire(size); + if (eb) + skb_copy_to_linear_data(eb, msg, size); + return eb; +} + +/* + * link_recv_changeover_msg(): Receive tunneled packet sent + * via other link. Node is locked. Return extracted buffer. + */ + +static int link_recv_changeover_msg(struct tipc_link **l_ptr, + struct sk_buff **buf) +{ + struct sk_buff *tunnel_buf = *buf; + struct tipc_link *dest_link; + struct tipc_msg *msg; + struct tipc_msg *tunnel_msg = buf_msg(tunnel_buf); + u32 msg_typ = msg_type(tunnel_msg); + u32 msg_count = msg_msgcnt(tunnel_msg); + + dest_link = (*l_ptr)->owner->links[msg_bearer_id(tunnel_msg)]; + if (!dest_link) + goto exit; + if (dest_link == *l_ptr) { + err("Unexpected changeover message on link <%s>\n", + (*l_ptr)->name); + goto exit; + } + *l_ptr = dest_link; + msg = msg_get_wrapped(tunnel_msg); + + if (msg_typ == DUPLICATE_MSG) { + if (less(msg_seqno(msg), mod(dest_link->next_in_no))) + goto exit; + *buf = buf_extract(tunnel_buf, INT_H_SIZE); + if (*buf == NULL) { + warn("Link changeover error, duplicate msg dropped\n"); + goto exit; + } + kfree_skb(tunnel_buf); + return 1; + } + + /* First original message ?: */ + + if (tipc_link_is_up(dest_link)) { + info("Resetting link <%s>, changeover initiated by peer\n", + dest_link->name); + tipc_link_reset(dest_link); + dest_link->exp_msg_count = msg_count; + if (!msg_count) + goto exit; + } else if (dest_link->exp_msg_count == START_CHANGEOVER) { + dest_link->exp_msg_count = msg_count; + if (!msg_count) + goto exit; + } + + /* Receive original message */ + + if (dest_link->exp_msg_count == 0) { + warn("Link switchover error, " + "got too many tunnelled messages\n"); + goto exit; + } + dest_link->exp_msg_count--; + if (less(msg_seqno(msg), dest_link->reset_checkpoint)) { + goto exit; + } else { + *buf = buf_extract(tunnel_buf, INT_H_SIZE); + if (*buf != NULL) { + kfree_skb(tunnel_buf); + return 1; + } else { + warn("Link changeover error, original msg dropped\n"); + } + } +exit: + *buf = NULL; + kfree_skb(tunnel_buf); + return 0; +} + +/* + * Bundler functionality: + */ +void tipc_link_recv_bundle(struct sk_buff *buf) +{ + u32 msgcount = msg_msgcnt(buf_msg(buf)); + u32 pos = INT_H_SIZE; + struct sk_buff *obuf; + + while (msgcount--) { + obuf = buf_extract(buf, pos); + if (obuf == NULL) { + warn("Link unable to unbundle message(s)\n"); + break; + } + pos += align(msg_size(buf_msg(obuf))); + tipc_net_route_msg(obuf); + } + kfree_skb(buf); +} + +/* + * Fragmentation/defragmentation: + */ + + +/* + * link_send_long_buf: Entry for buffers needing fragmentation. + * The buffer is complete, inclusive total message length. + * Returns user data length. + */ +static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf) +{ + struct sk_buff *buf_chain = NULL; + struct sk_buff *buf_chain_tail = (struct sk_buff *)&buf_chain; + struct tipc_msg *inmsg = buf_msg(buf); + struct tipc_msg fragm_hdr; + u32 insize = msg_size(inmsg); + u32 dsz = msg_data_sz(inmsg); + unchar *crs = buf->data; + u32 rest = insize; + u32 pack_sz = l_ptr->max_pkt; + u32 fragm_sz = pack_sz - INT_H_SIZE; + u32 fragm_no = 0; + u32 destaddr; + + if (msg_short(inmsg)) + destaddr = l_ptr->addr; + else + destaddr = msg_destnode(inmsg); + + /* Prepare reusable fragment header: */ + + tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT, + INT_H_SIZE, destaddr); + + /* Chop up message: */ + + while (rest > 0) { + struct sk_buff *fragm; + + if (rest <= fragm_sz) { + fragm_sz = rest; + msg_set_type(&fragm_hdr, LAST_FRAGMENT); + } + fragm = tipc_buf_acquire(fragm_sz + INT_H_SIZE); + if (fragm == NULL) { + kfree_skb(buf); + while (buf_chain) { + buf = buf_chain; + buf_chain = buf_chain->next; + kfree_skb(buf); + } + return -ENOMEM; + } + msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE); + fragm_no++; + msg_set_fragm_no(&fragm_hdr, fragm_no); + skb_copy_to_linear_data(fragm, &fragm_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(fragm, INT_H_SIZE, crs, + fragm_sz); + buf_chain_tail->next = fragm; + buf_chain_tail = fragm; + + rest -= fragm_sz; + crs += fragm_sz; + msg_set_type(&fragm_hdr, FRAGMENT); + } + kfree_skb(buf); + + /* Append chain of fragments to send queue & send them */ + + l_ptr->long_msg_seq_no++; + link_add_chain_to_outqueue(l_ptr, buf_chain, l_ptr->long_msg_seq_no); + l_ptr->stats.sent_fragments += fragm_no; + l_ptr->stats.sent_fragmented++; + tipc_link_push_queue(l_ptr); + + return dsz; +} + +/* + * A pending message being re-assembled must store certain values + * to handle subsequent fragments correctly. The following functions + * help storing these values in unused, available fields in the + * pending message. This makes dynamic memory allocation unnecessary. + */ + +static void set_long_msg_seqno(struct sk_buff *buf, u32 seqno) +{ + msg_set_seqno(buf_msg(buf), seqno); +} + +static u32 get_fragm_size(struct sk_buff *buf) +{ + return msg_ack(buf_msg(buf)); +} + +static void set_fragm_size(struct sk_buff *buf, u32 sz) +{ + msg_set_ack(buf_msg(buf), sz); +} + +static u32 get_expected_frags(struct sk_buff *buf) +{ + return msg_bcast_ack(buf_msg(buf)); +} + +static void set_expected_frags(struct sk_buff *buf, u32 exp) +{ + msg_set_bcast_ack(buf_msg(buf), exp); +} + +static u32 get_timer_cnt(struct sk_buff *buf) +{ + return msg_reroute_cnt(buf_msg(buf)); +} + +static void incr_timer_cnt(struct sk_buff *buf) +{ + msg_incr_reroute_cnt(buf_msg(buf)); +} + +/* + * tipc_link_recv_fragment(): Called with node lock on. Returns + * the reassembled buffer if message is complete. + */ +int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb, + struct tipc_msg **m) +{ + struct sk_buff *prev = NULL; + struct sk_buff *fbuf = *fb; + struct tipc_msg *fragm = buf_msg(fbuf); + struct sk_buff *pbuf = *pending; + u32 long_msg_seq_no = msg_long_msgno(fragm); + + *fb = NULL; + + /* Is there an incomplete message waiting for this fragment? */ + + while (pbuf && ((buf_seqno(pbuf) != long_msg_seq_no) || + (msg_orignode(fragm) != msg_orignode(buf_msg(pbuf))))) { + prev = pbuf; + pbuf = pbuf->next; + } + + if (!pbuf && (msg_type(fragm) == FIRST_FRAGMENT)) { + struct tipc_msg *imsg = (struct tipc_msg *)msg_data(fragm); + u32 msg_sz = msg_size(imsg); + u32 fragm_sz = msg_data_sz(fragm); + u32 exp_fragm_cnt = msg_sz/fragm_sz + !!(msg_sz % fragm_sz); + u32 max = TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE; + if (msg_type(imsg) == TIPC_MCAST_MSG) + max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE; + if (msg_size(imsg) > max) { + kfree_skb(fbuf); + return 0; + } + pbuf = tipc_buf_acquire(msg_size(imsg)); + if (pbuf != NULL) { + pbuf->next = *pending; + *pending = pbuf; + skb_copy_to_linear_data(pbuf, imsg, + msg_data_sz(fragm)); + /* Prepare buffer for subsequent fragments. */ + + set_long_msg_seqno(pbuf, long_msg_seq_no); + set_fragm_size(pbuf, fragm_sz); + set_expected_frags(pbuf, exp_fragm_cnt - 1); + } else { + dbg("Link unable to reassemble fragmented message\n"); + kfree_skb(fbuf); + return -1; + } + kfree_skb(fbuf); + return 0; + } else if (pbuf && (msg_type(fragm) != FIRST_FRAGMENT)) { + u32 dsz = msg_data_sz(fragm); + u32 fsz = get_fragm_size(pbuf); + u32 crs = ((msg_fragm_no(fragm) - 1) * fsz); + u32 exp_frags = get_expected_frags(pbuf) - 1; + skb_copy_to_linear_data_offset(pbuf, crs, + msg_data(fragm), dsz); + kfree_skb(fbuf); + + /* Is message complete? */ + + if (exp_frags == 0) { + if (prev) + prev->next = pbuf->next; + else + *pending = pbuf->next; + msg_reset_reroute_cnt(buf_msg(pbuf)); + *fb = pbuf; + *m = buf_msg(pbuf); + return 1; + } + set_expected_frags(pbuf, exp_frags); + return 0; + } + kfree_skb(fbuf); + return 0; +} + +/** + * link_check_defragm_bufs - flush stale incoming message fragments + * @l_ptr: pointer to link + */ + +static void link_check_defragm_bufs(struct tipc_link *l_ptr) +{ + struct sk_buff *prev = NULL; + struct sk_buff *next = NULL; + struct sk_buff *buf = l_ptr->defragm_buf; + + if (!buf) + return; + if (!link_working_working(l_ptr)) + return; + while (buf) { + u32 cnt = get_timer_cnt(buf); + + next = buf->next; + if (cnt < 4) { + incr_timer_cnt(buf); + prev = buf; + } else { + if (prev) + prev->next = buf->next; + else + l_ptr->defragm_buf = buf->next; + kfree_skb(buf); + } + buf = next; + } +} + + + +static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance) +{ + if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > 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); +} + + +void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) +{ + /* Data messages from this node, inclusive FIRST_FRAGM */ + l_ptr->queue_limit[TIPC_LOW_IMPORTANCE] = window; + l_ptr->queue_limit[TIPC_MEDIUM_IMPORTANCE] = (window / 3) * 4; + l_ptr->queue_limit[TIPC_HIGH_IMPORTANCE] = (window / 3) * 5; + l_ptr->queue_limit[TIPC_CRITICAL_IMPORTANCE] = (window / 3) * 6; + /* Transiting data messages,inclusive FIRST_FRAGM */ + l_ptr->queue_limit[TIPC_LOW_IMPORTANCE + 4] = 300; + l_ptr->queue_limit[TIPC_MEDIUM_IMPORTANCE + 4] = 600; + l_ptr->queue_limit[TIPC_HIGH_IMPORTANCE + 4] = 900; + l_ptr->queue_limit[TIPC_CRITICAL_IMPORTANCE + 4] = 1200; + l_ptr->queue_limit[CONN_MANAGER] = 1200; + l_ptr->queue_limit[CHANGEOVER_PROTOCOL] = 2500; + l_ptr->queue_limit[NAME_DISTRIBUTOR] = 3000; + /* FRAGMENT and LAST_FRAGMENT packets */ + l_ptr->queue_limit[MSG_FRAGMENTER] = 4000; +} + +/** + * link_find_link - locate link by name + * @name - ptr to link name string + * @node - ptr to area to be filled with ptr to associated node + * + * Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted; + * this also prevents link deletion. + * + * Returns pointer to link (or 0 if invalid link name). + */ + +static struct tipc_link *link_find_link(const char *name, + struct tipc_node **node) +{ + struct tipc_link_name link_name_parts; + struct tipc_bearer *b_ptr; + struct tipc_link *l_ptr; + + if (!link_name_validate(name, &link_name_parts)) + return NULL; + + b_ptr = tipc_bearer_find_interface(link_name_parts.if_local); + if (!b_ptr) + return NULL; + + *node = tipc_node_find(link_name_parts.addr_peer); + if (!*node) + return NULL; + + l_ptr = (*node)->links[b_ptr->identity]; + if (!l_ptr || strcmp(l_ptr->name, name)) + return NULL; + + return l_ptr; +} + +/** + * 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 'tipc_net_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; + + l_ptr = link_find_link(name, &node); + if (l_ptr) { + /* + * acquire node lock for tipc_link_send_proto_msg(). + * see "TIPC locking policy" in net.c. + */ + tipc_node_lock(node); + switch (cmd) { + case TIPC_CMD_SET_LINK_TOL: + link_set_supervision_props(l_ptr, new_value); + tipc_link_send_proto_msg(l_ptr, + STATE_MSG, 0, 0, new_value, 0, 0); + break; + case TIPC_CMD_SET_LINK_PRI: + l_ptr->priority = new_value; + tipc_link_send_proto_msg(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; + } + tipc_node_unlock(node); + return 0; + } + + b_ptr = tipc_bearer_find(name); + if (b_ptr) { + switch (cmd) { + case TIPC_CMD_SET_LINK_TOL: + b_ptr->tolerance = new_value; + return 0; + case TIPC_CMD_SET_LINK_PRI: + b_ptr->priority = new_value; + return 0; + case TIPC_CMD_SET_LINK_WINDOW: + b_ptr->window = new_value; + return 0; + } + return -EINVAL; + } + + m_ptr = tipc_media_find(name); + if (!m_ptr) + return -ENODEV; + switch (cmd) { + case TIPC_CMD_SET_LINK_TOL: + m_ptr->tolerance = new_value; + return 0; + case TIPC_CMD_SET_LINK_PRI: + m_ptr->priority = new_value; + return 0; + case TIPC_CMD_SET_LINK_WINDOW: + m_ptr->window = new_value; + return 0; + } + return -EINVAL; +} + +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)"); + } + + read_lock_bh(&tipc_net_lock); + res = link_cmd_set_value(args->name, new_value, cmd); + read_unlock_bh(&tipc_net_lock); + 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 + */ + +static void link_reset_statistics(struct tipc_link *l_ptr) +{ + memset(&l_ptr->stats, 0, sizeof(l_ptr->stats)); + l_ptr->stats.sent_info = l_ptr->next_out_no; + 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; + + 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(); + } + + read_lock_bh(&tipc_net_lock); + l_ptr = link_find_link(link_name, &node); + if (!l_ptr) { + read_unlock_bh(&tipc_net_lock); + return tipc_cfg_reply_error_string("link not found"); + } + + tipc_node_lock(node); + link_reset_statistics(l_ptr); + tipc_node_unlock(node); + read_unlock_bh(&tipc_net_lock); + 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 print_buf pb; + struct tipc_link *l_ptr; + struct tipc_node *node; + char *status; + u32 profile_total = 0; + + if (!strcmp(name, tipc_bclink_name)) + return tipc_bclink_stats(buf, buf_size); + + tipc_printbuf_init(&pb, buf, buf_size); + + read_lock_bh(&tipc_net_lock); + l_ptr = link_find_link(name, &node); + if (!l_ptr) { + read_unlock_bh(&tipc_net_lock); + return 0; + } + tipc_node_lock(node); + + if (tipc_link_is_active(l_ptr)) + status = "ACTIVE"; + else if (tipc_link_is_up(l_ptr)) + status = "STANDBY"; + else + status = "DEFUNCT"; + tipc_printf(&pb, "Link <%s>\n" + " %s MTU:%u Priority:%u Tolerance:%u ms" + " Window:%u packets\n", + l_ptr->name, status, l_ptr->max_pkt, + l_ptr->priority, l_ptr->tolerance, l_ptr->queue_limit[0]); + tipc_printf(&pb, " RX packets:%u fragments:%u/%u bundles:%u/%u\n", + l_ptr->next_in_no - l_ptr->stats.recv_info, + l_ptr->stats.recv_fragments, + l_ptr->stats.recv_fragmented, + l_ptr->stats.recv_bundles, + l_ptr->stats.recv_bundled); + tipc_printf(&pb, " TX packets:%u fragments:%u/%u bundles:%u/%u\n", + l_ptr->next_out_no - l_ptr->stats.sent_info, + l_ptr->stats.sent_fragments, + l_ptr->stats.sent_fragmented, + l_ptr->stats.sent_bundles, + l_ptr->stats.sent_bundled); + profile_total = l_ptr->stats.msg_length_counts; + if (!profile_total) + profile_total = 1; + tipc_printf(&pb, " 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", + l_ptr->stats.msg_length_counts, + l_ptr->stats.msg_lengths_total / profile_total, + percent(l_ptr->stats.msg_length_profile[0], profile_total), + percent(l_ptr->stats.msg_length_profile[1], profile_total), + percent(l_ptr->stats.msg_length_profile[2], profile_total), + percent(l_ptr->stats.msg_length_profile[3], profile_total), + percent(l_ptr->stats.msg_length_profile[4], profile_total), + percent(l_ptr->stats.msg_length_profile[5], profile_total), + percent(l_ptr->stats.msg_length_profile[6], profile_total)); + tipc_printf(&pb, " RX states:%u probes:%u naks:%u defs:%u dups:%u\n", + l_ptr->stats.recv_states, + l_ptr->stats.recv_probes, + l_ptr->stats.recv_nacks, + l_ptr->stats.deferred_recv, + l_ptr->stats.duplicates); + tipc_printf(&pb, " TX states:%u probes:%u naks:%u acks:%u dups:%u\n", + l_ptr->stats.sent_states, + l_ptr->stats.sent_probes, + l_ptr->stats.sent_nacks, + l_ptr->stats.sent_acks, + l_ptr->stats.retransmitted); + tipc_printf(&pb, " Congestion bearer:%u link:%u Send queue max:%u avg:%u\n", + l_ptr->stats.bearer_congs, + l_ptr->stats.link_congs, + l_ptr->stats.max_queue_sz, + l_ptr->stats.queue_sz_counts + ? (l_ptr->stats.accu_queue_sz / l_ptr->stats.queue_sz_counts) + : 0); + + tipc_node_unlock(node); + read_unlock_bh(&tipc_net_lock); + return tipc_printbuf_validate(&pb); +} + +#define MAX_LINK_STATS_INFO 2000 + +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; + + 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(MAX_LINK_STATS_INFO)); + if (!buf) + return NULL; + + rep_tlv = (struct tlv_desc *)buf->data; + + str_len = tipc_link_stats((char *)TLV_DATA(req_tlv_area), + (char *)TLV_DATA(rep_tlv), MAX_LINK_STATS_INFO); + if (!str_len) { + kfree_skb(buf); + return tipc_cfg_reply_error_string("link not found"); + } + + 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; + + read_lock_bh(&tipc_net_lock); + 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); + } + read_unlock_bh(&tipc_net_lock); + return res; +} + +static void link_print(struct tipc_link *l_ptr, const char *str) +{ + char print_area[256]; + struct print_buf pb; + struct print_buf *buf = &pb; + + tipc_printbuf_init(buf, print_area, sizeof(print_area)); + + tipc_printf(buf, str); + tipc_printf(buf, "Link %x<%s>:", + l_ptr->addr, l_ptr->b_ptr->name); + +#ifdef CONFIG_TIPC_DEBUG + if (link_reset_reset(l_ptr) || link_reset_unknown(l_ptr)) + goto print_state; + + tipc_printf(buf, ": NXO(%u):", mod(l_ptr->next_out_no)); + tipc_printf(buf, "NXI(%u):", mod(l_ptr->next_in_no)); + tipc_printf(buf, "SQUE"); + if (l_ptr->first_out) { + tipc_printf(buf, "[%u..", buf_seqno(l_ptr->first_out)); + if (l_ptr->next_out) + tipc_printf(buf, "%u..", buf_seqno(l_ptr->next_out)); + tipc_printf(buf, "%u]", buf_seqno(l_ptr->last_out)); + if ((mod(buf_seqno(l_ptr->last_out) - + buf_seqno(l_ptr->first_out)) + != (l_ptr->out_queue_size - 1)) || + (l_ptr->last_out->next != NULL)) { + tipc_printf(buf, "\nSend queue inconsistency\n"); + tipc_printf(buf, "first_out= %p ", l_ptr->first_out); + tipc_printf(buf, "next_out= %p ", l_ptr->next_out); + tipc_printf(buf, "last_out= %p ", l_ptr->last_out); + } + } else + tipc_printf(buf, "[]"); + tipc_printf(buf, "SQSIZ(%u)", l_ptr->out_queue_size); + if (l_ptr->oldest_deferred_in) { + u32 o = buf_seqno(l_ptr->oldest_deferred_in); + u32 n = buf_seqno(l_ptr->newest_deferred_in); + tipc_printf(buf, ":RQUE[%u..%u]", o, n); + if (l_ptr->deferred_inqueue_sz != mod((n + 1) - o)) { + tipc_printf(buf, ":RQSIZ(%u)", + l_ptr->deferred_inqueue_sz); + } + } +print_state: +#endif + + if (link_working_unknown(l_ptr)) + tipc_printf(buf, ":WU"); + else if (link_reset_reset(l_ptr)) + tipc_printf(buf, ":RR"); + else if (link_reset_unknown(l_ptr)) + tipc_printf(buf, ":RU"); + else if (link_working_working(l_ptr)) + tipc_printf(buf, ":WW"); + tipc_printf(buf, "\n"); + + tipc_printbuf_validate(buf); + info("%s", print_area); +} + diff --git a/net/tipc/link.h b/net/tipc/link.h new file mode 100644 index 00000000..73c18c14 --- /dev/null +++ b/net/tipc/link.h @@ -0,0 +1,328 @@ +/* + * net/tipc/link.h: Include file for TIPC link code + * + * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 2004-2005, 2010-2011, 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_LINK_H +#define _TIPC_LINK_H + +#include "log.h" +#include "msg.h" +#include "node.h" + +#define PUSH_FAILED 1 +#define PUSH_FINISHED 2 + +/* + * Out-of-range value for link sequence numbers + */ + +#define INVALID_LINK_SEQ 0x10000 + +/* + * Link states + */ + +#define WORKING_WORKING 560810u +#define WORKING_UNKNOWN 560811u +#define RESET_UNKNOWN 560812u +#define RESET_RESET 560813u + +/* + * Starting value for maximum packet size negotiation on unicast links + * (unless bearer MTU is less) + */ + +#define MAX_PKT_DEFAULT 1500 + +/** + * struct tipc_link - TIPC link data structure + * @addr: network address of link's peer node + * @name: link name character string + * @media_addr: media address to use when sending messages over link + * @timer: link timer + * @owner: pointer to peer node + * @link_list: adjacent links in bearer's list of links + * @started: indicates if link has been started + * @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 + * @b_ptr: pointer to bearer used by link + * @tolerance: minimum link continuity loss needed to reset link [in ms] + * @continuity_interval: link continuity testing interval [in ms] + * @abort_limit: # of unacknowledged continuity probes needed to reset link + * @state: current state of link FSM + * @blocked: indicates if link has been administratively blocked + * @fsm_msg_cnt: # of protocol messages link FSM has sent in current state + * @proto_msg: template for control messages generated by link + * @pmsg: convenience pointer to "proto_msg" field + * @priority: current link priority + * @queue_limit: outbound message queue congestion thresholds (indexed by user) + * @exp_msg_count: # of tunnelled messages expected during link changeover + * @reset_checkpoint: seq # of last acknowledged message at time of link reset + * @max_pkt: current maximum packet size for this link + * @max_pkt_target: desired maximum packet size for this link + * @max_pkt_probes: # of probes based on current (max_pkt, max_pkt_target) + * @out_queue_size: # of messages in outbound message queue + * @first_out: ptr to first outbound message in queue + * @last_out: ptr to last outbound message in queue + * @next_out_no: next sequence number to use for outbound messages + * @last_retransmitted: sequence number of most recently retransmitted message + * @stale_count: # of identical retransmit requests made by peer + * @next_in_no: next sequence number to expect for inbound messages + * @deferred_inqueue_sz: # of messages in inbound message queue + * @oldest_deferred_in: ptr to first inbound message in queue + * @newest_deferred_in: ptr to last inbound message in queue + * @unacked_window: # of inbound messages rx'd without ack'ing back to peer + * @proto_msg_queue: ptr to (single) outbound control message + * @retransm_queue_size: number of messages to retransmit + * @retransm_queue_head: sequence number of first message to retransmit + * @next_out: ptr to first unsent outbound message in queue + * @waiting_ports: linked list of ports waiting for link congestion to abate + * @long_msg_seq_no: next identifier to use for outbound fragmented messages + * @defragm_buf: list of partially reassembled inbound message fragments + * @stats: collects statistics regarding link activity + */ + +struct tipc_link { + u32 addr; + char name[TIPC_MAX_LINK_NAME]; + struct tipc_media_addr media_addr; + struct timer_list timer; + struct tipc_node *owner; + struct list_head link_list; + + /* Management and link supervision data */ + int started; + u32 checkpoint; + u32 peer_session; + u32 peer_bearer_id; + struct tipc_bearer *b_ptr; + u32 tolerance; + u32 continuity_interval; + u32 abort_limit; + int state; + int blocked; + u32 fsm_msg_cnt; + struct { + unchar hdr[INT_H_SIZE]; + unchar body[TIPC_MAX_IF_NAME]; + } proto_msg; + struct tipc_msg *pmsg; + u32 priority; + u32 queue_limit[15]; /* queue_limit[0]==window limit */ + + /* Changeover */ + u32 exp_msg_count; + u32 reset_checkpoint; + + /* Max packet negotiation */ + u32 max_pkt; + u32 max_pkt_target; + u32 max_pkt_probes; + + /* Sending */ + u32 out_queue_size; + struct sk_buff *first_out; + struct sk_buff *last_out; + u32 next_out_no; + u32 last_retransmitted; + u32 stale_count; + + /* Reception */ + u32 next_in_no; + u32 deferred_inqueue_sz; + struct sk_buff *oldest_deferred_in; + struct sk_buff *newest_deferred_in; + u32 unacked_window; + + /* Congestion handling */ + struct sk_buff *proto_msg_queue; + u32 retransm_queue_size; + u32 retransm_queue_head; + struct sk_buff *next_out; + struct list_head waiting_ports; + + /* Fragmentation/defragmentation */ + u32 long_msg_seq_no; + struct sk_buff *defragm_buf; + + /* Statistics */ + struct { + u32 sent_info; /* used in counting # sent packets */ + u32 recv_info; /* used in counting # recv'd packets */ + u32 sent_states; + u32 recv_states; + u32 sent_probes; + u32 recv_probes; + u32 sent_nacks; + u32 recv_nacks; + u32 sent_acks; + u32 sent_bundled; + u32 sent_bundles; + u32 recv_bundled; + u32 recv_bundles; + u32 retransmitted; + u32 sent_fragmented; + u32 sent_fragments; + u32 recv_fragmented; + u32 recv_fragments; + u32 link_congs; /* # port sends blocked by congestion */ + u32 bearer_congs; + u32 deferred_recv; + u32 duplicates; + u32 max_queue_sz; /* send queue size high water mark */ + u32 accu_queue_sz; /* used for send queue size profiling */ + u32 queue_sz_counts; /* used for send queue size profiling */ + u32 msg_length_counts; /* used for message length profiling */ + u32 msg_lengths_total; /* used for message length profiling */ + u32 msg_length_profile[7]; /* used for msg. length profiling */ + } stats; +}; + +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(struct tipc_link *l_ptr); +void tipc_link_changeover(struct tipc_link *l_ptr); +void tipc_link_send_duplicate(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); +u32 tipc_link_push_packet(struct tipc_link *l_ptr); +void tipc_link_stop(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(struct tipc_link *l_ptr); +int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector); +void tipc_link_send_names(struct list_head *message_list, u32 dest); +int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf); +u32 tipc_link_get_max_pkt(u32 dest, u32 selector); +int tipc_link_send_sections_fast(struct tipc_port *sender, + struct iovec const *msg_sect, + const u32 num_sect, + unsigned int total_len, + u32 destnode); +void tipc_link_recv_bundle(struct sk_buff *buf); +int tipc_link_recv_fragment(struct sk_buff **pending, + struct sk_buff **fb, + struct tipc_msg **msg); +void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ, int prob, + u32 gap, u32 tolerance, u32 priority, + u32 acked_mtu); +void tipc_link_push_queue(struct tipc_link *l_ptr); +u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail, + struct sk_buff *buf); +void tipc_link_wakeup_ports(struct tipc_link *l_ptr, int all); +void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window); +void tipc_link_retransmit(struct tipc_link *l_ptr, + struct sk_buff *start, u32 retransmits); + +/* + * Link sequence number manipulation routines (uses modulo 2**16 arithmetic) + */ + +static inline u32 buf_seqno(struct sk_buff *buf) +{ + return msg_seqno(buf_msg(buf)); +} + +static inline u32 mod(u32 x) +{ + return x & 0xffffu; +} + +static inline int between(u32 lower, u32 upper, u32 n) +{ + if ((lower < n) && (n < upper)) + return 1; + if ((upper < lower) && ((n > lower) || (n < upper))) + return 1; + return 0; +} + +static inline int less_eq(u32 left, u32 right) +{ + return mod(right - left) < 32768u; +} + +static inline int less(u32 left, u32 right) +{ + return less_eq(left, right) && (mod(right) != mod(left)); +} + +static inline u32 lesser(u32 left, u32 right) +{ + return less_eq(left, right) ? left : right; +} + + +/* + * Link status checking routines + */ + +static inline int link_working_working(struct tipc_link *l_ptr) +{ + return l_ptr->state == WORKING_WORKING; +} + +static inline int link_working_unknown(struct tipc_link *l_ptr) +{ + return l_ptr->state == WORKING_UNKNOWN; +} + +static inline int link_reset_unknown(struct tipc_link *l_ptr) +{ + return l_ptr->state == RESET_UNKNOWN; +} + +static inline int link_reset_reset(struct tipc_link *l_ptr) +{ + return l_ptr->state == RESET_RESET; +} + +static inline int link_blocked(struct tipc_link *l_ptr) +{ + return l_ptr->exp_msg_count || l_ptr->blocked; +} + +static inline int link_congested(struct tipc_link *l_ptr) +{ + return l_ptr->out_queue_size >= l_ptr->queue_limit[0]; +} + +#endif diff --git a/net/tipc/log.c b/net/tipc/log.c new file mode 100644 index 00000000..895c6e53 --- /dev/null +++ b/net/tipc/log.c @@ -0,0 +1,351 @@ +/* + * 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" +#include "log.h" + +/* + * TIPC pre-defines the following print buffers: + * + * TIPC_NULL : null buffer (i.e. print nowhere) + * TIPC_CONS : system console + * TIPC_LOG : TIPC log buffer + * + * Additional user-defined print buffers are also permitted. + */ + +static struct print_buf null_buf = { NULL, 0, NULL, 0 }; +struct print_buf *const TIPC_NULL = &null_buf; + +static struct print_buf cons_buf = { NULL, 0, NULL, 1 }; +struct print_buf *const TIPC_CONS = &cons_buf; + +static struct print_buf log_buf = { NULL, 0, NULL, 1 }; +struct print_buf *const TIPC_LOG = &log_buf; + +/* + * Locking policy when using print buffers. + * + * 1) tipc_printf() uses 'print_lock' to protect against concurrent access to + * 'print_string' when writing to a print buffer. This also protects against + * concurrent writes to the print buffer being written to. + * + * 2) tipc_log_XXX() leverages the aforementioned use of 'print_lock' to + * protect against all types of concurrent operations on their associated + * print buffer (not just write operations). + * + * Note: All routines of the form tipc_printbuf_XXX() are lock-free, and rely + * on the caller to prevent simultaneous use of the print buffer(s) being + * manipulated. + */ + +static char print_string[TIPC_PB_MAX_STR]; +static DEFINE_SPINLOCK(print_lock); + +static void tipc_printbuf_move(struct print_buf *pb_to, + struct print_buf *pb_from); + +#define FORMAT(PTR, LEN, FMT) \ +{\ + va_list args;\ + va_start(args, FMT);\ + LEN = vsprintf(PTR, FMT, args);\ + va_end(args);\ + *(PTR + LEN) = '\0';\ +} + +/** + * tipc_printbuf_init - initialize print buffer to empty + * @pb: pointer to print buffer structure + * @raw: pointer to character array used by print buffer + * @size: size of character array + * + * Note: If the character array is too small (or absent), the print buffer + * becomes a null device that discards anything written to it. + */ + +void tipc_printbuf_init(struct print_buf *pb, char *raw, u32 size) +{ + pb->buf = raw; + pb->crs = raw; + pb->size = size; + pb->echo = 0; + + if (size < TIPC_PB_MIN_SIZE) { + pb->buf = NULL; + } else if (raw) { + pb->buf[0] = 0; + pb->buf[size - 1] = ~0; + } +} + +/** + * tipc_printbuf_reset - reinitialize print buffer to empty state + * @pb: pointer to print buffer structure + */ + +static void tipc_printbuf_reset(struct print_buf *pb) +{ + if (pb->buf) { + pb->crs = pb->buf; + pb->buf[0] = 0; + pb->buf[pb->size - 1] = ~0; + } +} + +/** + * tipc_printbuf_empty - test if print buffer is in empty state + * @pb: pointer to print buffer structure + * + * Returns non-zero if print buffer is empty. + */ + +static int tipc_printbuf_empty(struct print_buf *pb) +{ + return !pb->buf || (pb->crs == pb->buf); +} + +/** + * tipc_printbuf_validate - check for print buffer overflow + * @pb: pointer to print buffer structure + * + * Verifies that a print buffer has captured all data written to it. + * If data has been lost, linearize buffer and prepend an error message + * + * Returns length of print buffer data string (including trailing NUL) + */ + +int tipc_printbuf_validate(struct print_buf *pb) +{ + char *err = "\n\n*** PRINT BUFFER OVERFLOW ***\n\n"; + char *cp_buf; + struct print_buf cb; + + if (!pb->buf) + return 0; + + if (pb->buf[pb->size - 1] == 0) { + cp_buf = kmalloc(pb->size, GFP_ATOMIC); + if (cp_buf) { + tipc_printbuf_init(&cb, cp_buf, pb->size); + tipc_printbuf_move(&cb, pb); + tipc_printbuf_move(pb, &cb); + kfree(cp_buf); + memcpy(pb->buf, err, strlen(err)); + } else { + tipc_printbuf_reset(pb); + tipc_printf(pb, err); + } + } + return pb->crs - pb->buf + 1; +} + +/** + * tipc_printbuf_move - move print buffer contents to another print buffer + * @pb_to: pointer to destination print buffer structure + * @pb_from: pointer to source print buffer structure + * + * Current contents of destination print buffer (if any) are discarded. + * Source print buffer becomes empty if a successful move occurs. + */ + +static void tipc_printbuf_move(struct print_buf *pb_to, + struct print_buf *pb_from) +{ + int len; + + /* Handle the cases where contents can't be moved */ + + if (!pb_to->buf) + return; + + if (!pb_from->buf) { + tipc_printbuf_reset(pb_to); + return; + } + + if (pb_to->size < pb_from->size) { + strcpy(pb_to->buf, "*** PRINT BUFFER MOVE ERROR ***"); + pb_to->buf[pb_to->size - 1] = ~0; + pb_to->crs = strchr(pb_to->buf, 0); + return; + } + + /* Copy data from char after cursor to end (if used) */ + + len = pb_from->buf + pb_from->size - pb_from->crs - 2; + if ((pb_from->buf[pb_from->size - 1] == 0) && (len > 0)) { + strcpy(pb_to->buf, pb_from->crs + 1); + pb_to->crs = pb_to->buf + len; + } else + pb_to->crs = pb_to->buf; + + /* Copy data from start to cursor (always) */ + + len = pb_from->crs - pb_from->buf; + strcpy(pb_to->crs, pb_from->buf); + pb_to->crs += len; + + tipc_printbuf_reset(pb_from); +} + +/** + * tipc_printf - append formatted output to print buffer + * @pb: pointer to print buffer + * @fmt: formatted info to be printed + */ + +void tipc_printf(struct print_buf *pb, const char *fmt, ...) +{ + int chars_to_add; + int chars_left; + char save_char; + + spin_lock_bh(&print_lock); + + FORMAT(print_string, chars_to_add, fmt); + if (chars_to_add >= TIPC_PB_MAX_STR) + strcpy(print_string, "*** PRINT BUFFER STRING TOO LONG ***"); + + if (pb->buf) { + chars_left = pb->buf + pb->size - pb->crs - 1; + if (chars_to_add <= chars_left) { + strcpy(pb->crs, print_string); + pb->crs += chars_to_add; + } else if (chars_to_add >= (pb->size - 1)) { + strcpy(pb->buf, print_string + chars_to_add + 1 + - pb->size); + pb->crs = pb->buf + pb->size - 1; + } else { + strcpy(pb->buf, print_string + chars_left); + save_char = print_string[chars_left]; + print_string[chars_left] = 0; + strcpy(pb->crs, print_string); + print_string[chars_left] = save_char; + pb->crs = pb->buf + chars_to_add - chars_left; + } + } + + if (pb->echo) + printk("%s", print_string); + + spin_unlock_bh(&print_lock); +} + +/** + * tipc_log_resize - change the size of the TIPC log buffer + * @log_size: print buffer size to use + */ + +int tipc_log_resize(int log_size) +{ + int res = 0; + + spin_lock_bh(&print_lock); + kfree(TIPC_LOG->buf); + TIPC_LOG->buf = NULL; + if (log_size) { + if (log_size < TIPC_PB_MIN_SIZE) + log_size = TIPC_PB_MIN_SIZE; + res = TIPC_LOG->echo; + tipc_printbuf_init(TIPC_LOG, kmalloc(log_size, GFP_ATOMIC), + log_size); + TIPC_LOG->echo = res; + res = !TIPC_LOG->buf; + } + spin_unlock_bh(&print_lock); + + return res; +} + +/** + * tipc_log_resize_cmd - reconfigure size of TIPC log buffer + */ + +struct sk_buff *tipc_log_resize_cmd(const void *req_tlv_area, int req_tlv_space) +{ + 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 > 32768) + return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE + " (log size must be 0-32768)"); + if (tipc_log_resize(value)) + return tipc_cfg_reply_error_string( + "unable to create specified log (log size is now 0)"); + return tipc_cfg_reply_none(); +} + +/** + * tipc_log_dump - capture TIPC log buffer contents in configuration message + */ + +struct sk_buff *tipc_log_dump(void) +{ + struct sk_buff *reply; + + spin_lock_bh(&print_lock); + if (!TIPC_LOG->buf) { + spin_unlock_bh(&print_lock); + reply = tipc_cfg_reply_ultra_string("log not activated\n"); + } else if (tipc_printbuf_empty(TIPC_LOG)) { + spin_unlock_bh(&print_lock); + reply = tipc_cfg_reply_ultra_string("log is empty\n"); + } else { + struct tlv_desc *rep_tlv; + struct print_buf pb; + int str_len; + + str_len = min(TIPC_LOG->size, 32768u); + spin_unlock_bh(&print_lock); + reply = tipc_cfg_reply_alloc(TLV_SPACE(str_len)); + if (reply) { + rep_tlv = (struct tlv_desc *)reply->data; + tipc_printbuf_init(&pb, TLV_DATA(rep_tlv), str_len); + spin_lock_bh(&print_lock); + tipc_printbuf_move(&pb, TIPC_LOG); + spin_unlock_bh(&print_lock); + str_len = strlen(TLV_DATA(rep_tlv)) + 1; + skb_put(reply, TLV_SPACE(str_len)); + TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); + } + } + return reply; +} diff --git a/net/tipc/log.h b/net/tipc/log.h new file mode 100644 index 00000000..2248d962 --- /dev/null +++ b/net/tipc/log.h @@ -0,0 +1,67 @@ +/* + * net/tipc/log.h: Include file for TIPC print buffer routines + * + * Copyright (c) 1997-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. + */ + +#ifndef _TIPC_LOG_H +#define _TIPC_LOG_H + +/** + * struct print_buf - TIPC print buffer structure + * @buf: pointer to character array containing print buffer contents + * @size: size of character array + * @crs: pointer to first unused space in character array (i.e. final NUL) + * @echo: echo output to system console if non-zero + */ + +struct print_buf { + char *buf; + u32 size; + char *crs; + int echo; +}; + +#define TIPC_PB_MIN_SIZE 64 /* minimum size for a print buffer's array */ +#define TIPC_PB_MAX_STR 512 /* max printable string (with trailing NUL) */ + +void tipc_printbuf_init(struct print_buf *pb, char *buf, u32 size); +int tipc_printbuf_validate(struct print_buf *pb); + +int tipc_log_resize(int log_size); + +struct sk_buff *tipc_log_resize_cmd(const void *req_tlv_area, + int req_tlv_space); +struct sk_buff *tipc_log_dump(void); + +#endif diff --git a/net/tipc/msg.c b/net/tipc/msg.c new file mode 100644 index 00000000..e3afe162 --- /dev/null +++ b/net/tipc/msg.c @@ -0,0 +1,356 @@ +/* + * net/tipc/msg.c: TIPC message header routines + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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 "msg.h" + +u32 tipc_msg_tot_importance(struct tipc_msg *m) +{ + if (likely(msg_isdata(m))) { + if (likely(msg_orignode(m) == tipc_own_addr)) + return msg_importance(m); + return msg_importance(m) + 4; + } + if ((msg_user(m) == MSG_FRAGMENTER) && + (msg_type(m) == FIRST_FRAGMENT)) + return msg_importance(msg_get_wrapped(m)); + return msg_importance(m); +} + + +void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, + u32 hsize, u32 destnode) +{ + 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_type(m, type); + msg_set_orignode(m, tipc_own_addr); + msg_set_destnode(m, destnode); +} + +/** + * tipc_msg_build - create message using specified header and data + * + * Note: Caller must not hold any locks in case copy_from_user() is interrupted! + * + * Returns message data size or errno + */ + +int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, + u32 num_sect, unsigned int total_len, + int max_size, int usrmem, struct sk_buff **buf) +{ + int dsz, sz, hsz, pos, res, cnt; + + dsz = total_len; + pos = hsz = msg_hdr_sz(hdr); + sz = hsz + dsz; + msg_set_size(hdr, sz); + if (unlikely(sz > max_size)) { + *buf = NULL; + return dsz; + } + + *buf = tipc_buf_acquire(sz); + if (!(*buf)) + return -ENOMEM; + skb_copy_to_linear_data(*buf, hdr, hsz); + for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) { + if (likely(usrmem)) + res = !copy_from_user((*buf)->data + pos, + msg_sect[cnt].iov_base, + msg_sect[cnt].iov_len); + else + skb_copy_to_linear_data_offset(*buf, pos, + msg_sect[cnt].iov_base, + msg_sect[cnt].iov_len); + pos += msg_sect[cnt].iov_len; + } + if (likely(res)) + return dsz; + + kfree_skb(*buf); + *buf = NULL; + return -EFAULT; +} + +#ifdef CONFIG_TIPC_DEBUG + +void tipc_msg_dbg(struct print_buf *buf, struct tipc_msg *msg, const char *str) +{ + u32 usr = msg_user(msg); + tipc_printf(buf, KERN_DEBUG); + tipc_printf(buf, str); + + switch (usr) { + case MSG_BUNDLER: + tipc_printf(buf, "BNDL::"); + tipc_printf(buf, "MSGS(%u):", msg_msgcnt(msg)); + break; + case BCAST_PROTOCOL: + tipc_printf(buf, "BCASTP::"); + break; + case MSG_FRAGMENTER: + tipc_printf(buf, "FRAGM::"); + switch (msg_type(msg)) { + case FIRST_FRAGMENT: + tipc_printf(buf, "FIRST:"); + break; + case FRAGMENT: + tipc_printf(buf, "BODY:"); + break; + case LAST_FRAGMENT: + tipc_printf(buf, "LAST:"); + break; + default: + tipc_printf(buf, "UNKNOWN:%x", msg_type(msg)); + + } + tipc_printf(buf, "NO(%u/%u):", msg_long_msgno(msg), + msg_fragm_no(msg)); + break; + case TIPC_LOW_IMPORTANCE: + case TIPC_MEDIUM_IMPORTANCE: + case TIPC_HIGH_IMPORTANCE: + case TIPC_CRITICAL_IMPORTANCE: + tipc_printf(buf, "DAT%u:", msg_user(msg)); + if (msg_short(msg)) { + tipc_printf(buf, "CON:"); + break; + } + switch (msg_type(msg)) { + case TIPC_CONN_MSG: + tipc_printf(buf, "CON:"); + break; + case TIPC_MCAST_MSG: + tipc_printf(buf, "MCST:"); + break; + case TIPC_NAMED_MSG: + tipc_printf(buf, "NAM:"); + break; + case TIPC_DIRECT_MSG: + tipc_printf(buf, "DIR:"); + break; + default: + tipc_printf(buf, "UNKNOWN TYPE %u", msg_type(msg)); + } + if (msg_reroute_cnt(msg)) + tipc_printf(buf, "REROUTED(%u):", + msg_reroute_cnt(msg)); + break; + case NAME_DISTRIBUTOR: + tipc_printf(buf, "NMD::"); + switch (msg_type(msg)) { + case PUBLICATION: + tipc_printf(buf, "PUBL(%u):", (msg_size(msg) - msg_hdr_sz(msg)) / 20); /* Items */ + break; + case WITHDRAWAL: + tipc_printf(buf, "WDRW:"); + break; + default: + tipc_printf(buf, "UNKNOWN:%x", msg_type(msg)); + } + if (msg_reroute_cnt(msg)) + tipc_printf(buf, "REROUTED(%u):", + msg_reroute_cnt(msg)); + break; + case CONN_MANAGER: + tipc_printf(buf, "CONN_MNG:"); + switch (msg_type(msg)) { + case CONN_PROBE: + tipc_printf(buf, "PROBE:"); + break; + case CONN_PROBE_REPLY: + tipc_printf(buf, "PROBE_REPLY:"); + break; + case CONN_ACK: + tipc_printf(buf, "CONN_ACK:"); + tipc_printf(buf, "ACK(%u):", msg_msgcnt(msg)); + break; + default: + tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); + } + if (msg_reroute_cnt(msg)) + tipc_printf(buf, "REROUTED(%u):", msg_reroute_cnt(msg)); + break; + case LINK_PROTOCOL: + switch (msg_type(msg)) { + case STATE_MSG: + tipc_printf(buf, "STATE:"); + tipc_printf(buf, "%s:", msg_probe(msg) ? "PRB" : ""); + tipc_printf(buf, "NXS(%u):", msg_next_sent(msg)); + tipc_printf(buf, "GAP(%u):", msg_seq_gap(msg)); + tipc_printf(buf, "LSTBC(%u):", msg_last_bcast(msg)); + break; + case RESET_MSG: + tipc_printf(buf, "RESET:"); + if (msg_size(msg) != msg_hdr_sz(msg)) + tipc_printf(buf, "BEAR:%s:", msg_data(msg)); + break; + case ACTIVATE_MSG: + tipc_printf(buf, "ACTIVATE:"); + break; + default: + tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); + } + tipc_printf(buf, "PLANE(%c):", msg_net_plane(msg)); + tipc_printf(buf, "SESS(%u):", msg_session(msg)); + break; + case CHANGEOVER_PROTOCOL: + tipc_printf(buf, "TUNL:"); + switch (msg_type(msg)) { + case DUPLICATE_MSG: + tipc_printf(buf, "DUPL:"); + break; + case ORIGINAL_MSG: + tipc_printf(buf, "ORIG:"); + tipc_printf(buf, "EXP(%u)", msg_msgcnt(msg)); + break; + default: + tipc_printf(buf, "UNKNOWN TYPE:%x", msg_type(msg)); + } + break; + case LINK_CONFIG: + tipc_printf(buf, "CFG:"); + switch (msg_type(msg)) { + case DSC_REQ_MSG: + tipc_printf(buf, "DSC_REQ:"); + break; + case DSC_RESP_MSG: + tipc_printf(buf, "DSC_RESP:"); + break; + default: + tipc_printf(buf, "UNKNOWN TYPE:%x:", msg_type(msg)); + break; + } + break; + default: + tipc_printf(buf, "UNKNOWN USER:"); + } + + switch (usr) { + case CONN_MANAGER: + case TIPC_LOW_IMPORTANCE: + case TIPC_MEDIUM_IMPORTANCE: + case TIPC_HIGH_IMPORTANCE: + case TIPC_CRITICAL_IMPORTANCE: + switch (msg_errcode(msg)) { + case TIPC_OK: + break; + case TIPC_ERR_NO_NAME: + tipc_printf(buf, "NO_NAME:"); + break; + case TIPC_ERR_NO_PORT: + tipc_printf(buf, "NO_PORT:"); + break; + case TIPC_ERR_NO_NODE: + tipc_printf(buf, "NO_PROC:"); + break; + case TIPC_ERR_OVERLOAD: + tipc_printf(buf, "OVERLOAD:"); + break; + case TIPC_CONN_SHUTDOWN: + tipc_printf(buf, "SHUTDOWN:"); + break; + default: + tipc_printf(buf, "UNKNOWN ERROR(%x):", + msg_errcode(msg)); + } + default: + break; + } + + tipc_printf(buf, "HZ(%u):", msg_hdr_sz(msg)); + tipc_printf(buf, "SZ(%u):", msg_size(msg)); + tipc_printf(buf, "SQNO(%u):", msg_seqno(msg)); + + if (msg_non_seq(msg)) + tipc_printf(buf, "NOSEQ:"); + else + tipc_printf(buf, "ACK(%u):", msg_ack(msg)); + tipc_printf(buf, "BACK(%u):", msg_bcast_ack(msg)); + tipc_printf(buf, "PRND(%x)", msg_prevnode(msg)); + + if (msg_isdata(msg)) { + if (msg_named(msg)) { + tipc_printf(buf, "NTYP(%u):", msg_nametype(msg)); + tipc_printf(buf, "NINST(%u)", msg_nameinst(msg)); + } + } + + if ((usr != LINK_PROTOCOL) && (usr != LINK_CONFIG) && + (usr != MSG_BUNDLER)) { + if (!msg_short(msg)) { + tipc_printf(buf, ":ORIG(%x:%u):", + msg_orignode(msg), msg_origport(msg)); + tipc_printf(buf, ":DEST(%x:%u):", + msg_destnode(msg), msg_destport(msg)); + } else { + tipc_printf(buf, ":OPRT(%u):", msg_origport(msg)); + tipc_printf(buf, ":DPRT(%u):", msg_destport(msg)); + } + } + if (msg_user(msg) == NAME_DISTRIBUTOR) { + tipc_printf(buf, ":ONOD(%x):", msg_orignode(msg)); + tipc_printf(buf, ":DNOD(%x):", msg_destnode(msg)); + } + + if (msg_user(msg) == LINK_CONFIG) { + struct tipc_media_addr orig; + + tipc_printf(buf, ":DDOM(%x):", msg_dest_domain(msg)); + tipc_printf(buf, ":NETID(%u):", msg_bc_netid(msg)); + memcpy(orig.value, msg_media_addr(msg), sizeof(orig.value)); + orig.media_id = 0; + orig.broadcast = 0; + tipc_media_addr_printf(buf, &orig); + } + if (msg_user(msg) == BCAST_PROTOCOL) { + tipc_printf(buf, "BCNACK:AFTER(%u):", msg_bcgap_after(msg)); + tipc_printf(buf, "TO(%u):", msg_bcgap_to(msg)); + } + tipc_printf(buf, "\n"); + if ((usr == CHANGEOVER_PROTOCOL) && (msg_msgcnt(msg))) + tipc_msg_dbg(buf, msg_get_wrapped(msg), " /"); + if ((usr == MSG_FRAGMENTER) && (msg_type(msg) == FIRST_FRAGMENT)) + tipc_msg_dbg(buf, msg_get_wrapped(msg), " /"); +} + +#endif diff --git a/net/tipc/msg.h b/net/tipc/msg.h new file mode 100644 index 00000000..eba524e3 --- /dev/null +++ b/net/tipc/msg.h @@ -0,0 +1,748 @@ +/* + * net/tipc/msg.h: Include file for TIPC message header routines + * + * Copyright (c) 2000-2007, Ericsson AB + * Copyright (c) 2005-2008, 2010-2011, 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_MSG_H +#define _TIPC_MSG_H + +#include "bearer.h" + +/* + * Constants and routines used to read and write TIPC payload message headers + * + * Note: Some items are also used with TIPC internal message headers + */ + +#define TIPC_VERSION 2 + +/* + * Payload message users are defined in TIPC's public API: + * - TIPC_LOW_IMPORTANCE + * - TIPC_MEDIUM_IMPORTANCE + * - TIPC_HIGH_IMPORTANCE + * - TIPC_CRITICAL_IMPORTANCE + */ + +/* + * Payload message types + */ + +#define TIPC_CONN_MSG 0 +#define TIPC_MCAST_MSG 1 +#define TIPC_NAMED_MSG 2 +#define TIPC_DIRECT_MSG 3 + +/* + * Message header sizes + */ + +#define SHORT_H_SIZE 24 /* In-cluster basic payload message */ +#define BASIC_H_SIZE 32 /* Basic payload message */ +#define NAMED_H_SIZE 40 /* Named payload message */ +#define MCAST_H_SIZE 44 /* Multicast payload message */ +#define INT_H_SIZE 40 /* Internal messages */ +#define MIN_H_SIZE 24 /* Smallest legal TIPC header size */ +#define MAX_H_SIZE 60 /* Largest possible TIPC header size */ + +#define MAX_MSG_SIZE (MAX_H_SIZE + TIPC_MAX_USER_MSG_SIZE) + +#define TIPC_MEDIA_ADDR_OFFSET 5 + + +struct tipc_msg { + __be32 hdr[15]; +}; + + +static inline u32 msg_word(struct tipc_msg *m, u32 pos) +{ + return ntohl(m->hdr[pos]); +} + +static inline void msg_set_word(struct tipc_msg *m, u32 w, u32 val) +{ + m->hdr[w] = htonl(val); +} + +static inline u32 msg_bits(struct tipc_msg *m, u32 w, u32 pos, u32 mask) +{ + return (msg_word(m, w) >> pos) & mask; +} + +static inline void msg_set_bits(struct tipc_msg *m, u32 w, + u32 pos, u32 mask, u32 val) +{ + val = (val & mask) << pos; + mask = mask << pos; + m->hdr[w] &= ~htonl(mask); + m->hdr[w] |= htonl(val); +} + +static inline void msg_swap_words(struct tipc_msg *msg, u32 a, u32 b) +{ + u32 temp = msg->hdr[a]; + + msg->hdr[a] = msg->hdr[b]; + msg->hdr[b] = temp; +} + +/* + * Word 0 + */ + +static inline u32 msg_version(struct tipc_msg *m) +{ + return msg_bits(m, 0, 29, 7); +} + +static inline void msg_set_version(struct tipc_msg *m) +{ + msg_set_bits(m, 0, 29, 7, TIPC_VERSION); +} + +static inline u32 msg_user(struct tipc_msg *m) +{ + return msg_bits(m, 0, 25, 0xf); +} + +static inline u32 msg_isdata(struct tipc_msg *m) +{ + return msg_user(m) <= TIPC_CRITICAL_IMPORTANCE; +} + +static inline void msg_set_user(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 0, 25, 0xf, n); +} + +static inline u32 msg_importance(struct tipc_msg *m) +{ + return msg_bits(m, 0, 25, 0xf); +} + +static inline void msg_set_importance(struct tipc_msg *m, u32 i) +{ + msg_set_user(m, i); +} + +static inline u32 msg_hdr_sz(struct tipc_msg *m) +{ + return msg_bits(m, 0, 21, 0xf) << 2; +} + +static inline void msg_set_hdr_sz(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 0, 21, 0xf, n>>2); +} + +static inline u32 msg_size(struct tipc_msg *m) +{ + return msg_bits(m, 0, 0, 0x1ffff); +} + +static inline u32 msg_data_sz(struct tipc_msg *m) +{ + return msg_size(m) - msg_hdr_sz(m); +} + +static inline int msg_non_seq(struct tipc_msg *m) +{ + return msg_bits(m, 0, 20, 1); +} + +static inline void msg_set_non_seq(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 0, 20, 1, n); +} + +static inline int msg_dest_droppable(struct tipc_msg *m) +{ + return msg_bits(m, 0, 19, 1); +} + +static inline void msg_set_dest_droppable(struct tipc_msg *m, u32 d) +{ + msg_set_bits(m, 0, 19, 1, d); +} + +static inline int msg_src_droppable(struct tipc_msg *m) +{ + return msg_bits(m, 0, 18, 1); +} + +static inline void msg_set_src_droppable(struct tipc_msg *m, u32 d) +{ + msg_set_bits(m, 0, 18, 1, d); +} + +static inline void msg_set_size(struct tipc_msg *m, u32 sz) +{ + m->hdr[0] = htonl((msg_word(m, 0) & ~0x1ffff) | sz); +} + + +/* + * Word 1 + */ + +static inline u32 msg_type(struct tipc_msg *m) +{ + return msg_bits(m, 1, 29, 0x7); +} + +static inline void msg_set_type(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 1, 29, 0x7, n); +} + +static inline u32 msg_named(struct tipc_msg *m) +{ + return msg_type(m) == TIPC_NAMED_MSG; +} + +static inline u32 msg_mcast(struct tipc_msg *m) +{ + return msg_type(m) == TIPC_MCAST_MSG; +} + +static inline u32 msg_connected(struct tipc_msg *m) +{ + return msg_type(m) == TIPC_CONN_MSG; +} + +static inline u32 msg_errcode(struct tipc_msg *m) +{ + return msg_bits(m, 1, 25, 0xf); +} + +static inline void msg_set_errcode(struct tipc_msg *m, u32 err) +{ + msg_set_bits(m, 1, 25, 0xf, err); +} + +static inline u32 msg_reroute_cnt(struct tipc_msg *m) +{ + return msg_bits(m, 1, 21, 0xf); +} + +static inline void msg_incr_reroute_cnt(struct tipc_msg *m) +{ + msg_set_bits(m, 1, 21, 0xf, msg_reroute_cnt(m) + 1); +} + +static inline void msg_reset_reroute_cnt(struct tipc_msg *m) +{ + msg_set_bits(m, 1, 21, 0xf, 0); +} + +static inline u32 msg_lookup_scope(struct tipc_msg *m) +{ + return msg_bits(m, 1, 19, 0x3); +} + +static inline void msg_set_lookup_scope(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 1, 19, 0x3, n); +} + +static inline u32 msg_bcast_ack(struct tipc_msg *m) +{ + return msg_bits(m, 1, 0, 0xffff); +} + +static inline void msg_set_bcast_ack(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 1, 0, 0xffff, n); +} + + +/* + * Word 2 + */ + +static inline u32 msg_ack(struct tipc_msg *m) +{ + return msg_bits(m, 2, 16, 0xffff); +} + +static inline void msg_set_ack(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 2, 16, 0xffff, n); +} + +static inline u32 msg_seqno(struct tipc_msg *m) +{ + return msg_bits(m, 2, 0, 0xffff); +} + +static inline void msg_set_seqno(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 2, 0, 0xffff, n); +} + +/* + * Words 3-10 + */ + + +static inline u32 msg_prevnode(struct tipc_msg *m) +{ + return msg_word(m, 3); +} + +static inline void msg_set_prevnode(struct tipc_msg *m, u32 a) +{ + msg_set_word(m, 3, a); +} + +static inline u32 msg_origport(struct tipc_msg *m) +{ + return msg_word(m, 4); +} + +static inline void msg_set_origport(struct tipc_msg *m, u32 p) +{ + msg_set_word(m, 4, p); +} + +static inline u32 msg_destport(struct tipc_msg *m) +{ + return msg_word(m, 5); +} + +static inline void msg_set_destport(struct tipc_msg *m, u32 p) +{ + msg_set_word(m, 5, p); +} + +static inline u32 msg_mc_netid(struct tipc_msg *m) +{ + return msg_word(m, 5); +} + +static inline void msg_set_mc_netid(struct tipc_msg *m, u32 p) +{ + msg_set_word(m, 5, p); +} + +static inline int msg_short(struct tipc_msg *m) +{ + return msg_hdr_sz(m) == SHORT_H_SIZE; +} + +static inline u32 msg_orignode(struct tipc_msg *m) +{ + if (likely(msg_short(m))) + return msg_prevnode(m); + return msg_word(m, 6); +} + +static inline void msg_set_orignode(struct tipc_msg *m, u32 a) +{ + msg_set_word(m, 6, a); +} + +static inline u32 msg_destnode(struct tipc_msg *m) +{ + return msg_word(m, 7); +} + +static inline void msg_set_destnode(struct tipc_msg *m, u32 a) +{ + msg_set_word(m, 7, a); +} + +static inline u32 msg_nametype(struct tipc_msg *m) +{ + return msg_word(m, 8); +} + +static inline void msg_set_nametype(struct tipc_msg *m, u32 n) +{ + msg_set_word(m, 8, n); +} + +static inline u32 msg_nameinst(struct tipc_msg *m) +{ + return msg_word(m, 9); +} + +static inline u32 msg_namelower(struct tipc_msg *m) +{ + return msg_nameinst(m); +} + +static inline void msg_set_namelower(struct tipc_msg *m, u32 n) +{ + msg_set_word(m, 9, n); +} + +static inline void msg_set_nameinst(struct tipc_msg *m, u32 n) +{ + msg_set_namelower(m, n); +} + +static inline u32 msg_nameupper(struct tipc_msg *m) +{ + return msg_word(m, 10); +} + +static inline void msg_set_nameupper(struct tipc_msg *m, u32 n) +{ + msg_set_word(m, 10, n); +} + +static inline unchar *msg_data(struct tipc_msg *m) +{ + return ((unchar *)m) + msg_hdr_sz(m); +} + +static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m) +{ + return (struct tipc_msg *)msg_data(m); +} + + +/* + * Constants and routines used to read and write TIPC internal message headers + */ + +/* + * Internal message users + */ + +#define BCAST_PROTOCOL 5 +#define MSG_BUNDLER 6 +#define LINK_PROTOCOL 7 +#define CONN_MANAGER 8 +#define ROUTE_DISTRIBUTOR 9 /* obsoleted */ +#define CHANGEOVER_PROTOCOL 10 +#define NAME_DISTRIBUTOR 11 +#define MSG_FRAGMENTER 12 +#define LINK_CONFIG 13 + +/* + * Connection management protocol message types + */ + +#define CONN_PROBE 0 +#define CONN_PROBE_REPLY 1 +#define CONN_ACK 2 + +/* + * Name distributor message types + */ + +#define PUBLICATION 0 +#define WITHDRAWAL 1 + +/* + * Segmentation message types + */ + +#define FIRST_FRAGMENT 0 +#define FRAGMENT 1 +#define LAST_FRAGMENT 2 + +/* + * Link management protocol message types + */ + +#define STATE_MSG 0 +#define RESET_MSG 1 +#define ACTIVATE_MSG 2 + +/* + * Changeover tunnel message types + */ +#define DUPLICATE_MSG 0 +#define ORIGINAL_MSG 1 + +/* + * Config protocol message types + */ + +#define DSC_REQ_MSG 0 +#define DSC_RESP_MSG 1 + + +/* + * Word 1 + */ + +static inline u32 msg_seq_gap(struct tipc_msg *m) +{ + return msg_bits(m, 1, 16, 0x1fff); +} + +static inline void msg_set_seq_gap(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 1, 16, 0x1fff, n); +} + +static inline u32 msg_node_sig(struct tipc_msg *m) +{ + return msg_bits(m, 1, 0, 0xffff); +} + +static inline void msg_set_node_sig(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 1, 0, 0xffff, n); +} + + +/* + * Word 2 + */ + +static inline u32 msg_dest_domain(struct tipc_msg *m) +{ + return msg_word(m, 2); +} + +static inline void msg_set_dest_domain(struct tipc_msg *m, u32 n) +{ + msg_set_word(m, 2, n); +} + +static inline u32 msg_bcgap_after(struct tipc_msg *m) +{ + return msg_bits(m, 2, 16, 0xffff); +} + +static inline void msg_set_bcgap_after(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 2, 16, 0xffff, n); +} + +static inline u32 msg_bcgap_to(struct tipc_msg *m) +{ + return msg_bits(m, 2, 0, 0xffff); +} + +static inline void msg_set_bcgap_to(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 2, 0, 0xffff, n); +} + + +/* + * Word 4 + */ + +static inline u32 msg_last_bcast(struct tipc_msg *m) +{ + return msg_bits(m, 4, 16, 0xffff); +} + +static inline void msg_set_last_bcast(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 4, 16, 0xffff, n); +} + + +static inline u32 msg_fragm_no(struct tipc_msg *m) +{ + return msg_bits(m, 4, 16, 0xffff); +} + +static inline void msg_set_fragm_no(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 4, 16, 0xffff, n); +} + + +static inline u32 msg_next_sent(struct tipc_msg *m) +{ + return msg_bits(m, 4, 0, 0xffff); +} + +static inline void msg_set_next_sent(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 4, 0, 0xffff, n); +} + + +static inline u32 msg_long_msgno(struct tipc_msg *m) +{ + return msg_bits(m, 4, 0, 0xffff); +} + +static inline void msg_set_long_msgno(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 4, 0, 0xffff, n); +} + +static inline u32 msg_bc_netid(struct tipc_msg *m) +{ + return msg_word(m, 4); +} + +static inline void msg_set_bc_netid(struct tipc_msg *m, u32 id) +{ + msg_set_word(m, 4, id); +} + +static inline u32 msg_link_selector(struct tipc_msg *m) +{ + return msg_bits(m, 4, 0, 1); +} + +static inline void msg_set_link_selector(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 4, 0, 1, n); +} + +/* + * Word 5 + */ + +static inline u32 msg_session(struct tipc_msg *m) +{ + return msg_bits(m, 5, 16, 0xffff); +} + +static inline void msg_set_session(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 5, 16, 0xffff, n); +} + +static inline u32 msg_probe(struct tipc_msg *m) +{ + return msg_bits(m, 5, 0, 1); +} + +static inline void msg_set_probe(struct tipc_msg *m, u32 val) +{ + msg_set_bits(m, 5, 0, 1, val); +} + +static inline char msg_net_plane(struct tipc_msg *m) +{ + return msg_bits(m, 5, 1, 7) + 'A'; +} + +static inline void msg_set_net_plane(struct tipc_msg *m, char n) +{ + msg_set_bits(m, 5, 1, 7, (n - 'A')); +} + +static inline u32 msg_linkprio(struct tipc_msg *m) +{ + return msg_bits(m, 5, 4, 0x1f); +} + +static inline void msg_set_linkprio(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 5, 4, 0x1f, n); +} + +static inline u32 msg_bearer_id(struct tipc_msg *m) +{ + return msg_bits(m, 5, 9, 0x7); +} + +static inline void msg_set_bearer_id(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 5, 9, 0x7, n); +} + +static inline u32 msg_redundant_link(struct tipc_msg *m) +{ + return msg_bits(m, 5, 12, 0x1); +} + +static inline void msg_set_redundant_link(struct tipc_msg *m, u32 r) +{ + msg_set_bits(m, 5, 12, 0x1, r); +} + +static inline char *msg_media_addr(struct tipc_msg *m) +{ + return (char *)&m->hdr[TIPC_MEDIA_ADDR_OFFSET]; +} + +/* + * Word 9 + */ + +static inline u32 msg_msgcnt(struct tipc_msg *m) +{ + return msg_bits(m, 9, 16, 0xffff); +} + +static inline void msg_set_msgcnt(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 9, 16, 0xffff, n); +} + +static inline u32 msg_bcast_tag(struct tipc_msg *m) +{ + return msg_bits(m, 9, 16, 0xffff); +} + +static inline void msg_set_bcast_tag(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 9, 16, 0xffff, n); +} + +static inline u32 msg_max_pkt(struct tipc_msg *m) +{ + return msg_bits(m, 9, 16, 0xffff) * 4; +} + +static inline void msg_set_max_pkt(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 9, 16, 0xffff, (n / 4)); +} + +static inline u32 msg_link_tolerance(struct tipc_msg *m) +{ + return msg_bits(m, 9, 0, 0xffff); +} + +static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 9, 0, 0xffff, n); +} + +u32 tipc_msg_tot_importance(struct tipc_msg *m); +void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, + u32 hsize, u32 destnode); +int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, + u32 num_sect, unsigned int total_len, + int max_size, int usrmem, struct sk_buff **buf); + +#endif diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c new file mode 100644 index 00000000..d57da615 --- /dev/null +++ b/net/tipc/name_distr.c @@ -0,0 +1,336 @@ +/* + * net/tipc/name_distr.c: TIPC name distribution code + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, Wind River Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "link.h" +#include "name_distr.h" + +#define ITEM_SIZE sizeof(struct distr_item) + +/** + * struct distr_item - publication info distributed to other nodes + * @type: name sequence type + * @lower: name sequence lower bound + * @upper: name sequence upper bound + * @ref: publishing port reference + * @key: publication key + * + * ===> All fields are stored in network byte order. <=== + * + * First 3 fields identify (name or) name sequence being published. + * Reference field uniquely identifies port that published name sequence. + * Key field uniquely identifies publication, in the event a port has + * multiple publications of the same name sequence. + * + * Note: There is no field that identifies the publishing node because it is + * the same for all items contained within a publication message. + */ + +struct distr_item { + __be32 type; + __be32 lower; + __be32 upper; + __be32 ref; + __be32 key; +}; + +/** + * List of externally visible publications by this node -- + * that is, all publications having scope > TIPC_NODE_SCOPE. + */ + +static LIST_HEAD(publ_root); +static u32 publ_cnt; + +/** + * publ_to_item - add publication info to a publication message + */ + +static void publ_to_item(struct distr_item *i, struct publication *p) +{ + i->type = htonl(p->type); + i->lower = htonl(p->lower); + i->upper = htonl(p->upper); + i->ref = htonl(p->ref); + i->key = htonl(p->key); +} + +/** + * named_prepare_buf - allocate & initialize a publication message + */ + +static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) +{ + 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); + msg_set_size(msg, INT_H_SIZE + size); + } + return buf; +} + +static void named_cluster_distribute(struct sk_buff *buf) +{ + struct sk_buff *buf_copy; + struct tipc_node *n_ptr; + + list_for_each_entry(n_ptr, &tipc_node_list, list) { + if (tipc_node_active_links(n_ptr)) { + buf_copy = skb_copy(buf, GFP_ATOMIC); + if (!buf_copy) + break; + msg_set_destnode(buf_msg(buf_copy), n_ptr->addr); + tipc_link_send(buf_copy, n_ptr->addr, n_ptr->addr); + } + } + + kfree_skb(buf); +} + +/** + * tipc_named_publish - tell other nodes about a new publication by this node + */ + +void tipc_named_publish(struct publication *publ) +{ + struct sk_buff *buf; + struct distr_item *item; + + list_add_tail(&publ->local_list, &publ_root); + publ_cnt++; + + buf = named_prepare_buf(PUBLICATION, ITEM_SIZE, 0); + if (!buf) { + warn("Publication distribution failure\n"); + return; + } + + item = (struct distr_item *)msg_data(buf_msg(buf)); + publ_to_item(item, publ); + named_cluster_distribute(buf); +} + +/** + * tipc_named_withdraw - tell other nodes about a withdrawn publication by this node + */ + +void tipc_named_withdraw(struct publication *publ) +{ + struct sk_buff *buf; + struct distr_item *item; + + list_del(&publ->local_list); + publ_cnt--; + + buf = named_prepare_buf(WITHDRAWAL, ITEM_SIZE, 0); + if (!buf) { + warn("Withdrawal distribution failure\n"); + return; + } + + item = (struct distr_item *)msg_data(buf_msg(buf)); + publ_to_item(item, publ); + named_cluster_distribute(buf); +} + +/** + * tipc_named_node_up - tell specified node about all publications by this node + */ + +void tipc_named_node_up(unsigned long nodearg) +{ + struct tipc_node *n_ptr; + struct tipc_link *l_ptr; + struct publication *publ; + struct distr_item *item = NULL; + struct sk_buff *buf = NULL; + struct list_head message_list; + u32 node = (u32)nodearg; + u32 left = 0; + u32 rest; + u32 max_item_buf = 0; + + /* compute maximum amount of publication data to send per message */ + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(node); + if (n_ptr) { + tipc_node_lock(n_ptr); + l_ptr = n_ptr->active_links[0]; + if (l_ptr) + max_item_buf = ((l_ptr->max_pkt - INT_H_SIZE) / + ITEM_SIZE) * ITEM_SIZE; + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); + if (!max_item_buf) + return; + + /* create list of publication messages, then send them as a unit */ + + INIT_LIST_HEAD(&message_list); + + read_lock_bh(&tipc_nametbl_lock); + rest = publ_cnt * ITEM_SIZE; + + list_for_each_entry(publ, &publ_root, local_list) { + if (!buf) { + left = (rest <= max_item_buf) ? rest : max_item_buf; + rest -= left; + buf = named_prepare_buf(PUBLICATION, left, node); + if (!buf) { + warn("Bulk publication distribution failure\n"); + goto exit; + } + item = (struct distr_item *)msg_data(buf_msg(buf)); + } + publ_to_item(item, publ); + item++; + left -= ITEM_SIZE; + if (!left) { + list_add_tail((struct list_head *)buf, &message_list); + buf = NULL; + } + } +exit: + read_unlock_bh(&tipc_nametbl_lock); + + tipc_link_send_names(&message_list, (u32)node); +} + +/** + * named_purge_publ - remove publication associated with a failed node + * + * Invoked for each publication issued by a newly failed node. + * Removes publication structure from name table & deletes it. + */ + +static void named_purge_publ(struct publication *publ) +{ + struct publication *p; + + write_lock_bh(&tipc_nametbl_lock); + p = tipc_nametbl_remove_publ(publ->type, publ->lower, + publ->node, publ->ref, publ->key); + if (p) + tipc_nodesub_unsubscribe(&p->subscr); + write_unlock_bh(&tipc_nametbl_lock); + + if (p != publ) { + err("Unable to remove publication from failed node\n" + "(type=%u, lower=%u, node=0x%x, ref=%u, key=%u)\n", + publ->type, publ->lower, publ->node, publ->ref, publ->key); + } + + kfree(p); +} + +/** + * tipc_named_recv - process name table update message sent by another node + */ + +void tipc_named_recv(struct sk_buff *buf) +{ + struct publication *publ; + 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; + + write_lock_bh(&tipc_nametbl_lock); + while (count--) { + if (msg_type(msg) == PUBLICATION) { + publ = tipc_nametbl_insert_publ(ntohl(item->type), + ntohl(item->lower), + ntohl(item->upper), + TIPC_CLUSTER_SCOPE, + msg_orignode(msg), + ntohl(item->ref), + ntohl(item->key)); + if (publ) { + tipc_nodesub_subscribe(&publ->subscr, + msg_orignode(msg), + publ, + (net_ev_handler) + named_purge_publ); + } + } else if (msg_type(msg) == WITHDRAWAL) { + publ = tipc_nametbl_remove_publ(ntohl(item->type), + ntohl(item->lower), + msg_orignode(msg), + ntohl(item->ref), + ntohl(item->key)); + + if (publ) { + tipc_nodesub_unsubscribe(&publ->subscr); + kfree(publ); + } else { + err("Unable to remove publication by node 0x%x\n" + "(type=%u, lower=%u, ref=%u, key=%u)\n", + msg_orignode(msg), + ntohl(item->type), ntohl(item->lower), + ntohl(item->ref), ntohl(item->key)); + } + } else { + warn("Unrecognized name table message received\n"); + } + item++; + } + write_unlock_bh(&tipc_nametbl_lock); + kfree_skb(buf); +} + +/** + * tipc_named_reinit - re-initialize local publication list + * + * This routine is called whenever TIPC networking is enabled. + * All existing publications by this node that have "cluster" or "zone" scope + * are updated to reflect the node's new network address. + */ + +void tipc_named_reinit(void) +{ + struct publication *publ; + + write_lock_bh(&tipc_nametbl_lock); + + list_for_each_entry(publ, &publ_root, local_list) + publ->node = tipc_own_addr; + + write_unlock_bh(&tipc_nametbl_lock); +} diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h new file mode 100644 index 00000000..1e41bdd4 --- /dev/null +++ b/net/tipc/name_distr.h @@ -0,0 +1,48 @@ +/* + * net/tipc/name_distr.h: Include file for TIPC name distribution code + * + * Copyright (c) 2000-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_NAME_DISTR_H +#define _TIPC_NAME_DISTR_H + +#include "name_table.h" + +void tipc_named_publish(struct publication *publ); +void tipc_named_withdraw(struct publication *publ); +void tipc_named_node_up(unsigned long node); +void tipc_named_recv(struct sk_buff *buf); +void tipc_named_reinit(void); + +#endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c new file mode 100644 index 00000000..c6a1ae36 --- /dev/null +++ b/net/tipc/name_table.c @@ -0,0 +1,983 @@ +/* + * net/tipc/name_table.c: TIPC name table code + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2004-2008, 2010-2011, 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" +#include "name_table.h" +#include "name_distr.h" +#include "subscr.h" +#include "port.h" + +static int tipc_nametbl_size = 1024; /* must be a power of 2 */ + +/** + * struct name_info - name sequence publication info + * @node_list: circular list of publications made by own node + * @cluster_list: circular list of publications made by own cluster + * @zone_list: circular list of publications made by own zone + * @node_list_size: number of entries in "node_list" + * @cluster_list_size: number of entries in "cluster_list" + * @zone_list_size: number of entries in "zone_list" + * + * Note: The zone list always contains at least one entry, since all + * publications of the associated name sequence belong to it. + * (The cluster and node lists may be empty.) + */ + +struct name_info { + struct list_head node_list; + struct list_head cluster_list; + struct list_head zone_list; + u32 node_list_size; + u32 cluster_list_size; + u32 zone_list_size; +}; + +/** + * struct sub_seq - container for all published instances of a name sequence + * @lower: name sequence lower bound + * @upper: name sequence upper bound + * @info: pointer to name sequence publication info + */ + +struct sub_seq { + u32 lower; + u32 upper; + struct name_info *info; +}; + +/** + * struct name_seq - container for all published instances of a name type + * @type: 32 bit 'type' value for name sequence + * @sseq: pointer to dynamically-sized array of sub-sequences of this 'type'; + * sub-sequences are sorted in ascending order + * @alloc: number of sub-sequences currently in array + * @first_free: array index of first unused sub-sequence entry + * @ns_list: links to adjacent name sequences in hash chain + * @subscriptions: list of subscriptions for this 'type' + * @lock: spinlock controlling access to publication lists of all sub-sequences + */ + +struct name_seq { + u32 type; + struct sub_seq *sseqs; + u32 alloc; + u32 first_free; + struct hlist_node ns_list; + struct list_head subscriptions; + spinlock_t lock; +}; + +/** + * struct name_table - table containing all existing port name publications + * @types: pointer to fixed-sized array of name sequence lists, + * accessed via hashing on 'type'; name sequence lists are *not* sorted + * @local_publ_count: number of publications issued by this node + */ + +struct name_table { + struct hlist_head *types; + u32 local_publ_count; +}; + +static struct name_table table; +DEFINE_RWLOCK(tipc_nametbl_lock); + +static int hash(int x) +{ + return x & (tipc_nametbl_size - 1); +} + +/** + * publ_create - create a publication structure + */ + +static struct publication *publ_create(u32 type, u32 lower, u32 upper, + u32 scope, u32 node, u32 port_ref, + u32 key) +{ + struct publication *publ = kzalloc(sizeof(*publ), GFP_ATOMIC); + if (publ == NULL) { + warn("Publication creation failure, no memory\n"); + return NULL; + } + + publ->type = type; + publ->lower = lower; + publ->upper = upper; + publ->scope = scope; + publ->node = node; + publ->ref = port_ref; + publ->key = key; + INIT_LIST_HEAD(&publ->local_list); + INIT_LIST_HEAD(&publ->pport_list); + INIT_LIST_HEAD(&publ->subscr.nodesub_list); + return publ; +} + +/** + * tipc_subseq_alloc - allocate a specified number of sub-sequence structures + */ + +static struct sub_seq *tipc_subseq_alloc(u32 cnt) +{ + struct sub_seq *sseq = kcalloc(cnt, sizeof(struct sub_seq), GFP_ATOMIC); + return sseq; +} + +/** + * tipc_nameseq_create - create a name sequence structure for the specified 'type' + * + * Allocates a single sub-sequence structure and sets it to all 0's. + */ + +static struct name_seq *tipc_nameseq_create(u32 type, struct hlist_head *seq_head) +{ + struct name_seq *nseq = kzalloc(sizeof(*nseq), GFP_ATOMIC); + struct sub_seq *sseq = tipc_subseq_alloc(1); + + if (!nseq || !sseq) { + warn("Name sequence creation failed, no memory\n"); + kfree(nseq); + kfree(sseq); + return NULL; + } + + spin_lock_init(&nseq->lock); + nseq->type = type; + nseq->sseqs = sseq; + nseq->alloc = 1; + INIT_HLIST_NODE(&nseq->ns_list); + INIT_LIST_HEAD(&nseq->subscriptions); + hlist_add_head(&nseq->ns_list, seq_head); + return nseq; +} + +/** + * nameseq_find_subseq - find sub-sequence (if any) matching a name instance + * + * Very time-critical, so binary searches through sub-sequence array. + */ + +static struct sub_seq *nameseq_find_subseq(struct name_seq *nseq, + u32 instance) +{ + struct sub_seq *sseqs = nseq->sseqs; + int low = 0; + int high = nseq->first_free - 1; + int mid; + + while (low <= high) { + mid = (low + high) / 2; + if (instance < sseqs[mid].lower) + high = mid - 1; + else if (instance > sseqs[mid].upper) + low = mid + 1; + else + return &sseqs[mid]; + } + return NULL; +} + +/** + * nameseq_locate_subseq - determine position of name instance in sub-sequence + * + * Returns index in sub-sequence array of the entry that contains the specified + * instance value; if no entry contains that value, returns the position + * where a new entry for it would be inserted in the array. + * + * Note: Similar to binary search code for locating a sub-sequence. + */ + +static u32 nameseq_locate_subseq(struct name_seq *nseq, u32 instance) +{ + struct sub_seq *sseqs = nseq->sseqs; + int low = 0; + int high = nseq->first_free - 1; + int mid; + + while (low <= high) { + mid = (low + high) / 2; + if (instance < sseqs[mid].lower) + high = mid - 1; + else if (instance > sseqs[mid].upper) + low = mid + 1; + else + return mid; + } + return low; +} + +/** + * 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) +{ + struct tipc_subscription *s; + struct tipc_subscription *st; + struct publication *publ; + struct sub_seq *sseq; + struct name_info *info; + int created_subseq = 0; + + sseq = nameseq_find_subseq(nseq, lower); + if (sseq) { + + /* Lower end overlaps existing entry => need an exact match */ + + if ((sseq->lower != lower) || (sseq->upper != upper)) { + warn("Cannot publish {%u,%u,%u}, overlap error\n", + type, lower, upper); + return NULL; + } + + info = sseq->info; + + /* Check if an identical publication already exists */ + list_for_each_entry(publ, &info->zone_list, zone_list) { + if ((publ->ref == port) && (publ->key == key) && + (!publ->node || (publ->node == node))) + return NULL; + } + } else { + u32 inspos; + struct sub_seq *freesseq; + + /* Find where lower end should be inserted */ + + inspos = nameseq_locate_subseq(nseq, lower); + + /* Fail if upper end overlaps into an existing entry */ + + if ((inspos < nseq->first_free) && + (upper >= nseq->sseqs[inspos].lower)) { + warn("Cannot publish {%u,%u,%u}, overlap error\n", + type, lower, upper); + return NULL; + } + + /* Ensure there is space for new sub-sequence */ + + if (nseq->first_free == nseq->alloc) { + struct sub_seq *sseqs = tipc_subseq_alloc(nseq->alloc * 2); + + if (!sseqs) { + warn("Cannot publish {%u,%u,%u}, no memory\n", + type, lower, upper); + return NULL; + } + memcpy(sseqs, nseq->sseqs, + nseq->alloc * sizeof(struct sub_seq)); + kfree(nseq->sseqs); + nseq->sseqs = sseqs; + nseq->alloc *= 2; + } + + info = kzalloc(sizeof(*info), GFP_ATOMIC); + if (!info) { + warn("Cannot publish {%u,%u,%u}, no memory\n", + type, lower, upper); + return NULL; + } + + INIT_LIST_HEAD(&info->node_list); + INIT_LIST_HEAD(&info->cluster_list); + INIT_LIST_HEAD(&info->zone_list); + + /* Insert new sub-sequence */ + + sseq = &nseq->sseqs[inspos]; + freesseq = &nseq->sseqs[nseq->first_free]; + memmove(sseq + 1, sseq, (freesseq - sseq) * sizeof(*sseq)); + memset(sseq, 0, sizeof(*sseq)); + nseq->first_free++; + sseq->lower = lower; + sseq->upper = upper; + sseq->info = info; + created_subseq = 1; + } + + /* Insert a publication: */ + + publ = publ_create(type, lower, upper, scope, node, port, key); + if (!publ) + return NULL; + + list_add(&publ->zone_list, &info->zone_list); + info->zone_list_size++; + + if (in_own_cluster(node)) { + list_add(&publ->cluster_list, &info->cluster_list); + info->cluster_list_size++; + } + + if (node == tipc_own_addr) { + list_add(&publ->node_list, &info->node_list); + info->node_list_size++; + } + + /* + * Any subscriptions waiting for notification? + */ + list_for_each_entry_safe(s, st, &nseq->subscriptions, nameseq_list) { + tipc_subscr_report_overlap(s, + publ->lower, + publ->upper, + TIPC_PUBLISHED, + publ->ref, + publ->node, + created_subseq); + } + return publ; +} + +/** + * tipc_nameseq_remove_publ - + * + * NOTE: There may be cases where TIPC is asked to remove a publication + * that is not in the name table. For example, if another node issues a + * publication for a name sequence that overlaps an existing name sequence + * the publication will not be recorded, which means the publication won't + * be found when the name sequence is later withdrawn by that node. + * 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) +{ + struct publication *publ; + struct sub_seq *sseq = nameseq_find_subseq(nseq, inst); + struct name_info *info; + struct sub_seq *free; + struct tipc_subscription *s, *st; + int removed_subseq = 0; + + if (!sseq) + return NULL; + + info = sseq->info; + + /* Locate publication, if it exists */ + + list_for_each_entry(publ, &info->zone_list, zone_list) { + if ((publ->key == key) && (publ->ref == ref) && + (!publ->node || (publ->node == node))) + goto found; + } + return NULL; + +found: + /* Remove publication from zone scope list */ + + list_del(&publ->zone_list); + info->zone_list_size--; + + /* Remove publication from cluster scope list, if present */ + + if (in_own_cluster(node)) { + list_del(&publ->cluster_list); + info->cluster_list_size--; + } + + /* Remove publication from node scope list, if present */ + + if (node == tipc_own_addr) { + list_del(&publ->node_list); + info->node_list_size--; + } + + /* Contract subseq list if no more publications for that subseq */ + + if (list_empty(&info->zone_list)) { + kfree(info); + free = &nseq->sseqs[nseq->first_free--]; + memmove(sseq, sseq + 1, (free - (sseq + 1)) * sizeof(*sseq)); + removed_subseq = 1; + } + + /* Notify any waiting subscriptions */ + + list_for_each_entry_safe(s, st, &nseq->subscriptions, nameseq_list) { + tipc_subscr_report_overlap(s, + publ->lower, + publ->upper, + TIPC_WITHDRAWN, + publ->ref, + publ->node, + removed_subseq); + } + + return publ; +} + +/** + * tipc_nameseq_subscribe: attach a subscription, and issue + * the prescribed number of events if there is any sub- + * sequence overlapping with the requested sequence + */ + +static void tipc_nameseq_subscribe(struct name_seq *nseq, + struct tipc_subscription *s) +{ + struct sub_seq *sseq = nseq->sseqs; + + list_add(&s->nameseq_list, &nseq->subscriptions); + + if (!sseq) + return; + + while (sseq != &nseq->sseqs[nseq->first_free]) { + if (tipc_subscr_overlap(s, sseq->lower, sseq->upper)) { + struct publication *crs; + struct name_info *info = sseq->info; + int must_report = 1; + + list_for_each_entry(crs, &info->zone_list, zone_list) { + tipc_subscr_report_overlap(s, + sseq->lower, + sseq->upper, + TIPC_PUBLISHED, + crs->ref, + crs->node, + must_report); + must_report = 0; + } + } + sseq++; + } +} + +static struct name_seq *nametbl_find_seq(u32 type) +{ + struct hlist_head *seq_head; + struct hlist_node *seq_node; + struct name_seq *ns; + + seq_head = &table.types[hash(type)]; + hlist_for_each_entry(ns, seq_node, seq_head, ns_list) { + if (ns->type == type) + return ns; + } + + return NULL; +}; + +struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, + u32 scope, u32 node, u32 port, u32 key) +{ + struct name_seq *seq = nametbl_find_seq(type); + + if (lower > upper) { + warn("Failed to publish illegal {%u,%u,%u}\n", + type, lower, upper); + return NULL; + } + + if (!seq) + seq = tipc_nameseq_create(type, &table.types[hash(type)]); + if (!seq) + return NULL; + + return tipc_nameseq_insert_publ(seq, type, lower, upper, + scope, node, port, key); +} + +struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, + u32 node, u32 ref, u32 key) +{ + struct publication *publ; + struct name_seq *seq = nametbl_find_seq(type); + + if (!seq) + return NULL; + + publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key); + + if (!seq->first_free && list_empty(&seq->subscriptions)) { + hlist_del_init(&seq->ns_list); + kfree(seq->sseqs); + kfree(seq); + } + return publ; +} + +/* + * tipc_nametbl_translate - perform name translation + * + * On entry, 'destnode' is the search domain used during translation. + * + * On exit: + * - if name translation is deferred to another node/cluster/zone, + * leaves 'destnode' unchanged (will be non-zero) and returns 0 + * - if name translation is attempted and succeeds, sets 'destnode' + * to publishing node and returns port reference (will be non-zero) + * - if name translation is attempted and fails, sets 'destnode' to 0 + * and returns 0 + */ + +u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) +{ + struct sub_seq *sseq; + struct name_info *info; + struct publication *publ; + struct name_seq *seq; + u32 ref = 0; + u32 node = 0; + + if (!tipc_in_scope(*destnode, tipc_own_addr)) + return 0; + + read_lock_bh(&tipc_nametbl_lock); + seq = nametbl_find_seq(type); + if (unlikely(!seq)) + goto not_found; + sseq = nameseq_find_subseq(seq, instance); + if (unlikely(!sseq)) + goto not_found; + spin_lock_bh(&seq->lock); + info = sseq->info; + + /* Closest-First Algorithm: */ + if (likely(!*destnode)) { + if (!list_empty(&info->node_list)) { + publ = list_first_entry(&info->node_list, + struct publication, + node_list); + list_move_tail(&publ->node_list, + &info->node_list); + } else if (!list_empty(&info->cluster_list)) { + publ = list_first_entry(&info->cluster_list, + struct publication, + cluster_list); + list_move_tail(&publ->cluster_list, + &info->cluster_list); + } else { + publ = list_first_entry(&info->zone_list, + struct publication, + zone_list); + list_move_tail(&publ->zone_list, + &info->zone_list); + } + } + + /* Round-Robin Algorithm: */ + else if (*destnode == tipc_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(*destnode)) { + if (list_empty(&info->cluster_list)) + goto no_match; + publ = list_first_entry(&info->cluster_list, struct publication, + cluster_list); + list_move_tail(&publ->cluster_list, &info->cluster_list); + } else { + publ = list_first_entry(&info->zone_list, struct publication, + zone_list); + list_move_tail(&publ->zone_list, &info->zone_list); + } + + ref = publ->ref; + node = publ->node; +no_match: + spin_unlock_bh(&seq->lock); +not_found: + read_unlock_bh(&tipc_nametbl_lock); + *destnode = node; + return ref; +} + +/** + * tipc_nametbl_mc_translate - find multicast destinations + * + * Creates list of all local ports that overlap the given multicast address; + * also determines if any off-node ports overlap. + * + * Note: Publications with a scope narrower than 'limit' are ignored. + * (i.e. local node-scope publications mustn't receive messages arriving + * from another node, even if the multcast link brought it here) + * + * 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) +{ + struct name_seq *seq; + struct sub_seq *sseq; + struct sub_seq *sseq_stop; + struct name_info *info; + int res = 0; + + read_lock_bh(&tipc_nametbl_lock); + seq = nametbl_find_seq(type); + if (!seq) + goto exit; + + spin_lock_bh(&seq->lock); + + sseq = seq->sseqs + nameseq_locate_subseq(seq, lower); + sseq_stop = seq->sseqs + seq->first_free; + for (; sseq != sseq_stop; sseq++) { + struct publication *publ; + + if (sseq->lower > upper) + break; + + info = sseq->info; + list_for_each_entry(publ, &info->node_list, node_list) { + if (publ->scope <= limit) + tipc_port_list_add(dports, publ->ref); + } + + if (info->cluster_list_size != info->node_list_size) + res = 1; + } + + spin_unlock_bh(&seq->lock); +exit: + read_unlock_bh(&tipc_nametbl_lock); + return res; +} + +/* + * 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 *publ; + + if (table.local_publ_count >= tipc_max_publications) { + warn("Publication failed, local publication limit reached (%u)\n", + tipc_max_publications); + return NULL; + } + + write_lock_bh(&tipc_nametbl_lock); + table.local_publ_count++; + publ = tipc_nametbl_insert_publ(type, lower, upper, scope, + tipc_own_addr, port_ref, key); + if (publ && (scope != TIPC_NODE_SCOPE)) + tipc_named_publish(publ); + write_unlock_bh(&tipc_nametbl_lock); + return publ; +} + +/** + * tipc_nametbl_withdraw - withdraw name publication from network name tables + */ + +int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) +{ + struct publication *publ; + + write_lock_bh(&tipc_nametbl_lock); + publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key); + if (likely(publ)) { + table.local_publ_count--; + if (publ->scope != TIPC_NODE_SCOPE) + tipc_named_withdraw(publ); + write_unlock_bh(&tipc_nametbl_lock); + list_del_init(&publ->pport_list); + kfree(publ); + return 1; + } + write_unlock_bh(&tipc_nametbl_lock); + err("Unable to remove local publication\n" + "(type=%u, lower=%u, ref=%u, key=%u)\n", + type, lower, ref, key); + return 0; +} + +/** + * tipc_nametbl_subscribe - add a subscription object to the name table + */ + +void tipc_nametbl_subscribe(struct tipc_subscription *s) +{ + u32 type = s->seq.type; + struct name_seq *seq; + + write_lock_bh(&tipc_nametbl_lock); + seq = nametbl_find_seq(type); + if (!seq) + seq = tipc_nameseq_create(type, &table.types[hash(type)]); + if (seq) { + spin_lock_bh(&seq->lock); + tipc_nameseq_subscribe(seq, s); + spin_unlock_bh(&seq->lock); + } else { + warn("Failed to create subscription for {%u,%u,%u}\n", + s->seq.type, s->seq.lower, s->seq.upper); + } + write_unlock_bh(&tipc_nametbl_lock); +} + +/** + * tipc_nametbl_unsubscribe - remove a subscription object from name table + */ + +void tipc_nametbl_unsubscribe(struct tipc_subscription *s) +{ + struct name_seq *seq; + + write_lock_bh(&tipc_nametbl_lock); + seq = nametbl_find_seq(s->seq.type); + if (seq != NULL) { + spin_lock_bh(&seq->lock); + list_del_init(&s->nameseq_list); + spin_unlock_bh(&seq->lock); + if ((seq->first_free == 0) && list_empty(&seq->subscriptions)) { + hlist_del_init(&seq->ns_list); + kfree(seq->sseqs); + kfree(seq); + } + } + write_unlock_bh(&tipc_nametbl_lock); +} + + +/** + * subseq_list: print specified sub-sequence contents into the given buffer + */ + +static void subseq_list(struct sub_seq *sseq, struct print_buf *buf, u32 depth, + u32 index) +{ + char portIdStr[27]; + const char *scope_str[] = {"", " zone", " cluster", " node"}; + struct publication *publ; + struct name_info *info; + + tipc_printf(buf, "%-10u %-10u ", sseq->lower, sseq->upper); + + if (depth == 2) { + tipc_printf(buf, "\n"); + return; + } + + 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); + tipc_printf(buf, "%-26s ", portIdStr); + if (depth > 3) { + tipc_printf(buf, "%-10u %s", publ->key, + scope_str[publ->scope]); + } + if (!list_is_last(&publ->zone_list, &info->zone_list)) + tipc_printf(buf, "\n%33s", " "); + }; + + tipc_printf(buf, "\n"); +} + +/** + * nameseq_list: print specified name sequence contents into the given buffer + */ + +static void nameseq_list(struct name_seq *seq, struct print_buf *buf, u32 depth, + u32 type, u32 lowbound, u32 upbound, u32 index) +{ + struct sub_seq *sseq; + char typearea[11]; + + if (seq->first_free == 0) + return; + + sprintf(typearea, "%-10u", seq->type); + + if (depth == 1) { + tipc_printf(buf, "%s\n", typearea); + return; + } + + for (sseq = seq->sseqs; sseq != &seq->sseqs[seq->first_free]; sseq++) { + if ((lowbound <= sseq->upper) && (upbound >= sseq->lower)) { + tipc_printf(buf, "%s ", typearea); + spin_lock_bh(&seq->lock); + subseq_list(sseq, buf, depth, index); + spin_unlock_bh(&seq->lock); + sprintf(typearea, "%10s", " "); + } + } +} + +/** + * nametbl_header - print name table header into the given buffer + */ + +static void nametbl_header(struct print_buf *buf, u32 depth) +{ + const char *header[] = { + "Type ", + "Lower Upper ", + "Port Identity ", + "Publication Scope" + }; + + int i; + + if (depth > 4) + depth = 4; + for (i = 0; i < depth; i++) + tipc_printf(buf, header[i]); + tipc_printf(buf, "\n"); +} + +/** + * nametbl_list - print specified name table contents into the given buffer + */ + +static void nametbl_list(struct print_buf *buf, u32 depth_info, + u32 type, u32 lowbound, u32 upbound) +{ + struct hlist_head *seq_head; + struct hlist_node *seq_node; + struct name_seq *seq; + int all_types; + u32 depth; + u32 i; + + all_types = (depth_info & TIPC_NTQ_ALLTYPES); + depth = (depth_info & ~TIPC_NTQ_ALLTYPES); + + if (depth == 0) + return; + + if (all_types) { + /* display all entries in name table to specified depth */ + nametbl_header(buf, depth); + lowbound = 0; + upbound = ~0; + for (i = 0; i < tipc_nametbl_size; i++) { + seq_head = &table.types[i]; + hlist_for_each_entry(seq, seq_node, seq_head, ns_list) { + nameseq_list(seq, buf, depth, seq->type, + lowbound, upbound, i); + } + } + } else { + /* display only the sequence that matches the specified type */ + if (upbound < lowbound) { + tipc_printf(buf, "invalid name sequence specified\n"); + return; + } + nametbl_header(buf, depth); + i = hash(type); + seq_head = &table.types[i]; + hlist_for_each_entry(seq, seq_node, seq_head, ns_list) { + if (seq->type == type) { + nameseq_list(seq, buf, depth, type, + lowbound, upbound, i); + break; + } + } + } +} + +#define MAX_NAME_TBL_QUERY 32768 + +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; + struct print_buf b; + 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(MAX_NAME_TBL_QUERY)); + if (!buf) + return NULL; + + rep_tlv = (struct tlv_desc *)buf->data; + tipc_printbuf_init(&b, TLV_DATA(rep_tlv), MAX_NAME_TBL_QUERY); + argv = (struct tipc_name_table_query *)TLV_DATA(req_tlv_area); + read_lock_bh(&tipc_nametbl_lock); + nametbl_list(&b, ntohl(argv->depth), ntohl(argv->type), + ntohl(argv->lowbound), ntohl(argv->upbound)); + read_unlock_bh(&tipc_nametbl_lock); + str_len = tipc_printbuf_validate(&b); + + 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) +{ + table.types = kcalloc(tipc_nametbl_size, sizeof(struct hlist_head), + GFP_ATOMIC); + if (!table.types) + return -ENOMEM; + + table.local_publ_count = 0; + return 0; +} + +void tipc_nametbl_stop(void) +{ + u32 i; + + if (!table.types) + return; + + /* Verify name table is empty, then release it */ + + write_lock_bh(&tipc_nametbl_lock); + for (i = 0; i < tipc_nametbl_size; i++) { + if (!hlist_empty(&table.types[i])) + err("tipc_nametbl_stop(): hash chain %u is non-null\n", i); + } + kfree(table.types); + table.types = NULL; + write_unlock_bh(&tipc_nametbl_lock); +} + diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h new file mode 100644 index 00000000..207d59eb --- /dev/null +++ b/net/tipc/name_table.h @@ -0,0 +1,106 @@ +/* + * net/tipc/name_table.h: Include file for TIPC name table code + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2004-2005, 2010-2011, 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_NAME_TABLE_H +#define _TIPC_NAME_TABLE_H + +#include "node_subscr.h" + +struct tipc_subscription; +struct tipc_port_list; + +/* + * TIPC name types reserved for internal TIPC use (both current and planned) + */ + +#define TIPC_ZM_SRV 3 /* zone master service name type */ + + +/** + * struct publication - info about a published (name or) name sequence + * @type: name sequence type + * @lower: name sequence lower bound + * @upper: name sequence upper bound + * @scope: scope of publication + * @node: network address of publishing port's node + * @ref: publishing port + * @key: publication key + * @subscr: subscription to "node down" event (for off-node publications only) + * @local_list: adjacent entries in list of publications made by this node + * @pport_list: adjacent entries in list of publications made by this port + * @node_list: adjacent matching name seq publications with >= node scope + * @cluster_list: adjacent matching name seq publications with >= cluster scope + * @zone_list: adjacent matching name seq publications with >= zone scope + * + * Note that the node list, cluster list, and zone list are circular lists. + */ + +struct publication { + u32 type; + u32 lower; + u32 upper; + u32 scope; + u32 node; + u32 ref; + u32 key; + struct tipc_node_subscr subscr; + struct list_head local_list; + struct list_head pport_list; + struct list_head node_list; + struct list_head cluster_list; + struct list_head zone_list; +}; + + +extern rwlock_t tipc_nametbl_lock; + +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 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); + +#endif diff --git a/net/tipc/net.c b/net/tipc/net.c new file mode 100644 index 00000000..d4531b07 --- /dev/null +++ b/net/tipc/net.c @@ -0,0 +1,209 @@ +/* + * net/tipc/net.c: TIPC network routing code + * + * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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 "net.h" +#include "name_distr.h" +#include "subscr.h" +#include "port.h" +#include "node.h" +#include "config.h" + +/* + * The TIPC locking policy is designed to ensure a very fine locking + * granularity, permitting complete parallel access to individual + * port and node/link instances. The code consists of three major + * locking domains, each protected with their own disjunct set of locks. + * + * 1: The routing hierarchy. + * Comprises the structures 'zone', 'cluster', 'node', 'link' + * and 'bearer'. The whole hierarchy is protected by a big + * read/write lock, tipc_net_lock, to enssure that nothing is added + * or removed while code is accessing any of these structures. + * This layer must not be called from the two others while they + * hold any of their own locks. + * Neither must it itself do any upcalls to the other two before + * it has released tipc_net_lock and other protective locks. + * + * Within the tipc_net_lock domain there are two sub-domains;'node' and + * 'bearer', where local write operations are permitted, + * provided that those are protected by individual spin_locks + * per instance. Code holding tipc_net_lock(read) and a node spin_lock + * is permitted to poke around in both the node itself and its + * subordinate links. I.e, it can update link counters and queues, + * change link state, send protocol messages, and alter the + * "active_links" array in the node; but it can _not_ remove a link + * or a node from the overall structure. + * Correspondingly, individual bearers may change status within a + * tipc_net_lock(read), protected by an individual spin_lock ber bearer + * instance, but it needs tipc_net_lock(write) to remove/add any bearers. + * + * + * 2: The transport level of the protocol. + * This consists of the structures port, (and its user level + * representations, such as user_port and tipc_sock), reference and + * tipc_user (port.c, reg.c, socket.c). + * + * This layer has four different locks: + * - The tipc_port spin_lock. This is protecting each port instance + * from parallel data access and removal. Since we can not place + * this lock in the port itself, it has been placed in the + * corresponding reference table entry, which has the same life + * cycle as the module. This entry is difficult to access from + * outside the TIPC core, however, so a pointer to the lock has + * been added in the port instance, -to be used for unlocking + * only. + * - A read/write lock to protect the reference table itself (teg.c). + * (Nobody is using read-only access to this, so it can just as + * well be changed to a spin_lock) + * - A spin lock to protect the registry of kernel/driver users (reg.c) + * - A global spin_lock (tipc_port_lock), which only task is to ensure + * consistency where more than one port is involved in an operation, + * i.e., whe a port is part of a linked list of ports. + * There are two such lists; 'port_list', which is used for management, + * and 'wait_list', which is used to queue ports during congestion. + * + * 3: The name table (name_table.c, name_distr.c, subscription.c) + * - There is one big read/write-lock (tipc_nametbl_lock) protecting the + * overall name table structure. Nothing must be added/removed to + * this structure without holding write access to it. + * - There is one local spin_lock per sub_sequence, which can be seen + * as a sub-domain to the tipc_nametbl_lock domain. It is used only + * for translation operations, and is needed because a translation + * steps the root of the 'publication' linked list between each lookup. + * This is always used within the scope of a tipc_nametbl_lock(read). + * - A local spin_lock protecting the queue of subscriber events. +*/ + +DEFINE_RWLOCK(tipc_net_lock); + +static void net_route_named_msg(struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + u32 dnode; + u32 dport; + + if (!msg_named(msg)) { + kfree_skb(buf); + return; + } + + dnode = addr_domain(msg_lookup_scope(msg)); + dport = tipc_nametbl_translate(msg_nametype(msg), msg_nameinst(msg), &dnode); + if (dport) { + msg_set_destnode(msg, dnode); + msg_set_destport(msg, dport); + tipc_net_route_msg(buf); + return; + } + tipc_reject_msg(buf, TIPC_ERR_NO_NAME); +} + +void tipc_net_route_msg(struct sk_buff *buf) +{ + struct tipc_msg *msg; + u32 dnode; + + if (!buf) + return; + msg = buf_msg(buf); + + /* Handle message for this node */ + dnode = msg_short(msg) ? tipc_own_addr : msg_destnode(msg); + if (tipc_in_scope(dnode, tipc_own_addr)) { + if (msg_isdata(msg)) { + if (msg_mcast(msg)) + tipc_port_recv_mcast(buf, NULL); + else if (msg_destport(msg)) + tipc_port_recv_msg(buf); + else + net_route_named_msg(buf); + return; + } + switch (msg_user(msg)) { + case NAME_DISTRIBUTOR: + tipc_named_recv(buf); + break; + case CONN_MANAGER: + tipc_port_recv_proto_msg(buf); + break; + default: + kfree_skb(buf); + } + return; + } + + /* Handle message for another node */ + skb_trim(buf, msg_size(msg)); + tipc_link_send(buf, dnode, msg_link_selector(msg)); +} + +int tipc_net_start(u32 addr) +{ + char addr_string[16]; + + tipc_subscr_stop(); + tipc_cfg_stop(); + + tipc_own_addr = addr; + tipc_named_reinit(); + tipc_port_reinit(); + + tipc_bclink_init(); + + tipc_k_signal((Handler)tipc_subscr_start, 0); + tipc_k_signal((Handler)tipc_cfg_init, 0); + + info("Started in network mode\n"); + info("Own node address %s, network identity %u\n", + tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id); + return 0; +} + +void tipc_net_stop(void) +{ + struct tipc_node *node, *t_node; + + if (!tipc_own_addr) + return; + write_lock_bh(&tipc_net_lock); + tipc_bearer_stop(); + tipc_bclink_stop(); + list_for_each_entry_safe(node, t_node, &tipc_node_list, list) + tipc_node_delete(node); + write_unlock_bh(&tipc_net_lock); + info("Left network mode\n"); +} diff --git a/net/tipc/net.h b/net/tipc/net.h new file mode 100644 index 00000000..9eb4b9e2 --- /dev/null +++ b/net/tipc/net.h @@ -0,0 +1,47 @@ +/* + * net/tipc/net.h: Include file for TIPC network routing code + * + * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_NET_H +#define _TIPC_NET_H + +extern rwlock_t tipc_net_lock; + +void tipc_net_route_msg(struct sk_buff *buf); + +int tipc_net_start(u32 addr); +void tipc_net_stop(void); + +#endif diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c new file mode 100644 index 00000000..7bda8e3d --- /dev/null +++ b/net/tipc/netlink.c @@ -0,0 +1,108 @@ +/* + * net/tipc/netlink.c: TIPC configuration handling + * + * Copyright (c) 2005-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" +#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_SPACE(GENL_HDRLEN + TIPC_GENL_HDRLEN); + u16 cmd; + + if ((req_userhdr->cmd & 0xC000) && (!capable(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_PAYLOAD(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).pid); + } + + return 0; +} + +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, +}; + +static struct genl_ops tipc_genl_ops = { + .cmd = TIPC_GENL_CMD, + .doit = handle_cmd, +}; + +static int tipc_genl_family_registered; + +int tipc_netlink_start(void) +{ + int res; + + res = genl_register_family_with_ops(&tipc_genl_family, + &tipc_genl_ops, 1); + if (res) { + err("Failed to register netlink interface\n"); + return res; + } + + tipc_genl_family_registered = 1; + return 0; +} + +void tipc_netlink_stop(void) +{ + if (!tipc_genl_family_registered) + return; + + genl_unregister_family(&tipc_genl_family); + tipc_genl_family_registered = 0; +} diff --git a/net/tipc/node.c b/net/tipc/node.c new file mode 100644 index 00000000..a34cabc2 --- /dev/null +++ b/net/tipc/node.c @@ -0,0 +1,458 @@ +/* + * net/tipc/node.c: TIPC node management routines + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2005-2006, 2010-2011, 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" +#include "node.h" +#include "name_distr.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 DEFINE_SPINLOCK(node_create_lock); + +static struct hlist_head node_htable[NODE_HTABLE_SIZE]; +LIST_HEAD(tipc_node_list); +static u32 tipc_num_nodes; + +static atomic_t tipc_num_links = ATOMIC_INIT(0); + +/* + * A trivial power-of-two bitmask technique is used for speed, since this + * operation is done for every incoming TIPC packet. The number of hash table + * entries has been chosen so that no hash chain exceeds 8 nodes and will + * usually be much smaller (typically only a single node). + */ +static inline unsigned int tipc_hashfn(u32 addr) +{ + return addr & (NODE_HTABLE_SIZE - 1); +} + +/* + * tipc_node_find - locate specified node object, if it exists + */ + +struct tipc_node *tipc_node_find(u32 addr) +{ + struct tipc_node *node; + struct hlist_node *pos; + + if (unlikely(!in_own_cluster(addr))) + return NULL; + + hlist_for_each_entry(node, pos, &node_htable[tipc_hashfn(addr)], hash) { + if (node->addr == addr) + return node; + } + return NULL; +} + +/** + * tipc_node_create - create neighboring node + * + * Currently, this routine is called by neighbor discovery code, which holds + * net_lock for reading only. We must take node_create_lock to ensure a node + * isn't created twice if two different bearers discover the node at the same + * time. (It would be preferable to switch to holding net_lock in write mode, + * but this is a non-trivial change.) + */ + +struct tipc_node *tipc_node_create(u32 addr) +{ + struct tipc_node *n_ptr, *temp_node; + + spin_lock_bh(&node_create_lock); + + n_ptr = tipc_node_find(addr); + if (n_ptr) { + spin_unlock_bh(&node_create_lock); + return n_ptr; + } + + n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC); + if (!n_ptr) { + spin_unlock_bh(&node_create_lock); + warn("Node creation failed, no memory\n"); + return NULL; + } + + n_ptr->addr = addr; + spin_lock_init(&n_ptr->lock); + INIT_HLIST_NODE(&n_ptr->hash); + INIT_LIST_HEAD(&n_ptr->list); + INIT_LIST_HEAD(&n_ptr->nsub); + + hlist_add_head(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); + + list_for_each_entry(temp_node, &tipc_node_list, list) { + if (n_ptr->addr < temp_node->addr) + break; + } + list_add_tail(&n_ptr->list, &temp_node->list); + n_ptr->block_setup = WAIT_PEER_DOWN; + n_ptr->signature = INVALID_NODE_SIG; + + tipc_num_nodes++; + + spin_unlock_bh(&node_create_lock); + return n_ptr; +} + +void tipc_node_delete(struct tipc_node *n_ptr) +{ + list_del(&n_ptr->list); + hlist_del(&n_ptr->hash); + kfree(n_ptr); + + tipc_num_nodes--; +} + + +/** + * tipc_node_link_up - handle addition of link + * + * Link becomes active (alone or shared) or standby, depending on its priority. + */ + +void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr) +{ + struct tipc_link **active = &n_ptr->active_links[0]; + + n_ptr->working_links++; + + info("Established link <%s> on network plane %c\n", + l_ptr->name, l_ptr->b_ptr->net_plane); + + if (!active[0]) { + active[0] = active[1] = l_ptr; + node_established_contact(n_ptr); + return; + } + if (l_ptr->priority < active[0]->priority) { + info("New link <%s> becomes standby\n", l_ptr->name); + return; + } + tipc_link_send_duplicate(active[0], l_ptr); + if (l_ptr->priority == active[0]->priority) { + active[0] = l_ptr; + return; + } + info("Old link <%s> becomes standby\n", active[0]->name); + if (active[1] != active[0]) + info("Old link <%s> becomes standby\n", active[1]->name); + active[0] = active[1] = l_ptr; +} + +/** + * node_select_active_links - select active link + */ + +static void node_select_active_links(struct tipc_node *n_ptr) +{ + struct tipc_link **active = &n_ptr->active_links[0]; + u32 i; + u32 highest_prio = 0; + + active[0] = active[1] = NULL; + + for (i = 0; i < MAX_BEARERS; i++) { + struct tipc_link *l_ptr = n_ptr->links[i]; + + if (!l_ptr || !tipc_link_is_up(l_ptr) || + (l_ptr->priority < highest_prio)) + continue; + + if (l_ptr->priority > highest_prio) { + highest_prio = l_ptr->priority; + active[0] = active[1] = l_ptr; + } else { + active[1] = l_ptr; + } + } +} + +/** + * tipc_node_link_down - handle loss of link + */ + +void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) +{ + struct tipc_link **active; + + n_ptr->working_links--; + + if (!tipc_link_is_active(l_ptr)) { + info("Lost standby link <%s> on network plane %c\n", + l_ptr->name, l_ptr->b_ptr->net_plane); + return; + } + info("Lost link <%s> on network plane %c\n", + l_ptr->name, l_ptr->b_ptr->net_plane); + + active = &n_ptr->active_links[0]; + if (active[0] == l_ptr) + active[0] = active[1]; + if (active[1] == l_ptr) + active[1] = active[0]; + if (active[0] == l_ptr) + node_select_active_links(n_ptr); + if (tipc_node_is_up(n_ptr)) + tipc_link_changeover(l_ptr); + else + node_lost_contact(n_ptr); +} + +int tipc_node_active_links(struct tipc_node *n_ptr) +{ + return n_ptr->active_links[0] != NULL; +} + +int tipc_node_redundant_links(struct tipc_node *n_ptr) +{ + return n_ptr->working_links > 1; +} + +int tipc_node_is_up(struct tipc_node *n_ptr) +{ + return tipc_node_active_links(n_ptr); +} + +void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) +{ + n_ptr->links[l_ptr->b_ptr->identity] = l_ptr; + atomic_inc(&tipc_num_links); + n_ptr->link_cnt++; +} + +void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr) +{ + n_ptr->links[l_ptr->b_ptr->identity] = NULL; + atomic_dec(&tipc_num_links); + n_ptr->link_cnt--; +} + +static void node_established_contact(struct tipc_node *n_ptr) +{ + tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr); + + if (n_ptr->bclink.supportable) { + n_ptr->bclink.acked = tipc_bclink_get_last_sent(); + tipc_bclink_add_node(n_ptr->addr); + n_ptr->bclink.supported = 1; + } +} + +static void node_name_purge_complete(unsigned long node_addr) +{ + struct tipc_node *n_ptr; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(node_addr); + if (n_ptr) { + tipc_node_lock(n_ptr); + n_ptr->block_setup &= ~WAIT_NAMES_GONE; + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); +} + +static void node_lost_contact(struct tipc_node *n_ptr) +{ + char addr_string[16]; + u32 i; + + info("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.supported) { + while (n_ptr->bclink.deferred_head) { + struct sk_buff *buf = n_ptr->bclink.deferred_head; + n_ptr->bclink.deferred_head = buf->next; + kfree_skb(buf); + } + n_ptr->bclink.deferred_size = 0; + + if (n_ptr->bclink.defragm) { + kfree_skb(n_ptr->bclink.defragm); + n_ptr->bclink.defragm = NULL; + } + + tipc_bclink_remove_node(n_ptr->addr); + tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ); + + n_ptr->bclink.supported = 0; + } + + /* Abort link changeover */ + for (i = 0; i < MAX_BEARERS; i++) { + struct tipc_link *l_ptr = n_ptr->links[i]; + if (!l_ptr) + continue; + l_ptr->reset_checkpoint = l_ptr->next_in_no; + l_ptr->exp_msg_count = 0; + tipc_link_reset_fragments(l_ptr); + } + + /* Notify subscribers */ + tipc_nodesub_notify(n_ptr); + + /* Prevent re-contact with node until cleanup is done */ + + n_ptr->block_setup = WAIT_PEER_DOWN | WAIT_NAMES_GONE; + tipc_k_signal((Handler)node_name_purge_complete, n_ptr->addr); +} + +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)"); + + read_lock_bh(&tipc_net_lock); + if (!tipc_num_nodes) { + read_unlock_bh(&tipc_net_lock); + return tipc_cfg_reply_none(); + } + + /* For now, get space for all other nodes */ + + payload_size = TLV_SPACE(sizeof(node_info)) * tipc_num_nodes; + if (payload_size > 32768u) { + read_unlock_bh(&tipc_net_lock); + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (too many nodes)"); + } + buf = tipc_cfg_reply_alloc(payload_size); + if (!buf) { + read_unlock_bh(&tipc_net_lock); + return NULL; + } + + /* Add TLVs for all nodes in scope */ + + list_for_each_entry(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)); + } + + read_unlock_bh(&tipc_net_lock); + 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(); + + read_lock_bh(&tipc_net_lock); + + /* Get space for all unicast links + broadcast link */ + + payload_size = TLV_SPACE(sizeof(link_info)) * + (atomic_read(&tipc_num_links) + 1); + if (payload_size > 32768u) { + read_unlock_bh(&tipc_net_lock); + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (too many links)"); + } + buf = tipc_cfg_reply_alloc(payload_size); + if (!buf) { + read_unlock_bh(&tipc_net_lock); + return NULL; + } + + /* 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)); + + /* Add TLVs for any other links in scope */ + + list_for_each_entry(n_ptr, &tipc_node_list, list) { + u32 i; + + 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)); + } + tipc_node_unlock(n_ptr); + } + + read_unlock_bh(&tipc_net_lock); + return buf; +} diff --git a/net/tipc/node.h b/net/tipc/node.h new file mode 100644 index 00000000..72561c97 --- /dev/null +++ b/net/tipc/node.h @@ -0,0 +1,135 @@ +/* + * net/tipc/node.h: Include file for TIPC node management routines + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_NODE_H +#define _TIPC_NODE_H + +#include "node_subscr.h" +#include "addr.h" +#include "net.h" +#include "bearer.h" + +/* + * Out-of-range value for node signature + */ +#define INVALID_NODE_SIG 0x10000 + +/* Flags used to block (re)establishment of contact with a neighboring node */ + +#define WAIT_PEER_DOWN 0x0001 /* wait to see that peer's links are down */ +#define WAIT_NAMES_GONE 0x0002 /* wait for peer's publications to be purged */ +#define WAIT_NODE_DOWN 0x0004 /* wait until peer node is declared down */ + +/** + * struct tipc_node - TIPC node structure + * @addr: network address of node + * @lock: spinlock governing access to structure + * @hash: links to adjacent nodes in unsorted hash chain + * @list: links to adjacent nodes in sorted list of cluster's nodes + * @nsub: list of "node down" subscriptions monitoring node + * @active_links: pointers to active links to node + * @links: pointers to all links to node + * @working_links: number of working links to node (both active and standby) + * @block_setup: bit mask of conditions preventing link establishment to node + * @link_cnt: number of links to node + * @permit_changeover: non-zero if node has redundant links to this system + * @signature: node instance identifier + * @bclink: broadcast-related info + * @supportable: non-zero if node supports TIPC b'cast link capability + * @supported: non-zero if node supports TIPC b'cast capability + * @acked: sequence # of last outbound b'cast message acknowledged by node + * @last_in: sequence # of last in-sequence b'cast message received from node + * @last_sent: sequence # of last b'cast message sent by node + * @oos_state: state tracker for handling OOS b'cast messages + * @deferred_size: number of OOS b'cast messages in deferred queue + * @deferred_head: oldest OOS b'cast message received from node + * @deferred_tail: newest OOS b'cast message received from node + * @defragm: list of partially reassembled b'cast message fragments from node + */ + +struct tipc_node { + u32 addr; + spinlock_t lock; + struct hlist_node hash; + struct list_head list; + struct list_head nsub; + struct tipc_link *active_links[2]; + struct tipc_link *links[MAX_BEARERS]; + int link_cnt; + int working_links; + int block_setup; + int permit_changeover; + u32 signature; + struct { + u8 supportable; + u8 supported; + u32 acked; + u32 last_in; + u32 last_sent; + u32 oos_state; + u32 deferred_size; + struct sk_buff *deferred_head; + struct sk_buff *deferred_tail; + struct sk_buff *defragm; + } bclink; +}; + +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_delete(struct tipc_node *n_ptr); +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_redundant_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); + +static inline void tipc_node_lock(struct tipc_node *n_ptr) +{ + spin_lock_bh(&n_ptr->lock); +} + +static inline void tipc_node_unlock(struct tipc_node *n_ptr) +{ + spin_unlock_bh(&n_ptr->lock); +} + +#endif diff --git a/net/tipc/node_subscr.c b/net/tipc/node_subscr.c new file mode 100644 index 00000000..c3c2815a --- /dev/null +++ b/net/tipc/node_subscr.c @@ -0,0 +1,97 @@ +/* + * net/tipc/node_subscr.c: TIPC "node down" subscription handling + * + * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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 "node_subscr.h" +#include "node.h" + +/** + * tipc_nodesub_subscribe - create "node down" subscription for specified node + */ + +void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr, + void *usr_handle, net_ev_handler handle_down) +{ + if (addr == tipc_own_addr) { + node_sub->node = NULL; + return; + } + + node_sub->node = tipc_node_find(addr); + if (!node_sub->node) { + warn("Node subscription rejected, unknown node 0x%x\n", addr); + return; + } + node_sub->handle_node_down = handle_down; + node_sub->usr_handle = usr_handle; + + tipc_node_lock(node_sub->node); + list_add_tail(&node_sub->nodesub_list, &node_sub->node->nsub); + tipc_node_unlock(node_sub->node); +} + +/** + * tipc_nodesub_unsubscribe - cancel "node down" subscription (if any) + */ + +void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub) +{ + if (!node_sub->node) + return; + + tipc_node_lock(node_sub->node); + list_del_init(&node_sub->nodesub_list); + tipc_node_unlock(node_sub->node); +} + +/** + * tipc_nodesub_notify - notify subscribers that a node is unreachable + * + * Note: node is locked by caller + */ + +void tipc_nodesub_notify(struct tipc_node *node) +{ + struct tipc_node_subscr *ns; + + list_for_each_entry(ns, &node->nsub, nodesub_list) { + if (ns->handle_node_down) { + tipc_k_signal((Handler)ns->handle_node_down, + (unsigned long)ns->usr_handle); + ns->handle_node_down = NULL; + } + } +} diff --git a/net/tipc/node_subscr.h b/net/tipc/node_subscr.h new file mode 100644 index 00000000..4bc2ca08 --- /dev/null +++ b/net/tipc/node_subscr.h @@ -0,0 +1,64 @@ +/* + * net/tipc/node_subscr.h: Include file for TIPC "node down" subscription handling + * + * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 2005, 2010-2011, 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_NODE_SUBSCR_H +#define _TIPC_NODE_SUBSCR_H + +#include "addr.h" + +typedef void (*net_ev_handler) (void *usr_handle); + +/** + * struct tipc_node_subscr - "node down" subscription entry + * @node: ptr to node structure of interest (or NULL, if none) + * @handle_node_down: routine to invoke when node fails + * @usr_handle: argument to pass to routine when node fails + * @nodesub_list: adjacent entries in list of subscriptions for the node + */ + +struct tipc_node_subscr { + struct tipc_node *node; + net_ev_handler handle_node_down; + void *usr_handle; + struct list_head nodesub_list; +}; + +void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr, + void *usr_handle, net_ev_handler handle_down); +void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub); +void tipc_nodesub_notify(struct tipc_node *node); + +#endif diff --git a/net/tipc/port.c b/net/tipc/port.c new file mode 100644 index 00000000..94d2904c --- /dev/null +++ b/net/tipc/port.c @@ -0,0 +1,1365 @@ +/* + * net/tipc/port.c: TIPC port code + * + * Copyright (c) 1992-2007, Ericsson AB + * Copyright (c) 2004-2008, 2010-2011, 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" +#include "port.h" +#include "name_table.h" + +/* Connection management: */ +#define PROBING_INTERVAL 3600000 /* [ms] => 1 h */ +#define CONFIRMED 0 +#define PROBING 1 + +#define MAX_REJECT_SIZE 1024 + +static struct sk_buff *msg_queue_head; +static struct sk_buff *msg_queue_tail; + +DEFINE_SPINLOCK(tipc_port_list_lock); +static DEFINE_SPINLOCK(queue_lock); + +static LIST_HEAD(ports); +static void port_handle_node_down(unsigned long ref); +static struct sk_buff *port_build_self_abort_msg(struct tipc_port *, u32 err); +static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *, u32 err); +static void port_timeout(unsigned long ref); + + +static u32 port_peernode(struct tipc_port *p_ptr) +{ + return msg_destnode(&p_ptr->phdr); +} + +static u32 port_peerport(struct tipc_port *p_ptr) +{ + return msg_destport(&p_ptr->phdr); +} + +/** + * tipc_multicast - send a multicast message to local and remote destinations + */ + +int tipc_multicast(u32 ref, struct tipc_name_seq const *seq, + u32 num_sect, struct iovec const *msg_sect, + unsigned int total_len) +{ + struct tipc_msg *hdr; + struct sk_buff *buf; + struct sk_buff *ibuf = NULL; + struct tipc_port_list dports = {0, NULL, }; + struct tipc_port *oport = tipc_port_deref(ref); + int ext_targets; + int res; + + if (unlikely(!oport)) + return -EINVAL; + + /* Create multicast message */ + + hdr = &oport->phdr; + msg_set_type(hdr, TIPC_MCAST_MSG); + msg_set_lookup_scope(hdr, TIPC_CLUSTER_SCOPE); + msg_set_destport(hdr, 0); + msg_set_destnode(hdr, 0); + msg_set_nametype(hdr, seq->type); + msg_set_namelower(hdr, seq->lower); + msg_set_nameupper(hdr, seq->upper); + msg_set_hdr_sz(hdr, MCAST_H_SIZE); + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, + !oport->user_port, &buf); + if (unlikely(!buf)) + return res; + + /* Figure out where to send multicast message */ + + ext_targets = tipc_nametbl_mc_translate(seq->type, seq->lower, seq->upper, + TIPC_NODE_SCOPE, &dports); + + /* Send message to destinations (duplicate it only if necessary) */ + + if (ext_targets) { + if (dports.count != 0) { + ibuf = skb_copy(buf, GFP_ATOMIC); + if (ibuf == NULL) { + tipc_port_list_free(&dports); + kfree_skb(buf); + return -ENOMEM; + } + } + res = tipc_bclink_send_msg(buf); + if ((res < 0) && (dports.count != 0)) + kfree_skb(ibuf); + } else { + ibuf = buf; + } + + if (res >= 0) { + if (ibuf) + tipc_port_recv_mcast(ibuf, &dports); + } else { + tipc_port_list_free(&dports); + } + return res; +} + +/** + * tipc_port_recv_mcast - deliver multicast message to all destination ports + * + * If there is no port list, perform a lookup to create one + */ + +void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp) +{ + struct tipc_msg *msg; + struct tipc_port_list dports = {0, NULL, }; + struct tipc_port_list *item = dp; + int cnt = 0; + + msg = buf_msg(buf); + + /* Create destination port list, if one wasn't supplied */ + + if (dp == NULL) { + tipc_nametbl_mc_translate(msg_nametype(msg), + msg_namelower(msg), + msg_nameupper(msg), + TIPC_CLUSTER_SCOPE, + &dports); + item = dp = &dports; + } + + /* Deliver a copy of message to each destination port */ + + if (dp->count != 0) { + msg_set_destnode(msg, tipc_own_addr); + if (dp->count == 1) { + msg_set_destport(msg, dp->ports[0]); + tipc_port_recv_msg(buf); + tipc_port_list_free(dp); + return; + } + for (; cnt < dp->count; cnt++) { + int index = cnt % PLSIZE; + struct sk_buff *b = skb_clone(buf, GFP_ATOMIC); + + if (b == NULL) { + warn("Unable to deliver multicast message(s)\n"); + goto exit; + } + if ((index == 0) && (cnt != 0)) + item = item->next; + msg_set_destport(buf_msg(b), item->ports[index]); + tipc_port_recv_msg(b); + } + } +exit: + kfree_skb(buf); + tipc_port_list_free(dp); +} + +/** + * tipc_createport_raw - create a generic TIPC port + * + * Returns pointer to (locked) TIPC port, or NULL if unable to create it + */ + +struct tipc_port *tipc_createport_raw(void *usr_handle, + u32 (*dispatcher)(struct tipc_port *, struct sk_buff *), + void (*wakeup)(struct tipc_port *), + const u32 importance) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + u32 ref; + + p_ptr = kzalloc(sizeof(*p_ptr), GFP_ATOMIC); + if (!p_ptr) { + warn("Port creation failed, no memory\n"); + return NULL; + } + ref = tipc_ref_acquire(p_ptr, &p_ptr->lock); + if (!ref) { + warn("Port creation failed, reference table exhausted\n"); + kfree(p_ptr); + return NULL; + } + + p_ptr->usr_handle = usr_handle; + p_ptr->max_pkt = MAX_PKT_DEFAULT; + p_ptr->ref = ref; + msg = &p_ptr->phdr; + tipc_msg_init(msg, importance, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); + msg_set_origport(msg, ref); + INIT_LIST_HEAD(&p_ptr->wait_list); + INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); + p_ptr->dispatcher = dispatcher; + p_ptr->wakeup = wakeup; + p_ptr->user_port = NULL; + k_init_timer(&p_ptr->timer, (Handler)port_timeout, ref); + spin_lock_bh(&tipc_port_list_lock); + INIT_LIST_HEAD(&p_ptr->publications); + INIT_LIST_HEAD(&p_ptr->port_list); + list_add_tail(&p_ptr->port_list, &ports); + spin_unlock_bh(&tipc_port_list_lock); + return p_ptr; +} + +int tipc_deleteport(u32 ref) +{ + struct tipc_port *p_ptr; + struct sk_buff *buf = NULL; + + tipc_withdraw(ref, 0, NULL); + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + + tipc_ref_discard(ref); + tipc_port_unlock(p_ptr); + + k_cancel_timer(&p_ptr->timer); + if (p_ptr->connected) { + buf = port_build_peer_abort_msg(p_ptr, TIPC_ERR_NO_PORT); + tipc_nodesub_unsubscribe(&p_ptr->subscription); + } + kfree(p_ptr->user_port); + + spin_lock_bh(&tipc_port_list_lock); + list_del(&p_ptr->port_list); + list_del(&p_ptr->wait_list); + spin_unlock_bh(&tipc_port_list_lock); + k_term_timer(&p_ptr->timer); + kfree(p_ptr); + tipc_net_route_msg(buf); + return 0; +} + +static int port_unreliable(struct tipc_port *p_ptr) +{ + return msg_src_droppable(&p_ptr->phdr); +} + +int tipc_portunreliable(u32 ref, unsigned int *isunreliable) +{ + struct tipc_port *p_ptr; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + *isunreliable = port_unreliable(p_ptr); + tipc_port_unlock(p_ptr); + return 0; +} + +int tipc_set_portunreliable(u32 ref, unsigned int isunreliable) +{ + struct tipc_port *p_ptr; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + msg_set_src_droppable(&p_ptr->phdr, (isunreliable != 0)); + tipc_port_unlock(p_ptr); + return 0; +} + +static int port_unreturnable(struct tipc_port *p_ptr) +{ + return msg_dest_droppable(&p_ptr->phdr); +} + +int tipc_portunreturnable(u32 ref, unsigned int *isunrejectable) +{ + struct tipc_port *p_ptr; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + *isunrejectable = port_unreturnable(p_ptr); + tipc_port_unlock(p_ptr); + return 0; +} + +int tipc_set_portunreturnable(u32 ref, unsigned int isunrejectable) +{ + struct tipc_port *p_ptr; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + msg_set_dest_droppable(&p_ptr->phdr, (isunrejectable != 0)); + tipc_port_unlock(p_ptr); + return 0; +} + +/* + * port_build_proto_msg(): create connection protocol message for port + * + * On entry the port must be locked and connected. + */ +static struct sk_buff *port_build_proto_msg(struct tipc_port *p_ptr, + u32 type, u32 ack) +{ + struct sk_buff *buf; + struct tipc_msg *msg; + + buf = tipc_buf_acquire(INT_H_SIZE); + if (buf) { + msg = buf_msg(buf); + tipc_msg_init(msg, CONN_MANAGER, type, INT_H_SIZE, + port_peernode(p_ptr)); + msg_set_destport(msg, port_peerport(p_ptr)); + msg_set_origport(msg, p_ptr->ref); + msg_set_msgcnt(msg, ack); + } + return buf; +} + +int tipc_reject_msg(struct sk_buff *buf, u32 err) +{ + struct tipc_msg *msg = buf_msg(buf); + struct sk_buff *rbuf; + struct tipc_msg *rmsg; + int hdr_sz; + u32 imp; + u32 data_sz = msg_data_sz(msg); + u32 src_node; + u32 rmsg_sz; + + /* discard rejected message if it shouldn't be returned to sender */ + + if (WARN(!msg_isdata(msg), + "attempt to reject message with user=%u", msg_user(msg))) { + dump_stack(); + goto exit; + } + if (msg_errcode(msg) || msg_dest_droppable(msg)) + goto exit; + + /* + * construct returned message by copying rejected message header and + * data (or subset), then updating header fields that need adjusting + */ + + hdr_sz = msg_hdr_sz(msg); + rmsg_sz = hdr_sz + min_t(u32, data_sz, MAX_REJECT_SIZE); + + rbuf = tipc_buf_acquire(rmsg_sz); + if (rbuf == NULL) + goto exit; + + rmsg = buf_msg(rbuf); + skb_copy_to_linear_data(rbuf, msg, rmsg_sz); + + if (msg_connected(rmsg)) { + imp = msg_importance(rmsg); + if (imp < TIPC_CRITICAL_IMPORTANCE) + msg_set_importance(rmsg, ++imp); + } + msg_set_non_seq(rmsg, 0); + msg_set_size(rmsg, rmsg_sz); + msg_set_errcode(rmsg, err); + msg_set_prevnode(rmsg, tipc_own_addr); + msg_swap_words(rmsg, 4, 5); + if (!msg_short(rmsg)) + msg_swap_words(rmsg, 6, 7); + + /* send self-abort message when rejecting on a connected port */ + if (msg_connected(msg)) { + struct tipc_port *p_ptr = tipc_port_lock(msg_destport(msg)); + + if (p_ptr) { + struct sk_buff *abuf = NULL; + + if (p_ptr->connected) + abuf = port_build_self_abort_msg(p_ptr, err); + tipc_port_unlock(p_ptr); + tipc_net_route_msg(abuf); + } + } + + /* send returned message & dispose of rejected message */ + + src_node = msg_prevnode(msg); + if (src_node == tipc_own_addr) + tipc_port_recv_msg(rbuf); + else + tipc_link_send(rbuf, src_node, msg_link_selector(rmsg)); +exit: + kfree_skb(buf); + return data_sz; +} + +int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, + struct iovec const *msg_sect, u32 num_sect, + unsigned int total_len, int err) +{ + struct sk_buff *buf; + int res; + + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, + !p_ptr->user_port, &buf); + if (!buf) + return res; + + return tipc_reject_msg(buf, err); +} + +static void port_timeout(unsigned long ref) +{ + struct tipc_port *p_ptr = tipc_port_lock(ref); + struct sk_buff *buf = NULL; + + if (!p_ptr) + return; + + if (!p_ptr->connected) { + tipc_port_unlock(p_ptr); + return; + } + + /* Last probe answered ? */ + if (p_ptr->probing_state == PROBING) { + buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_PORT); + } else { + buf = port_build_proto_msg(p_ptr, CONN_PROBE, 0); + p_ptr->probing_state = PROBING; + k_start_timer(&p_ptr->timer, p_ptr->probing_interval); + } + tipc_port_unlock(p_ptr); + tipc_net_route_msg(buf); +} + + +static void port_handle_node_down(unsigned long ref) +{ + struct tipc_port *p_ptr = tipc_port_lock(ref); + struct sk_buff *buf = NULL; + + if (!p_ptr) + return; + buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_NODE); + tipc_port_unlock(p_ptr); + tipc_net_route_msg(buf); +} + + +static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 err) +{ + struct sk_buff *buf = port_build_peer_abort_msg(p_ptr, err); + + if (buf) { + struct tipc_msg *msg = buf_msg(buf); + msg_swap_words(msg, 4, 5); + msg_swap_words(msg, 6, 7); + } + return buf; +} + + +static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 err) +{ + struct sk_buff *buf; + struct tipc_msg *msg; + u32 imp; + + if (!p_ptr->connected) + return NULL; + + buf = tipc_buf_acquire(BASIC_H_SIZE); + if (buf) { + msg = buf_msg(buf); + memcpy(msg, &p_ptr->phdr, BASIC_H_SIZE); + msg_set_hdr_sz(msg, BASIC_H_SIZE); + msg_set_size(msg, BASIC_H_SIZE); + imp = msg_importance(msg); + if (imp < TIPC_CRITICAL_IMPORTANCE) + msg_set_importance(msg, ++imp); + msg_set_errcode(msg, err); + } + return buf; +} + +void tipc_port_recv_proto_msg(struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + struct tipc_port *p_ptr; + struct sk_buff *r_buf = NULL; + u32 orignode = msg_orignode(msg); + u32 origport = msg_origport(msg); + u32 destport = msg_destport(msg); + int wakeable; + + /* Validate connection */ + + p_ptr = tipc_port_lock(destport); + if (!p_ptr || !p_ptr->connected || + (port_peernode(p_ptr) != orignode) || + (port_peerport(p_ptr) != origport)) { + r_buf = tipc_buf_acquire(BASIC_H_SIZE); + if (r_buf) { + msg = buf_msg(r_buf); + tipc_msg_init(msg, TIPC_HIGH_IMPORTANCE, TIPC_CONN_MSG, + BASIC_H_SIZE, orignode); + msg_set_errcode(msg, TIPC_ERR_NO_PORT); + msg_set_origport(msg, destport); + msg_set_destport(msg, origport); + } + if (p_ptr) + tipc_port_unlock(p_ptr); + goto exit; + } + + /* Process protocol message sent by peer */ + + switch (msg_type(msg)) { + case CONN_ACK: + wakeable = tipc_port_congested(p_ptr) && p_ptr->congested && + p_ptr->wakeup; + p_ptr->acked += msg_msgcnt(msg); + if (!tipc_port_congested(p_ptr)) { + p_ptr->congested = 0; + if (wakeable) + p_ptr->wakeup(p_ptr); + } + break; + case CONN_PROBE: + r_buf = port_build_proto_msg(p_ptr, CONN_PROBE_REPLY, 0); + break; + default: + /* CONN_PROBE_REPLY or unrecognized - no action required */ + break; + } + p_ptr->probing_state = CONFIRMED; + tipc_port_unlock(p_ptr); +exit: + tipc_net_route_msg(r_buf); + kfree_skb(buf); +} + +static void port_print(struct tipc_port *p_ptr, struct print_buf *buf, int full_id) +{ + struct publication *publ; + + if (full_id) + tipc_printf(buf, "<%u.%u.%u:%u>:", + tipc_zone(tipc_own_addr), tipc_cluster(tipc_own_addr), + tipc_node(tipc_own_addr), p_ptr->ref); + else + tipc_printf(buf, "%-10u:", p_ptr->ref); + + if (p_ptr->connected) { + u32 dport = port_peerport(p_ptr); + u32 destnode = port_peernode(p_ptr); + + tipc_printf(buf, " connected to <%u.%u.%u:%u>", + tipc_zone(destnode), tipc_cluster(destnode), + tipc_node(destnode), dport); + if (p_ptr->conn_type != 0) + tipc_printf(buf, " via {%u,%u}", + p_ptr->conn_type, + p_ptr->conn_instance); + } else if (p_ptr->published) { + tipc_printf(buf, " bound to"); + list_for_each_entry(publ, &p_ptr->publications, pport_list) { + if (publ->lower == publ->upper) + tipc_printf(buf, " {%u,%u}", publ->type, + publ->lower); + else + tipc_printf(buf, " {%u,%u,%u}", publ->type, + publ->lower, publ->upper); + } + } + tipc_printf(buf, "\n"); +} + +#define MAX_PORT_QUERY 32768 + +struct sk_buff *tipc_port_get_ports(void) +{ + struct sk_buff *buf; + struct tlv_desc *rep_tlv; + struct print_buf pb; + struct tipc_port *p_ptr; + int str_len; + + buf = tipc_cfg_reply_alloc(TLV_SPACE(MAX_PORT_QUERY)); + if (!buf) + return NULL; + rep_tlv = (struct tlv_desc *)buf->data; + + tipc_printbuf_init(&pb, TLV_DATA(rep_tlv), MAX_PORT_QUERY); + spin_lock_bh(&tipc_port_list_lock); + list_for_each_entry(p_ptr, &ports, port_list) { + spin_lock_bh(p_ptr->lock); + port_print(p_ptr, &pb, 0); + spin_unlock_bh(p_ptr->lock); + } + spin_unlock_bh(&tipc_port_list_lock); + str_len = tipc_printbuf_validate(&pb); + + skb_put(buf, TLV_SPACE(str_len)); + TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); + + return buf; +} + +void tipc_port_reinit(void) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + + spin_lock_bh(&tipc_port_list_lock); + list_for_each_entry(p_ptr, &ports, port_list) { + msg = &p_ptr->phdr; + if (msg_orignode(msg) == tipc_own_addr) + break; + msg_set_prevnode(msg, tipc_own_addr); + msg_set_orignode(msg, tipc_own_addr); + } + spin_unlock_bh(&tipc_port_list_lock); +} + + +/* + * port_dispatcher_sigh(): Signal handler for messages destinated + * to the tipc_port interface. + */ + +static void port_dispatcher_sigh(void *dummy) +{ + struct sk_buff *buf; + + spin_lock_bh(&queue_lock); + buf = msg_queue_head; + msg_queue_head = NULL; + spin_unlock_bh(&queue_lock); + + while (buf) { + struct tipc_port *p_ptr; + struct user_port *up_ptr; + struct tipc_portid orig; + struct tipc_name_seq dseq; + void *usr_handle; + int connected; + int published; + u32 message_type; + + struct sk_buff *next = buf->next; + struct tipc_msg *msg = buf_msg(buf); + u32 dref = msg_destport(msg); + + message_type = msg_type(msg); + if (message_type > TIPC_DIRECT_MSG) + goto reject; /* Unsupported message type */ + + p_ptr = tipc_port_lock(dref); + if (!p_ptr) + goto reject; /* Port deleted while msg in queue */ + + orig.ref = msg_origport(msg); + orig.node = msg_orignode(msg); + up_ptr = p_ptr->user_port; + usr_handle = up_ptr->usr_handle; + connected = p_ptr->connected; + published = p_ptr->published; + + if (unlikely(msg_errcode(msg))) + goto err; + + switch (message_type) { + + case TIPC_CONN_MSG:{ + tipc_conn_msg_event cb = up_ptr->conn_msg_cb; + u32 peer_port = port_peerport(p_ptr); + u32 peer_node = port_peernode(p_ptr); + u32 dsz; + + tipc_port_unlock(p_ptr); + if (unlikely(!cb)) + goto reject; + if (unlikely(!connected)) { + if (tipc_connect2port(dref, &orig)) + goto reject; + } else if ((msg_origport(msg) != peer_port) || + (msg_orignode(msg) != peer_node)) + goto reject; + dsz = msg_data_sz(msg); + if (unlikely(dsz && + (++p_ptr->conn_unacked >= + TIPC_FLOW_CONTROL_WIN))) + tipc_acknowledge(dref, + p_ptr->conn_unacked); + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), dsz); + break; + } + case TIPC_DIRECT_MSG:{ + tipc_msg_event cb = up_ptr->msg_cb; + + tipc_port_unlock(p_ptr); + if (unlikely(!cb || connected)) + goto reject; + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), + msg_data_sz(msg), msg_importance(msg), + &orig); + break; + } + case TIPC_MCAST_MSG: + case TIPC_NAMED_MSG:{ + tipc_named_msg_event cb = up_ptr->named_msg_cb; + + tipc_port_unlock(p_ptr); + if (unlikely(!cb || connected || !published)) + goto reject; + dseq.type = msg_nametype(msg); + dseq.lower = msg_nameinst(msg); + dseq.upper = (message_type == TIPC_NAMED_MSG) + ? dseq.lower : msg_nameupper(msg); + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), + msg_data_sz(msg), msg_importance(msg), + &orig, &dseq); + break; + } + } + if (buf) + kfree_skb(buf); + buf = next; + continue; +err: + switch (message_type) { + + case TIPC_CONN_MSG:{ + tipc_conn_shutdown_event cb = + up_ptr->conn_err_cb; + u32 peer_port = port_peerport(p_ptr); + u32 peer_node = port_peernode(p_ptr); + + tipc_port_unlock(p_ptr); + if (!cb || !connected) + break; + if ((msg_origport(msg) != peer_port) || + (msg_orignode(msg) != peer_node)) + break; + tipc_disconnect(dref); + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), + msg_data_sz(msg), msg_errcode(msg)); + break; + } + case TIPC_DIRECT_MSG:{ + tipc_msg_err_event cb = up_ptr->err_cb; + + tipc_port_unlock(p_ptr); + if (!cb || connected) + break; + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), + msg_data_sz(msg), msg_errcode(msg), &orig); + break; + } + case TIPC_MCAST_MSG: + case TIPC_NAMED_MSG:{ + tipc_named_msg_err_event cb = + up_ptr->named_err_cb; + + tipc_port_unlock(p_ptr); + if (!cb || connected) + break; + dseq.type = msg_nametype(msg); + dseq.lower = msg_nameinst(msg); + dseq.upper = (message_type == TIPC_NAMED_MSG) + ? dseq.lower : msg_nameupper(msg); + skb_pull(buf, msg_hdr_sz(msg)); + cb(usr_handle, dref, &buf, msg_data(msg), + msg_data_sz(msg), msg_errcode(msg), &dseq); + break; + } + } + if (buf) + kfree_skb(buf); + buf = next; + continue; +reject: + tipc_reject_msg(buf, TIPC_ERR_NO_PORT); + buf = next; + } +} + +/* + * port_dispatcher(): Dispatcher for messages destinated + * to the tipc_port interface. Called with port locked. + */ + +static u32 port_dispatcher(struct tipc_port *dummy, struct sk_buff *buf) +{ + buf->next = NULL; + spin_lock_bh(&queue_lock); + if (msg_queue_head) { + msg_queue_tail->next = buf; + msg_queue_tail = buf; + } else { + msg_queue_tail = msg_queue_head = buf; + tipc_k_signal((Handler)port_dispatcher_sigh, 0); + } + spin_unlock_bh(&queue_lock); + return 0; +} + +/* + * Wake up port after congestion: Called with port locked, + * + */ + +static void port_wakeup_sh(unsigned long ref) +{ + struct tipc_port *p_ptr; + struct user_port *up_ptr; + tipc_continue_event cb = NULL; + void *uh = NULL; + + p_ptr = tipc_port_lock(ref); + if (p_ptr) { + up_ptr = p_ptr->user_port; + if (up_ptr) { + cb = up_ptr->continue_event_cb; + uh = up_ptr->usr_handle; + } + tipc_port_unlock(p_ptr); + } + if (cb) + cb(uh, ref); +} + + +static void port_wakeup(struct tipc_port *p_ptr) +{ + tipc_k_signal((Handler)port_wakeup_sh, p_ptr->ref); +} + +void tipc_acknowledge(u32 ref, u32 ack) +{ + struct tipc_port *p_ptr; + struct sk_buff *buf = NULL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return; + if (p_ptr->connected) { + p_ptr->conn_unacked -= ack; + buf = port_build_proto_msg(p_ptr, CONN_ACK, ack); + } + tipc_port_unlock(p_ptr); + tipc_net_route_msg(buf); +} + +/* + * tipc_createport(): user level call. + */ + +int tipc_createport(void *usr_handle, + unsigned int importance, + tipc_msg_err_event error_cb, + tipc_named_msg_err_event named_error_cb, + tipc_conn_shutdown_event conn_error_cb, + tipc_msg_event msg_cb, + tipc_named_msg_event named_msg_cb, + tipc_conn_msg_event conn_msg_cb, + tipc_continue_event continue_event_cb,/* May be zero */ + u32 *portref) +{ + struct user_port *up_ptr; + struct tipc_port *p_ptr; + + up_ptr = kmalloc(sizeof(*up_ptr), GFP_ATOMIC); + if (!up_ptr) { + warn("Port creation failed, no memory\n"); + return -ENOMEM; + } + p_ptr = (struct tipc_port *)tipc_createport_raw(NULL, port_dispatcher, + port_wakeup, importance); + if (!p_ptr) { + kfree(up_ptr); + return -ENOMEM; + } + + p_ptr->user_port = up_ptr; + up_ptr->usr_handle = usr_handle; + up_ptr->ref = p_ptr->ref; + up_ptr->err_cb = error_cb; + up_ptr->named_err_cb = named_error_cb; + up_ptr->conn_err_cb = conn_error_cb; + up_ptr->msg_cb = msg_cb; + up_ptr->named_msg_cb = named_msg_cb; + up_ptr->conn_msg_cb = conn_msg_cb; + up_ptr->continue_event_cb = continue_event_cb; + *portref = p_ptr->ref; + tipc_port_unlock(p_ptr); + return 0; +} + +int tipc_portimportance(u32 ref, unsigned int *importance) +{ + struct tipc_port *p_ptr; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + *importance = (unsigned int)msg_importance(&p_ptr->phdr); + tipc_port_unlock(p_ptr); + return 0; +} + +int tipc_set_portimportance(u32 ref, unsigned int imp) +{ + struct tipc_port *p_ptr; + + if (imp > TIPC_CRITICAL_IMPORTANCE) + return -EINVAL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + msg_set_importance(&p_ptr->phdr, (u32)imp); + tipc_port_unlock(p_ptr); + return 0; +} + + +int tipc_publish(u32 ref, unsigned int scope, struct tipc_name_seq const *seq) +{ + struct tipc_port *p_ptr; + struct publication *publ; + u32 key; + int res = -EINVAL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + + if (p_ptr->connected) + goto exit; + if (seq->lower > seq->upper) + goto exit; + if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE)) + goto exit; + key = ref + p_ptr->pub_count + 1; + if (key == ref) { + res = -EADDRINUSE; + goto exit; + } + publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, + scope, p_ptr->ref, key); + if (publ) { + list_add(&publ->pport_list, &p_ptr->publications); + p_ptr->pub_count++; + p_ptr->published = 1; + res = 0; + } +exit: + tipc_port_unlock(p_ptr); + return res; +} + +int tipc_withdraw(u32 ref, unsigned int scope, struct tipc_name_seq const *seq) +{ + struct tipc_port *p_ptr; + struct publication *publ; + struct publication *tpubl; + int res = -EINVAL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + if (!seq) { + list_for_each_entry_safe(publ, tpubl, + &p_ptr->publications, pport_list) { + tipc_nametbl_withdraw(publ->type, publ->lower, + publ->ref, publ->key); + } + res = 0; + } else { + list_for_each_entry_safe(publ, tpubl, + &p_ptr->publications, pport_list) { + if (publ->scope != scope) + continue; + if (publ->type != seq->type) + continue; + if (publ->lower != seq->lower) + continue; + if (publ->upper != seq->upper) + break; + tipc_nametbl_withdraw(publ->type, publ->lower, + publ->ref, publ->key); + res = 0; + break; + } + } + if (list_empty(&p_ptr->publications)) + p_ptr->published = 0; + tipc_port_unlock(p_ptr); + return res; +} + +int tipc_connect2port(u32 ref, struct tipc_portid const *peer) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + int res = -EINVAL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + if (p_ptr->published || p_ptr->connected) + goto exit; + if (!peer->ref) + goto exit; + + msg = &p_ptr->phdr; + msg_set_destnode(msg, peer->node); + msg_set_destport(msg, peer->ref); + msg_set_type(msg, TIPC_CONN_MSG); + msg_set_lookup_scope(msg, 0); + msg_set_hdr_sz(msg, SHORT_H_SIZE); + + p_ptr->probing_interval = PROBING_INTERVAL; + p_ptr->probing_state = CONFIRMED; + p_ptr->connected = 1; + k_start_timer(&p_ptr->timer, p_ptr->probing_interval); + + tipc_nodesub_subscribe(&p_ptr->subscription, peer->node, + (void *)(unsigned long)ref, + (net_ev_handler)port_handle_node_down); + res = 0; +exit: + tipc_port_unlock(p_ptr); + p_ptr->max_pkt = tipc_link_get_max_pkt(peer->node, ref); + return res; +} + +/** + * tipc_disconnect_port - disconnect port from peer + * + * Port must be locked. + */ + +int tipc_disconnect_port(struct tipc_port *tp_ptr) +{ + int res; + + if (tp_ptr->connected) { + tp_ptr->connected = 0; + /* let timer expire on it's own to avoid deadlock! */ + tipc_nodesub_unsubscribe( + &((struct tipc_port *)tp_ptr)->subscription); + res = 0; + } else { + res = -ENOTCONN; + } + return res; +} + +/* + * tipc_disconnect(): Disconnect port form peer. + * This is a node local operation. + */ + +int tipc_disconnect(u32 ref) +{ + struct tipc_port *p_ptr; + int res; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + res = tipc_disconnect_port((struct tipc_port *)p_ptr); + tipc_port_unlock(p_ptr); + return res; +} + +/* + * tipc_shutdown(): Send a SHUTDOWN msg to peer and disconnect + */ +int tipc_shutdown(u32 ref) +{ + struct tipc_port *p_ptr; + struct sk_buff *buf = NULL; + + p_ptr = tipc_port_lock(ref); + if (!p_ptr) + return -EINVAL; + + buf = port_build_peer_abort_msg(p_ptr, TIPC_CONN_SHUTDOWN); + tipc_port_unlock(p_ptr); + tipc_net_route_msg(buf); + return tipc_disconnect(ref); +} + +/** + * tipc_port_recv_msg - receive message from lower layer and deliver to port user + */ + +int tipc_port_recv_msg(struct sk_buff *buf) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg = buf_msg(buf); + u32 destport = msg_destport(msg); + u32 dsz = msg_data_sz(msg); + u32 err; + + /* forward unresolved named message */ + if (unlikely(!destport)) { + tipc_net_route_msg(buf); + return dsz; + } + + /* validate destination & pass to port, otherwise reject message */ + p_ptr = tipc_port_lock(destport); + if (likely(p_ptr)) { + if (likely(p_ptr->connected)) { + if ((unlikely(msg_origport(msg) != + tipc_peer_port(p_ptr))) || + (unlikely(msg_orignode(msg) != + tipc_peer_node(p_ptr))) || + (unlikely(!msg_connected(msg)))) { + err = TIPC_ERR_NO_PORT; + tipc_port_unlock(p_ptr); + goto reject; + } + } + err = p_ptr->dispatcher(p_ptr, buf); + tipc_port_unlock(p_ptr); + if (likely(!err)) + return dsz; + } else { + err = TIPC_ERR_NO_PORT; + } +reject: + return tipc_reject_msg(buf, err); +} + +/* + * tipc_port_recv_sections(): Concatenate and deliver sectioned + * message for this node. + */ + +static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_sect, + struct iovec const *msg_sect, + unsigned int total_len) +{ + struct sk_buff *buf; + int res; + + res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, total_len, + MAX_MSG_SIZE, !sender->user_port, &buf); + if (likely(buf)) + tipc_port_recv_msg(buf); + return res; +} + +/** + * tipc_send - send message sections on connection + */ + +int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) +{ + struct tipc_port *p_ptr; + u32 destnode; + int res; + + p_ptr = tipc_port_deref(ref); + if (!p_ptr || !p_ptr->connected) + return -EINVAL; + + p_ptr->congested = 1; + if (!tipc_port_congested(p_ptr)) { + destnode = port_peernode(p_ptr); + if (likely(destnode != tipc_own_addr)) + res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, + total_len, destnode); + else + res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, + total_len); + + if (likely(res != -ELINKCONG)) { + p_ptr->congested = 0; + if (res > 0) + p_ptr->sent++; + return res; + } + } + if (port_unreliable(p_ptr)) { + p_ptr->congested = 0; + return total_len; + } + return -ELINKCONG; +} + +/** + * tipc_send2name - send message sections to port name + */ + +int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + u32 destnode = domain; + u32 destport; + int res; + + p_ptr = tipc_port_deref(ref); + if (!p_ptr || p_ptr->connected) + return -EINVAL; + + msg = &p_ptr->phdr; + msg_set_type(msg, TIPC_NAMED_MSG); + msg_set_hdr_sz(msg, NAMED_H_SIZE); + msg_set_nametype(msg, name->type); + msg_set_nameinst(msg, name->instance); + msg_set_lookup_scope(msg, tipc_addr_scope(domain)); + destport = tipc_nametbl_translate(name->type, name->instance, &destnode); + msg_set_destnode(msg, destnode); + msg_set_destport(msg, destport); + + if (likely(destport || destnode)) { + if (likely(destnode == tipc_own_addr)) + res = tipc_port_recv_sections(p_ptr, num_sect, + msg_sect, total_len); + else + res = tipc_link_send_sections_fast(p_ptr, msg_sect, + num_sect, total_len, + destnode); + if (likely(res != -ELINKCONG)) { + if (res > 0) + p_ptr->sent++; + return res; + } + if (port_unreliable(p_ptr)) { + return total_len; + } + return -ELINKCONG; + } + return tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect, + total_len, TIPC_ERR_NO_NAME); +} + +/** + * tipc_send2port - send message sections to port identity + */ + +int tipc_send2port(u32 ref, struct tipc_portid const *dest, + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + int res; + + p_ptr = tipc_port_deref(ref); + if (!p_ptr || p_ptr->connected) + return -EINVAL; + + msg = &p_ptr->phdr; + msg_set_type(msg, TIPC_DIRECT_MSG); + msg_set_lookup_scope(msg, 0); + msg_set_destnode(msg, dest->node); + msg_set_destport(msg, dest->ref); + msg_set_hdr_sz(msg, BASIC_H_SIZE); + + if (dest->node == tipc_own_addr) + res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, + total_len); + else + res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, + total_len, dest->node); + if (likely(res != -ELINKCONG)) { + if (res > 0) + p_ptr->sent++; + return res; + } + if (port_unreliable(p_ptr)) { + return total_len; + } + return -ELINKCONG; +} + +/** + * tipc_send_buf2port - send message buffer to port identity + */ + +int tipc_send_buf2port(u32 ref, struct tipc_portid const *dest, + struct sk_buff *buf, unsigned int dsz) +{ + struct tipc_port *p_ptr; + struct tipc_msg *msg; + int res; + + p_ptr = (struct tipc_port *)tipc_ref_deref(ref); + if (!p_ptr || p_ptr->connected) + return -EINVAL; + + msg = &p_ptr->phdr; + msg_set_type(msg, TIPC_DIRECT_MSG); + msg_set_destnode(msg, dest->node); + msg_set_destport(msg, dest->ref); + msg_set_hdr_sz(msg, BASIC_H_SIZE); + msg_set_size(msg, BASIC_H_SIZE + dsz); + if (skb_cow(buf, BASIC_H_SIZE)) + return -ENOMEM; + + skb_push(buf, BASIC_H_SIZE); + skb_copy_to_linear_data(buf, msg, BASIC_H_SIZE); + + if (dest->node == tipc_own_addr) + res = tipc_port_recv_msg(buf); + else + res = tipc_send_buf_fast(buf, dest->node); + if (likely(res != -ELINKCONG)) { + if (res > 0) + p_ptr->sent++; + return res; + } + if (port_unreliable(p_ptr)) + return dsz; + return -ELINKCONG; +} + diff --git a/net/tipc/port.h b/net/tipc/port.h new file mode 100644 index 00000000..9b88531e --- /dev/null +++ b/net/tipc/port.h @@ -0,0 +1,275 @@ +/* + * net/tipc/port.h: Include file for TIPC port code + * + * Copyright (c) 1994-2007, Ericsson AB + * Copyright (c) 2004-2007, 2010-2011, 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_PORT_H +#define _TIPC_PORT_H + +#include "ref.h" +#include "net.h" +#include "msg.h" +#include "node_subscr.h" + +#define TIPC_FLOW_CONTROL_WIN 512 + +typedef void (*tipc_msg_err_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size, int reason, + struct tipc_portid const *attmpt_destid); + +typedef void (*tipc_named_msg_err_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size, int reason, + struct tipc_name_seq const *attmpt_dest); + +typedef void (*tipc_conn_shutdown_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size, int reason); + +typedef void (*tipc_msg_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size, unsigned int importance, + struct tipc_portid const *origin); + +typedef void (*tipc_named_msg_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size, unsigned int importance, + struct tipc_portid const *orig, + struct tipc_name_seq const *dest); + +typedef void (*tipc_conn_msg_event) (void *usr_handle, u32 portref, + struct sk_buff **buf, unsigned char const *data, + unsigned int size); + +typedef void (*tipc_continue_event) (void *usr_handle, u32 portref); + +/** + * struct user_port - TIPC user port (used with native API) + * @usr_handle: user-specified field + * @ref: object reference to associated TIPC port + * <various callback routines> + */ + +struct user_port { + void *usr_handle; + u32 ref; + tipc_msg_err_event err_cb; + tipc_named_msg_err_event named_err_cb; + tipc_conn_shutdown_event conn_err_cb; + tipc_msg_event msg_cb; + tipc_named_msg_event named_msg_cb; + tipc_conn_msg_event conn_msg_cb; + tipc_continue_event continue_event_cb; +}; + +/** + * struct tipc_port - TIPC port structure + * @usr_handle: pointer to additional user-defined information about port + * @lock: pointer to spinlock for controlling access to port + * @connected: non-zero if port is currently connected to a peer port + * @conn_type: TIPC type used when connection was established + * @conn_instance: TIPC instance used when connection was established + * @conn_unacked: number of unacknowledged messages received from peer port + * @published: non-zero if port has one or more associated names + * @congested: non-zero if cannot send because of link or port congestion + * @max_pkt: maximum packet size "hint" used when building messages sent by port + * @ref: unique reference to port in TIPC object registry + * @phdr: preformatted message header used when sending messages + * @port_list: adjacent ports in TIPC's global list of ports + * @dispatcher: ptr to routine which handles received messages + * @wakeup: ptr to routine to call when port is no longer congested + * @user_port: ptr to user port associated with port (if any) + * @wait_list: adjacent ports in list of ports waiting on link congestion + * @waiting_pkts: + * @sent: # of non-empty messages sent by port + * @acked: # of non-empty message acknowledgements from connected port's peer + * @publications: list of publications for port + * @pub_count: total # of publications port has made during its lifetime + * @probing_state: + * @probing_interval: + * @timer_ref: + * @subscription: "node down" subscription used to terminate failed connections + */ +struct tipc_port { + void *usr_handle; + spinlock_t *lock; + int connected; + u32 conn_type; + u32 conn_instance; + u32 conn_unacked; + int published; + u32 congested; + u32 max_pkt; + u32 ref; + struct tipc_msg phdr; + struct list_head port_list; + u32 (*dispatcher)(struct tipc_port *, struct sk_buff *); + void (*wakeup)(struct tipc_port *); + struct user_port *user_port; + struct list_head wait_list; + u32 waiting_pkts; + u32 sent; + u32 acked; + struct list_head publications; + u32 pub_count; + u32 probing_state; + u32 probing_interval; + struct timer_list timer; + struct tipc_node_subscr subscription; +}; + +extern spinlock_t tipc_port_list_lock; +struct tipc_port_list; + +/* + * TIPC port manipulation routines + */ +struct tipc_port *tipc_createport_raw(void *usr_handle, + u32 (*dispatcher)(struct tipc_port *, struct sk_buff *), + void (*wakeup)(struct tipc_port *), const u32 importance); + +int tipc_reject_msg(struct sk_buff *buf, u32 err); + +int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode); + +void tipc_acknowledge(u32 port_ref, u32 ack); + +int tipc_createport(void *usr_handle, + unsigned int importance, tipc_msg_err_event error_cb, + tipc_named_msg_err_event named_error_cb, + tipc_conn_shutdown_event conn_error_cb, tipc_msg_event msg_cb, + tipc_named_msg_event named_msg_cb, + tipc_conn_msg_event conn_msg_cb, + tipc_continue_event continue_event_cb, u32 *portref); + +int tipc_deleteport(u32 portref); + +int tipc_portimportance(u32 portref, unsigned int *importance); +int tipc_set_portimportance(u32 portref, unsigned int importance); + +int tipc_portunreliable(u32 portref, unsigned int *isunreliable); +int tipc_set_portunreliable(u32 portref, unsigned int isunreliable); + +int tipc_portunreturnable(u32 portref, unsigned int *isunreturnable); +int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable); + +int tipc_publish(u32 portref, unsigned int scope, + struct tipc_name_seq const *name_seq); +int tipc_withdraw(u32 portref, unsigned int scope, + struct tipc_name_seq const *name_seq); + +int tipc_connect2port(u32 portref, struct tipc_portid const *port); + +int tipc_disconnect(u32 portref); + +int tipc_shutdown(u32 ref); + + +/* + * The following routines require that the port be locked on entry + */ +int tipc_disconnect_port(struct tipc_port *tp_ptr); + +/* + * TIPC messaging routines + */ +int tipc_port_recv_msg(struct sk_buff *buf); +int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); + +int tipc_send2name(u32 portref, struct tipc_name const *name, u32 domain, + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); + +int tipc_send2port(u32 portref, struct tipc_portid const *dest, + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); + +int tipc_send_buf2port(u32 portref, struct tipc_portid const *dest, + struct sk_buff *buf, unsigned int dsz); + +int tipc_multicast(u32 portref, struct tipc_name_seq const *seq, + unsigned int section_count, struct iovec const *msg, + unsigned int total_len); + +int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, + struct iovec const *msg_sect, u32 num_sect, + unsigned int total_len, int err); +struct sk_buff *tipc_port_get_ports(void); +void tipc_port_recv_proto_msg(struct sk_buff *buf); +void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp); +void tipc_port_reinit(void); + +/** + * tipc_port_lock - lock port instance referred to and return its pointer + */ + +static inline struct tipc_port *tipc_port_lock(u32 ref) +{ + return (struct tipc_port *)tipc_ref_lock(ref); +} + +/** + * tipc_port_unlock - unlock a port instance + * + * Can use pointer instead of tipc_ref_unlock() since port is already locked. + */ + +static inline void tipc_port_unlock(struct tipc_port *p_ptr) +{ + spin_unlock_bh(p_ptr->lock); +} + +static inline struct tipc_port *tipc_port_deref(u32 ref) +{ + return (struct tipc_port *)tipc_ref_deref(ref); +} + +static inline u32 tipc_peer_port(struct tipc_port *p_ptr) +{ + return msg_destport(&p_ptr->phdr); +} + +static inline u32 tipc_peer_node(struct tipc_port *p_ptr) +{ + return msg_destnode(&p_ptr->phdr); +} + +static inline int tipc_port_congested(struct tipc_port *p_ptr) +{ + return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2); +} + +#endif diff --git a/net/tipc/ref.c b/net/tipc/ref.c new file mode 100644 index 00000000..9e37b781 --- /dev/null +++ b/net/tipc/ref.c @@ -0,0 +1,299 @@ +/* + * net/tipc/ref.c: TIPC object registry code + * + * Copyright (c) 1991-2006, Ericsson AB + * Copyright (c) 2004-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 "ref.h" + +/** + * struct reference - TIPC object reference entry + * @object: pointer to object associated with reference entry + * @lock: spinlock controlling access to object + * @ref: reference value for object (combines instance & array index info) + */ + +struct reference { + void *object; + spinlock_t lock; + u32 ref; +}; + +/** + * struct tipc_ref_table - table of TIPC object 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 object reference entry + * @last_free: array index of last unused object 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; +}; + +/* + * Object reference table consists of 2**N entries. + * + * State Object 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 objects + */ + +int tipc_ref_table_init(u32 requested_size, u32 start) +{ + struct reference *table; + u32 actual_size; + + /* account for unused entry, then round up size to a power of 2 */ + + requested_size++; + for (actual_size = 16; actual_size < requested_size; actual_size <<= 1) + /* do nothing */ ; + + /* allocate table & mark all entries as uninitialized */ + + table = vzalloc(actual_size * sizeof(struct reference)); + if (table == NULL) + return -ENOMEM; + + tipc_ref_table.entries = table; + tipc_ref_table.capacity = requested_size; + tipc_ref_table.init_point = 1; + tipc_ref_table.first_free = 0; + tipc_ref_table.last_free = 0; + tipc_ref_table.index_mask = actual_size - 1; + tipc_ref_table.start_mask = start & ~tipc_ref_table.index_mask; + + return 0; +} + +/** + * tipc_ref_table_stop - destroy reference table for objects + */ + +void tipc_ref_table_stop(void) +{ + if (!tipc_ref_table.entries) + return; + + vfree(tipc_ref_table.entries); + tipc_ref_table.entries = NULL; +} + +/** + * tipc_ref_acquire - create reference to an object + * + * Register an object pointer in reference table and lock the object. + * Returns a unique reference value that is used from then on to retrieve the + * object pointer, or to determine that the object has been deregistered. + * + * Note: The object is returned in the locked state so that the caller can + * register a partially initialized object, without running the risk that + * the object will be accessed before initialization is complete. + */ + +u32 tipc_ref_acquire(void *object, spinlock_t **lock) +{ + u32 index; + u32 index_mask; + u32 next_plus_upper; + u32 ref; + struct reference *entry = NULL; + + if (!object) { + err("Attempt to acquire reference to non-existent object\n"); + return 0; + } + if (!tipc_ref_table.entries) { + err("Reference table not found during acquisition attempt\n"); + return 0; + } + + /* take a free entry, if available; otherwise initialize a new entry */ + + write_lock_bh(&ref_table_lock); + if (tipc_ref_table.first_free) { + 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; + } else if (tipc_ref_table.init_point < tipc_ref_table.capacity) { + index = tipc_ref_table.init_point++; + entry = &(tipc_ref_table.entries[index]); + spin_lock_init(&entry->lock); + ref = tipc_ref_table.start_mask + index; + } else { + ref = 0; + } + write_unlock_bh(&ref_table_lock); + + /* + * Grab the lock so no one else can modify this entry + * While we assign its ref value & object pointer + */ + if (entry) { + spin_lock_bh(&entry->lock); + entry->ref = ref; + entry->object = object; + *lock = &entry->lock; + /* + * keep it locked, the caller is responsible + * for unlocking this when they're done with it + */ + } + + return ref; +} + +/** + * tipc_ref_discard - invalidate references to an object + * + * Disallow future references to an object and free up the entry for re-use. + * Note: The entry's spin_lock may still be busy after discard + */ + +void tipc_ref_discard(u32 ref) +{ + struct reference *entry; + u32 index; + u32 index_mask; + + if (!tipc_ref_table.entries) { + err("Reference 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); + + if (!entry->object) { + err("Attempt to discard reference to non-existent object\n"); + goto exit; + } + if (entry->ref != ref) { + 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->object = NULL; + entry->ref = (ref & ~index_mask) + (index_mask + 1); + + /* append entry to free entry list */ + + if (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_ref_lock - lock referenced object and return pointer to it + */ + +void *tipc_ref_lock(u32 ref) +{ + if (likely(tipc_ref_table.entries)) { + struct reference *entry; + + entry = &tipc_ref_table.entries[ref & + tipc_ref_table.index_mask]; + if (likely(entry->ref != 0)) { + spin_lock_bh(&entry->lock); + if (likely((entry->ref == ref) && (entry->object))) + return entry->object; + spin_unlock_bh(&entry->lock); + } + } + return NULL; +} + + +/** + * tipc_ref_deref - return pointer referenced object (without locking it) + */ + +void *tipc_ref_deref(u32 ref) +{ + if (likely(tipc_ref_table.entries)) { + struct reference *entry; + + entry = &tipc_ref_table.entries[ref & + tipc_ref_table.index_mask]; + if (likely(entry->ref == ref)) + return entry->object; + } + return NULL; +} + diff --git a/net/tipc/ref.h b/net/tipc/ref.h new file mode 100644 index 00000000..5bc8e7ab --- /dev/null +++ b/net/tipc/ref.h @@ -0,0 +1,49 @@ +/* + * net/tipc/ref.h: Include file for TIPC object registry code + * + * Copyright (c) 1991-2006, Ericsson AB + * Copyright (c) 2005-2006, 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_REF_H +#define _TIPC_REF_H + +int tipc_ref_table_init(u32 requested_size, u32 start); +void tipc_ref_table_stop(void); + +u32 tipc_ref_acquire(void *object, spinlock_t **lock); +void tipc_ref_discard(u32 ref); + +void *tipc_ref_lock(u32 ref); +void *tipc_ref_deref(u32 ref); + +#endif diff --git a/net/tipc/socket.c b/net/tipc/socket.c new file mode 100644 index 00000000..29e957f6 --- /dev/null +++ b/net/tipc/socket.c @@ -0,0 +1,1899 @@ +/* + * net/tipc/socket.c: TIPC socket API + * + * Copyright (c) 2001-2007, Ericsson AB + * Copyright (c) 2004-2008, 2010-2011, 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 <linux/export.h> +#include <net/sock.h> + +#include "core.h" +#include "port.h" + +#define SS_LISTENING -1 /* socket is listening */ +#define SS_READY -2 /* socket is connectionless */ + +#define OVERLOAD_LIMIT_BASE 5000 +#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ + +struct tipc_sock { + struct sock sk; + struct tipc_port *p; + struct tipc_portid peer_name; + unsigned int conn_timeout; +}; + +#define tipc_sk(sk) ((struct tipc_sock *)(sk)) +#define tipc_sk_port(sk) ((struct tipc_port *)(tipc_sk(sk)->p)) + +#define tipc_rx_ready(sock) (!skb_queue_empty(&sock->sk->sk_receive_queue) || \ + (sock->state == SS_DISCONNECTING)) + +static int backlog_rcv(struct sock *sk, struct sk_buff *skb); +static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf); +static void wakeupdispatch(struct tipc_port *tport); + +static const struct proto_ops packet_ops; +static const struct proto_ops stream_ops; +static const struct proto_ops msg_ops; + +static struct proto tipc_proto; + +static int sockets_enabled; + +static atomic_t tipc_queue_size = ATOMIC_INIT(0); + +/* + * Revised TIPC socket locking policy: + * + * Most socket operations take the standard socket lock when they start + * and hold it until they finish (or until they need to sleep). Acquiring + * this lock grants the owner exclusive access to the fields of the socket + * data structures, with the exception of the backlog queue. A few socket + * operations can be done without taking the socket lock because they only + * read socket information that never changes during the life of the socket. + * + * Socket operations may acquire the lock for the associated TIPC port if they + * need to perform an operation on the port. If any routine needs to acquire + * both the socket lock and the port lock it must take the socket lock first + * to avoid the risk of deadlock. + * + * The dispatcher handling incoming messages cannot grab the socket lock in + * the standard fashion, since invoked it runs at the BH level and cannot block. + * Instead, it checks to see if the socket lock is currently owned by someone, + * and either handles the message itself or adds it to the socket's backlog + * queue; in the latter case the queued message is processed once the process + * owning the socket lock releases it. + * + * NOTE: Releasing the socket lock while an operation is sleeping overcomes + * the problem of a blocked socket operation preventing any other operations + * from occurring. However, applications must be careful if they have + * multiple threads trying to send (or receive) on the same socket, as these + * operations might interfere with each other. For example, doing a connect + * and a receive at the same time might allow the receive to consume the + * ACK message meant for the connect. While additional work could be done + * to try and overcome this, it doesn't seem to be worthwhile at the present. + * + * NOTE: Releasing the socket lock while an operation is sleeping also ensures + * that another operation that must be performed in a non-blocking manner is + * not delayed for very long because the lock has already been taken. + * + * NOTE: This code assumes that certain fields of a port/socket pair are + * constant over its lifetime; such fields can be examined without taking + * the socket lock and/or port lock, and do not need to be re-read even + * after resuming processing after waiting. These fields include: + * - socket type + * - pointer to socket sk structure (aka tipc_sock structure) + * - pointer to port structure + * - port reference + */ + +/** + * advance_rx_queue - discard first buffer in socket receive queue + * + * Caller must hold socket lock + */ + +static void advance_rx_queue(struct sock *sk) +{ + kfree_skb(__skb_dequeue(&sk->sk_receive_queue)); + atomic_dec(&tipc_queue_size); +} + +/** + * discard_rx_queue - discard all buffers in socket receive queue + * + * Caller must hold socket lock + */ + +static void discard_rx_queue(struct sock *sk) +{ + struct sk_buff *buf; + + while ((buf = __skb_dequeue(&sk->sk_receive_queue))) { + atomic_dec(&tipc_queue_size); + kfree_skb(buf); + } +} + +/** + * reject_rx_queue - reject all buffers in socket receive queue + * + * Caller must hold socket lock + */ + +static void reject_rx_queue(struct sock *sk) +{ + struct sk_buff *buf; + + while ((buf = __skb_dequeue(&sk->sk_receive_queue))) { + tipc_reject_msg(buf, TIPC_ERR_NO_PORT); + atomic_dec(&tipc_queue_size); + } +} + +/** + * tipc_create - create a TIPC socket + * @net: network namespace (must be default network) + * @sock: pre-allocated socket structure + * @protocol: protocol indicator (must be 0) + * @kern: caused by kernel or by userspace? + * + * This routine creates additional data structures used by the TIPC socket, + * initializes them, and links them together. + * + * Returns 0 on success, errno otherwise + */ + +static int tipc_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + const struct proto_ops *ops; + socket_state state; + struct sock *sk; + struct tipc_port *tp_ptr; + + /* Validate arguments */ + + if (unlikely(protocol != 0)) + return -EPROTONOSUPPORT; + + switch (sock->type) { + case SOCK_STREAM: + ops = &stream_ops; + state = SS_UNCONNECTED; + break; + case SOCK_SEQPACKET: + ops = &packet_ops; + state = SS_UNCONNECTED; + break; + case SOCK_DGRAM: + case SOCK_RDM: + ops = &msg_ops; + state = SS_READY; + break; + default: + return -EPROTOTYPE; + } + + /* Allocate socket's protocol area */ + + sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto); + if (sk == NULL) + return -ENOMEM; + + /* Allocate TIPC port for socket to use */ + + tp_ptr = tipc_createport_raw(sk, &dispatch, &wakeupdispatch, + TIPC_LOW_IMPORTANCE); + if (unlikely(!tp_ptr)) { + sk_free(sk); + return -ENOMEM; + } + + /* Finish initializing socket data structures */ + + sock->ops = ops; + sock->state = state; + + sock_init_data(sock, sk); + sk->sk_backlog_rcv = backlog_rcv; + tipc_sk(sk)->p = tp_ptr; + tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT; + + spin_unlock_bh(tp_ptr->lock); + + if (sock->state == SS_READY) { + tipc_set_portunreturnable(tp_ptr->ref, 1); + if (sock->type == SOCK_DGRAM) + tipc_set_portunreliable(tp_ptr->ref, 1); + } + + return 0; +} + +/** + * release - destroy a TIPC socket + * @sock: socket to destroy + * + * This routine cleans up any messages that are still queued on the socket. + * For DGRAM and RDM socket types, all queued messages are rejected. + * For SEQPACKET and STREAM socket types, the first message is rejected + * and any others are discarded. (If the first message on a STREAM socket + * is partially-read, it is discarded and the next one is rejected instead.) + * + * NOTE: Rejected messages are not necessarily returned to the sender! They + * are returned or discarded according to the "destination droppable" setting + * specified for the message by the sender. + * + * Returns 0 on success, errno otherwise + */ + +static int release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport; + struct sk_buff *buf; + int res; + + /* + * Exit if socket isn't fully initialized (occurs when a failed accept() + * releases a pre-allocated child socket that was never used) + */ + + if (sk == NULL) + return 0; + + tport = tipc_sk_port(sk); + lock_sock(sk); + + /* + * Reject all unreceived messages, except on an active connection + * (which disconnects locally & sends a 'FIN+' to peer) + */ + + while (sock->state != SS_DISCONNECTING) { + buf = __skb_dequeue(&sk->sk_receive_queue); + if (buf == NULL) + break; + atomic_dec(&tipc_queue_size); + if (TIPC_SKB_CB(buf)->handle != 0) + kfree_skb(buf); + else { + if ((sock->state == SS_CONNECTING) || + (sock->state == SS_CONNECTED)) { + sock->state = SS_DISCONNECTING; + tipc_disconnect(tport->ref); + } + tipc_reject_msg(buf, TIPC_ERR_NO_PORT); + } + } + + /* + * Delete TIPC port; this ensures no more messages are queued + * (also disconnects an active connection & sends a 'FIN-' to peer) + */ + + res = tipc_deleteport(tport->ref); + + /* Discard any remaining (connection-based) messages in receive queue */ + + discard_rx_queue(sk); + + /* Reject any messages that accumulated in backlog queue */ + + sock->state = SS_DISCONNECTING; + release_sock(sk); + + sock_put(sk); + sock->sk = NULL; + + return res; +} + +/** + * bind - associate or disassocate TIPC name(s) with a socket + * @sock: socket structure + * @uaddr: socket address describing name(s) and desired operation + * @uaddr_len: size of socket address data structure + * + * Name and name sequence binding is indicated using a positive scope value; + * a negative scope value unbinds the specified name. Specifying no name + * (i.e. a socket address length of 0) unbinds all names from the socket. + * + * Returns 0 on success, errno otherwise + * + * NOTE: This routine doesn't need to take the socket lock since it doesn't + * access any non-constant socket information. + */ + +static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len) +{ + struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; + u32 portref = tipc_sk_port(sock->sk)->ref; + + if (unlikely(!uaddr_len)) + return tipc_withdraw(portref, 0, NULL); + + if (uaddr_len < sizeof(struct sockaddr_tipc)) + return -EINVAL; + if (addr->family != AF_TIPC) + return -EAFNOSUPPORT; + + if (addr->addrtype == TIPC_ADDR_NAME) + addr->addr.nameseq.upper = addr->addr.nameseq.lower; + else if (addr->addrtype != TIPC_ADDR_NAMESEQ) + return -EAFNOSUPPORT; + + if (addr->addr.nameseq.type < TIPC_RESERVED_TYPES) + return -EACCES; + + return (addr->scope > 0) ? + tipc_publish(portref, addr->scope, &addr->addr.nameseq) : + tipc_withdraw(portref, -addr->scope, &addr->addr.nameseq); +} + +/** + * get_name - get port ID of socket or peer socket + * @sock: socket structure + * @uaddr: area for returned socket address + * @uaddr_len: area for returned length of socket address + * @peer: 0 = own ID, 1 = current peer ID, 2 = current/former peer ID + * + * Returns 0 on success, errno otherwise + * + * NOTE: This routine doesn't need to take the socket lock since it only + * accesses socket information that is unchanging (or which changes in + * a completely predictable manner). + */ + +static int get_name(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; + struct tipc_sock *tsock = tipc_sk(sock->sk); + + memset(addr, 0, sizeof(*addr)); + if (peer) { + if ((sock->state != SS_CONNECTED) && + ((peer != 2) || (sock->state != SS_DISCONNECTING))) + return -ENOTCONN; + addr->addr.id.ref = tsock->peer_name.ref; + addr->addr.id.node = tsock->peer_name.node; + } else { + addr->addr.id.ref = tsock->p->ref; + addr->addr.id.node = tipc_own_addr; + } + + *uaddr_len = sizeof(*addr); + addr->addrtype = TIPC_ADDR_ID; + addr->family = AF_TIPC; + addr->scope = 0; + addr->addr.name.domain = 0; + + return 0; +} + +/** + * poll - read and possibly block on pollmask + * @file: file structure associated with the socket + * @sock: socket for which to calculate the poll bits + * @wait: ??? + * + * Returns pollmask value + * + * COMMENTARY: + * It appears that the usual socket locking mechanisms are not useful here + * since the pollmask info is potentially out-of-date the moment this routine + * exits. TCP and other protocols seem to rely on higher level poll routines + * to handle any preventable race conditions, so TIPC will do the same ... + * + * TIPC sets the returned events as follows: + * + * socket state flags set + * ------------ --------- + * unconnected no read flags + * no write flags + * + * connecting POLLIN/POLLRDNORM if ACK/NACK in rx queue + * no write flags + * + * connected POLLIN/POLLRDNORM if data in rx queue + * POLLOUT if port is not congested + * + * disconnecting POLLIN/POLLRDNORM/POLLHUP + * no write flags + * + * listening POLLIN if SYN in rx queue + * no write flags + * + * ready POLLIN/POLLRDNORM if data in rx queue + * [connectionless] POLLOUT (since port cannot be congested) + * + * IMPORTANT: The fact that a read or write operation is indicated does NOT + * imply that the operation will succeed, merely that it should be performed + * and will not block. + */ + +static unsigned int poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + u32 mask = 0; + + poll_wait(file, sk_sleep(sk), wait); + + switch ((int)sock->state) { + case SS_READY: + case SS_CONNECTED: + if (!tipc_sk_port(sk)->congested) + mask |= POLLOUT; + /* fall thru' */ + case SS_CONNECTING: + case SS_LISTENING: + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= (POLLIN | POLLRDNORM); + break; + case SS_DISCONNECTING: + mask = (POLLIN | POLLRDNORM | POLLHUP); + break; + } + + return mask; +} + +/** + * dest_name_check - verify user is permitted to send to specified port name + * @dest: destination address + * @m: descriptor for message to be sent + * + * Prevents restricted configuration commands from being issued by + * unauthorized users. + * + * Returns 0 if permission is granted, otherwise errno + */ + +static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m) +{ + struct tipc_cfg_msg_hdr hdr; + + if (likely(dest->addr.name.name.type >= TIPC_RESERVED_TYPES)) + return 0; + if (likely(dest->addr.name.name.type == TIPC_TOP_SRV)) + return 0; + if (likely(dest->addr.name.name.type != TIPC_CFG_SRV)) + return -EACCES; + + if (!m->msg_iovlen || (m->msg_iov[0].iov_len < sizeof(hdr))) + return -EMSGSIZE; + if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr))) + return -EFAULT; + if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN))) + return -EACCES; + + return 0; +} + +/** + * send_msg - send message in connectionless manner + * @iocb: if NULL, indicates that socket lock is already held + * @sock: socket structure + * @m: message to send + * @total_len: length of message + * + * Message must have an destination specified explicitly. + * Used for SOCK_RDM and SOCK_DGRAM messages, + * and for 'SYN' messages on SOCK_SEQPACKET and SOCK_STREAM connections. + * (Note: 'SYN+' is prohibited on SOCK_STREAM.) + * + * Returns the number of bytes sent on success, or errno otherwise + */ + +static int send_msg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t total_len) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; + int needs_conn; + long timeout_val; + int res = -EINVAL; + + if (unlikely(!dest)) + return -EDESTADDRREQ; + if (unlikely((m->msg_namelen < sizeof(*dest)) || + (dest->family != AF_TIPC))) + return -EINVAL; + if ((total_len > TIPC_MAX_USER_MSG_SIZE) || + (m->msg_iovlen > (unsigned)INT_MAX)) + return -EMSGSIZE; + + if (iocb) + lock_sock(sk); + + needs_conn = (sock->state != SS_READY); + if (unlikely(needs_conn)) { + if (sock->state == SS_LISTENING) { + res = -EPIPE; + goto exit; + } + if (sock->state != SS_UNCONNECTED) { + res = -EISCONN; + goto exit; + } + if ((tport->published) || + ((sock->type == SOCK_STREAM) && (total_len != 0))) { + res = -EOPNOTSUPP; + goto exit; + } + if (dest->addrtype == TIPC_ADDR_NAME) { + tport->conn_type = dest->addr.name.name.type; + tport->conn_instance = dest->addr.name.name.instance; + } + + /* Abort any pending connection attempts (very unlikely) */ + + reject_rx_queue(sk); + } + + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + + do { + if (dest->addrtype == TIPC_ADDR_NAME) { + res = dest_name_check(dest, m); + if (res) + break; + res = tipc_send2name(tport->ref, + &dest->addr.name.name, + dest->addr.name.domain, + m->msg_iovlen, + m->msg_iov, + total_len); + } else if (dest->addrtype == TIPC_ADDR_ID) { + res = tipc_send2port(tport->ref, + &dest->addr.id, + m->msg_iovlen, + m->msg_iov, + total_len); + } else if (dest->addrtype == TIPC_ADDR_MCAST) { + if (needs_conn) { + res = -EOPNOTSUPP; + break; + } + res = dest_name_check(dest, m); + if (res) + break; + res = tipc_multicast(tport->ref, + &dest->addr.nameseq, + m->msg_iovlen, + m->msg_iov, + total_len); + } + if (likely(res != -ELINKCONG)) { + if (needs_conn && (res >= 0)) + sock->state = SS_CONNECTING; + break; + } + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; + break; + } + release_sock(sk); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + !tport->congested, timeout_val); + lock_sock(sk); + } while (1); + +exit: + if (iocb) + release_sock(sk); + return res; +} + +/** + * send_packet - send a connection-oriented message + * @iocb: if NULL, indicates that socket lock is already held + * @sock: socket structure + * @m: message to send + * @total_len: length of message + * + * Used for SOCK_SEQPACKET messages and SOCK_STREAM data. + * + * Returns the number of bytes sent on success, or errno otherwise + */ + +static int send_packet(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t total_len) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; + long timeout_val; + int res; + + /* Handle implied connection establishment */ + + if (unlikely(dest)) + return send_msg(iocb, sock, m, total_len); + + if ((total_len > TIPC_MAX_USER_MSG_SIZE) || + (m->msg_iovlen > (unsigned)INT_MAX)) + return -EMSGSIZE; + + if (iocb) + lock_sock(sk); + + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + + do { + if (unlikely(sock->state != SS_CONNECTED)) { + if (sock->state == SS_DISCONNECTING) + res = -EPIPE; + else + res = -ENOTCONN; + break; + } + + res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov, + total_len); + if (likely(res != -ELINKCONG)) + break; + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; + break; + } + release_sock(sk); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + (!tport->congested || !tport->connected), timeout_val); + lock_sock(sk); + } while (1); + + if (iocb) + release_sock(sk); + return res; +} + +/** + * send_stream - send stream-oriented data + * @iocb: (unused) + * @sock: socket structure + * @m: data to send + * @total_len: total length of data to be sent + * + * Used for SOCK_STREAM data. + * + * Returns the number of bytes sent on success (or partial success), + * or errno if no data sent + */ + +static int send_stream(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t total_len) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct msghdr my_msg; + struct iovec my_iov; + struct iovec *curr_iov; + int curr_iovlen; + char __user *curr_start; + u32 hdr_size; + int curr_left; + int bytes_to_send; + int bytes_sent; + int res; + + lock_sock(sk); + + /* Handle special cases where there is no connection */ + + if (unlikely(sock->state != SS_CONNECTED)) { + if (sock->state == SS_UNCONNECTED) { + res = send_packet(NULL, sock, m, total_len); + goto exit; + } else if (sock->state == SS_DISCONNECTING) { + res = -EPIPE; + goto exit; + } else { + res = -ENOTCONN; + goto exit; + } + } + + if (unlikely(m->msg_name)) { + res = -EISCONN; + goto exit; + } + + if ((total_len > (unsigned)INT_MAX) || + (m->msg_iovlen > (unsigned)INT_MAX)) { + res = -EMSGSIZE; + goto exit; + } + + /* + * Send each iovec entry using one or more messages + * + * Note: This algorithm is good for the most likely case + * (i.e. one large iovec entry), but could be improved to pass sets + * of small iovec entries into send_packet(). + */ + + curr_iov = m->msg_iov; + curr_iovlen = m->msg_iovlen; + my_msg.msg_iov = &my_iov; + my_msg.msg_iovlen = 1; + my_msg.msg_flags = m->msg_flags; + my_msg.msg_name = NULL; + bytes_sent = 0; + + hdr_size = msg_hdr_sz(&tport->phdr); + + while (curr_iovlen--) { + curr_start = curr_iov->iov_base; + curr_left = curr_iov->iov_len; + + while (curr_left) { + bytes_to_send = tport->max_pkt - hdr_size; + if (bytes_to_send > TIPC_MAX_USER_MSG_SIZE) + bytes_to_send = TIPC_MAX_USER_MSG_SIZE; + if (curr_left < bytes_to_send) + bytes_to_send = curr_left; + my_iov.iov_base = curr_start; + my_iov.iov_len = bytes_to_send; + res = send_packet(NULL, sock, &my_msg, bytes_to_send); + if (res < 0) { + if (bytes_sent) + res = bytes_sent; + goto exit; + } + curr_left -= bytes_to_send; + curr_start += bytes_to_send; + bytes_sent += bytes_to_send; + } + + curr_iov++; + } + res = bytes_sent; +exit: + release_sock(sk); + return res; +} + +/** + * auto_connect - complete connection setup to a remote port + * @sock: socket structure + * @msg: peer's response message + * + * Returns 0 on success, errno otherwise + */ + +static int auto_connect(struct socket *sock, struct tipc_msg *msg) +{ + struct tipc_sock *tsock = tipc_sk(sock->sk); + + if (msg_errcode(msg)) { + sock->state = SS_DISCONNECTING; + return -ECONNREFUSED; + } + + tsock->peer_name.ref = msg_origport(msg); + tsock->peer_name.node = msg_orignode(msg); + tipc_connect2port(tsock->p->ref, &tsock->peer_name); + tipc_set_portimportance(tsock->p->ref, msg_importance(msg)); + sock->state = SS_CONNECTED; + return 0; +} + +/** + * set_orig_addr - capture sender's address for received message + * @m: descriptor for message info + * @msg: received message header + * + * Note: Address is not captured if not requested by receiver. + */ + +static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg) +{ + struct sockaddr_tipc *addr = (struct sockaddr_tipc *)m->msg_name; + + if (addr) { + addr->family = AF_TIPC; + addr->addrtype = TIPC_ADDR_ID; + addr->addr.id.ref = msg_origport(msg); + addr->addr.id.node = msg_orignode(msg); + addr->addr.name.domain = 0; /* could leave uninitialized */ + addr->scope = 0; /* could leave uninitialized */ + m->msg_namelen = sizeof(struct sockaddr_tipc); + } +} + +/** + * anc_data_recv - optionally capture ancillary data for received message + * @m: descriptor for message info + * @msg: received message header + * @tport: TIPC port associated with message + * + * Note: Ancillary data is not captured if not requested by receiver. + * + * Returns 0 if successful, otherwise errno + */ + +static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg, + struct tipc_port *tport) +{ + u32 anc_data[3]; + u32 err; + u32 dest_type; + int has_name; + int res; + + if (likely(m->msg_controllen == 0)) + return 0; + + /* Optionally capture errored message object(s) */ + + err = msg ? msg_errcode(msg) : 0; + if (unlikely(err)) { + anc_data[0] = err; + anc_data[1] = msg_data_sz(msg); + res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data); + if (res) + return res; + if (anc_data[1]) { + res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1], + msg_data(msg)); + if (res) + return res; + } + } + + /* Optionally capture message destination object */ + + dest_type = msg ? msg_type(msg) : TIPC_DIRECT_MSG; + switch (dest_type) { + case TIPC_NAMED_MSG: + has_name = 1; + anc_data[0] = msg_nametype(msg); + anc_data[1] = msg_namelower(msg); + anc_data[2] = msg_namelower(msg); + break; + case TIPC_MCAST_MSG: + has_name = 1; + anc_data[0] = msg_nametype(msg); + anc_data[1] = msg_namelower(msg); + anc_data[2] = msg_nameupper(msg); + break; + case TIPC_CONN_MSG: + has_name = (tport->conn_type != 0); + anc_data[0] = tport->conn_type; + anc_data[1] = tport->conn_instance; + anc_data[2] = tport->conn_instance; + break; + default: + has_name = 0; + } + if (has_name) { + res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data); + if (res) + return res; + } + + return 0; +} + +/** + * recv_msg - receive packet-oriented message + * @iocb: (unused) + * @m: descriptor for message info + * @buf_len: total size of user buffer area + * @flags: receive flags + * + * Used for SOCK_DGRAM, SOCK_RDM, and SOCK_SEQPACKET messages. + * If the complete message doesn't fit in user area, truncate it. + * + * Returns size of returned message data, errno otherwise + */ + +static int recv_msg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t buf_len, int flags) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct sk_buff *buf; + struct tipc_msg *msg; + long timeout; + unsigned int sz; + u32 err; + int res; + + /* Catch invalid receive requests */ + + if (unlikely(!buf_len)) + return -EINVAL; + + lock_sock(sk); + + if (unlikely(sock->state == SS_UNCONNECTED)) { + res = -ENOTCONN; + goto exit; + } + + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); +restart: + + /* Look for a message in receive queue; wait if necessary */ + + while (skb_queue_empty(&sk->sk_receive_queue)) { + if (sock->state == SS_DISCONNECTING) { + res = -ENOTCONN; + goto exit; + } + if (timeout <= 0L) { + res = timeout ? timeout : -EWOULDBLOCK; + goto exit; + } + release_sock(sk); + timeout = wait_event_interruptible_timeout(*sk_sleep(sk), + tipc_rx_ready(sock), + timeout); + lock_sock(sk); + } + + /* Look at first message in receive queue */ + + buf = skb_peek(&sk->sk_receive_queue); + msg = buf_msg(buf); + sz = msg_data_sz(msg); + err = msg_errcode(msg); + + /* Complete connection setup for an implied connect */ + + if (unlikely(sock->state == SS_CONNECTING)) { + res = auto_connect(sock, msg); + if (res) + goto exit; + } + + /* Discard an empty non-errored message & try again */ + + if ((!sz) && (!err)) { + advance_rx_queue(sk); + goto restart; + } + + /* Capture sender's address (optional) */ + + set_orig_addr(m, msg); + + /* Capture ancillary data (optional) */ + + res = anc_data_recv(m, msg, tport); + if (res) + goto exit; + + /* Capture message data (if valid) & compute return value (always) */ + + if (!err) { + if (unlikely(buf_len < sz)) { + sz = buf_len; + m->msg_flags |= MSG_TRUNC; + } + res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg), + m->msg_iov, sz); + if (res) + goto exit; + res = sz; + } else { + if ((sock->state == SS_READY) || + ((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) + res = 0; + else + res = -ECONNRESET; + } + + /* Consume received message (optional) */ + + if (likely(!(flags & MSG_PEEK))) { + if ((sock->state != SS_READY) && + (++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) + tipc_acknowledge(tport->ref, tport->conn_unacked); + advance_rx_queue(sk); + } +exit: + release_sock(sk); + return res; +} + +/** + * recv_stream - receive stream-oriented data + * @iocb: (unused) + * @m: descriptor for message info + * @buf_len: total size of user buffer area + * @flags: receive flags + * + * Used for SOCK_STREAM messages only. If not enough data is available + * will optionally wait for more; never truncates data. + * + * Returns size of returned message data, errno otherwise + */ + +static int recv_stream(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t buf_len, int flags) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct sk_buff *buf; + struct tipc_msg *msg; + long timeout; + unsigned int sz; + int sz_to_copy, target, needed; + int sz_copied = 0; + u32 err; + int res = 0; + + /* Catch invalid receive attempts */ + + if (unlikely(!buf_len)) + return -EINVAL; + + lock_sock(sk); + + if (unlikely((sock->state == SS_UNCONNECTED) || + (sock->state == SS_CONNECTING))) { + res = -ENOTCONN; + goto exit; + } + + target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); +restart: + + /* Look for a message in receive queue; wait if necessary */ + + while (skb_queue_empty(&sk->sk_receive_queue)) { + if (sock->state == SS_DISCONNECTING) { + res = -ENOTCONN; + goto exit; + } + if (timeout <= 0L) { + res = timeout ? timeout : -EWOULDBLOCK; + goto exit; + } + release_sock(sk); + timeout = wait_event_interruptible_timeout(*sk_sleep(sk), + tipc_rx_ready(sock), + timeout); + lock_sock(sk); + } + + /* Look at first message in receive queue */ + + buf = skb_peek(&sk->sk_receive_queue); + msg = buf_msg(buf); + sz = msg_data_sz(msg); + err = msg_errcode(msg); + + /* Discard an empty non-errored message & try again */ + + if ((!sz) && (!err)) { + advance_rx_queue(sk); + goto restart; + } + + /* Optionally capture sender's address & ancillary data of first msg */ + + if (sz_copied == 0) { + set_orig_addr(m, msg); + res = anc_data_recv(m, msg, tport); + if (res) + goto exit; + } + + /* Capture message data (if valid) & compute return value (always) */ + + if (!err) { + u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle); + + sz -= offset; + needed = (buf_len - sz_copied); + sz_to_copy = (sz <= needed) ? sz : needed; + + res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset, + m->msg_iov, sz_to_copy); + if (res) + goto exit; + + sz_copied += sz_to_copy; + + if (sz_to_copy < sz) { + if (!(flags & MSG_PEEK)) + TIPC_SKB_CB(buf)->handle = + (void *)(unsigned long)(offset + sz_to_copy); + goto exit; + } + } else { + if (sz_copied != 0) + goto exit; /* can't add error msg to valid data */ + + if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) + res = 0; + else + res = -ECONNRESET; + } + + /* Consume received message (optional) */ + + if (likely(!(flags & MSG_PEEK))) { + if (unlikely(++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) + tipc_acknowledge(tport->ref, tport->conn_unacked); + advance_rx_queue(sk); + } + + /* Loop around if more data is required */ + + if ((sz_copied < buf_len) && /* didn't get all requested data */ + (!skb_queue_empty(&sk->sk_receive_queue) || + (sz_copied < target)) && /* and more is ready or required */ + (!(flags & MSG_PEEK)) && /* and aren't just peeking at data */ + (!err)) /* and haven't reached a FIN */ + goto restart; + +exit: + release_sock(sk); + return sz_copied ? sz_copied : res; +} + +/** + * rx_queue_full - determine if receive queue can accept another message + * @msg: message to be added to queue + * @queue_size: current size of queue + * @base: nominal maximum size of queue + * + * Returns 1 if queue is unable to accept message, 0 otherwise + */ + +static int rx_queue_full(struct tipc_msg *msg, u32 queue_size, u32 base) +{ + u32 threshold; + u32 imp = msg_importance(msg); + + if (imp == TIPC_LOW_IMPORTANCE) + threshold = base; + else if (imp == TIPC_MEDIUM_IMPORTANCE) + threshold = base * 2; + else if (imp == TIPC_HIGH_IMPORTANCE) + threshold = base * 100; + else + return 0; + + if (msg_connected(msg)) + threshold *= 4; + + return queue_size >= threshold; +} + +/** + * filter_rcv - validate incoming message + * @sk: socket + * @buf: message + * + * 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. + * + * Returns TIPC error status code (TIPC_OK if message is not to be rejected) + */ + +static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) +{ + struct socket *sock = sk->sk_socket; + struct tipc_msg *msg = buf_msg(buf); + u32 recv_q_len; + + /* Reject message if it is wrong sort of message for socket */ + + /* + * WOULD IT BE BETTER TO JUST DISCARD THESE MESSAGES INSTEAD? + * "NO PORT" ISN'T REALLY THE RIGHT ERROR CODE, AND THERE MAY + * BE SECURITY IMPLICATIONS INHERENT IN REJECTING INVALID TRAFFIC + */ + + if (sock->state == SS_READY) { + if (msg_connected(msg)) + return TIPC_ERR_NO_PORT; + } else { + if (msg_mcast(msg)) + return TIPC_ERR_NO_PORT; + if (sock->state == SS_CONNECTED) { + if (!msg_connected(msg)) + return TIPC_ERR_NO_PORT; + } else if (sock->state == SS_CONNECTING) { + if (!msg_connected(msg) && (msg_errcode(msg) == 0)) + return TIPC_ERR_NO_PORT; + } else if (sock->state == SS_LISTENING) { + if (msg_connected(msg) || msg_errcode(msg)) + return TIPC_ERR_NO_PORT; + } else if (sock->state == SS_DISCONNECTING) { + return TIPC_ERR_NO_PORT; + } else /* (sock->state == SS_UNCONNECTED) */ { + if (msg_connected(msg) || msg_errcode(msg)) + return TIPC_ERR_NO_PORT; + } + } + + /* Reject message if there isn't room to queue it */ + + recv_q_len = (u32)atomic_read(&tipc_queue_size); + if (unlikely(recv_q_len >= OVERLOAD_LIMIT_BASE)) { + if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE)) + return TIPC_ERR_OVERLOAD; + } + recv_q_len = skb_queue_len(&sk->sk_receive_queue); + if (unlikely(recv_q_len >= (OVERLOAD_LIMIT_BASE / 2))) { + if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE / 2)) + return TIPC_ERR_OVERLOAD; + } + + /* Enqueue message (finally!) */ + + TIPC_SKB_CB(buf)->handle = 0; + atomic_inc(&tipc_queue_size); + __skb_queue_tail(&sk->sk_receive_queue, buf); + + /* Initiate connection termination for an incoming 'FIN' */ + + if (unlikely(msg_errcode(msg) && (sock->state == SS_CONNECTED))) { + sock->state = SS_DISCONNECTING; + tipc_disconnect_port(tipc_sk_port(sk)); + } + + if (waitqueue_active(sk_sleep(sk))) + wake_up_interruptible(sk_sleep(sk)); + return TIPC_OK; +} + +/** + * backlog_rcv - handle incoming message from backlog queue + * @sk: socket + * @buf: message + * + * Caller must hold socket lock, but not port lock. + * + * Returns 0 + */ + +static int backlog_rcv(struct sock *sk, struct sk_buff *buf) +{ + u32 res; + + res = filter_rcv(sk, buf); + if (res) + tipc_reject_msg(buf, res); + return 0; +} + +/** + * dispatch - handle incoming message + * @tport: TIPC port that received message + * @buf: message + * + * Called with port lock already taken. + * + * Returns TIPC error status code (TIPC_OK if message is not to be rejected) + */ + +static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf) +{ + struct sock *sk = (struct sock *)tport->usr_handle; + u32 res; + + /* + * Process message if socket is unlocked; otherwise add to backlog queue + * + * This code is based on sk_receive_skb(), but must be distinct from it + * since a TIPC-specific filter/reject mechanism is utilized + */ + + bh_lock_sock(sk); + if (!sock_owned_by_user(sk)) { + res = filter_rcv(sk, buf); + } else { + if (sk_add_backlog(sk, buf)) + res = TIPC_ERR_OVERLOAD; + else + res = TIPC_OK; + } + bh_unlock_sock(sk); + + return res; +} + +/** + * wakeupdispatch - wake up port after congestion + * @tport: port to wakeup + * + * Called with port lock already taken. + */ + +static void wakeupdispatch(struct tipc_port *tport) +{ + struct sock *sk = (struct sock *)tport->usr_handle; + + if (waitqueue_active(sk_sleep(sk))) + wake_up_interruptible(sk_sleep(sk)); +} + +/** + * connect - establish a connection to another TIPC port + * @sock: socket structure + * @dest: socket address for destination port + * @destlen: size of socket address data structure + * @flags: file-related flags associated with socket + * + * Returns 0 on success, errno otherwise + */ + +static int connect(struct socket *sock, struct sockaddr *dest, int destlen, + int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; + struct msghdr m = {NULL,}; + struct sk_buff *buf; + struct tipc_msg *msg; + unsigned int timeout; + int res; + + lock_sock(sk); + + /* For now, TIPC does not allow use of connect() with DGRAM/RDM types */ + + if (sock->state == SS_READY) { + res = -EOPNOTSUPP; + goto exit; + } + + /* For now, TIPC does not support the non-blocking form of connect() */ + + if (flags & O_NONBLOCK) { + res = -EOPNOTSUPP; + goto exit; + } + + /* Issue Posix-compliant error code if socket is in the wrong state */ + + if (sock->state == SS_LISTENING) { + res = -EOPNOTSUPP; + goto exit; + } + if (sock->state == SS_CONNECTING) { + res = -EALREADY; + goto exit; + } + if (sock->state != SS_UNCONNECTED) { + res = -EISCONN; + goto exit; + } + + /* + * Reject connection attempt using multicast address + * + * Note: send_msg() validates the rest of the address fields, + * so there's no need to do it here + */ + + if (dst->addrtype == TIPC_ADDR_MCAST) { + res = -EINVAL; + goto exit; + } + + /* Reject any messages already in receive queue (very unlikely) */ + + reject_rx_queue(sk); + + /* Send a 'SYN-' to destination */ + + m.msg_name = dest; + m.msg_namelen = destlen; + res = send_msg(NULL, sock, &m, 0); + if (res < 0) + goto exit; + + /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ + + timeout = tipc_sk(sk)->conn_timeout; + release_sock(sk); + res = wait_event_interruptible_timeout(*sk_sleep(sk), + (!skb_queue_empty(&sk->sk_receive_queue) || + (sock->state != SS_CONNECTING)), + timeout ? (long)msecs_to_jiffies(timeout) + : MAX_SCHEDULE_TIMEOUT); + lock_sock(sk); + + if (res > 0) { + buf = skb_peek(&sk->sk_receive_queue); + if (buf != NULL) { + msg = buf_msg(buf); + res = auto_connect(sock, msg); + if (!res) { + if (!msg_data_sz(msg)) + advance_rx_queue(sk); + } + } else { + if (sock->state == SS_CONNECTED) + res = -EISCONN; + else + res = -ECONNREFUSED; + } + } else { + if (res == 0) + res = -ETIMEDOUT; + else + ; /* leave "res" unchanged */ + sock->state = SS_DISCONNECTING; + } + +exit: + release_sock(sk); + return res; +} + +/** + * listen - allow socket to listen for incoming connections + * @sock: socket structure + * @len: (unused) + * + * Returns 0 on success, errno otherwise + */ + +static int listen(struct socket *sock, int len) +{ + struct sock *sk = sock->sk; + int res; + + lock_sock(sk); + + if (sock->state != SS_UNCONNECTED) + res = -EINVAL; + else { + sock->state = SS_LISTENING; + res = 0; + } + + release_sock(sk); + return res; +} + +/** + * accept - wait for connection request + * @sock: listening socket + * @newsock: new socket that is to be connected + * @flags: file-related flags associated with socket + * + * Returns 0 on success, errno otherwise + */ + +static int accept(struct socket *sock, struct socket *new_sock, int flags) +{ + struct sock *sk = sock->sk; + struct sk_buff *buf; + int res; + + lock_sock(sk); + + if (sock->state != SS_LISTENING) { + res = -EINVAL; + goto exit; + } + + while (skb_queue_empty(&sk->sk_receive_queue)) { + if (flags & O_NONBLOCK) { + res = -EWOULDBLOCK; + goto exit; + } + release_sock(sk); + res = wait_event_interruptible(*sk_sleep(sk), + (!skb_queue_empty(&sk->sk_receive_queue))); + lock_sock(sk); + if (res) + goto exit; + } + + buf = skb_peek(&sk->sk_receive_queue); + + res = tipc_create(sock_net(sock->sk), new_sock, 0, 0); + if (!res) { + struct sock *new_sk = new_sock->sk; + struct tipc_sock *new_tsock = tipc_sk(new_sk); + struct tipc_port *new_tport = new_tsock->p; + u32 new_ref = new_tport->ref; + struct tipc_msg *msg = buf_msg(buf); + + lock_sock(new_sk); + + /* + * Reject any stray messages received by new socket + * before the socket lock was taken (very, very unlikely) + */ + + reject_rx_queue(new_sk); + + /* Connect new socket to it's peer */ + + new_tsock->peer_name.ref = msg_origport(msg); + new_tsock->peer_name.node = msg_orignode(msg); + tipc_connect2port(new_ref, &new_tsock->peer_name); + new_sock->state = SS_CONNECTED; + + tipc_set_portimportance(new_ref, msg_importance(msg)); + if (msg_named(msg)) { + new_tport->conn_type = msg_nametype(msg); + new_tport->conn_instance = msg_nameinst(msg); + } + + /* + * Respond to 'SYN-' by discarding it & returning 'ACK'-. + * Respond to 'SYN+' by queuing it on new socket. + */ + + if (!msg_data_sz(msg)) { + struct msghdr m = {NULL,}; + + advance_rx_queue(sk); + send_packet(NULL, new_sock, &m, 0); + } else { + __skb_dequeue(&sk->sk_receive_queue); + __skb_queue_head(&new_sk->sk_receive_queue, buf); + } + release_sock(new_sk); + } +exit: + release_sock(sk); + return res; +} + +/** + * shutdown - shutdown socket connection + * @sock: socket structure + * @how: direction to close (must be SHUT_RDWR) + * + * Terminates connection (if necessary), then purges socket's receive queue. + * + * Returns 0 on success, errno otherwise + */ + +static int shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + struct sk_buff *buf; + int res; + + if (how != SHUT_RDWR) + return -EINVAL; + + lock_sock(sk); + + switch (sock->state) { + case SS_CONNECTING: + case SS_CONNECTED: + + /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ +restart: + buf = __skb_dequeue(&sk->sk_receive_queue); + if (buf) { + atomic_dec(&tipc_queue_size); + if (TIPC_SKB_CB(buf)->handle != 0) { + kfree_skb(buf); + goto restart; + } + tipc_disconnect(tport->ref); + tipc_reject_msg(buf, TIPC_CONN_SHUTDOWN); + } else { + tipc_shutdown(tport->ref); + } + + sock->state = SS_DISCONNECTING; + + /* fall through */ + + case SS_DISCONNECTING: + + /* Discard any unreceived messages; wake up sleeping tasks */ + + discard_rx_queue(sk); + if (waitqueue_active(sk_sleep(sk))) + wake_up_interruptible(sk_sleep(sk)); + res = 0; + break; + + default: + res = -ENOTCONN; + } + + release_sock(sk); + return res; +} + +/** + * setsockopt - set socket option + * @sock: socket structure + * @lvl: option level + * @opt: option identifier + * @ov: pointer to new option value + * @ol: length of option value + * + * For stream sockets only, accepts and ignores all IPPROTO_TCP options + * (to ease compatibility). + * + * Returns 0 on success, errno otherwise + */ + +static int setsockopt(struct socket *sock, + int lvl, int opt, char __user *ov, unsigned int ol) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + u32 value; + int res; + + if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) + return 0; + if (lvl != SOL_TIPC) + return -ENOPROTOOPT; + if (ol < sizeof(value)) + return -EINVAL; + res = get_user(value, (u32 __user *)ov); + if (res) + return res; + + lock_sock(sk); + + switch (opt) { + case TIPC_IMPORTANCE: + res = tipc_set_portimportance(tport->ref, value); + break; + case TIPC_SRC_DROPPABLE: + if (sock->type != SOCK_STREAM) + res = tipc_set_portunreliable(tport->ref, value); + else + res = -ENOPROTOOPT; + break; + case TIPC_DEST_DROPPABLE: + res = tipc_set_portunreturnable(tport->ref, value); + break; + case TIPC_CONN_TIMEOUT: + tipc_sk(sk)->conn_timeout = value; + /* no need to set "res", since already 0 at this point */ + break; + default: + res = -EINVAL; + } + + release_sock(sk); + + return res; +} + +/** + * getsockopt - get socket option + * @sock: socket structure + * @lvl: option level + * @opt: option identifier + * @ov: receptacle for option value + * @ol: receptacle for length of option value + * + * For stream sockets only, returns 0 length result for all IPPROTO_TCP options + * (to ease compatibility). + * + * Returns 0 on success, errno otherwise + */ + +static int getsockopt(struct socket *sock, + int lvl, int opt, char __user *ov, int __user *ol) +{ + struct sock *sk = sock->sk; + struct tipc_port *tport = tipc_sk_port(sk); + int len; + u32 value; + int res; + + if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) + return put_user(0, ol); + if (lvl != SOL_TIPC) + return -ENOPROTOOPT; + res = get_user(len, ol); + if (res) + return res; + + lock_sock(sk); + + switch (opt) { + case TIPC_IMPORTANCE: + res = tipc_portimportance(tport->ref, &value); + break; + case TIPC_SRC_DROPPABLE: + res = tipc_portunreliable(tport->ref, &value); + break; + case TIPC_DEST_DROPPABLE: + res = tipc_portunreturnable(tport->ref, &value); + break; + case TIPC_CONN_TIMEOUT: + value = tipc_sk(sk)->conn_timeout; + /* no need to set "res", since already 0 at this point */ + break; + case TIPC_NODE_RECVQ_DEPTH: + value = (u32)atomic_read(&tipc_queue_size); + break; + case TIPC_SOCK_RECVQ_DEPTH: + value = skb_queue_len(&sk->sk_receive_queue); + break; + default: + res = -EINVAL; + } + + release_sock(sk); + + if (res) + return res; /* "get" failed */ + + if (len < sizeof(value)) + return -EINVAL; + + if (copy_to_user(ov, &value, sizeof(value))) + return -EFAULT; + + return put_user(sizeof(value), ol); +} + +/** + * Protocol switches for the various types of TIPC sockets + */ + +static const struct proto_ops msg_ops = { + .owner = THIS_MODULE, + .family = AF_TIPC, + .release = release, + .bind = bind, + .connect = connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = get_name, + .poll = poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = shutdown, + .setsockopt = setsockopt, + .getsockopt = getsockopt, + .sendmsg = send_msg, + .recvmsg = recv_msg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage +}; + +static const struct proto_ops packet_ops = { + .owner = THIS_MODULE, + .family = AF_TIPC, + .release = release, + .bind = bind, + .connect = connect, + .socketpair = sock_no_socketpair, + .accept = accept, + .getname = get_name, + .poll = poll, + .ioctl = sock_no_ioctl, + .listen = listen, + .shutdown = shutdown, + .setsockopt = setsockopt, + .getsockopt = getsockopt, + .sendmsg = send_packet, + .recvmsg = recv_msg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage +}; + +static const struct proto_ops stream_ops = { + .owner = THIS_MODULE, + .family = AF_TIPC, + .release = release, + .bind = bind, + .connect = connect, + .socketpair = sock_no_socketpair, + .accept = accept, + .getname = get_name, + .poll = poll, + .ioctl = sock_no_ioctl, + .listen = listen, + .shutdown = shutdown, + .setsockopt = setsockopt, + .getsockopt = getsockopt, + .sendmsg = send_stream, + .recvmsg = recv_stream, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage +}; + +static const struct net_proto_family tipc_family_ops = { + .owner = THIS_MODULE, + .family = AF_TIPC, + .create = tipc_create +}; + +static struct proto tipc_proto = { + .name = "TIPC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct tipc_sock) +}; + +/** + * tipc_socket_init - initialize TIPC socket interface + * + * Returns 0 on success, errno otherwise + */ +int tipc_socket_init(void) +{ + int res; + + res = proto_register(&tipc_proto, 1); + if (res) { + err("Failed to register TIPC protocol type\n"); + goto out; + } + + res = sock_register(&tipc_family_ops); + if (res) { + err("Failed to register TIPC socket type\n"); + proto_unregister(&tipc_proto); + goto out; + } + + sockets_enabled = 1; + out: + return res; +} + +/** + * tipc_socket_stop - stop TIPC socket interface + */ + +void tipc_socket_stop(void) +{ + if (!sockets_enabled) + return; + + sockets_enabled = 0; + sock_unregister(tipc_family_ops.family); + proto_unregister(&tipc_proto); +} + diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c new file mode 100644 index 00000000..b2964e98 --- /dev/null +++ b/net/tipc/subscr.c @@ -0,0 +1,588 @@ +/* + * net/tipc/subscr.c: TIPC network topology service + * + * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2005-2007, 2010-2011, 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 "name_table.h" +#include "port.h" +#include "subscr.h" + +/** + * struct tipc_subscriber - TIPC network topology subscriber + * @port_ref: object reference to server port connecting to subscriber + * @lock: pointer to spinlock controlling access to subscriber's server port + * @subscriber_list: adjacent subscribers in top. server's list of subscribers + * @subscription_list: list of subscription objects for this subscriber + */ + +struct tipc_subscriber { + u32 port_ref; + spinlock_t *lock; + struct list_head subscriber_list; + struct list_head subscription_list; +}; + +/** + * struct top_srv - TIPC network topology subscription service + * @user_ref: TIPC userid of subscription service + * @setup_port: reference to TIPC port that handles subscription requests + * @subscription_count: number of active subscriptions (not subscribers!) + * @subscriber_list: list of ports subscribing to service + * @lock: spinlock govering access to subscriber list + */ + +struct top_srv { + u32 setup_port; + atomic_t subscription_count; + struct list_head subscriber_list; + spinlock_t lock; +}; + +static struct top_srv topsrv; + +/** + * htohl - convert value to endianness used by destination + * @in: value to convert + * @swap: non-zero if endianness must be reversed + * + * Returns converted value + */ + +static u32 htohl(u32 in, int swap) +{ + return swap ? swab32(in) : in; +} + +/** + * subscr_send_event - send a message containing a tipc_event to the subscriber + * + * Note: Must not hold subscriber's server port lock, since tipc_send() will + * try to take the lock if the message is rejected and returned! + */ + +static void subscr_send_event(struct tipc_subscription *sub, + u32 found_lower, + u32 found_upper, + u32 event, + u32 port_ref, + u32 node) +{ + struct iovec msg_sect; + + msg_sect.iov_base = (void *)&sub->evt; + msg_sect.iov_len = sizeof(struct tipc_event); + + sub->evt.event = htohl(event, sub->swap); + sub->evt.found_lower = htohl(found_lower, sub->swap); + 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_send(sub->server_ref, 1, &msg_sect, msg_sect.iov_len); +} + +/** + * tipc_subscr_overlap - test for subscription overlap with the given values + * + * Returns 1 if there is overlap, otherwise 0. + */ + +int tipc_subscr_overlap(struct tipc_subscription *sub, + u32 found_lower, + u32 found_upper) + +{ + if (found_lower < sub->seq.lower) + found_lower = sub->seq.lower; + if (found_upper > sub->seq.upper) + found_upper = sub->seq.upper; + if (found_lower > found_upper) + return 0; + return 1; +} + +/** + * tipc_subscr_report_overlap - issue event if there is subscription overlap + * + * Protected by nameseq.lock in name_table.c + */ + +void tipc_subscr_report_overlap(struct tipc_subscription *sub, + u32 found_lower, + u32 found_upper, + u32 event, + u32 port_ref, + u32 node, + int must) +{ + if (!tipc_subscr_overlap(sub, found_lower, found_upper)) + return; + if (!must && !(sub->filter & TIPC_SUB_PORTS)) + return; + + subscr_send_event(sub, found_lower, found_upper, event, port_ref, node); +} + +/** + * subscr_timeout - subscription timeout has occurred + */ + +static void subscr_timeout(struct tipc_subscription *sub) +{ + struct tipc_port *server_port; + + /* Validate server port reference (in case subscriber is terminating) */ + + server_port = tipc_port_lock(sub->server_ref); + if (server_port == NULL) + return; + + /* Validate timeout (in case subscription is being cancelled) */ + + if (sub->timeout == TIPC_WAIT_FOREVER) { + tipc_port_unlock(server_port); + return; + } + + /* Unlink subscription from name table */ + + tipc_nametbl_unsubscribe(sub); + + /* Unlink subscription from subscriber */ + + list_del(&sub->subscription_list); + + /* Release subscriber's server port */ + + tipc_port_unlock(server_port); + + /* Notify subscriber of timeout */ + + subscr_send_event(sub, sub->evt.s.seq.lower, sub->evt.s.seq.upper, + TIPC_SUBSCR_TIMEOUT, 0, 0); + + /* Now destroy subscription */ + + k_term_timer(&sub->timer); + kfree(sub); + atomic_dec(&topsrv.subscription_count); +} + +/** + * subscr_del - delete a subscription within a subscription list + * + * Called with subscriber port locked. + */ + +static void subscr_del(struct tipc_subscription *sub) +{ + tipc_nametbl_unsubscribe(sub); + list_del(&sub->subscription_list); + kfree(sub); + atomic_dec(&topsrv.subscription_count); +} + +/** + * subscr_terminate - terminate communication with a subscriber + * + * Called with subscriber port locked. Routine must temporarily release lock + * to enable subscription timeout routine(s) to finish without deadlocking; + * the lock is then reclaimed to allow caller to release it upon return. + * (This should work even in the unlikely event some other thread creates + * a new object reference in the interim that uses this lock; this routine will + * simply wait for it to be released, then claim it.) + */ + +static void subscr_terminate(struct tipc_subscriber *subscriber) +{ + u32 port_ref; + struct tipc_subscription *sub; + struct tipc_subscription *sub_temp; + + /* Invalidate subscriber reference */ + + port_ref = subscriber->port_ref; + subscriber->port_ref = 0; + spin_unlock_bh(subscriber->lock); + + /* Sever connection to subscriber */ + + tipc_shutdown(port_ref); + tipc_deleteport(port_ref); + + /* Destroy any existing subscriptions for subscriber */ + + list_for_each_entry_safe(sub, sub_temp, &subscriber->subscription_list, + subscription_list) { + if (sub->timeout != TIPC_WAIT_FOREVER) { + k_cancel_timer(&sub->timer); + k_term_timer(&sub->timer); + } + subscr_del(sub); + } + + /* Remove subscriber from topology server's subscriber list */ + + spin_lock_bh(&topsrv.lock); + list_del(&subscriber->subscriber_list); + spin_unlock_bh(&topsrv.lock); + + /* Reclaim subscriber lock */ + + spin_lock_bh(subscriber->lock); + + /* Now destroy subscriber */ + + kfree(subscriber); +} + +/** + * subscr_cancel - handle subscription cancellation request + * + * Called with subscriber port locked. Routine must temporarily release lock + * to enable the subscription timeout routine to finish without deadlocking; + * the lock is then reclaimed to allow caller to release it upon return. + * + * Note that fields of 's' use subscriber's endianness! + */ + +static void subscr_cancel(struct tipc_subscr *s, + struct tipc_subscriber *subscriber) +{ + struct tipc_subscription *sub; + struct tipc_subscription *sub_temp; + int found = 0; + + /* Find first matching subscription, exit if not found */ + + list_for_each_entry_safe(sub, sub_temp, &subscriber->subscription_list, + subscription_list) { + if (!memcmp(s, &sub->evt.s, sizeof(struct tipc_subscr))) { + found = 1; + break; + } + } + if (!found) + return; + + /* Cancel subscription timer (if used), then delete subscription */ + + 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); + spin_lock_bh(subscriber->lock); + } + subscr_del(sub); +} + +/** + * subscr_subscribe - create subscription for subscriber + * + * Called with subscriber port locked. + */ + +static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s, + struct tipc_subscriber *subscriber) +{ + struct tipc_subscription *sub; + int swap; + + /* Determine subscriber's endianness */ + + swap = !(s->filter & (TIPC_SUB_PORTS | TIPC_SUB_SERVICE)); + + /* Detect & process a subscription cancellation request */ + + if (s->filter & htohl(TIPC_SUB_CANCEL, swap)) { + s->filter &= ~htohl(TIPC_SUB_CANCEL, swap); + subscr_cancel(s, subscriber); + return NULL; + } + + /* Refuse subscription if global limit exceeded */ + + if (atomic_read(&topsrv.subscription_count) >= tipc_max_subscriptions) { + warn("Subscription rejected, subscription limit reached (%u)\n", + tipc_max_subscriptions); + subscr_terminate(subscriber); + return NULL; + } + + /* Allocate subscription object */ + + sub = kmalloc(sizeof(*sub), GFP_ATOMIC); + if (!sub) { + warn("Subscription rejected, no memory\n"); + subscr_terminate(subscriber); + return NULL; + } + + /* Initialize subscription object */ + + 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->filter = htohl(s->filter, swap); + if ((!(sub->filter & TIPC_SUB_PORTS) == + !(sub->filter & TIPC_SUB_SERVICE)) || + (sub->seq.lower > sub->seq.upper)) { + warn("Subscription rejected, illegal request\n"); + kfree(sub); + subscr_terminate(subscriber); + return NULL; + } + INIT_LIST_HEAD(&sub->nameseq_list); + list_add(&sub->subscription_list, &subscriber->subscription_list); + sub->server_ref = subscriber->port_ref; + sub->swap = swap; + memcpy(&sub->evt.s, s, sizeof(struct tipc_subscr)); + atomic_inc(&topsrv.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); + } + + return sub; +} + +/** + * subscr_conn_shutdown_event - handle termination request from subscriber + * + * Called with subscriber's server port unlocked. + */ + +static void subscr_conn_shutdown_event(void *usr_handle, + u32 port_ref, + struct sk_buff **buf, + unsigned char const *data, + unsigned int size, + int reason) +{ + struct tipc_subscriber *subscriber = usr_handle; + spinlock_t *subscriber_lock; + + if (tipc_port_lock(port_ref) == NULL) + return; + + subscriber_lock = subscriber->lock; + subscr_terminate(subscriber); + spin_unlock_bh(subscriber_lock); +} + +/** + * subscr_conn_msg_event - handle new subscription request from subscriber + * + * Called with subscriber's server port unlocked. + */ + +static void subscr_conn_msg_event(void *usr_handle, + u32 port_ref, + struct sk_buff **buf, + const unchar *data, + u32 size) +{ + struct tipc_subscriber *subscriber = usr_handle; + spinlock_t *subscriber_lock; + struct tipc_subscription *sub; + + /* + * Lock subscriber's server port (& make a local copy of lock pointer, + * in case subscriber is deleted while processing subscription request) + */ + + if (tipc_port_lock(port_ref) == NULL) + return; + + subscriber_lock = subscriber->lock; + + if (size != sizeof(struct tipc_subscr)) { + subscr_terminate(subscriber); + spin_unlock_bh(subscriber_lock); + } else { + sub = subscr_subscribe((struct tipc_subscr *)data, subscriber); + spin_unlock_bh(subscriber_lock); + if (sub != NULL) { + + /* + * We must release the server port lock before adding a + * subscription to the name table since TIPC needs to be + * able to (re)acquire the port lock if an event message + * issued by the subscription process is rejected and + * returned. The subscription cannot be deleted while + * it is being added to the name table because: + * a) the single-threading of the native API port code + * ensures the subscription cannot be cancelled and + * the subscriber connection cannot be broken, and + * b) the name table lock ensures the subscription + * timeout code cannot delete the subscription, + * so the subscription object is still protected. + */ + + tipc_nametbl_subscribe(sub); + } + } +} + +/** + * subscr_named_msg_event - handle request to establish a new subscriber + */ + +static void subscr_named_msg_event(void *usr_handle, + u32 port_ref, + struct sk_buff **buf, + const unchar *data, + u32 size, + u32 importance, + struct tipc_portid const *orig, + struct tipc_name_seq const *dest) +{ + struct tipc_subscriber *subscriber; + u32 server_port_ref; + + /* Create subscriber object */ + + subscriber = kzalloc(sizeof(struct tipc_subscriber), GFP_ATOMIC); + if (subscriber == NULL) { + warn("Subscriber rejected, no memory\n"); + return; + } + INIT_LIST_HEAD(&subscriber->subscription_list); + INIT_LIST_HEAD(&subscriber->subscriber_list); + + /* Create server port & establish connection to subscriber */ + + tipc_createport(subscriber, + importance, + NULL, + NULL, + subscr_conn_shutdown_event, + NULL, + NULL, + subscr_conn_msg_event, + NULL, + &subscriber->port_ref); + if (subscriber->port_ref == 0) { + warn("Subscriber rejected, unable to create port\n"); + kfree(subscriber); + return; + } + tipc_connect2port(subscriber->port_ref, orig); + + /* Lock server port (& save lock address for future use) */ + + subscriber->lock = tipc_port_lock(subscriber->port_ref)->lock; + + /* Add subscriber to topology server's subscriber list */ + + spin_lock_bh(&topsrv.lock); + list_add(&subscriber->subscriber_list, &topsrv.subscriber_list); + spin_unlock_bh(&topsrv.lock); + + /* Unlock server port */ + + server_port_ref = subscriber->port_ref; + spin_unlock_bh(subscriber->lock); + + /* Send an ACK- to complete connection handshaking */ + + tipc_send(server_port_ref, 0, NULL, 0); + + /* Handle optional subscription request */ + + if (size != 0) { + subscr_conn_msg_event(subscriber, server_port_ref, + buf, data, size); + } +} + +int tipc_subscr_start(void) +{ + struct tipc_name_seq seq = {TIPC_TOP_SRV, TIPC_TOP_SRV, TIPC_TOP_SRV}; + int res; + + memset(&topsrv, 0, sizeof(topsrv)); + spin_lock_init(&topsrv.lock); + INIT_LIST_HEAD(&topsrv.subscriber_list); + + res = tipc_createport(NULL, + TIPC_CRITICAL_IMPORTANCE, + NULL, + NULL, + NULL, + NULL, + subscr_named_msg_event, + NULL, + NULL, + &topsrv.setup_port); + if (res) + goto failed; + + res = tipc_publish(topsrv.setup_port, TIPC_NODE_SCOPE, &seq); + if (res) { + tipc_deleteport(topsrv.setup_port); + topsrv.setup_port = 0; + goto failed; + } + + return 0; + +failed: + err("Failed to create subscription service\n"); + return res; +} + +void tipc_subscr_stop(void) +{ + struct tipc_subscriber *subscriber; + struct tipc_subscriber *subscriber_temp; + spinlock_t *subscriber_lock; + + if (topsrv.setup_port) { + tipc_deleteport(topsrv.setup_port); + topsrv.setup_port = 0; + + list_for_each_entry_safe(subscriber, subscriber_temp, + &topsrv.subscriber_list, + subscriber_list) { + subscriber_lock = subscriber->lock; + spin_lock_bh(subscriber_lock); + subscr_terminate(subscriber); + spin_unlock_bh(subscriber_lock); + } + } +} diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h new file mode 100644 index 00000000..ef6529c8 --- /dev/null +++ b/net/tipc/subscr.h @@ -0,0 +1,84 @@ +/* + * net/tipc/subscr.h: Include file for TIPC network topology service + * + * Copyright (c) 2003-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. + */ + +#ifndef _TIPC_SUBSCR_H +#define _TIPC_SUBSCR_H + +struct tipc_subscription; + +/** + * struct tipc_subscription - TIPC network topology subscription object + * @seq: name sequence associated with subscription + * @timeout: duration of subscription (in ms) + * @filter: event filtering to be done for subscription + * @timer: timer governing subscription duration (optional) + * @nameseq_list: adjacent subscriptions in name sequence's subscription list + * @subscription_list: adjacent subscriptions in subscriber's subscription list + * @server_ref: object reference of server port associated with subscription + * @swap: indicates if subscriber uses opposite endianness in its messages + * @evt: template for events generated by subscription + */ + +struct tipc_subscription { + struct tipc_name_seq seq; + u32 timeout; + u32 filter; + struct timer_list timer; + struct list_head nameseq_list; + struct list_head subscription_list; + u32 server_ref; + int swap; + struct tipc_event evt; +}; + +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_report); + +int tipc_subscr_start(void); + +void tipc_subscr_stop(void); + + +#endif |