diff options
author | Ying Xue <ying.xue@windriver.com> | 2015-03-26 06:10:24 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 15:40:28 -0400 |
commit | 8a0f6ebe8494c5c6ccfe12264385b64c280e3241 (patch) | |
tree | 2f106a95ffda73b8e8f1596cc95152398cd5379b /net/tipc/node.c | |
parent | b952b2befb6f6b009e91f087285b9a0a6beb1cc8 (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.c | 85 |
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 | ||
43 | static void node_lost_contact(struct tipc_node *n_ptr); | 43 | static void node_lost_contact(struct tipc_node *n_ptr); |
44 | static void node_established_contact(struct tipc_node *n_ptr); | 44 | static void node_established_contact(struct tipc_node *n_ptr); |
45 | static void tipc_node_delete(struct tipc_node *node); | ||
45 | 46 | ||
46 | struct tipc_sock_conn { | 47 | struct 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 | ||
71 | static 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 | |||
78 | void tipc_node_put(struct tipc_node *node) | ||
79 | { | ||
80 | kref_put(&node->kref, tipc_node_kref_release); | ||
81 | } | ||
82 | |||
83 | static 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); | ||
123 | exit: | 144 | exit: |
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 | ||
128 | static void tipc_node_delete(struct tipc_net *tn, struct tipc_node *n_ptr) | 149 | static 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 | ||
135 | void tipc_node_stop(struct net *net) | 156 | void 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; | 193 | exit: |
194 | tipc_node_put(node); | ||
195 | return err; | ||
170 | } | 196 | } |
171 | 197 | ||
172 | void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) | 198 | 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) | |||
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 | } |
462 | exit: | ||
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 | ||
435 | void tipc_node_unlock(struct tipc_node *node) | 468 | 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) | |||
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) { |