Merge tag 'armsoc-late' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[deliverable/linux.git] / net / tipc / node.c
index 86152de8248da7164cde4b932ac7be1b6b6b2245..0b1d61a5f85334b3553780e8c0dd64c3f0549aa3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/node.c: TIPC node management routines
  *
- * Copyright (c) 2000-2006, 2012-2014, Ericsson AB
+ * Copyright (c) 2000-2006, 2012-2015, Ericsson AB
  * Copyright (c) 2005-2006, 2010-2014, Wind River Systems
  * All rights reserved.
  *
 #include "node.h"
 #include "name_distr.h"
 #include "socket.h"
+#include "bcast.h"
 
 static void node_lost_contact(struct tipc_node *n_ptr);
 static void node_established_contact(struct tipc_node *n_ptr);
+static void tipc_node_delete(struct tipc_node *node);
 
 struct tipc_sock_conn {
        u32 port;
@@ -67,6 +69,23 @@ static unsigned int tipc_hashfn(u32 addr)
        return addr & (NODE_HTABLE_SIZE - 1);
 }
 
+static void tipc_node_kref_release(struct kref *kref)
+{
+       struct tipc_node *node = container_of(kref, struct tipc_node, kref);
+
+       tipc_node_delete(node);
+}
+
+void tipc_node_put(struct tipc_node *node)
+{
+       kref_put(&node->kref, tipc_node_kref_release);
+}
+
+static void tipc_node_get(struct tipc_node *node)
+{
+       kref_get(&node->kref);
+}
+
 /*
  * tipc_node_find - locate specified node object, if it exists
  */
@@ -82,6 +101,7 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr)
        hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)],
                                 hash) {
                if (node->addr == addr) {
+                       tipc_node_get(node);
                        rcu_read_unlock();
                        return node;
                }
@@ -106,12 +126,13 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
        }
        n_ptr->addr = addr;
        n_ptr->net = net;
+       kref_init(&n_ptr->kref);
        spin_lock_init(&n_ptr->lock);
        INIT_HLIST_NODE(&n_ptr->hash);
        INIT_LIST_HEAD(&n_ptr->list);
        INIT_LIST_HEAD(&n_ptr->publ_list);
        INIT_LIST_HEAD(&n_ptr->conn_sks);
-       __skb_queue_head_init(&n_ptr->bclink.deferred_queue);
+       __skb_queue_head_init(&n_ptr->bclink.deferdq);
        hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
        list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
                if (n_ptr->addr < temp_node->addr)
@@ -120,16 +141,17 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
        list_add_tail_rcu(&n_ptr->list, &temp_node->list);
        n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
        n_ptr->signature = INVALID_NODE_SIG;
+       tipc_node_get(n_ptr);
 exit:
        spin_unlock_bh(&tn->node_list_lock);
        return n_ptr;
 }
 
-static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr)
+static void tipc_node_delete(struct tipc_node *node)
 {
-       list_del_rcu(&n_ptr->list);
-       hlist_del_rcu(&n_ptr->hash);
-       kfree_rcu(n_ptr, rcu);
+       list_del_rcu(&node->list);
+       hlist_del_rcu(&node->hash);
+       kfree_rcu(node, rcu);
 }
 
 void tipc_node_stop(struct net *net)
@@ -139,7 +161,7 @@ void tipc_node_stop(struct net *net)
 
        spin_lock_bh(&tn->node_list_lock);
        list_for_each_entry_safe(node, t_node, &tn->node_list, list)
-               tipc_node_delete(tn, node);
+               tipc_node_put(node);
        spin_unlock_bh(&tn->node_list_lock);
 }
 
@@ -147,6 +169,7 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
 {
        struct tipc_node *node;
        struct tipc_sock_conn *conn;
+       int err = 0;
 
        if (in_own_node(net, dnode))
                return 0;
@@ -157,8 +180,10 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
                return -EHOSTUNREACH;
        }
        conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
-       if (!conn)
-               return -EHOSTUNREACH;
+       if (!conn) {
+               err = -EHOSTUNREACH;
+               goto exit;
+       }
        conn->peer_node = dnode;
        conn->port = port;
        conn->peer_port = peer_port;
@@ -166,7 +191,9 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
        tipc_node_lock(node);
        list_add_tail(&conn->list, &node->conn_sks);
        tipc_node_unlock(node);
-       return 0;
+exit:
+       tipc_node_put(node);
+       return err;
 }
 
 void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
@@ -189,6 +216,7 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
                kfree(conn);
        }
        tipc_node_unlock(node);
+       tipc_node_put(node);
 }
 
 /**
@@ -227,8 +255,8 @@ void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
        active[0] = active[1] = l_ptr;
 exit:
        /* Leave room for changeover header when returning 'mtu' to users: */
-       n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
-       n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
+       n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
+       n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
 }
 
 /**
@@ -292,11 +320,10 @@ void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 
        /* Leave room for changeover header when returning 'mtu' to users: */
        if (active[0]) {
-               n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
-               n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
+               n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
+               n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
                return;
        }
-
        /* Loopback link went down? No fragmentation needed from now on. */
        if (n_ptr->addr == tn->own_addr) {
                n_ptr->act_mtus[0] = MAX_MSG_SIZE;
@@ -354,7 +381,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)
 
        /* Flush broadcast link info associated with lost node */
        if (n_ptr->bclink.recv_permitted) {
-               __skb_queue_purge(&n_ptr->bclink.deferred_queue);
+               __skb_queue_purge(&n_ptr->bclink.deferdq);
 
                if (n_ptr->bclink.reasm_buf) {
                        kfree_skb(n_ptr->bclink.reasm_buf);
@@ -367,18 +394,17 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                n_ptr->bclink.recv_permitted = false;
        }
 
-       /* Abort link changeover */
+       /* Abort any ongoing link failover */
        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;
+               l_ptr->flags &= ~LINK_FAILINGOVER;
+               l_ptr->failover_checkpt = 0;
+               l_ptr->failover_pkts = 0;
+               kfree_skb(l_ptr->failover_skb);
+               l_ptr->failover_skb = NULL;
                tipc_link_reset_fragments(l_ptr);
-
-               /* Link marked for deletion after failover? => do it now */
-               if (l_ptr->flags & LINK_STOPPED)
-                       tipc_link_delete(l_ptr);
        }
 
        n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;
@@ -417,19 +443,25 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
                           char *linkname, size_t len)
 {
        struct tipc_link *link;
+       int err = -EINVAL;
        struct tipc_node *node = tipc_node_find(net, addr);
 
-       if ((bearer_id >= MAX_BEARERS) || !node)
-               return -EINVAL;
+       if (!node)
+               return err;
+
+       if (bearer_id >= MAX_BEARERS)
+               goto exit;
+
        tipc_node_lock(node);
        link = node->links[bearer_id];
        if (link) {
                strncpy(linkname, link->name, len);
-               tipc_node_unlock(node);
-               return 0;
+               err = 0;
        }
+exit:
        tipc_node_unlock(node);
-       return -EINVAL;
+       tipc_node_put(node);
+       return err;
 }
 
 void tipc_node_unlock(struct tipc_node *node)
@@ -459,7 +491,7 @@ void tipc_node_unlock(struct tipc_node *node)
                                TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
                                TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP |
                                TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT |
-                               TIPC_NAMED_MSG_EVT);
+                               TIPC_NAMED_MSG_EVT | TIPC_BCAST_RESET);
 
        spin_unlock_bh(&node->lock);
 
@@ -488,6 +520,9 @@ void tipc_node_unlock(struct tipc_node *node)
 
        if (flags & TIPC_BCAST_MSG_EVT)
                tipc_bclink_input(net);
+
+       if (flags & TIPC_BCAST_RESET)
+               tipc_link_reset_all(node);
 }
 
 /* Caller should hold node lock for the passed node */
@@ -542,17 +577,21 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
        msg.seq = cb->nlh->nlmsg_seq;
 
        rcu_read_lock();
-
-       if (last_addr && !tipc_node_find(net, last_addr)) {
-               rcu_read_unlock();
-               /* We never set seq or call nl_dump_check_consistent() this
-                * means that setting prev_seq here will cause the consistence
-                * check to fail in the netlink callback handler. Resulting in
-                * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if
-                * the node state changed while we released the lock.
-                */
-               cb->prev_seq = 1;
-               return -EPIPE;
+       if (last_addr) {
+               node = tipc_node_find(net, last_addr);
+               if (!node) {
+                       rcu_read_unlock();
+                       /* We never set seq or call nl_dump_check_consistent()
+                        * this means that setting prev_seq here will cause the
+                        * consistence check to fail in the netlink callback
+                        * handler. Resulting in the NLMSG_DONE message having
+                        * the NLM_F_DUMP_INTR flag set if the node state
+                        * changed while we released the lock.
+                        */
+                       cb->prev_seq = 1;
+                       return -EPIPE;
+               }
+               tipc_node_put(node);
        }
 
        list_for_each_entry_rcu(node, &tn->node_list, list) {
This page took 0.032499 seconds and 5 git commands to generate.