diff options
Diffstat (limited to 'net/tipc/node.c')
-rw-r--r-- | net/tipc/node.c | 90 |
1 files changed, 75 insertions, 15 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c index 0453bd451ce8..68014f1b6976 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include "netlink.h" | 45 | #include "netlink.h" |
46 | 46 | ||
47 | #define INVALID_NODE_SIG 0x10000 | 47 | #define INVALID_NODE_SIG 0x10000 |
48 | #define NODE_CLEANUP_AFTER 300000 | ||
48 | 49 | ||
49 | /* Flags used to take different actions according to flag type | 50 | /* Flags used to take different actions according to flag type |
50 | * TIPC_NOTIFY_NODE_DOWN: notify node is down | 51 | * TIPC_NOTIFY_NODE_DOWN: notify node is down |
@@ -96,6 +97,7 @@ struct tipc_bclink_entry { | |||
96 | * @link_id: local and remote bearer ids of changing link, if any | 97 | * @link_id: local and remote bearer ids of changing link, if any |
97 | * @publ_list: list of publications | 98 | * @publ_list: list of publications |
98 | * @rcu: rcu struct for tipc_node | 99 | * @rcu: rcu struct for tipc_node |
100 | * @delete_at: indicates the time for deleting a down node | ||
99 | */ | 101 | */ |
100 | struct tipc_node { | 102 | struct tipc_node { |
101 | u32 addr; | 103 | u32 addr; |
@@ -121,6 +123,7 @@ struct tipc_node { | |||
121 | unsigned long keepalive_intv; | 123 | unsigned long keepalive_intv; |
122 | struct timer_list timer; | 124 | struct timer_list timer; |
123 | struct rcu_head rcu; | 125 | struct rcu_head rcu; |
126 | unsigned long delete_at; | ||
124 | }; | 127 | }; |
125 | 128 | ||
126 | /* Node FSM states and events: | 129 | /* Node FSM states and events: |
@@ -160,6 +163,7 @@ static struct tipc_node *tipc_node_find(struct net *net, u32 addr); | |||
160 | static struct tipc_node *tipc_node_find_by_id(struct net *net, u8 *id); | 163 | static struct tipc_node *tipc_node_find_by_id(struct net *net, u8 *id); |
161 | static void tipc_node_put(struct tipc_node *node); | 164 | static void tipc_node_put(struct tipc_node *node); |
162 | static bool node_is_up(struct tipc_node *n); | 165 | static bool node_is_up(struct tipc_node *n); |
166 | static void tipc_node_delete_from_list(struct tipc_node *node); | ||
163 | 167 | ||
164 | struct tipc_sock_conn { | 168 | struct tipc_sock_conn { |
165 | u32 port; | 169 | u32 port; |
@@ -359,13 +363,24 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr, | |||
359 | { | 363 | { |
360 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 364 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
361 | struct tipc_node *n, *temp_node; | 365 | struct tipc_node *n, *temp_node; |
366 | struct tipc_link *l; | ||
367 | int bearer_id; | ||
362 | int i; | 368 | int i; |
363 | 369 | ||
364 | spin_lock_bh(&tn->node_list_lock); | 370 | spin_lock_bh(&tn->node_list_lock); |
365 | n = tipc_node_find(net, addr); | 371 | n = tipc_node_find(net, addr); |
366 | if (n) { | 372 | if (n) { |
373 | if (n->capabilities == capabilities) | ||
374 | goto exit; | ||
367 | /* Same node may come back with new capabilities */ | 375 | /* Same node may come back with new capabilities */ |
376 | write_lock_bh(&n->lock); | ||
368 | n->capabilities = capabilities; | 377 | n->capabilities = capabilities; |
378 | for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { | ||
379 | l = n->links[bearer_id].link; | ||
380 | if (l) | ||
381 | tipc_link_update_caps(l, capabilities); | ||
382 | } | ||
383 | write_unlock_bh(&n->lock); | ||
369 | goto exit; | 384 | goto exit; |
370 | } | 385 | } |
371 | n = kzalloc(sizeof(*n), GFP_ATOMIC); | 386 | n = kzalloc(sizeof(*n), GFP_ATOMIC); |
@@ -390,6 +405,7 @@ static struct tipc_node *tipc_node_create(struct net *net, u32 addr, | |||
390 | for (i = 0; i < MAX_BEARERS; i++) | 405 | for (i = 0; i < MAX_BEARERS; i++) |
391 | spin_lock_init(&n->links[i].lock); | 406 | spin_lock_init(&n->links[i].lock); |
392 | n->state = SELF_DOWN_PEER_LEAVING; | 407 | n->state = SELF_DOWN_PEER_LEAVING; |
408 | n->delete_at = jiffies + msecs_to_jiffies(NODE_CLEANUP_AFTER); | ||
393 | n->signature = INVALID_NODE_SIG; | 409 | n->signature = INVALID_NODE_SIG; |
394 | n->active_links[0] = INVALID_BEARER_ID; | 410 | n->active_links[0] = INVALID_BEARER_ID; |
395 | n->active_links[1] = INVALID_BEARER_ID; | 411 | n->active_links[1] = INVALID_BEARER_ID; |
@@ -433,11 +449,16 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l) | |||
433 | tipc_link_set_abort_limit(l, tol / n->keepalive_intv); | 449 | tipc_link_set_abort_limit(l, tol / n->keepalive_intv); |
434 | } | 450 | } |
435 | 451 | ||
436 | static void tipc_node_delete(struct tipc_node *node) | 452 | static void tipc_node_delete_from_list(struct tipc_node *node) |
437 | { | 453 | { |
438 | list_del_rcu(&node->list); | 454 | list_del_rcu(&node->list); |
439 | hlist_del_rcu(&node->hash); | 455 | hlist_del_rcu(&node->hash); |
440 | tipc_node_put(node); | 456 | tipc_node_put(node); |
457 | } | ||
458 | |||
459 | static void tipc_node_delete(struct tipc_node *node) | ||
460 | { | ||
461 | tipc_node_delete_from_list(node); | ||
441 | 462 | ||
442 | del_timer_sync(&node->timer); | 463 | del_timer_sync(&node->timer); |
443 | tipc_node_put(node); | 464 | tipc_node_put(node); |
@@ -544,6 +565,42 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port) | |||
544 | tipc_node_put(node); | 565 | tipc_node_put(node); |
545 | } | 566 | } |
546 | 567 | ||
568 | static void tipc_node_clear_links(struct tipc_node *node) | ||
569 | { | ||
570 | int i; | ||
571 | |||
572 | for (i = 0; i < MAX_BEARERS; i++) { | ||
573 | struct tipc_link_entry *le = &node->links[i]; | ||
574 | |||
575 | if (le->link) { | ||
576 | kfree(le->link); | ||
577 | le->link = NULL; | ||
578 | node->link_cnt--; | ||
579 | } | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /* tipc_node_cleanup - delete nodes that does not | ||
584 | * have active links for NODE_CLEANUP_AFTER time | ||
585 | */ | ||
586 | static int tipc_node_cleanup(struct tipc_node *peer) | ||
587 | { | ||
588 | struct tipc_net *tn = tipc_net(peer->net); | ||
589 | bool deleted = false; | ||
590 | |||
591 | spin_lock_bh(&tn->node_list_lock); | ||
592 | tipc_node_write_lock(peer); | ||
593 | |||
594 | if (!node_is_up(peer) && time_after(jiffies, peer->delete_at)) { | ||
595 | tipc_node_clear_links(peer); | ||
596 | tipc_node_delete_from_list(peer); | ||
597 | deleted = true; | ||
598 | } | ||
599 | tipc_node_write_unlock(peer); | ||
600 | spin_unlock_bh(&tn->node_list_lock); | ||
601 | return deleted; | ||
602 | } | ||
603 | |||
547 | /* tipc_node_timeout - handle expiration of node timer | 604 | /* tipc_node_timeout - handle expiration of node timer |
548 | */ | 605 | */ |
549 | static void tipc_node_timeout(struct timer_list *t) | 606 | static void tipc_node_timeout(struct timer_list *t) |
@@ -551,21 +608,29 @@ static void tipc_node_timeout(struct timer_list *t) | |||
551 | struct tipc_node *n = from_timer(n, t, timer); | 608 | struct tipc_node *n = from_timer(n, t, timer); |
552 | struct tipc_link_entry *le; | 609 | struct tipc_link_entry *le; |
553 | struct sk_buff_head xmitq; | 610 | struct sk_buff_head xmitq; |
611 | int remains = n->link_cnt; | ||
554 | int bearer_id; | 612 | int bearer_id; |
555 | int rc = 0; | 613 | int rc = 0; |
556 | 614 | ||
615 | if (!node_is_up(n) && tipc_node_cleanup(n)) { | ||
616 | /*Removing the reference of Timer*/ | ||
617 | tipc_node_put(n); | ||
618 | return; | ||
619 | } | ||
620 | |||
557 | __skb_queue_head_init(&xmitq); | 621 | __skb_queue_head_init(&xmitq); |
558 | 622 | ||
559 | for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { | 623 | for (bearer_id = 0; remains && (bearer_id < MAX_BEARERS); bearer_id++) { |
560 | tipc_node_read_lock(n); | 624 | tipc_node_read_lock(n); |
561 | le = &n->links[bearer_id]; | 625 | le = &n->links[bearer_id]; |
562 | spin_lock_bh(&le->lock); | ||
563 | if (le->link) { | 626 | if (le->link) { |
627 | spin_lock_bh(&le->lock); | ||
564 | /* Link tolerance may change asynchronously: */ | 628 | /* Link tolerance may change asynchronously: */ |
565 | tipc_node_calculate_timer(n, le->link); | 629 | tipc_node_calculate_timer(n, le->link); |
566 | rc = tipc_link_timeout(le->link, &xmitq); | 630 | rc = tipc_link_timeout(le->link, &xmitq); |
631 | spin_unlock_bh(&le->lock); | ||
632 | remains--; | ||
567 | } | 633 | } |
568 | spin_unlock_bh(&le->lock); | ||
569 | tipc_node_read_unlock(n); | 634 | tipc_node_read_unlock(n); |
570 | tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr); | 635 | tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr); |
571 | if (rc & TIPC_LINK_DOWN_EVT) | 636 | if (rc & TIPC_LINK_DOWN_EVT) |
@@ -1174,6 +1239,7 @@ static void node_lost_contact(struct tipc_node *n, | |||
1174 | uint i; | 1239 | uint i; |
1175 | 1240 | ||
1176 | pr_debug("Lost contact with %x\n", n->addr); | 1241 | pr_debug("Lost contact with %x\n", n->addr); |
1242 | n->delete_at = jiffies + msecs_to_jiffies(NODE_CLEANUP_AFTER); | ||
1177 | 1243 | ||
1178 | /* Clean up broadcast state */ | 1244 | /* Clean up broadcast state */ |
1179 | tipc_bcast_remove_peer(n->net, n->bc_entry.link); | 1245 | tipc_bcast_remove_peer(n->net, n->bc_entry.link); |
@@ -1481,7 +1547,7 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id | |||
1481 | * tipc_node_check_state - check and if necessary update node state | 1547 | * tipc_node_check_state - check and if necessary update node state |
1482 | * @skb: TIPC packet | 1548 | * @skb: TIPC packet |
1483 | * @bearer_id: identity of bearer delivering the packet | 1549 | * @bearer_id: identity of bearer delivering the packet |
1484 | * Returns true if state is ok, otherwise consumes buffer and returns false | 1550 | * Returns true if state and msg are ok, otherwise false |
1485 | */ | 1551 | */ |
1486 | static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb, | 1552 | static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb, |
1487 | int bearer_id, struct sk_buff_head *xmitq) | 1553 | int bearer_id, struct sk_buff_head *xmitq) |
@@ -1515,6 +1581,9 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb, | |||
1515 | } | 1581 | } |
1516 | } | 1582 | } |
1517 | 1583 | ||
1584 | if (!tipc_link_validate_msg(l, hdr)) | ||
1585 | return false; | ||
1586 | |||
1518 | /* Check and update node accesibility if applicable */ | 1587 | /* Check and update node accesibility if applicable */ |
1519 | if (state == SELF_UP_PEER_COMING) { | 1588 | if (state == SELF_UP_PEER_COMING) { |
1520 | if (!tipc_link_is_up(l)) | 1589 | if (!tipc_link_is_up(l)) |
@@ -1743,7 +1812,6 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info) | |||
1743 | struct tipc_node *peer; | 1812 | struct tipc_node *peer; |
1744 | u32 addr; | 1813 | u32 addr; |
1745 | int err; | 1814 | int err; |
1746 | int i; | ||
1747 | 1815 | ||
1748 | /* We identify the peer by its net */ | 1816 | /* We identify the peer by its net */ |
1749 | if (!info->attrs[TIPC_NLA_NET]) | 1817 | if (!info->attrs[TIPC_NLA_NET]) |
@@ -1778,15 +1846,7 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info) | |||
1778 | goto err_out; | 1846 | goto err_out; |
1779 | } | 1847 | } |
1780 | 1848 | ||
1781 | for (i = 0; i < MAX_BEARERS; i++) { | 1849 | tipc_node_clear_links(peer); |
1782 | struct tipc_link_entry *le = &peer->links[i]; | ||
1783 | |||
1784 | if (le->link) { | ||
1785 | kfree(le->link); | ||
1786 | le->link = NULL; | ||
1787 | peer->link_cnt--; | ||
1788 | } | ||
1789 | } | ||
1790 | tipc_node_write_unlock(peer); | 1850 | tipc_node_write_unlock(peer); |
1791 | tipc_node_delete(peer); | 1851 | tipc_node_delete(peer); |
1792 | 1852 | ||