|
@@ -42,6 +42,7 @@
|
|
|
|
|
|
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 +68,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 +100,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,6 +125,7 @@ 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);
|
|
@@ -120,16 +140,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 +160,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 +168,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 +179,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 +190,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 +215,7 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
|
|
|
kfree(conn);
|
|
|
}
|
|
|
tipc_node_unlock(node);
|
|
|
+ tipc_node_put(node);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -417,19 +444,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)
|
|
@@ -545,17 +578,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) {
|