tipc: use message to abort connections when losing contact to node
authorJon Paul Maloy <jon.maloy@ericsson.com>
Fri, 22 Aug 2014 22:09:08 +0000 (18:09 -0400)
committerDavid S. Miller <davem@davemloft.net>
Sat, 23 Aug 2014 18:18:33 +0000 (11:18 -0700)
In the current implementation, each 'struct tipc_node' instance keeps
a linked list of those ports/sockets that are connected to the node
represented by that struct. The purpose of this is to let the node
object know which sockets to alert when it loses contact with its peer
node, i.e., which sockets need to have their connections aborted.

This entails an unwanted direct reference from the node structure
back to the port/socket structure, and a need to grab port_lock
when we have to make an upcall to the port. We want to get rid of
this unecessary BH entry point into the socket, and also eliminate
its use of port_lock.

In this commit, we instead let the node struct keep list of "connected
socket" structs, which each represents a connected socket, but is
allocated independently by the node at the moment of connection. If
the node loses contact with its peer node, the list is traversed, and
a "connection abort" message is created for each entry in the list. The
message is sent to it respective connected socket using the ordinary
data path, and the receiving socket aborts its connections upon reception
of the message.

This enables us to get rid of the direct reference from 'struct node' to
´struct port', and another unwanted BH access point to the latter.

Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Reviewed-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/tipc/node.c
net/tipc/node.h
net/tipc/port.c

index 6ea2c15cfc883d2d92d2c2484837be5682f909a5..17e6378c4dfe479790d085916cb5bfe40b445ae9 100644 (file)
@@ -51,6 +51,13 @@ static u32 tipc_num_nodes;
 static u32 tipc_num_links;
 static DEFINE_SPINLOCK(node_list_lock);
 
+struct tipc_sock_conn {
+       u32 port;
+       u32 peer_port;
+       u32 peer_node;
+       struct list_head list;
+};
+
 /*
  * 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
@@ -101,6 +108,7 @@ struct tipc_node *tipc_node_create(u32 addr)
        INIT_HLIST_NODE(&n_ptr->hash);
        INIT_LIST_HEAD(&n_ptr->list);
        INIT_LIST_HEAD(&n_ptr->nsub);
+       INIT_LIST_HEAD(&n_ptr->conn_sks);
        __skb_queue_head_init(&n_ptr->waiting_sks);
 
        hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]);
@@ -138,6 +146,71 @@ void tipc_node_stop(void)
        spin_unlock_bh(&node_list_lock);
 }
 
+int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port)
+{
+       struct tipc_node *node;
+       struct tipc_sock_conn *conn;
+
+       if (in_own_node(dnode))
+               return 0;
+
+       node = tipc_node_find(dnode);
+       if (!node) {
+               pr_warn("Connecting sock to node 0x%x failed\n", dnode);
+               return -EHOSTUNREACH;
+       }
+       conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
+       if (!conn)
+               return -EHOSTUNREACH;
+       conn->peer_node = dnode;
+       conn->port = port;
+       conn->peer_port = peer_port;
+
+       tipc_node_lock(node);
+       list_add_tail(&conn->list, &node->conn_sks);
+       tipc_node_unlock(node);
+       return 0;
+}
+
+void tipc_node_remove_conn(u32 dnode, u32 port)
+{
+       struct tipc_node *node;
+       struct tipc_sock_conn *conn, *safe;
+
+       if (in_own_node(dnode))
+               return;
+
+       node = tipc_node_find(dnode);
+       if (!node)
+               return;
+
+       tipc_node_lock(node);
+       list_for_each_entry_safe(conn, safe, &node->conn_sks, list) {
+               if (port != conn->port)
+                       continue;
+               list_del(&conn->list);
+               kfree(conn);
+       }
+       tipc_node_unlock(node);
+}
+
+void tipc_node_abort_sock_conns(struct list_head *conns)
+{
+       struct tipc_sock_conn *conn, *safe;
+       struct sk_buff *buf;
+
+       list_for_each_entry_safe(conn, safe, conns, list) {
+               buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
+                                     SHORT_H_SIZE, 0, tipc_own_addr,
+                                     conn->peer_node, conn->port,
+                                     conn->peer_port, TIPC_ERR_NO_NODE);
+               if (likely(buf))
+                       tipc_sk_rcv(buf);
+               list_del(&conn->list);
+               kfree(conn);
+       }
+}
+
 /**
  * tipc_node_link_up - handle addition of link
  *
@@ -476,6 +549,7 @@ int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len)
 void tipc_node_unlock(struct tipc_node *node)
 {
        LIST_HEAD(nsub_list);
+       LIST_HEAD(conn_sks);
        struct sk_buff_head waiting_sks;
        u32 addr = 0;
 
@@ -491,6 +565,7 @@ void tipc_node_unlock(struct tipc_node *node)
        }
        if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) {
                list_replace_init(&node->nsub, &nsub_list);
+               list_replace_init(&node->conn_sks, &conn_sks);
                node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN;
        }
        if (node->action_flags & TIPC_NOTIFY_NODE_UP) {
@@ -502,6 +577,9 @@ void tipc_node_unlock(struct tipc_node *node)
        while (!skb_queue_empty(&waiting_sks))
                tipc_sk_rcv(__skb_dequeue(&waiting_sks));
 
+       if (!list_empty(&conn_sks))
+               tipc_node_abort_sock_conns(&conn_sks);
+
        if (!list_empty(&nsub_list))
                tipc_nodesub_notify(&nsub_list);
 
index 2ebf9e8b50fd0f3b119f4619645ecf8a6db302c6..522d6f3157b32cb9aa2d28086700728216e17e81 100644 (file)
@@ -117,6 +117,7 @@ struct tipc_node {
        u32 signature;
        struct list_head nsub;
        struct sk_buff_head waiting_sks;
+       struct list_head conn_sks;
        struct rcu_head rcu;
 };
 
@@ -135,6 +136,8 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space);
 int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len);
 void tipc_node_unlock(struct tipc_node *node);
+int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port);
+void tipc_node_remove_conn(u32 dnode, u32 port);
 
 static inline void tipc_node_lock(struct tipc_node *node)
 {
index b58a777a4399702b7806d013448cb74251172ea0..edbd83d223c5e846e352d7fbdb9c2690bac985af 100644 (file)
@@ -48,7 +48,6 @@
 DEFINE_SPINLOCK(tipc_port_list_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);
@@ -126,10 +125,10 @@ void tipc_port_destroy(struct tipc_port *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);
                msg = buf_msg(buf);
                peer = msg_destnode(msg);
                tipc_link_xmit(buf, peer, msg_link_selector(msg));
+               tipc_node_remove_conn(peer, p_ptr->ref);
        }
        spin_lock_bh(&tipc_port_list_lock);
        list_del(&p_ptr->port_list);
@@ -188,22 +187,6 @@ static void port_timeout(unsigned long ref)
        tipc_link_xmit(buf, msg_destnode(msg),  msg_link_selector(msg));
 }
 
-
-static void port_handle_node_down(unsigned long ref)
-{
-       struct tipc_port *p_ptr = tipc_port_lock(ref);
-       struct sk_buff *buf = NULL;
-       struct tipc_msg *msg = NULL;
-
-       if (!p_ptr)
-               return;
-       buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_NODE);
-       tipc_port_unlock(p_ptr);
-       msg = buf_msg(buf);
-       tipc_link_xmit(buf, msg_destnode(msg),  msg_link_selector(msg));
-}
-
-
 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);
@@ -217,7 +200,6 @@ static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 er
        return buf;
 }
 
-
 static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 err)
 {
        struct sk_buff *buf;
@@ -447,11 +429,8 @@ int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr,
        p_ptr->probing_state = TIPC_CONN_OK;
        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;
+       res = tipc_node_add_conn(tipc_port_peernode(p_ptr), p_ptr->ref,
+                                tipc_port_peerport(p_ptr));
 exit:
        p_ptr->max_pkt = tipc_node_get_mtu(peer->node, ref);
        return res;
@@ -467,7 +446,7 @@ int __tipc_port_disconnect(struct tipc_port *tp_ptr)
        if (tp_ptr->connected) {
                tp_ptr->connected = 0;
                /* let timer expire on it's own to avoid deadlock! */
-               tipc_nodesub_unsubscribe(&tp_ptr->subscription);
+               tipc_node_remove_conn(tipc_port_peernode(tp_ptr), tp_ptr->ref);
                return 0;
        }