aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJon Paul Maloy <jon.maloy@ericsson.com>2015-10-15 14:52:44 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-16 02:55:21 -0400
commit73f646cec35477b5099d7e952297cb9e1855be45 (patch)
tree91356371025167120e9919cc25d806523da367f6 /net
parent8306f99a517b91ebf8fa94d017c2c84ca62e107c (diff)
tipc: delay ESTABLISH state event when link is established
Link establishing, just like link teardown, is a non-atomic action, in the sense that discovering that conditions are right to establish a link, and the actual adding of the link to one of the node's send slots is done in two different lock contexts. The link FSM is designed to help bridging the gap between the two contexts in a safe manner. We have now discovered a weakness in the implementaton of this FSM. Because we directly let the link go from state LINK_ESTABLISHING to state LINK_ESTABLISHED already in the first lock context, we are unable to distinguish between a fully established link, i.e., a link that has been added to its slot, and a link that has not yet reached the second lock context. It may hence happen that a manual intervention, e.g., when disabling an interface, causes the function tipc_node_link_down() to try removing the link from the node slots, decrementing its active link counter etc, although the link was never added there in the first place. We solve this by delaying the actual state change until we reach the second lock context, inside the function tipc_node_link_up(). This makes it possible for potentail callers of __tipc_node_link_down() to know if they should proceed or not, and the problem is solved. Unforunately, the situation described above also has a second problem. Since there by necessity is a tipc_node_link_up() call pending once the node lock has been released, we must defuse that call by setting the link back from LINK_ESTABLISHING to LINK_RESET state. This forces us to make a slight modification to the link FSM, which will now look as follows. +------------------------------------+ |RESET_EVT | | | | +--------------+ | +-----------------| SYNCHING |-----------------+ | |FAILURE_EVT +--------------+ PEER_RESET_EVT| | | A | | | | | | | | | | | | | | |SYNCH_ |SYNCH_ | | | |BEGIN_EVT |END_EVT | | | | | | | V | V V | +-------------+ +--------------+ +------------+ | | RESETTING |<---------| ESTABLISHED |--------->| PEER_RESET | | +-------------+ FAILURE_ +--------------+ PEER_ +------------+ | | EVT | A RESET_EVT | | | | | | | | +----------------+ | | | RESET_EVT| |RESET_EVT | | | | | | | | | | |ESTABLISH_EVT | | | | +-------------+ | | | | | | RESET_EVT | | | | | | | | | | | V V V | | | | +-------------+ +--------------+ RESET_EVT| +--->| RESET |--------->| ESTABLISHING |<----------------+ +-------------+ PEER_ +--------------+ | A RESET_EVT | | | | | | | |FAILOVER_ |FAILOVER_ |FAILOVER_ |BEGIN_EVT |END_EVT |BEGIN_EVT | | | V | | +-------------+ | | FAILINGOVER |<----------------+ +-------------+ Signed-off-by: Jon Maloy <jon.maloy@ericsson.com> Acked-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/tipc/link.c40
-rw-r--r--net/tipc/link.h1
-rw-r--r--net/tipc/node.c31
3 files changed, 49 insertions, 23 deletions
diff --git a/net/tipc/link.c b/net/tipc/link.c
index e7c608631276..8c794c1dd531 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -125,6 +125,11 @@ bool tipc_link_is_reset(struct tipc_link *l)
125 return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING); 125 return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING);
126} 126}
127 127
128bool tipc_link_is_establishing(struct tipc_link *l)
129{
130 return l->state == LINK_ESTABLISHING;
131}
132
128bool tipc_link_is_synching(struct tipc_link *l) 133bool tipc_link_is_synching(struct tipc_link *l)
129{ 134{
130 return l->state == LINK_SYNCHING; 135 return l->state == LINK_SYNCHING;
@@ -321,14 +326,15 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt)
321 switch (evt) { 326 switch (evt) {
322 case LINK_ESTABLISH_EVT: 327 case LINK_ESTABLISH_EVT:
323 l->state = LINK_ESTABLISHED; 328 l->state = LINK_ESTABLISHED;
324 rc |= TIPC_LINK_UP_EVT;
325 break; 329 break;
326 case LINK_FAILOVER_BEGIN_EVT: 330 case LINK_FAILOVER_BEGIN_EVT:
327 l->state = LINK_FAILINGOVER; 331 l->state = LINK_FAILINGOVER;
328 break; 332 break;
329 case LINK_PEER_RESET_EVT:
330 case LINK_RESET_EVT: 333 case LINK_RESET_EVT:
334 l->state = LINK_RESET;
335 break;
331 case LINK_FAILURE_EVT: 336 case LINK_FAILURE_EVT:
337 case LINK_PEER_RESET_EVT:
332 case LINK_SYNCH_BEGIN_EVT: 338 case LINK_SYNCH_BEGIN_EVT:
333 case LINK_FAILOVER_END_EVT: 339 case LINK_FAILOVER_END_EVT:
334 break; 340 break;
@@ -1091,9 +1097,9 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
1091 return tipc_link_proto_rcv(l, skb, xmitq); 1097 return tipc_link_proto_rcv(l, skb, xmitq);
1092 1098
1093 if (unlikely(!link_is_up(l))) { 1099 if (unlikely(!link_is_up(l))) {
1094 rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); 1100 if (l->state == LINK_ESTABLISHING)
1095 if (!link_is_up(l)) 1101 rc = TIPC_LINK_UP_EVT;
1096 goto drop; 1102 goto drop;
1097 } 1103 }
1098 1104
1099 /* Don't send probe at next timeout expiration */ 1105 /* Don't send probe at next timeout expiration */
@@ -1338,6 +1344,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
1338 u16 peers_tol = msg_link_tolerance(hdr); 1344 u16 peers_tol = msg_link_tolerance(hdr);
1339 u16 peers_prio = msg_linkprio(hdr); 1345 u16 peers_prio = msg_linkprio(hdr);
1340 u16 rcv_nxt = l->rcv_nxt; 1346 u16 rcv_nxt = l->rcv_nxt;
1347 int mtyp = msg_type(hdr);
1341 char *if_name; 1348 char *if_name;
1342 int rc = 0; 1349 int rc = 0;
1343 1350
@@ -1347,7 +1354,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
1347 if (link_own_addr(l) > msg_prevnode(hdr)) 1354 if (link_own_addr(l) > msg_prevnode(hdr))
1348 l->net_plane = msg_net_plane(hdr); 1355 l->net_plane = msg_net_plane(hdr);
1349 1356
1350 switch (msg_type(hdr)) { 1357 switch (mtyp) {
1351 case RESET_MSG: 1358 case RESET_MSG:
1352 1359
1353 /* Ignore duplicate RESET with old session number */ 1360 /* Ignore duplicate RESET with old session number */
@@ -1374,12 +1381,14 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
1374 if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI)) 1381 if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
1375 l->priority = peers_prio; 1382 l->priority = peers_prio;
1376 1383
1377 if (msg_type(hdr) == RESET_MSG) { 1384 /* ACTIVATE_MSG serves as PEER_RESET if link is already down */
1378 rc |= tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT); 1385 if ((mtyp == RESET_MSG) || !link_is_up(l))
1379 } else if (!link_is_up(l)) { 1386 rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
1380 tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT); 1387
1381 rc |= tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); 1388 /* ACTIVATE_MSG takes up link if it was already locally reset */
1382 } 1389 if ((mtyp == ACTIVATE_MSG) && (l->state == LINK_ESTABLISHING))
1390 rc = TIPC_LINK_UP_EVT;
1391
1383 l->peer_session = msg_session(hdr); 1392 l->peer_session = msg_session(hdr);
1384 l->peer_bearer_id = msg_bearer_id(hdr); 1393 l->peer_bearer_id = msg_bearer_id(hdr);
1385 if (l->mtu > msg_max_pkt(hdr)) 1394 if (l->mtu > msg_max_pkt(hdr))
@@ -1396,9 +1405,12 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
1396 l->stats.recv_states++; 1405 l->stats.recv_states++;
1397 if (msg_probe(hdr)) 1406 if (msg_probe(hdr))
1398 l->stats.recv_probes++; 1407 l->stats.recv_probes++;
1399 rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT); 1408
1400 if (!link_is_up(l)) 1409 if (!link_is_up(l)) {
1410 if (l->state == LINK_ESTABLISHING)
1411 rc = TIPC_LINK_UP_EVT;
1401 break; 1412 break;
1413 }
1402 1414
1403 /* Send NACK if peer has sent pkts we haven't received yet */ 1415 /* Send NACK if peer has sent pkts we haven't received yet */
1404 if (more(peers_snd_nxt, rcv_nxt) && !tipc_link_is_synching(l)) 1416 if (more(peers_snd_nxt, rcv_nxt) && !tipc_link_is_synching(l))
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 7a1ad4294b7a..d42dfc0e7bf5 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -217,6 +217,7 @@ int tipc_link_fsm_evt(struct tipc_link *l, int evt);
217void tipc_link_reset_fragments(struct tipc_link *l_ptr); 217void tipc_link_reset_fragments(struct tipc_link *l_ptr);
218bool tipc_link_is_up(struct tipc_link *l); 218bool tipc_link_is_up(struct tipc_link *l);
219bool tipc_link_is_reset(struct tipc_link *l); 219bool tipc_link_is_reset(struct tipc_link *l);
220bool tipc_link_is_establishing(struct tipc_link *l);
220bool tipc_link_is_synching(struct tipc_link *l); 221bool tipc_link_is_synching(struct tipc_link *l);
221bool tipc_link_is_failingover(struct tipc_link *l); 222bool tipc_link_is_failingover(struct tipc_link *l);
222bool tipc_link_is_blocked(struct tipc_link *l); 223bool tipc_link_is_blocked(struct tipc_link *l);
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 703875fd6cde..656b5791f1a5 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -317,7 +317,11 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
317 struct tipc_link *ol = node_active_link(n, 0); 317 struct tipc_link *ol = node_active_link(n, 0);
318 struct tipc_link *nl = n->links[bearer_id].link; 318 struct tipc_link *nl = n->links[bearer_id].link;
319 319
320 if (!nl || !tipc_link_is_up(nl)) 320 if (!nl)
321 return;
322
323 tipc_link_fsm_evt(nl, LINK_ESTABLISH_EVT);
324 if (!tipc_link_is_up(nl))
321 return; 325 return;
322 326
323 n->working_links++; 327 n->working_links++;
@@ -437,17 +441,26 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
437static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) 441static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
438{ 442{
439 struct tipc_link_entry *le = &n->links[bearer_id]; 443 struct tipc_link_entry *le = &n->links[bearer_id];
444 struct tipc_link *l = le->link;
440 struct tipc_media_addr *maddr; 445 struct tipc_media_addr *maddr;
441 struct sk_buff_head xmitq; 446 struct sk_buff_head xmitq;
442 447
448 if (!l)
449 return;
450
443 __skb_queue_head_init(&xmitq); 451 __skb_queue_head_init(&xmitq);
444 452
445 tipc_node_lock(n); 453 tipc_node_lock(n);
446 __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); 454 if (!tipc_link_is_establishing(l)) {
447 if (delete && le->link) { 455 __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
448 kfree(le->link); 456 if (delete) {
449 le->link = NULL; 457 kfree(l);
450 n->link_cnt--; 458 le->link = NULL;
459 n->link_cnt--;
460 }
461 } else {
462 /* Defuse pending tipc_node_link_up() */
463 tipc_link_fsm_evt(l, LINK_RESET_EVT);
451 } 464 }
452 tipc_node_unlock(n); 465 tipc_node_unlock(n);
453 466
@@ -579,7 +592,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
579 memcpy(&le->maddr, maddr, sizeof(*maddr)); 592 memcpy(&le->maddr, maddr, sizeof(*maddr));
580exit: 593exit:
581 tipc_node_unlock(n); 594 tipc_node_unlock(n);
582 if (reset) 595 if (reset && !tipc_link_is_reset(l))
583 tipc_node_link_down(n, b->identity, false); 596 tipc_node_link_down(n, b->identity, false);
584 tipc_node_put(n); 597 tipc_node_put(n);
585} 598}
@@ -686,10 +699,10 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt)
686 break; 699 break;
687 case SELF_ESTABL_CONTACT_EVT: 700 case SELF_ESTABL_CONTACT_EVT:
688 case PEER_LOST_CONTACT_EVT: 701 case PEER_LOST_CONTACT_EVT:
689 break;
690 case NODE_SYNCH_END_EVT: 702 case NODE_SYNCH_END_EVT:
691 case NODE_SYNCH_BEGIN_EVT:
692 case NODE_FAILOVER_BEGIN_EVT: 703 case NODE_FAILOVER_BEGIN_EVT:
704 break;
705 case NODE_SYNCH_BEGIN_EVT:
693 case NODE_FAILOVER_END_EVT: 706 case NODE_FAILOVER_END_EVT:
694 default: 707 default:
695 goto illegal_evt; 708 goto illegal_evt;