aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc/node.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/tipc/node.c')
-rw-r--r--net/tipc/node.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 5781634e957d..72a75d4838b2 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -58,6 +58,12 @@ struct tipc_sock_conn {
58 struct list_head list; 58 struct list_head list;
59}; 59};
60 60
61static 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
61/* 67/*
62 * 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
63 * 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
@@ -601,3 +607,93 @@ void tipc_node_unlock(struct tipc_node *node)
601 tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, 607 tipc_nametbl_withdraw(TIPC_LINK_STATE, addr,
602 link_id, addr); 608 link_id, addr);
603} 609}
610
611/* Caller should hold node lock for the passed node */
612int __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
637attr_msg_full:
638 nla_nest_cancel(msg->skb, attrs);
639msg_full:
640 genlmsg_cancel(msg->skb, hdr);
641
642 return -EMSGSIZE;
643}
644
645int 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;
693out:
694 cb->args[0] = done;
695 cb->args[1] = last_addr;
696 rcu_read_unlock();
697
698 return skb->len;
699}