aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc/node.c
diff options
context:
space:
mode:
authorYing Xue <ying.xue@windriver.com>2015-03-26 06:10:24 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-29 15:40:28 -0400
commit8a0f6ebe8494c5c6ccfe12264385b64c280e3241 (patch)
tree2f106a95ffda73b8e8f1596cc95152398cd5379b /net/tipc/node.c
parentb952b2befb6f6b009e91f087285b9a0a6beb1cc8 (diff)
tipc: involve reference counter for node structure
TIPC node hash node table is protected with rcu lock on read side. tipc_node_find() is used to look for a node object with node address through iterating the hash node table. As the entire process of what tipc_node_find() traverses the table is guarded with rcu read lock, it's safe for us. However, when callers use the node object returned by tipc_node_find(), there is no rcu read lock applied. Therefore, this is absolutely unsafe for callers of tipc_node_find(). Now we introduce a reference counter for node structure. Before tipc_node_find() returns node object to its caller, it first increases the reference counter. Accordingly, after its caller used it up, it decreases the counter again. This can prevent a node being used by one thread from being freed by another thread. Reviewed-by: Erik Hugne <erik.hugne@ericsson.com> Reviewed-by: Jon Maloy <jon.maloy@ericson.com> Signed-off-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/node.c')
-rw-r--r--net/tipc/node.c85
1 files changed, 61 insertions, 24 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 5cc43d34ad0a..3e4f04897c03 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -42,6 +42,7 @@
42 42
43static void node_lost_contact(struct tipc_node *n_ptr); 43static void node_lost_contact(struct tipc_node *n_ptr);
44static void node_established_contact(struct tipc_node *n_ptr); 44static void node_established_contact(struct tipc_node *n_ptr);
45static void tipc_node_delete(struct tipc_node *node);
45 46
46struct tipc_sock_conn { 47struct tipc_sock_conn {
47 u32 port; 48 u32 port;
@@ -67,6 +68,23 @@ static unsigned int tipc_hashfn(u32 addr)
67 return addr & (NODE_HTABLE_SIZE - 1); 68 return addr & (NODE_HTABLE_SIZE - 1);
68} 69}
69 70
71static void tipc_node_kref_release(struct kref *kref)
72{
73 struct tipc_node *node = container_of(kref, struct tipc_node, kref);
74
75 tipc_node_delete(node);
76}
77
78void tipc_node_put(struct tipc_node *node)
79{
80 kref_put(&node->kref, tipc_node_kref_release);
81}
82
83static void tipc_node_get(struct tipc_node *node)
84{
85 kref_get(&node->kref);
86}
87
70/* 88/*
71 * tipc_node_find - locate specified node object, if it exists 89 * tipc_node_find - locate specified node object, if it exists
72 */ 90 */
@@ -82,6 +100,7 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr)
82 hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], 100 hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)],
83 hash) { 101 hash) {
84 if (node->addr == addr) { 102 if (node->addr == addr) {
103 tipc_node_get(node);
85 rcu_read_unlock(); 104 rcu_read_unlock();
86 return node; 105 return node;
87 } 106 }
@@ -106,6 +125,7 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
106 } 125 }
107 n_ptr->addr = addr; 126 n_ptr->addr = addr;
108 n_ptr->net = net; 127 n_ptr->net = net;
128 kref_init(&n_ptr->kref);
109 spin_lock_init(&n_ptr->lock); 129 spin_lock_init(&n_ptr->lock);
110 INIT_HLIST_NODE(&n_ptr->hash); 130 INIT_HLIST_NODE(&n_ptr->hash);
111 INIT_LIST_HEAD(&n_ptr->list); 131 INIT_LIST_HEAD(&n_ptr->list);
@@ -120,16 +140,17 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
120 list_add_tail_rcu(&n_ptr->list, &temp_node->list); 140 list_add_tail_rcu(&n_ptr->list, &temp_node->list);
121 n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN; 141 n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
122 n_ptr->signature = INVALID_NODE_SIG; 142 n_ptr->signature = INVALID_NODE_SIG;
143 tipc_node_get(n_ptr);
123exit: 144exit:
124 spin_unlock_bh(&tn->node_list_lock); 145 spin_unlock_bh(&tn->node_list_lock);
125 return n_ptr; 146 return n_ptr;
126} 147}
127 148
128static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr) 149static void tipc_node_delete(struct tipc_node *node)
129{ 150{
130 list_del_rcu(&n_ptr->list); 151 list_del_rcu(&node->list);
131 hlist_del_rcu(&n_ptr->hash); 152 hlist_del_rcu(&node->hash);
132 kfree_rcu(n_ptr, rcu); 153 kfree_rcu(node, rcu);
133} 154}
134 155
135void tipc_node_stop(struct net *net) 156void tipc_node_stop(struct net *net)
@@ -139,7 +160,7 @@ void tipc_node_stop(struct net *net)
139 160
140 spin_lock_bh(&tn->node_list_lock); 161 spin_lock_bh(&tn->node_list_lock);
141 list_for_each_entry_safe(node, t_node, &tn->node_list, list) 162 list_for_each_entry_safe(node, t_node, &tn->node_list, list)
142 tipc_node_delete(tn, node); 163 tipc_node_put(node);
143 spin_unlock_bh(&tn->node_list_lock); 164 spin_unlock_bh(&tn->node_list_lock);
144} 165}
145 166
@@ -147,6 +168,7 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
147{ 168{
148 struct tipc_node *node; 169 struct tipc_node *node;
149 struct tipc_sock_conn *conn; 170 struct tipc_sock_conn *conn;
171 int err = 0;
150 172
151 if (in_own_node(net, dnode)) 173 if (in_own_node(net, dnode))
152 return 0; 174 return 0;
@@ -157,8 +179,10 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
157 return -EHOSTUNREACH; 179 return -EHOSTUNREACH;
158 } 180 }
159 conn = kmalloc(sizeof(*conn), GFP_ATOMIC); 181 conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
160 if (!conn) 182 if (!conn) {
161 return -EHOSTUNREACH; 183 err = -EHOSTUNREACH;
184 goto exit;
185 }
162 conn->peer_node = dnode; 186 conn->peer_node = dnode;
163 conn->port = port; 187 conn->port = port;
164 conn->peer_port = peer_port; 188 conn->peer_port = peer_port;
@@ -166,7 +190,9 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
166 tipc_node_lock(node); 190 tipc_node_lock(node);
167 list_add_tail(&conn->list, &node->conn_sks); 191 list_add_tail(&conn->list, &node->conn_sks);
168 tipc_node_unlock(node); 192 tipc_node_unlock(node);
169 return 0; 193exit:
194 tipc_node_put(node);
195 return err;
170} 196}
171 197
172void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) 198void 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)
189 kfree(conn); 215 kfree(conn);
190 } 216 }
191 tipc_node_unlock(node); 217 tipc_node_unlock(node);
218 tipc_node_put(node);
192} 219}
193 220
194/** 221/**
@@ -417,19 +444,25 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
417 char *linkname, size_t len) 444 char *linkname, size_t len)
418{ 445{
419 struct tipc_link *link; 446 struct tipc_link *link;
447 int err = -EINVAL;
420 struct tipc_node *node = tipc_node_find(net, addr); 448 struct tipc_node *node = tipc_node_find(net, addr);
421 449
422 if ((bearer_id >= MAX_BEARERS) || !node) 450 if (!node)
423 return -EINVAL; 451 return err;
452
453 if (bearer_id >= MAX_BEARERS)
454 goto exit;
455
424 tipc_node_lock(node); 456 tipc_node_lock(node);
425 link = node->links[bearer_id]; 457 link = node->links[bearer_id];
426 if (link) { 458 if (link) {
427 strncpy(linkname, link->name, len); 459 strncpy(linkname, link->name, len);
428 tipc_node_unlock(node); 460 err = 0;
429 return 0;
430 } 461 }
462exit:
431 tipc_node_unlock(node); 463 tipc_node_unlock(node);
432 return -EINVAL; 464 tipc_node_put(node);
465 return err;
433} 466}
434 467
435void tipc_node_unlock(struct tipc_node *node) 468void tipc_node_unlock(struct tipc_node *node)
@@ -545,17 +578,21 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
545 msg.seq = cb->nlh->nlmsg_seq; 578 msg.seq = cb->nlh->nlmsg_seq;
546 579
547 rcu_read_lock(); 580 rcu_read_lock();
548 581 if (last_addr) {
549 if (last_addr && !tipc_node_find(net, last_addr)) { 582 node = tipc_node_find(net, last_addr);
550 rcu_read_unlock(); 583 if (!node) {
551 /* We never set seq or call nl_dump_check_consistent() this 584 rcu_read_unlock();
552 * means that setting prev_seq here will cause the consistence 585 /* We never set seq or call nl_dump_check_consistent()
553 * check to fail in the netlink callback handler. Resulting in 586 * this means that setting prev_seq here will cause the
554 * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if 587 * consistence check to fail in the netlink callback
555 * the node state changed while we released the lock. 588 * handler. Resulting in the NLMSG_DONE message having
556 */ 589 * the NLM_F_DUMP_INTR flag set if the node state
557 cb->prev_seq = 1; 590 * changed while we released the lock.
558 return -EPIPE; 591 */
592 cb->prev_seq = 1;
593 return -EPIPE;
594 }
595 tipc_node_put(node);
559 } 596 }
560 597
561 list_for_each_entry_rcu(node, &tn->node_list, list) { 598 list_for_each_entry_rcu(node, &tn->node_list, list) {