diff options
| author | Julian Anastasov <ja@ssi.bg> | 2012-09-04 07:03:15 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2012-09-05 15:15:02 -0400 |
| commit | d23ff701643a4a725e2c7a8ba2d567d39daa29ea (patch) | |
| tree | 039dbda9d59c1e18df6a89f600226924d452fbed | |
| parent | ab868256f8d6095e7200d928fcc054b66d0f13a3 (diff) | |
tcp: add generic netlink support for tcp_metrics
Add support for genl "tcp_metrics". No locking
is changed, only that now we can unlink and delete
entries after grace period. We implement get/del for
single entry and dump to support show/flush filtering
in user space. Del without address attribute causes
flush for all addresses, sadly under genl_mutex.
v2:
- remove rcu_assign_pointer as suggested by Eric Dumazet,
it is not needed because there are no other writes under lock
- move the flushing code in tcp_metrics_flush_all
v3:
- remove synchronize_rcu on flush as suggested by Eric Dumazet
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/linux/Kbuild | 1 | ||||
| -rw-r--r-- | include/linux/tcp_metrics.h | 54 | ||||
| -rw-r--r-- | net/ipv4/tcp_metrics.c | 354 |
3 files changed, 396 insertions, 13 deletions
diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 1f2c1c787f17..90da0af28352 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild | |||
| @@ -363,6 +363,7 @@ header-y += sysctl.h | |||
| 363 | header-y += sysinfo.h | 363 | header-y += sysinfo.h |
| 364 | header-y += taskstats.h | 364 | header-y += taskstats.h |
| 365 | header-y += tcp.h | 365 | header-y += tcp.h |
| 366 | header-y += tcp_metrics.h | ||
| 366 | header-y += telephony.h | 367 | header-y += telephony.h |
| 367 | header-y += termios.h | 368 | header-y += termios.h |
| 368 | header-y += time.h | 369 | header-y += time.h |
diff --git a/include/linux/tcp_metrics.h b/include/linux/tcp_metrics.h new file mode 100644 index 000000000000..cb5157b55f32 --- /dev/null +++ b/include/linux/tcp_metrics.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | /* tcp_metrics.h - TCP Metrics Interface */ | ||
| 2 | |||
| 3 | #ifndef _LINUX_TCP_METRICS_H | ||
| 4 | #define _LINUX_TCP_METRICS_H | ||
| 5 | |||
| 6 | #include <linux/types.h> | ||
| 7 | |||
| 8 | /* NETLINK_GENERIC related info | ||
| 9 | */ | ||
| 10 | #define TCP_METRICS_GENL_NAME "tcp_metrics" | ||
| 11 | #define TCP_METRICS_GENL_VERSION 0x1 | ||
| 12 | |||
| 13 | enum tcp_metric_index { | ||
| 14 | TCP_METRIC_RTT, | ||
| 15 | TCP_METRIC_RTTVAR, | ||
| 16 | TCP_METRIC_SSTHRESH, | ||
| 17 | TCP_METRIC_CWND, | ||
| 18 | TCP_METRIC_REORDERING, | ||
| 19 | |||
| 20 | /* Always last. */ | ||
| 21 | __TCP_METRIC_MAX, | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define TCP_METRIC_MAX (__TCP_METRIC_MAX - 1) | ||
| 25 | |||
| 26 | enum { | ||
| 27 | TCP_METRICS_ATTR_UNSPEC, | ||
| 28 | TCP_METRICS_ATTR_ADDR_IPV4, /* u32 */ | ||
| 29 | TCP_METRICS_ATTR_ADDR_IPV6, /* binary */ | ||
| 30 | TCP_METRICS_ATTR_AGE, /* msecs */ | ||
| 31 | TCP_METRICS_ATTR_TW_TSVAL, /* u32, raw, rcv tsval */ | ||
| 32 | TCP_METRICS_ATTR_TW_TS_STAMP, /* s32, sec age */ | ||
| 33 | TCP_METRICS_ATTR_VALS, /* nested +1, u32 */ | ||
| 34 | TCP_METRICS_ATTR_FOPEN_MSS, /* u16 */ | ||
| 35 | TCP_METRICS_ATTR_FOPEN_SYN_DROPS, /* u16, count of drops */ | ||
| 36 | TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS, /* msecs age */ | ||
| 37 | TCP_METRICS_ATTR_FOPEN_COOKIE, /* binary */ | ||
| 38 | |||
| 39 | __TCP_METRICS_ATTR_MAX, | ||
| 40 | }; | ||
| 41 | |||
| 42 | #define TCP_METRICS_ATTR_MAX (__TCP_METRICS_ATTR_MAX - 1) | ||
| 43 | |||
| 44 | enum { | ||
| 45 | TCP_METRICS_CMD_UNSPEC, | ||
| 46 | TCP_METRICS_CMD_GET, | ||
| 47 | TCP_METRICS_CMD_DEL, | ||
| 48 | |||
| 49 | __TCP_METRICS_CMD_MAX, | ||
| 50 | }; | ||
| 51 | |||
| 52 | #define TCP_METRICS_CMD_MAX (__TCP_METRICS_CMD_MAX - 1) | ||
| 53 | |||
| 54 | #endif /* _LINUX_TCP_METRICS_H */ | ||
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 0abe67bb4d3a..988edb63ee73 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <linux/init.h> | 8 | #include <linux/init.h> |
| 9 | #include <linux/tcp.h> | 9 | #include <linux/tcp.h> |
| 10 | #include <linux/hash.h> | 10 | #include <linux/hash.h> |
| 11 | #include <linux/tcp_metrics.h> | ||
| 11 | 12 | ||
| 12 | #include <net/inet_connection_sock.h> | 13 | #include <net/inet_connection_sock.h> |
| 13 | #include <net/net_namespace.h> | 14 | #include <net/net_namespace.h> |
| @@ -17,20 +18,10 @@ | |||
| 17 | #include <net/ipv6.h> | 18 | #include <net/ipv6.h> |
| 18 | #include <net/dst.h> | 19 | #include <net/dst.h> |
| 19 | #include <net/tcp.h> | 20 | #include <net/tcp.h> |
| 21 | #include <net/genetlink.h> | ||
| 20 | 22 | ||
| 21 | int sysctl_tcp_nometrics_save __read_mostly; | 23 | int sysctl_tcp_nometrics_save __read_mostly; |
| 22 | 24 | ||
| 23 | enum tcp_metric_index { | ||
| 24 | TCP_METRIC_RTT, | ||
| 25 | TCP_METRIC_RTTVAR, | ||
| 26 | TCP_METRIC_SSTHRESH, | ||
| 27 | TCP_METRIC_CWND, | ||
| 28 | TCP_METRIC_REORDERING, | ||
| 29 | |||
| 30 | /* Always last. */ | ||
| 31 | TCP_METRIC_MAX, | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct tcp_fastopen_metrics { | 25 | struct tcp_fastopen_metrics { |
| 35 | u16 mss; | 26 | u16 mss; |
| 36 | u16 syn_loss:10; /* Recurring Fast Open SYN losses */ | 27 | u16 syn_loss:10; /* Recurring Fast Open SYN losses */ |
| @@ -45,8 +36,10 @@ struct tcp_metrics_block { | |||
| 45 | u32 tcpm_ts; | 36 | u32 tcpm_ts; |
| 46 | u32 tcpm_ts_stamp; | 37 | u32 tcpm_ts_stamp; |
| 47 | u32 tcpm_lock; | 38 | u32 tcpm_lock; |
| 48 | u32 tcpm_vals[TCP_METRIC_MAX]; | 39 | u32 tcpm_vals[TCP_METRIC_MAX + 1]; |
| 49 | struct tcp_fastopen_metrics tcpm_fastopen; | 40 | struct tcp_fastopen_metrics tcpm_fastopen; |
| 41 | |||
| 42 | struct rcu_head rcu_head; | ||
| 50 | }; | 43 | }; |
| 51 | 44 | ||
| 52 | static bool tcp_metric_locked(struct tcp_metrics_block *tm, | 45 | static bool tcp_metric_locked(struct tcp_metrics_block *tm, |
| @@ -690,6 +683,325 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, | |||
| 690 | rcu_read_unlock(); | 683 | rcu_read_unlock(); |
| 691 | } | 684 | } |
| 692 | 685 | ||
| 686 | static struct genl_family tcp_metrics_nl_family = { | ||
| 687 | .id = GENL_ID_GENERATE, | ||
| 688 | .hdrsize = 0, | ||
| 689 | .name = TCP_METRICS_GENL_NAME, | ||
| 690 | .version = TCP_METRICS_GENL_VERSION, | ||
| 691 | .maxattr = TCP_METRICS_ATTR_MAX, | ||
| 692 | .netnsok = true, | ||
| 693 | }; | ||
| 694 | |||
| 695 | static struct nla_policy tcp_metrics_nl_policy[TCP_METRICS_ATTR_MAX + 1] = { | ||
| 696 | [TCP_METRICS_ATTR_ADDR_IPV4] = { .type = NLA_U32, }, | ||
| 697 | [TCP_METRICS_ATTR_ADDR_IPV6] = { .type = NLA_BINARY, | ||
| 698 | .len = sizeof(struct in6_addr), }, | ||
| 699 | /* Following attributes are not received for GET/DEL, | ||
| 700 | * we keep them for reference | ||
| 701 | */ | ||
| 702 | #if 0 | ||
| 703 | [TCP_METRICS_ATTR_AGE] = { .type = NLA_MSECS, }, | ||
| 704 | [TCP_METRICS_ATTR_TW_TSVAL] = { .type = NLA_U32, }, | ||
| 705 | [TCP_METRICS_ATTR_TW_TS_STAMP] = { .type = NLA_S32, }, | ||
| 706 | [TCP_METRICS_ATTR_VALS] = { .type = NLA_NESTED, }, | ||
| 707 | [TCP_METRICS_ATTR_FOPEN_MSS] = { .type = NLA_U16, }, | ||
| 708 | [TCP_METRICS_ATTR_FOPEN_SYN_DROPS] = { .type = NLA_U16, }, | ||
| 709 | [TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS] = { .type = NLA_MSECS, }, | ||
| 710 | [TCP_METRICS_ATTR_FOPEN_COOKIE] = { .type = NLA_BINARY, | ||
| 711 | .len = TCP_FASTOPEN_COOKIE_MAX, }, | ||
| 712 | #endif | ||
| 713 | }; | ||
| 714 | |||
| 715 | /* Add attributes, caller cancels its header on failure */ | ||
| 716 | static int tcp_metrics_fill_info(struct sk_buff *msg, | ||
| 717 | struct tcp_metrics_block *tm) | ||
| 718 | { | ||
| 719 | struct nlattr *nest; | ||
| 720 | int i; | ||
| 721 | |||
| 722 | switch (tm->tcpm_addr.family) { | ||
| 723 | case AF_INET: | ||
| 724 | if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, | ||
| 725 | tm->tcpm_addr.addr.a4) < 0) | ||
| 726 | goto nla_put_failure; | ||
| 727 | break; | ||
| 728 | case AF_INET6: | ||
| 729 | if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, | ||
| 730 | tm->tcpm_addr.addr.a6) < 0) | ||
| 731 | goto nla_put_failure; | ||
| 732 | break; | ||
| 733 | default: | ||
| 734 | return -EAFNOSUPPORT; | ||
| 735 | } | ||
| 736 | |||
| 737 | if (nla_put_msecs(msg, TCP_METRICS_ATTR_AGE, | ||
| 738 | jiffies - tm->tcpm_stamp) < 0) | ||
| 739 | goto nla_put_failure; | ||
| 740 | if (tm->tcpm_ts_stamp) { | ||
| 741 | if (nla_put_s32(msg, TCP_METRICS_ATTR_TW_TS_STAMP, | ||
| 742 | (s32) (get_seconds() - tm->tcpm_ts_stamp)) < 0) | ||
| 743 | goto nla_put_failure; | ||
| 744 | if (nla_put_u32(msg, TCP_METRICS_ATTR_TW_TSVAL, | ||
| 745 | tm->tcpm_ts) < 0) | ||
| 746 | goto nla_put_failure; | ||
| 747 | } | ||
| 748 | |||
| 749 | { | ||
| 750 | int n = 0; | ||
| 751 | |||
| 752 | nest = nla_nest_start(msg, TCP_METRICS_ATTR_VALS); | ||
| 753 | if (!nest) | ||
| 754 | goto nla_put_failure; | ||
| 755 | for (i = 0; i < TCP_METRIC_MAX + 1; i++) { | ||
| 756 | if (!tm->tcpm_vals[i]) | ||
| 757 | continue; | ||
| 758 | if (nla_put_u32(msg, i + 1, tm->tcpm_vals[i]) < 0) | ||
| 759 | goto nla_put_failure; | ||
| 760 | n++; | ||
| 761 | } | ||
| 762 | if (n) | ||
| 763 | nla_nest_end(msg, nest); | ||
| 764 | else | ||
| 765 | nla_nest_cancel(msg, nest); | ||
| 766 | } | ||
| 767 | |||
| 768 | { | ||
| 769 | struct tcp_fastopen_metrics tfom_copy[1], *tfom; | ||
