diff options
author | Richard Alpe <richard.alpe@ericsson.com> | 2014-11-20 04:29:12 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-21 15:01:30 -0500 |
commit | 7be57fc6918470ecacd16b89c0d4f73d8fc265c4 (patch) | |
tree | 858e3477e2bf49f4f0d052e2ef5c82d93b8bd1b4 /net/tipc | |
parent | 1a1a143daf84db95dd7212086042004a3abb7bc2 (diff) |
tipc: add link get/dump to new netlink api
Add TIPC_NL_LINK_GET command to the new tipc netlink API.
This command supports dumping all information about all links
(including the broadcast link) or getting all information about a
specific link (not the broadcast link).
The information about a link includes name, transmission info,
properties and link statistics.
As the tipc broadcast link is special we unfortunately have to treat
it specially. It is a deliberate decision not to abstract the
broadcast link on this (API) level.
Netlink logical layout of link response message:
-> port
-> name
-> MTU
-> RX
-> TX
-> up flag
-> active flag
-> properties
-> priority
-> tolerance
-> window
-> statistics
-> rx_info
-> rx_fragments
-> rx_fragmented
-> rx_bundles
-> rx_bundled
-> tx_info
-> tx_fragments
-> tx_fragmented
-> tx_bundles
-> tx_bundled
-> msg_prof_tot
-> msg_len_cnt
-> msg_len_tot
-> msg_len_p0
-> msg_len_p1
-> msg_len_p2
-> msg_len_p3
-> msg_len_p4
-> msg_len_p5
-> msg_len_p6
-> rx_states
-> rx_probes
-> rx_nacks
-> rx_deferred
-> tx_states
-> tx_probes
-> tx_nacks
-> tx_acks
-> retransmitted
-> duplicates
-> link_congs
-> max_queue
-> avg_queue
Signed-off-by: Richard Alpe <richard.alpe@ericsson.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Reviewed-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/tipc')
-rw-r--r-- | net/tipc/bcast.c | 111 | ||||
-rw-r--r-- | net/tipc/bcast.h | 4 | ||||
-rw-r--r-- | net/tipc/link.c | 287 | ||||
-rw-r--r-- | net/tipc/link.h | 2 | ||||
-rw-r--r-- | net/tipc/netlink.c | 10 |
5 files changed, 413 insertions, 1 deletions
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index b8670bf262e2..dcf3589e3cc5 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c | |||
@@ -767,6 +767,117 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action) | |||
767 | tipc_bclink_unlock(); | 767 | tipc_bclink_unlock(); |
768 | } | 768 | } |
769 | 769 | ||
770 | int __tipc_nl_add_bc_link_stat(struct sk_buff *skb, struct tipc_stats *stats) | ||
771 | { | ||
772 | int i; | ||
773 | struct nlattr *nest; | ||
774 | |||
775 | struct nla_map { | ||
776 | __u32 key; | ||
777 | __u32 val; | ||
778 | }; | ||
779 | |||
780 | struct nla_map map[] = { | ||
781 | {TIPC_NLA_STATS_RX_INFO, stats->recv_info}, | ||
782 | {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments}, | ||
783 | {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented}, | ||
784 | {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles}, | ||
785 | {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled}, | ||
786 | {TIPC_NLA_STATS_TX_INFO, stats->sent_info}, | ||
787 | {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments}, | ||
788 | {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented}, | ||
789 | {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles}, | ||
790 | {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled}, | ||
791 | {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks}, | ||
792 | {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv}, | ||
793 | {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks}, | ||
794 | {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks}, | ||
795 | {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted}, | ||
796 | {TIPC_NLA_STATS_DUPLICATES, stats->duplicates}, | ||
797 | {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs}, | ||
798 | {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz}, | ||
799 | {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ? | ||
800 | (stats->accu_queue_sz / stats->queue_sz_counts) : 0} | ||
801 | }; | ||
802 | |||
803 | nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS); | ||
804 | if (!nest) | ||
805 | return -EMSGSIZE; | ||
806 | |||
807 | for (i = 0; i < ARRAY_SIZE(map); i++) | ||
808 | if (nla_put_u32(skb, map[i].key, map[i].val)) | ||
809 | goto msg_full; | ||
810 | |||
811 | nla_nest_end(skb, nest); | ||
812 | |||
813 | return 0; | ||
814 | msg_full: | ||
815 | nla_nest_cancel(skb, nest); | ||
816 | |||
817 | return -EMSGSIZE; | ||
818 | } | ||
819 | |||
820 | int tipc_nl_add_bc_link(struct tipc_nl_msg *msg) | ||
821 | { | ||
822 | int err; | ||
823 | void *hdr; | ||
824 | struct nlattr *attrs; | ||
825 | struct nlattr *prop; | ||
826 | |||
827 | if (!bcl) | ||
828 | return 0; | ||
829 | |||
830 | tipc_bclink_lock(); | ||
831 | |||
832 | hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, | ||
833 | NLM_F_MULTI, TIPC_NL_LINK_GET); | ||
834 | if (!hdr) | ||
835 | return -EMSGSIZE; | ||
836 | |||
837 | attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); | ||
838 | if (!attrs) | ||
839 | goto msg_full; | ||
840 | |||
841 | /* The broadcast link is always up */ | ||
842 | if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP)) | ||
843 | goto attr_msg_full; | ||
844 | |||
845 | if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST)) | ||
846 | goto attr_msg_full; | ||
847 | if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name)) | ||
848 | goto attr_msg_full; | ||
849 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->next_in_no)) | ||
850 | goto attr_msg_full; | ||
851 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->next_out_no)) | ||
852 | goto attr_msg_full; | ||
853 | |||
854 | prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); | ||
855 | if (!prop) | ||
856 | goto attr_msg_full; | ||
857 | if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->queue_limit[0])) | ||
858 | goto prop_msg_full; | ||
859 | nla_nest_end(msg->skb, prop); | ||
860 | |||
861 | err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats); | ||
862 | if (err) | ||
863 | goto attr_msg_full; | ||
864 | |||
865 | tipc_bclink_unlock(); | ||
866 | nla_nest_end(msg->skb, attrs); | ||
867 | genlmsg_end(msg->skb, hdr); | ||
868 | |||
869 | return 0; | ||
870 | |||
871 | prop_msg_full: | ||
872 | nla_nest_cancel(msg->skb, prop); | ||
873 | attr_msg_full: | ||
874 | nla_nest_cancel(msg->skb, attrs); | ||
875 | msg_full: | ||
876 | tipc_bclink_unlock(); | ||
877 | genlmsg_cancel(msg->skb, hdr); | ||
878 | |||
879 | return -EMSGSIZE; | ||
880 | } | ||
770 | 881 | ||
771 | int tipc_bclink_stats(char *buf, const u32 buf_size) | 882 | int tipc_bclink_stats(char *buf, const u32 buf_size) |
772 | { | 883 | { |
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index e7b0f85a82bc..443de084d3e8 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h | |||
@@ -37,6 +37,8 @@ | |||
37 | #ifndef _TIPC_BCAST_H | 37 | #ifndef _TIPC_BCAST_H |
38 | #define _TIPC_BCAST_H | 38 | #define _TIPC_BCAST_H |
39 | 39 | ||
40 | #include "netlink.h" | ||
41 | |||
40 | #define MAX_NODES 4096 | 42 | #define MAX_NODES 4096 |
41 | #define WSIZE 32 | 43 | #define WSIZE 32 |
42 | #define TIPC_BCLINK_RESET 1 | 44 | #define TIPC_BCLINK_RESET 1 |
@@ -100,4 +102,6 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action); | |||
100 | uint tipc_bclink_get_mtu(void); | 102 | uint tipc_bclink_get_mtu(void); |
101 | int tipc_bclink_xmit(struct sk_buff *buf); | 103 | int tipc_bclink_xmit(struct sk_buff *buf); |
102 | void tipc_bclink_wakeup_users(void); | 104 | void tipc_bclink_wakeup_users(void); |
105 | int tipc_nl_add_bc_link(struct tipc_nl_msg *msg); | ||
106 | |||
103 | #endif | 107 | #endif |
diff --git a/net/tipc/link.c b/net/tipc/link.c index e7f365012bd1..bdc4b5e5bf56 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c | |||
@@ -36,6 +36,7 @@ | |||
36 | 36 | ||
37 | #include "core.h" | 37 | #include "core.h" |
38 | #include "link.h" | 38 | #include "link.h" |
39 | #include "bcast.h" | ||
39 | #include "socket.h" | 40 | #include "socket.h" |
40 | #include "name_distr.h" | 41 | #include "name_distr.h" |
41 | #include "discover.h" | 42 | #include "discover.h" |
@@ -51,6 +52,22 @@ static const char *link_co_err = "Link changeover error, "; | |||
51 | static const char *link_rst_msg = "Resetting link "; | 52 | static const char *link_rst_msg = "Resetting link "; |
52 | static const char *link_unk_evt = "Unknown link event "; | 53 | static const char *link_unk_evt = "Unknown link event "; |
53 | 54 | ||
55 | static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = { | ||
56 | [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC }, | ||
57 | [TIPC_NLA_LINK_NAME] = { | ||
58 | .type = NLA_STRING, | ||
59 | .len = TIPC_MAX_LINK_NAME | ||
60 | }, | ||
61 | [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 }, | ||
62 | [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG }, | ||
63 | [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG }, | ||
64 | [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG }, | ||
65 | [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED }, | ||
66 | [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED }, | ||
67 | [TIPC_NLA_LINK_RX] = { .type = NLA_U32 }, | ||
68 | [TIPC_NLA_LINK_TX] = { .type = NLA_U32 } | ||
69 | }; | ||
70 | |||
54 | /* Properties valid for media, bearar and link */ | 71 | /* Properties valid for media, bearar and link */ |
55 | static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { | 72 | static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { |
56 | [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, | 73 | [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, |
@@ -2423,3 +2440,273 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]) | |||
2423 | 2440 | ||
2424 | return 0; | 2441 | return 0; |
2425 | } | 2442 | } |
2443 | |||
2444 | int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s) | ||
2445 | { | ||
2446 | int i; | ||
2447 | struct nlattr *stats; | ||
2448 | |||
2449 | struct nla_map { | ||
2450 | u32 key; | ||
2451 | u32 val; | ||
2452 | }; | ||
2453 | |||
2454 | struct nla_map map[] = { | ||
2455 | {TIPC_NLA_STATS_RX_INFO, s->recv_info}, | ||
2456 | {TIPC_NLA_STATS_RX_FRAGMENTS, s->recv_fragments}, | ||
2457 | {TIPC_NLA_STATS_RX_FRAGMENTED, s->recv_fragmented}, | ||
2458 | {TIPC_NLA_STATS_RX_BUNDLES, s->recv_bundles}, | ||
2459 | {TIPC_NLA_STATS_RX_BUNDLED, s->recv_bundled}, | ||
2460 | {TIPC_NLA_STATS_TX_INFO, s->sent_info}, | ||
2461 | {TIPC_NLA_STATS_TX_FRAGMENTS, s->sent_fragments}, | ||
2462 | {TIPC_NLA_STATS_TX_FRAGMENTED, s->sent_fragmented}, | ||
2463 | {TIPC_NLA_STATS_TX_BUNDLES, s->sent_bundles}, | ||
2464 | {TIPC_NLA_STATS_TX_BUNDLED, s->sent_bundled}, | ||
2465 | {TIPC_NLA_STATS_MSG_PROF_TOT, (s->msg_length_counts) ? | ||
2466 | s->msg_length_counts : 1}, | ||
2467 | {TIPC_NLA_STATS_MSG_LEN_CNT, s->msg_length_counts}, | ||
2468 | {TIPC_NLA_STATS_MSG_LEN_TOT, s->msg_lengths_total}, | ||
2469 | {TIPC_NLA_STATS_MSG_LEN_P0, s->msg_length_profile[0]}, | ||
2470 | {TIPC_NLA_STATS_MSG_LEN_P1, s->msg_length_profile[1]}, | ||
2471 | {TIPC_NLA_STATS_MSG_LEN_P2, s->msg_length_profile[2]}, | ||
2472 | {TIPC_NLA_STATS_MSG_LEN_P3, s->msg_length_profile[3]}, | ||
2473 | {TIPC_NLA_STATS_MSG_LEN_P4, s->msg_length_profile[4]}, | ||
2474 | {TIPC_NLA_STATS_MSG_LEN_P5, s->msg_length_profile[5]}, | ||
2475 | {TIPC_NLA_STATS_MSG_LEN_P6, s->msg_length_profile[6]}, | ||
2476 | {TIPC_NLA_STATS_RX_STATES, s->recv_states}, | ||
2477 | {TIPC_NLA_STATS_RX_PROBES, s->recv_probes}, | ||
2478 | {TIPC_NLA_STATS_RX_NACKS, s->recv_nacks}, | ||
2479 | {TIPC_NLA_STATS_RX_DEFERRED, s->deferred_recv}, | ||
2480 | {TIPC_NLA_STATS_TX_STATES, s->sent_states}, | ||
2481 | {TIPC_NLA_STATS_TX_PROBES, s->sent_probes}, | ||
2482 | {TIPC_NLA_STATS_TX_NACKS, s->sent_nacks}, | ||
2483 | {TIPC_NLA_STATS_TX_ACKS, s->sent_acks}, | ||
2484 | {TIPC_NLA_STATS_RETRANSMITTED, s->retransmitted}, | ||
2485 | {TIPC_NLA_STATS_DUPLICATES, s->duplicates}, | ||
2486 | {TIPC_NLA_STATS_LINK_CONGS, s->link_congs}, | ||
2487 | {TIPC_NLA_STATS_MAX_QUEUE, s->max_queue_sz}, | ||
2488 | {TIPC_NLA_STATS_AVG_QUEUE, s->queue_sz_counts ? | ||
2489 | (s->accu_queue_sz / s->queue_sz_counts) : 0} | ||
2490 | }; | ||
2491 | |||
2492 | stats = nla_nest_start(skb, TIPC_NLA_LINK_STATS); | ||
2493 | if (!stats) | ||
2494 | return -EMSGSIZE; | ||
2495 | |||
2496 | for (i = 0; i < ARRAY_SIZE(map); i++) | ||
2497 | if (nla_put_u32(skb, map[i].key, map[i].val)) | ||
2498 | goto msg_full; | ||
2499 | |||
2500 | nla_nest_end(skb, stats); | ||
2501 | |||
2502 | return 0; | ||
2503 | msg_full: | ||
2504 | nla_nest_cancel(skb, stats); | ||
2505 | |||
2506 | return -EMSGSIZE; | ||
2507 | } | ||
2508 | |||
2509 | /* Caller should hold appropriate locks to protect the link */ | ||
2510 | int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link) | ||
2511 | { | ||
2512 | int err; | ||
2513 | void *hdr; | ||
2514 | struct nlattr *attrs; | ||
2515 | struct nlattr *prop; | ||
2516 | |||
2517 | hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, | ||
2518 | NLM_F_MULTI, TIPC_NL_LINK_GET); | ||
2519 | if (!hdr) | ||
2520 | return -EMSGSIZE; | ||
2521 | |||
2522 | attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); | ||
2523 | if (!attrs) | ||
2524 | goto msg_full; | ||
2525 | |||
2526 | if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name)) | ||
2527 | goto attr_msg_full; | ||
2528 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, | ||
2529 | tipc_cluster_mask(tipc_own_addr))) | ||
2530 | goto attr_msg_full; | ||
2531 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt)) | ||
2532 | goto attr_msg_full; | ||
2533 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->next_in_no)) | ||
2534 | goto attr_msg_full; | ||
2535 | if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->next_out_no)) | ||
2536 | goto attr_msg_full; | ||
2537 | |||
2538 | if (tipc_link_is_up(link)) | ||
2539 | if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP)) | ||
2540 | goto attr_msg_full; | ||
2541 | if (tipc_link_is_active(link)) | ||
2542 | if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE)) | ||
2543 | goto attr_msg_full; | ||
2544 | |||
2545 | prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); | ||
2546 | if (!prop) | ||
2547 | goto attr_msg_full; | ||
2548 | if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) | ||
2549 | goto prop_msg_full; | ||
2550 | if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance)) | ||
2551 | goto prop_msg_full; | ||
2552 | if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, | ||
2553 | link->queue_limit[TIPC_LOW_IMPORTANCE])) | ||
2554 | goto prop_msg_full; | ||
2555 | if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) | ||
2556 | goto prop_msg_full; | ||
2557 | nla_nest_end(msg->skb, prop); | ||
2558 | |||
2559 | err = __tipc_nl_add_stats(msg->skb, &link->stats); | ||
2560 | if (err) | ||
2561 | goto attr_msg_full; | ||
2562 | |||
2563 | nla_nest_end(msg->skb, attrs); | ||
2564 | genlmsg_end(msg->skb, hdr); | ||
2565 | |||
2566 | return 0; | ||
2567 | |||
2568 | prop_msg_full: | ||
2569 | nla_nest_cancel(msg->skb, prop); | ||
2570 | attr_msg_full: | ||
2571 | nla_nest_cancel(msg->skb, attrs); | ||
2572 | msg_full: | ||
2573 | genlmsg_cancel(msg->skb, hdr); | ||
2574 | |||
2575 | return -EMSGSIZE; | ||
2576 | } | ||
2577 | |||
2578 | /* Caller should hold node lock */ | ||
2579 | int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, struct tipc_node *node, | ||
2580 | u32 *prev_link) | ||
2581 | { | ||
2582 | u32 i; | ||
2583 | int err; | ||
2584 | |||
2585 | for (i = *prev_link; i < MAX_BEARERS; i++) { | ||
2586 | *prev_link = i; | ||
2587 | |||
2588 | if (!node->links[i]) | ||
2589 | continue; | ||
2590 | |||
2591 | err = __tipc_nl_add_link(msg, node->links[i]); | ||
2592 | if (err) | ||
2593 | return err; | ||
2594 | } | ||
2595 | *prev_link = 0; | ||
2596 | |||
2597 | return 0; | ||
2598 | } | ||
2599 | |||
2600 | int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
2601 | { | ||
2602 | struct tipc_node *node; | ||
2603 | struct tipc_nl_msg msg; | ||
2604 | u32 prev_node = cb->args[0]; | ||
2605 | u32 prev_link = cb->args[1]; | ||
2606 | int done = cb->args[2]; | ||
2607 | int err; | ||
2608 | |||
2609 | if (done) | ||
2610 | return 0; | ||
2611 | |||
2612 | msg.skb = skb; | ||
2613 | msg.portid = NETLINK_CB(cb->skb).portid; | ||
2614 | msg.seq = cb->nlh->nlmsg_seq; | ||
2615 | |||
2616 | rcu_read_lock(); | ||
2617 | |||
2618 | if (prev_node) { | ||
2619 | node = tipc_node_find(prev_node); | ||
2620 | if (!node) { | ||
2621 | /* We never set seq or call nl_dump_check_consistent() | ||
2622 | * this means that setting prev_seq here will cause the | ||
2623 | * consistence check to fail in the netlink callback | ||
2624 | * handler. Resulting in the last NLMSG_DONE message | ||
2625 | * having the NLM_F_DUMP_INTR flag set. | ||
2626 | */ | ||
2627 | cb->prev_seq = 1; | ||
2628 | goto out; | ||
2629 | } | ||
2630 | |||
2631 | list_for_each_entry_continue_rcu(node, &tipc_node_list, list) { | ||
2632 | tipc_node_lock(node); | ||
2633 | err = __tipc_nl_add_node_links(&msg, node, &prev_link); | ||
2634 | tipc_node_unlock(node); | ||
2635 | if (err) | ||
2636 | goto out; | ||
2637 | |||
2638 | prev_node = node->addr; | ||
2639 | } | ||
2640 | } else { | ||
2641 | err = tipc_nl_add_bc_link(&msg); | ||
2642 | if (err) | ||
2643 | goto out; | ||
2644 | |||
2645 | list_for_each_entry_rcu(node, &tipc_node_list, list) { | ||
2646 | tipc_node_lock(node); | ||
2647 | err = __tipc_nl_add_node_links(&msg, node, &prev_link); | ||
2648 | tipc_node_unlock(node); | ||
2649 | if (err) | ||
2650 | goto out; | ||
2651 | |||
2652 | prev_node = node->addr; | ||
2653 | } | ||
2654 | } | ||
2655 | done = 1; | ||
2656 | out: | ||
2657 | rcu_read_unlock(); | ||
2658 | |||
2659 | cb->args[0] = prev_node; | ||
2660 | cb->args[1] = prev_link; | ||
2661 | cb->args[2] = done; | ||
2662 | |||
2663 | return skb->len; | ||
2664 | } | ||
2665 | |||
2666 | int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info) | ||
2667 | { | ||
2668 | struct sk_buff *ans_skb; | ||
2669 | struct tipc_nl_msg msg; | ||
2670 | struct tipc_link *link; | ||
2671 | struct tipc_node *node; | ||
2672 | char *name; | ||
2673 | int bearer_id; | ||
2674 | int err; | ||
2675 | |||
2676 | if (!info->attrs[TIPC_NLA_LINK_NAME]) | ||
2677 | return -EINVAL; | ||
2678 | |||
2679 | name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]); | ||
2680 | node = tipc_link_find_owner(name, &bearer_id); | ||
2681 | if (!node) | ||
2682 | return -EINVAL; | ||
2683 | |||
2684 | ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
2685 | if (!ans_skb) | ||
2686 | return -ENOMEM; | ||
2687 | |||
2688 | msg.skb = ans_skb; | ||
2689 | msg.portid = info->snd_portid; | ||
2690 | msg.seq = info->snd_seq; | ||
2691 | |||
2692 | tipc_node_lock(node); | ||
2693 | link = node->links[bearer_id]; | ||
2694 | if (!link) { | ||
2695 | err = -EINVAL; | ||
2696 | goto err_out; | ||
2697 | } | ||
2698 | |||
2699 | err = __tipc_nl_add_link(&msg, link); | ||
2700 | if (err) | ||
2701 | goto err_out; | ||
2702 | |||
2703 | tipc_node_unlock(node); | ||
2704 | |||
2705 | return genlmsg_reply(ans_skb, info); | ||
2706 | |||
2707 | err_out: | ||
2708 | tipc_node_unlock(node); | ||
2709 | nlmsg_free(ans_skb); | ||
2710 | |||
2711 | return err; | ||
2712 | } | ||
diff --git a/net/tipc/link.h b/net/tipc/link.h index 4338294f20d4..8e3542a4a037 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h | |||
@@ -240,6 +240,8 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window); | |||
240 | void tipc_link_retransmit(struct tipc_link *l_ptr, | 240 | void tipc_link_retransmit(struct tipc_link *l_ptr, |
241 | struct sk_buff *start, u32 retransmits); | 241 | struct sk_buff *start, u32 retransmits); |
242 | 242 | ||
243 | int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb); | ||
244 | int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info); | ||
243 | int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]); | 245 | int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]); |
244 | 246 | ||
245 | /* | 247 | /* |
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 9bc64aaff466..ea168b889caa 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include "config.h" | 38 | #include "config.h" |
39 | #include "socket.h" | 39 | #include "socket.h" |
40 | #include "bearer.h" | 40 | #include "bearer.h" |
41 | #include "link.h" | ||
41 | #include <net/genetlink.h> | 42 | #include <net/genetlink.h> |
42 | 43 | ||
43 | static int handle_cmd(struct sk_buff *skb, struct genl_info *info) | 44 | static int handle_cmd(struct sk_buff *skb, struct genl_info *info) |
@@ -74,7 +75,8 @@ static const struct nla_policy tipc_nl_policy[TIPC_NLA_MAX + 1] = { | |||
74 | [TIPC_NLA_UNSPEC] = { .type = NLA_UNSPEC, }, | 75 | [TIPC_NLA_UNSPEC] = { .type = NLA_UNSPEC, }, |
75 | [TIPC_NLA_BEARER] = { .type = NLA_NESTED, }, | 76 | [TIPC_NLA_BEARER] = { .type = NLA_NESTED, }, |
76 | [TIPC_NLA_SOCK] = { .type = NLA_NESTED, }, | 77 | [TIPC_NLA_SOCK] = { .type = NLA_NESTED, }, |
77 | [TIPC_NLA_PUBL] = { .type = NLA_NESTED, } | 78 | [TIPC_NLA_PUBL] = { .type = NLA_NESTED, }, |
79 | [TIPC_NLA_LINK] = { .type = NLA_NESTED, }, | ||
78 | }; | 80 | }; |
79 | 81 | ||
80 | /* Legacy ASCII API */ | 82 | /* Legacy ASCII API */ |
@@ -136,6 +138,12 @@ static const struct genl_ops tipc_genl_v2_ops[] = { | |||
136 | .cmd = TIPC_NL_PUBL_GET, | 138 | .cmd = TIPC_NL_PUBL_GET, |
137 | .dumpit = tipc_nl_publ_dump, | 139 | .dumpit = tipc_nl_publ_dump, |
138 | .policy = tipc_nl_policy, | 140 | .policy = tipc_nl_policy, |
141 | }, | ||
142 | { | ||
143 | .cmd = TIPC_NL_LINK_GET, | ||
144 | .doit = tipc_nl_link_get, | ||
145 | .dumpit = tipc_nl_link_dump, | ||
146 | .policy = tipc_nl_policy, | ||
139 | } | 147 | } |
140 | }; | 148 | }; |
141 | 149 | ||