diff options
Diffstat (limited to 'net/tipc/node.c')
| -rw-r--r-- | net/tipc/node.c | 237 |
1 files changed, 219 insertions, 18 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c index f7069299943f..8d353ec77a66 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include "config.h" | 38 | #include "config.h" |
| 39 | #include "node.h" | 39 | #include "node.h" |
| 40 | #include "name_distr.h" | 40 | #include "name_distr.h" |
| 41 | #include "socket.h" | ||
| 41 | 42 | ||
| 42 | #define NODE_HTABLE_SIZE 512 | 43 | #define NODE_HTABLE_SIZE 512 |
| 43 | 44 | ||
| @@ -50,6 +51,19 @@ static u32 tipc_num_nodes; | |||
| 50 | static u32 tipc_num_links; | 51 | static u32 tipc_num_links; |
| 51 | static DEFINE_SPINLOCK(node_list_lock); | 52 | static DEFINE_SPINLOCK(node_list_lock); |
| 52 | 53 | ||
| 54 | struct tipc_sock_conn { | ||
| 55 | u32 port; | ||
| 56 | u32 peer_port; | ||
| 57 | u32 peer_node; | ||
| 58 | struct list_head list; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { | ||
| 62 | [TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC }, | ||
| 63 | [TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 }, | ||
| 64 | [TIPC_NLA_NODE_UP] = { .type = NLA_FLAG } | ||
| 65 | }; | ||
| 66 | |||
| 53 | /* | 67 | /* |
| 54 | * A trivial power-of-two bitmask technique is used for speed, since this | 68 | * A trivial power-of-two bitmask technique is used for speed, since this |
| 55 | * operation is done for every incoming TIPC packet. The number of hash table | 69 | * operation is done for every incoming TIPC packet. The number of hash table |
| @@ -99,7 +113,10 @@ struct tipc_node *tipc_node_create(u32 addr) | |||
| 99 | spin_lock_init(&n_ptr->lock); | 113 | spin_lock_init(&n_ptr->lock); |
| 100 | INIT_HLIST_NODE(&n_ptr->hash); | 114 | INIT_HLIST_NODE(&n_ptr->hash); |
| 101 | INIT_LIST_HEAD(&n_ptr->list); | 115 | INIT_LIST_HEAD(&n_ptr->list); |
| 102 | INIT_LIST_HEAD(&n_ptr->nsub); | 116 | INIT_LIST_HEAD(&n_ptr->publ_list); |
| 117 | INIT_LIST_HEAD(&n_ptr->conn_sks); | ||
| 118 | skb_queue_head_init(&n_ptr->waiting_sks); | ||
| 119 | __skb_queue_head_init(&n_ptr->bclink.deferred_queue); | ||
| 103 | 120 | ||
| 104 | hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); | 121 | hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); |
| 105 | 122 | ||
| @@ -136,6 +153,71 @@ void tipc_node_stop(void) | |||
| 136 | spin_unlock_bh(&node_list_lock); | 153 | spin_unlock_bh(&node_list_lock); |
| 137 | } | 154 | } |
| 138 | 155 | ||
| 156 | int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) | ||
| 157 | { | ||
| 158 | struct tipc_node *node; | ||
| 159 | struct tipc_sock_conn *conn; | ||
| 160 | |||
| 161 | if (in_own_node(dnode)) | ||
| 162 | return 0; | ||
| 163 | |||
| 164 | node = tipc_node_find(dnode); | ||
| 165 | if (!node) { | ||
| 166 | pr_warn("Connecting sock to node 0x%x failed\n", dnode); | ||
| 167 | return -EHOSTUNREACH; | ||
| 168 | } | ||
| 169 | conn = kmalloc(sizeof(*conn), GFP_ATOMIC); | ||
| 170 | if (!conn) | ||
| 171 | return -EHOSTUNREACH; | ||
| 172 | conn->peer_node = dnode; | ||
| 173 | conn->port = port; | ||
| 174 | conn->peer_port = peer_port; | ||
| 175 | |||
| 176 | tipc_node_lock(node); | ||
| 177 | list_add_tail(&conn->list, &node->conn_sks); | ||
| 178 | tipc_node_unlock(node); | ||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | void tipc_node_remove_conn(u32 dnode, u32 port) | ||
| 183 | { | ||
| 184 | struct tipc_node *node; | ||
| 185 | struct tipc_sock_conn *conn, *safe; | ||
| 186 | |||
| 187 | if (in_own_node(dnode)) | ||
| 188 | return; | ||
| 189 | |||
| 190 | node = tipc_node_find(dnode); | ||
| 191 | if (!node) | ||
| 192 | return; | ||
| 193 | |||
| 194 | tipc_node_lock(node); | ||
| 195 | list_for_each_entry_safe(conn, safe, &node->conn_sks, list) { | ||
| 196 | if (port != conn->port) | ||
| 197 | continue; | ||
| 198 | list_del(&conn->list); | ||
| 199 | kfree(conn); | ||
| 200 | } | ||
| 201 | tipc_node_unlock(node); | ||
| 202 | } | ||
| 203 | |||
| 204 | void tipc_node_abort_sock_conns(struct list_head *conns) | ||
| 205 | { | ||
| 206 | struct tipc_sock_conn *conn, *safe; | ||
| 207 | struct sk_buff *buf; | ||
| 208 | |||
| 209 | list_for_each_entry_safe(conn, safe, conns, list) { | ||
| 210 | buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, | ||
| 211 | SHORT_H_SIZE, 0, tipc_own_addr, | ||
| 212 | conn->peer_node, conn->port, | ||
| 213 | conn->peer_port, TIPC_ERR_NO_NODE); | ||
| 214 | if (likely(buf)) | ||
| 215 | tipc_sk_rcv(buf); | ||
| 216 | list_del(&conn->list); | ||
| 217 | kfree(conn); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 139 | /** | 221 | /** |
| 140 | * tipc_node_link_up - handle addition of link | 222 | * tipc_node_link_up - handle addition of link |
| 141 | * | 223 | * |
| @@ -144,11 +226,11 @@ void tipc_node_stop(void) | |||
| 144 | void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr) | 226 | void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr) |
| 145 | { | 227 | { |
| 146 | struct tipc_link **active = &n_ptr->active_links[0]; | 228 | struct tipc_link **active = &n_ptr->active_links[0]; |
| 147 | u32 addr = n_ptr->addr; | ||
| 148 | 229 | ||
| 149 | n_ptr->working_links++; | 230 | n_ptr->working_links++; |
| 150 | tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE, | 231 | n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP; |
| 151 | l_ptr->bearer_id, addr); | 232 | n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; |
| 233 | |||
| 152 | pr_info("Established link <%s> on network plane %c\n", | 234 | pr_info("Established link <%s> on network plane %c\n", |
| 153 | l_ptr->name, l_ptr->net_plane); | 235 | l_ptr->name, l_ptr->net_plane); |
| 154 | 236 | ||
| @@ -209,10 +291,10 @@ static void node_select_active_links(struct tipc_node *n_ptr) | |||
| 209 | void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) | 291 | void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) |
| 210 | { | 292 | { |
| 211 | struct tipc_link **active; | 293 | struct tipc_link **active; |
| 212 | u32 addr = n_ptr->addr; | ||
| 213 | 294 | ||
| 214 | n_ptr->working_links--; | 295 | n_ptr->working_links--; |
| 215 | tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr); | 296 | n_ptr->action_flags |= TIPC_NOTIFY_LINK_DOWN; |
| 297 | n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; | ||
| 216 | 298 | ||
| 217 | if (!tipc_link_is_active(l_ptr)) { | 299 | if (!tipc_link_is_active(l_ptr)) { |
| 218 | pr_info("Lost standby link <%s> on network plane %c\n", | 300 | pr_info("Lost standby link <%s> on network plane %c\n", |
| @@ -300,8 +382,7 @@ static void node_lost_contact(struct tipc_node *n_ptr) | |||
| 300 | 382 | ||
| 301 | /* Flush broadcast link info associated with lost node */ | 383 | /* Flush broadcast link info associated with lost node */ |
| 302 | if (n_ptr->bclink.recv_permitted) { | 384 | if (n_ptr->bclink.recv_permitted) { |
| 303 | kfree_skb_list(n_ptr->bclink.deferred_head); | 385 | __skb_queue_purge(&n_ptr->bclink.deferred_queue); |
| 304 | n_ptr->bclink.deferred_size = 0; | ||
| 305 | 386 | ||
| 306 | if (n_ptr->bclink.reasm_buf) { | 387 | if (n_ptr->bclink.reasm_buf) { |
| 307 | kfree_skb(n_ptr->bclink.reasm_buf); | 388 | kfree_skb(n_ptr->bclink.reasm_buf); |
| @@ -474,25 +555,145 @@ int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) | |||
| 474 | void tipc_node_unlock(struct tipc_node *node) | 555 | void tipc_node_unlock(struct tipc_node *node) |
| 475 | { | 556 | { |
| 476 | LIST_HEAD(nsub_list); | 557 | LIST_HEAD(nsub_list); |
| 558 | LIST_HEAD(conn_sks); | ||
| 559 | struct sk_buff_head waiting_sks; | ||
| 477 | u32 addr = 0; | 560 | u32 addr = 0; |
| 561 | int flags = node->action_flags; | ||
| 562 | u32 link_id = 0; | ||
| 478 | 563 | ||
| 479 | if (likely(!node->action_flags)) { | 564 | if (likely(!flags)) { |
| 480 | spin_unlock_bh(&node->lock); | 565 | spin_unlock_bh(&node->lock); |
| 481 | return; | 566 | return; |
| 482 | } | 567 | } |
| 483 | 568 | ||
| 484 | if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) { | 569 | addr = node->addr; |
| 485 | list_replace_init(&node->nsub, &nsub_list); | 570 | link_id = node->link_id; |
| 486 | node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN; | 571 | __skb_queue_head_init(&waiting_sks); |
| 487 | } | 572 | |
| 488 | if (node->action_flags & TIPC_NOTIFY_NODE_UP) { | 573 | if (flags & TIPC_WAKEUP_USERS) |
| 489 | node->action_flags &= ~TIPC_NOTIFY_NODE_UP; | 574 | skb_queue_splice_init(&node->waiting_sks, &waiting_sks); |
| 490 | addr = node->addr; | 575 | |
| 576 | if (flags & TIPC_NOTIFY_NODE_DOWN) { | ||
| 577 | list_replace_init(&node->publ_list, &nsub_list); | ||
| 578 | list_replace_init(&node->conn_sks, &conn_sks); | ||
| 491 | } | 579 | } |
| 580 | node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN | | ||
| 581 | TIPC_NOTIFY_NODE_UP | TIPC_NOTIFY_LINK_UP | | ||
| 582 | TIPC_NOTIFY_LINK_DOWN | | ||
| 583 | TIPC_WAKEUP_BCAST_USERS); | ||
| 584 | |||
| 492 | spin_unlock_bh(&node->lock); | 585 | spin_unlock_bh(&node->lock); |
| 493 | 586 | ||
| 587 | while (!skb_queue_empty(&waiting_sks)) | ||
| 588 | tipc_sk_rcv(__skb_dequeue(&waiting_sks)); | ||
| 589 | |||
| 590 | if (!list_empty(&conn_sks)) | ||
| 591 | tipc_node_abort_sock_conns(&conn_sks); | ||
| 592 | |||
| 494 | if (!list_empty(&nsub_list)) | 593 | if (!list_empty(&nsub_list)) |
| 495 | tipc_nodesub_notify(&nsub_list); | 594 | tipc_publ_notify(&nsub_list, addr); |
| 496 | if (addr) | 595 | |
| 596 | if (flags & TIPC_WAKEUP_BCAST_USERS) | ||
| 597 | tipc_bclink_wakeup_users(); | ||
| 598 | |||
| 599 | if (flags & TIPC_NOTIFY_NODE_UP) | ||
| 497 | tipc_named_node_up(addr); | 600 | tipc_named_node_up(addr); |
| 601 | |||
| 602 | if (flags & TIPC_NOTIFY_LINK_UP) | ||
| 603 | tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, | ||
| 604 | TIPC_NODE_SCOPE, link_id, addr); | ||
| 605 | |||
| 606 | if (flags & TIPC_NOTIFY_LINK_DOWN) | ||
| 607 | tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, | ||
| 608 | link_id, addr); | ||
| 609 | } | ||
| 610 | |||
| 611 | /* Caller should hold node lock for the passed node */ | ||
| 612 | static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node) | ||
| 613 | { | ||
| 614 | void *hdr; | ||
| 615 | struct nlattr *attrs; | ||
| 616 | |||
| 617 | hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, | ||
| 618 | NLM_F_MULTI, TIPC_NL_NODE_GET); | ||
| 619 | if (!hdr) | ||
| 620 | return -EMSGSIZE; | ||
| 621 | |||
| 622 | attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE); | ||
| 623 | if (!attrs) | ||
| 624 | goto msg_full; | ||
| 625 | |||
| 626 | if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr)) | ||
| 627 | goto attr_msg_full; | ||
| 628 | if (tipc_node_is_up(node)) | ||
| 629 | if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP)) | ||
| 630 | goto attr_msg_full; | ||
| 631 | |||
| 632 | nla_nest_end(msg->skb, attrs); | ||
| 633 | genlmsg_end(msg->skb, hdr); | ||
| 634 | |||
| 635 | return 0; | ||
| 636 | |||
| 637 | attr_msg_full: | ||
| 638 | nla_nest_cancel(msg->skb, attrs); | ||
| 639 | msg_full: | ||
| 640 | genlmsg_cancel(msg->skb, hdr); | ||
| 641 | |||
| 642 | return -EMSGSIZE; | ||
| 643 | } | ||
| 644 | |||
| 645 | int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
| 646 | { | ||
| 647 | int err; | ||
| 648 | int done = cb->args[0]; | ||
| 649 | int last_addr = cb->args[1]; | ||
| 650 | struct tipc_node *node; | ||
| 651 | struct tipc_nl_msg msg; | ||
| 652 | |||
| 653 | if (done) | ||
| 654 | return 0; | ||
| 655 | |||
| 656 | msg.skb = skb; | ||
| 657 | msg.portid = NETLINK_CB(cb->skb).portid; | ||
| 658 | msg.seq = cb->nlh->nlmsg_seq; | ||
| 659 | |||
| 660 | rcu_read_lock(); | ||
| 661 | |||
| 662 | if (last_addr && !tipc_node_find(last_addr)) { | ||
| 663 | rcu_read_unlock(); | ||
| 664 | /* We never set seq or call nl_dump_check_consistent() this | ||
| 665 | * means that setting prev_seq here will cause the consistence | ||
| 666 | * check to fail in the netlink callback handler. Resulting in | ||
| 667 | * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if | ||
| 668 | * the node state changed while we released the lock. | ||
| 669 | */ | ||
| 670 | cb->prev_seq = 1; | ||
| 671 | return -EPIPE; | ||
| 672 | } | ||
| 673 | |||
| 674 | list_for_each_entry_rcu(node, &tipc_node_list, list) { | ||
| 675 | if (last_addr) { | ||
| 676 | if (node->addr == last_addr) | ||
| 677 | last_addr = 0; | ||
| 678 | else | ||
| 679 | continue; | ||
| 680 | } | ||
| 681 | |||
| 682 | tipc_node_lock(node); | ||
| 683 | err = __tipc_nl_add_node(&msg, node); | ||
| 684 | if (err) { | ||
| 685 | last_addr = node->addr; | ||
| 686 | tipc_node_unlock(node); | ||
| 687 | goto out; | ||
| 688 | } | ||
| 689 | |||
| 690 | tipc_node_unlock(node); | ||
| 691 | } | ||
| 692 | done = 1; | ||
| 693 | out: | ||
| 694 | cb->args[0] = done; | ||
| 695 | cb->args[1] = last_addr; | ||
| 696 | rcu_read_unlock(); | ||
| 697 | |||
| 698 | return skb->len; | ||
| 498 | } | 699 | } |
