diff options
author | David S. Miller <davem@davemloft.net> | 2015-03-29 15:40:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 15:40:37 -0400 |
commit | ae7633c841b3d17ab54e372ad9cc0f37a9a7c72d (patch) | |
tree | 2f106a95ffda73b8e8f1596cc95152398cd5379b /net | |
parent | faadb05f4b7f11b3063a20d7fc5afcbf1124dbeb (diff) | |
parent | 8a0f6ebe8494c5c6ccfe12264385b64c280e3241 (diff) |
Merge branch 'tipc-next'
Ying Xue says:
====================
tipc: fix two corner issues
The patch set aims at resolving the following two critical issues:
Patch #1: Resolve a deadlock which happens while all links are reset
Patch #2: Correct a mistake usage of RCU lock which is used to protect
node list
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/tipc/bcast.c | 28 | ||||
-rw-r--r-- | net/tipc/bcast.h | 4 | ||||
-rw-r--r-- | net/tipc/discover.c | 1 | ||||
-rw-r--r-- | net/tipc/link.c | 12 | ||||
-rw-r--r-- | net/tipc/name_distr.c | 2 | ||||
-rw-r--r-- | net/tipc/node.c | 90 | ||||
-rw-r--r-- | net/tipc/node.h | 12 |
7 files changed, 87 insertions, 62 deletions
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 79355531c3e2..ae558dd7f8ee 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c | |||
@@ -62,21 +62,8 @@ static void tipc_bclink_lock(struct net *net) | |||
62 | static void tipc_bclink_unlock(struct net *net) | 62 | static void tipc_bclink_unlock(struct net *net) |
63 | { | 63 | { |
64 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 64 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
65 | struct tipc_node *node = NULL; | ||
66 | 65 | ||
67 | if (likely(!tn->bclink->flags)) { | ||
68 | spin_unlock_bh(&tn->bclink->lock); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | if (tn->bclink->flags & TIPC_BCLINK_RESET) { | ||
73 | tn->bclink->flags &= ~TIPC_BCLINK_RESET; | ||
74 | node = tipc_bclink_retransmit_to(net); | ||
75 | } | ||
76 | spin_unlock_bh(&tn->bclink->lock); | 66 | spin_unlock_bh(&tn->bclink->lock); |
77 | |||
78 | if (node) | ||
79 | tipc_link_reset_all(node); | ||
80 | } | 67 | } |
81 | 68 | ||
82 | void tipc_bclink_input(struct net *net) | 69 | void tipc_bclink_input(struct net *net) |
@@ -91,13 +78,6 @@ uint tipc_bclink_get_mtu(void) | |||
91 | return MAX_PKT_DEFAULT_MCAST; | 78 | return MAX_PKT_DEFAULT_MCAST; |
92 | } | 79 | } |
93 | 80 | ||
94 | void tipc_bclink_set_flags(struct net *net, unsigned int flags) | ||
95 | { | ||
96 | struct tipc_net *tn = net_generic(net, tipc_net_id); | ||
97 | |||
98 | tn->bclink->flags |= flags; | ||
99 | } | ||
100 | |||
101 | static u32 bcbuf_acks(struct sk_buff *buf) | 81 | static u32 bcbuf_acks(struct sk_buff *buf) |
102 | { | 82 | { |
103 | return (u32)(unsigned long)TIPC_SKB_CB(buf)->handle; | 83 | return (u32)(unsigned long)TIPC_SKB_CB(buf)->handle; |
@@ -156,7 +136,6 @@ static void bclink_update_last_sent(struct tipc_node *node, u32 seqno) | |||
156 | seqno : node->bclink.last_sent; | 136 | seqno : node->bclink.last_sent; |
157 | } | 137 | } |
158 | 138 | ||
159 | |||
160 | /** | 139 | /** |
161 | * tipc_bclink_retransmit_to - get most recent node to request retransmission | 140 | * tipc_bclink_retransmit_to - get most recent node to request retransmission |
162 | * | 141 | * |
@@ -350,13 +329,12 @@ static void bclink_peek_nack(struct net *net, struct tipc_msg *msg) | |||
350 | return; | 329 | return; |
351 | 330 | ||
352 | tipc_node_lock(n_ptr); | 331 | tipc_node_lock(n_ptr); |
353 | |||
354 | if (n_ptr->bclink.recv_permitted && | 332 | if (n_ptr->bclink.recv_permitted && |
355 | (n_ptr->bclink.last_in != n_ptr->bclink.last_sent) && | 333 | (n_ptr->bclink.last_in != n_ptr->bclink.last_sent) && |
356 | (n_ptr->bclink.last_in == msg_bcgap_after(msg))) | 334 | (n_ptr->bclink.last_in == msg_bcgap_after(msg))) |
357 | n_ptr->bclink.oos_state = 2; | 335 | n_ptr->bclink.oos_state = 2; |
358 | |||
359 | tipc_node_unlock(n_ptr); | 336 | tipc_node_unlock(n_ptr); |
337 | tipc_node_put(n_ptr); | ||
360 | } | 338 | } |
361 | 339 | ||
362 | /* tipc_bclink_xmit - deliver buffer chain to all nodes in cluster | 340 | /* tipc_bclink_xmit - deliver buffer chain to all nodes in cluster |
@@ -476,17 +454,18 @@ void tipc_bclink_rcv(struct net *net, struct sk_buff *buf) | |||
476 | goto unlock; | 454 | goto unlock; |
477 | if (msg_destnode(msg) == tn->own_addr) { | 455 | if (msg_destnode(msg) == tn->own_addr) { |
478 | tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); | 456 | tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); |
479 | tipc_node_unlock(node); | ||
480 | tipc_bclink_lock(net); | 457 | tipc_bclink_lock(net); |
481 | bcl->stats.recv_nacks++; | 458 | bcl->stats.recv_nacks++; |
482 | tn->bclink->retransmit_to = node; | 459 | tn->bclink->retransmit_to = node; |
483 | bclink_retransmit_pkt(tn, msg_bcgap_after(msg), | 460 | bclink_retransmit_pkt(tn, msg_bcgap_after(msg), |
484 | msg_bcgap_to(msg)); | 461 | msg_bcgap_to(msg)); |
485 | tipc_bclink_unlock(net); | 462 | tipc_bclink_unlock(net); |
463 | tipc_node_unlock(node); | ||
486 | } else { | 464 | } else { |
487 | tipc_node_unlock(node); | 465 | tipc_node_unlock(node); |
488 | bclink_peek_nack(net, msg); | 466 | bclink_peek_nack(net, msg); |
489 | } | 467 | } |
468 | tipc_node_put(node); | ||
490 | goto exit; | 469 | goto exit; |
491 | } | 470 | } |
492 | 471 | ||
@@ -591,6 +570,7 @@ receive: | |||
591 | 570 | ||
592 | unlock: | 571 | unlock: |
593 | tipc_node_unlock(node); | 572 | tipc_node_unlock(node); |
573 | tipc_node_put(node); | ||
594 | exit: | 574 | exit: |
595 | kfree_skb(buf); | 575 | kfree_skb(buf); |
596 | } | 576 | } |
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 43f397fbac55..4bdc12277d33 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h | |||
@@ -55,7 +55,6 @@ struct tipc_bcbearer_pair { | |||
55 | struct tipc_bearer *secondary; | 55 | struct tipc_bearer *secondary; |
56 | }; | 56 | }; |
57 | 57 | ||
58 | #define TIPC_BCLINK_RESET 1 | ||
59 | #define BCBEARER MAX_BEARERS | 58 | #define BCBEARER MAX_BEARERS |
60 | 59 | ||
61 | /** | 60 | /** |
@@ -86,7 +85,6 @@ struct tipc_bcbearer { | |||
86 | * @lock: spinlock governing access to structure | 85 | * @lock: spinlock governing access to structure |
87 | * @link: (non-standard) broadcast link structure | 86 | * @link: (non-standard) broadcast link structure |
88 | * @node: (non-standard) node structure representing b'cast link's peer node | 87 | * @node: (non-standard) node structure representing b'cast link's peer node |
89 | * @flags: represent bclink states | ||
90 | * @bcast_nodes: map of broadcast-capable nodes | 88 | * @bcast_nodes: map of broadcast-capable nodes |
91 | * @retransmit_to: node that most recently requested a retransmit | 89 | * @retransmit_to: node that most recently requested a retransmit |
92 | * | 90 | * |
@@ -96,7 +94,6 @@ struct tipc_bclink { | |||
96 | spinlock_t lock; | 94 | spinlock_t lock; |
97 | struct tipc_link link; | 95 | struct tipc_link link; |
98 | struct tipc_node node; | 96 | struct tipc_node node; |
99 | unsigned int flags; | ||
100 | struct sk_buff_head arrvq; | 97 | struct sk_buff_head arrvq; |
101 | struct sk_buff_head inputq; | 98 | struct sk_buff_head inputq; |
102 | struct tipc_node_map bcast_nodes; | 99 | struct tipc_node_map bcast_nodes; |
@@ -117,7 +114,6 @@ static inline int tipc_nmap_equal(struct tipc_node_map *nm_a, | |||
117 | 114 | ||
118 | int tipc_bclink_init(struct net *net); | 115 | int tipc_bclink_init(struct net *net); |
119 | void tipc_bclink_stop(struct net *net); | 116 | void tipc_bclink_stop(struct net *net); |
120 | void tipc_bclink_set_flags(struct net *tn, unsigned int flags); | ||
121 | void tipc_bclink_add_node(struct net *net, u32 addr); | 117 | void tipc_bclink_add_node(struct net *net, u32 addr); |
122 | void tipc_bclink_remove_node(struct net *net, u32 addr); | 118 | void tipc_bclink_remove_node(struct net *net, u32 addr); |
123 | struct tipc_node *tipc_bclink_retransmit_to(struct net *tn); | 119 | struct tipc_node *tipc_bclink_retransmit_to(struct net *tn); |
diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 169f3dd038b9..967e292f53c8 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c | |||
@@ -260,6 +260,7 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf, | |||
260 | } | 260 | } |
261 | } | 261 | } |
262 | tipc_node_unlock(node); | 262 | tipc_node_unlock(node); |
263 | tipc_node_put(node); | ||
263 | } | 264 | } |
264 | 265 | ||
265 | /** | 266 | /** |
diff --git a/net/tipc/link.c b/net/tipc/link.c index 1287161e9424..514466efc25c 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c | |||
@@ -854,6 +854,7 @@ int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode, | |||
854 | if (link) | 854 | if (link) |
855 | rc = __tipc_link_xmit(net, link, list); | 855 | rc = __tipc_link_xmit(net, link, list); |
856 | tipc_node_unlock(node); | 856 | tipc_node_unlock(node); |
857 | tipc_node_put(node); | ||
857 | } | 858 | } |
858 | if (link) | 859 | if (link) |
859 | return rc; | 860 | return rc; |
@@ -980,7 +981,6 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, | |||
980 | (unsigned long) TIPC_SKB_CB(buf)->handle); | 981 | (unsigned long) TIPC_SKB_CB(buf)->handle); |
981 | 982 | ||
982 | n_ptr = tipc_bclink_retransmit_to(net); | 983 | n_ptr = tipc_bclink_retransmit_to(net); |
983 | tipc_node_lock(n_ptr); | ||
984 | 984 | ||
985 | tipc_addr_string_fill(addr_string, n_ptr->addr); | 985 | tipc_addr_string_fill(addr_string, n_ptr->addr); |
986 | pr_info("Broadcast link info for %s\n", addr_string); | 986 | pr_info("Broadcast link info for %s\n", addr_string); |
@@ -992,9 +992,7 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, | |||
992 | n_ptr->bclink.oos_state, | 992 | n_ptr->bclink.oos_state, |
993 | n_ptr->bclink.last_sent); | 993 | n_ptr->bclink.last_sent); |
994 | 994 | ||
995 | tipc_node_unlock(n_ptr); | 995 | n_ptr->action_flags |= TIPC_BCAST_RESET; |
996 | |||
997 | tipc_bclink_set_flags(net, TIPC_BCLINK_RESET); | ||
998 | l_ptr->stale_count = 0; | 996 | l_ptr->stale_count = 0; |
999 | } | 997 | } |
1000 | } | 998 | } |
@@ -1119,8 +1117,8 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) | |||
1119 | n_ptr = tipc_node_find(net, msg_prevnode(msg)); | 1117 | n_ptr = tipc_node_find(net, msg_prevnode(msg)); |
1120 | if (unlikely(!n_ptr)) | 1118 | if (unlikely(!n_ptr)) |
1121 | goto discard; | 1119 | goto discard; |
1122 | tipc_node_lock(n_ptr); | ||
1123 | 1120 | ||
1121 | tipc_node_lock(n_ptr); | ||
1124 | /* Locate unicast link endpoint that should handle message */ | 1122 | /* Locate unicast link endpoint that should handle message */ |
1125 | l_ptr = n_ptr->links[b_ptr->identity]; | 1123 | l_ptr = n_ptr->links[b_ptr->identity]; |
1126 | if (unlikely(!l_ptr)) | 1124 | if (unlikely(!l_ptr)) |
@@ -1208,6 +1206,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr) | |||
1208 | skb = NULL; | 1206 | skb = NULL; |
1209 | unlock: | 1207 | unlock: |
1210 | tipc_node_unlock(n_ptr); | 1208 | tipc_node_unlock(n_ptr); |
1209 | tipc_node_put(n_ptr); | ||
1211 | discard: | 1210 | discard: |
1212 | if (unlikely(skb)) | 1211 | if (unlikely(skb)) |
1213 | kfree_skb(skb); | 1212 | kfree_skb(skb); |
@@ -2239,7 +2238,6 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
2239 | msg.seq = cb->nlh->nlmsg_seq; | 2238 | msg.seq = cb->nlh->nlmsg_seq; |
2240 | 2239 | ||
2241 | rcu_read_lock(); | 2240 | rcu_read_lock(); |
2242 | |||
2243 | if (prev_node) { | 2241 | if (prev_node) { |
2244 | node = tipc_node_find(net, prev_node); | 2242 | node = tipc_node_find(net, prev_node); |
2245 | if (!node) { | 2243 | if (!node) { |
@@ -2252,6 +2250,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
2252 | cb->prev_seq = 1; | 2250 | cb->prev_seq = 1; |
2253 | goto out; | 2251 | goto out; |
2254 | } | 2252 | } |
2253 | tipc_node_put(node); | ||
2255 | 2254 | ||
2256 | list_for_each_entry_continue_rcu(node, &tn->node_list, | 2255 | list_for_each_entry_continue_rcu(node, &tn->node_list, |
2257 | list) { | 2256 | list) { |
@@ -2259,6 +2258,7 @@ int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
2259 | err = __tipc_nl_add_node_links(net, &msg, node, | 2258 | err = __tipc_nl_add_node_links(net, &msg, node, |
2260 | &prev_link); | 2259 | &prev_link); |
2261 | tipc_node_unlock(node); | 2260 | tipc_node_unlock(node); |
2261 | tipc_node_put(node); | ||
2262 | if (err) | 2262 | if (err) |
2263 | goto out; | 2263 | goto out; |
2264 | 2264 | ||
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 506aaa565da7..41e7b7e4dda0 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c | |||
@@ -244,6 +244,7 @@ static void tipc_publ_subscribe(struct net *net, struct publication *publ, | |||
244 | tipc_node_lock(node); | 244 | tipc_node_lock(node); |
245 | list_add_tail(&publ->nodesub_list, &node->publ_list); | 245 | list_add_tail(&publ->nodesub_list, &node->publ_list); |
246 | tipc_node_unlock(node); | 246 | tipc_node_unlock(node); |
247 | tipc_node_put(node); | ||
247 | } | 248 | } |
248 | 249 | ||
249 | static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, | 250 | static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, |
@@ -258,6 +259,7 @@ static void tipc_publ_unsubscribe(struct net *net, struct publication *publ, | |||
258 | tipc_node_lock(node); | 259 | tipc_node_lock(node); |
259 | list_del_init(&publ->nodesub_list); | 260 | list_del_init(&publ->nodesub_list); |
260 | tipc_node_unlock(node); | 261 | tipc_node_unlock(node); |
262 | tipc_node_put(node); | ||
261 | } | 263 | } |
262 | 264 | ||
263 | /** | 265 | /** |
diff --git a/net/tipc/node.c b/net/tipc/node.c index 26d1de1bf34d..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) |
@@ -459,7 +492,7 @@ void tipc_node_unlock(struct tipc_node *node) | |||
459 | TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP | | 492 | TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP | |
460 | TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP | | 493 | TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP | |
461 | TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT | | 494 | TIPC_WAKEUP_BCAST_USERS | TIPC_BCAST_MSG_EVT | |
462 | TIPC_NAMED_MSG_EVT); | 495 | TIPC_NAMED_MSG_EVT | TIPC_BCAST_RESET); |
463 | 496 | ||
464 | spin_unlock_bh(&node->lock); | 497 | spin_unlock_bh(&node->lock); |
465 | 498 | ||
@@ -488,6 +521,9 @@ void tipc_node_unlock(struct tipc_node *node) | |||
488 | 521 | ||
489 | if (flags & TIPC_BCAST_MSG_EVT) | 522 | if (flags & TIPC_BCAST_MSG_EVT) |
490 | tipc_bclink_input(net); | 523 | tipc_bclink_input(net); |
524 | |||
525 | if (flags & TIPC_BCAST_RESET) | ||
526 | tipc_link_reset_all(node); | ||
491 | } | 527 | } |
492 | 528 | ||
493 | /* Caller should hold node lock for the passed node */ | 529 | /* Caller should hold node lock for the passed node */ |
@@ -542,17 +578,21 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
542 | msg.seq = cb->nlh->nlmsg_seq; | 578 | msg.seq = cb->nlh->nlmsg_seq; |
543 | 579 | ||
544 | rcu_read_lock(); | 580 | rcu_read_lock(); |
545 | 581 | if (last_addr) { | |
546 | if (last_addr && !tipc_node_find(net, last_addr)) { | 582 | node = tipc_node_find(net, last_addr); |
547 | rcu_read_unlock(); | 583 | if (!node) { |
548 | /* We never set seq or call nl_dump_check_consistent() this | 584 | rcu_read_unlock(); |
549 | * means that setting prev_seq here will cause the consistence | 585 | /* We never set seq or call nl_dump_check_consistent() |
550 | * check to fail in the netlink callback handler. Resulting in | 586 | * this means that setting prev_seq here will cause the |
551 | * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if | 587 | * consistence check to fail in the netlink callback |
552 | * the node state changed while we released the lock. | 588 | * handler. Resulting in the NLMSG_DONE message having |
553 | */ | 589 | * the NLM_F_DUMP_INTR flag set if the node state |
554 | cb->prev_seq = 1; | 590 | * changed while we released the lock. |
555 | return -EPIPE; | 591 | */ |
592 | cb->prev_seq = 1; | ||
593 | return -EPIPE; | ||
594 | } | ||
595 | tipc_node_put(node); | ||
556 | } | 596 | } |
557 | 597 | ||
558 | list_for_each_entry_rcu(node, &tn->node_list, list) { | 598 | list_for_each_entry_rcu(node, &tn->node_list, list) { |
diff --git a/net/tipc/node.h b/net/tipc/node.h index e89ac04ec2c3..02d5c20dc551 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h | |||
@@ -64,7 +64,8 @@ enum { | |||
64 | TIPC_NOTIFY_LINK_UP = (1 << 6), | 64 | TIPC_NOTIFY_LINK_UP = (1 << 6), |
65 | TIPC_NOTIFY_LINK_DOWN = (1 << 7), | 65 | TIPC_NOTIFY_LINK_DOWN = (1 << 7), |
66 | TIPC_NAMED_MSG_EVT = (1 << 8), | 66 | TIPC_NAMED_MSG_EVT = (1 << 8), |
67 | TIPC_BCAST_MSG_EVT = (1 << 9) | 67 | TIPC_BCAST_MSG_EVT = (1 << 9), |
68 | TIPC_BCAST_RESET = (1 << 10) | ||
68 | }; | 69 | }; |
69 | 70 | ||
70 | /** | 71 | /** |
@@ -93,6 +94,7 @@ struct tipc_node_bclink { | |||
93 | /** | 94 | /** |
94 | * struct tipc_node - TIPC node structure | 95 | * struct tipc_node - TIPC node structure |
95 | * @addr: network address of node | 96 | * @addr: network address of node |
97 | * @ref: reference counter to node object | ||
96 | * @lock: spinlock governing access to structure | 98 | * @lock: spinlock governing access to structure |
97 | * @net: the applicable net namespace | 99 | * @net: the applicable net namespace |
98 | * @hash: links to adjacent nodes in unsorted hash chain | 100 | * @hash: links to adjacent nodes in unsorted hash chain |
@@ -114,6 +116,7 @@ struct tipc_node_bclink { | |||
114 | */ | 116 | */ |
115 | struct tipc_node { | 117 | struct tipc_node { |
116 | u32 addr; | 118 | u32 addr; |
119 | struct kref kref; | ||
117 | spinlock_t lock; | 120 | spinlock_t lock; |
118 | struct net *net; | 121 | struct net *net; |
119 | struct hlist_node hash; | 122 | struct hlist_node hash; |
@@ -136,6 +139,7 @@ struct tipc_node { | |||
136 | }; | 139 | }; |
137 | 140 | ||
138 | struct tipc_node *tipc_node_find(struct net *net, u32 addr); | 141 | struct tipc_node *tipc_node_find(struct net *net, u32 addr); |
142 | void tipc_node_put(struct tipc_node *node); | ||
139 | struct tipc_node *tipc_node_create(struct net *net, u32 addr); | 143 | struct tipc_node *tipc_node_create(struct net *net, u32 addr); |
140 | void tipc_node_stop(struct net *net); | 144 | void tipc_node_stop(struct net *net); |
141 | void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr); | 145 | void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr); |
@@ -170,10 +174,12 @@ static inline uint tipc_node_get_mtu(struct net *net, u32 addr, u32 selector) | |||
170 | 174 | ||
171 | node = tipc_node_find(net, addr); | 175 | node = tipc_node_find(net, addr); |
172 | 176 | ||
173 | if (likely(node)) | 177 | if (likely(node)) { |
174 | mtu = node->act_mtus[selector & 1]; | 178 | mtu = node->act_mtus[selector & 1]; |
175 | else | 179 | tipc_node_put(node); |
180 | } else { | ||
176 | mtu = MAX_MSG_SIZE; | 181 | mtu = MAX_MSG_SIZE; |
182 | } | ||
177 | 183 | ||
178 | return mtu; | 184 | return mtu; |
179 | } | 185 | } |