From 03fcbae2ecd73a8a4c70b1f3b4afbe36e4463fd9 Mon Sep 17 00:00:00 2001 From: Xuan Zhuo Date: Tue, 19 May 2020 11:52:37 +0800 Subject: [PATCH 1718/2944] alinux: tcp_rt module: add tcp_rt module to #27804112 TCP-RT is a kernel module for monitoring services at the tcp level. TCP-RT is essentially a trace method. By burying points in advance in the corresponding position in the kernel tcp protocol stack, we can identify the request and response from the scenario where there is only one concurrent request for a single connection, then collect the time when the request is received in the protocol stack and the time-consuming information on the processing of the service process and so on. In addition, TCP-RT also supports some statistical analysis in the kernel and periodically outputs statistical information about the specified connection. Signed-off-by: Xuan Zhuo Acked-by: Dust Li Reviewed-by: Ya Zhao Reviewed-by: Cambda Zhu --- MAINTAINERS | 1 + net/ipv4/Kconfig | 2 + net/ipv4/Makefile | 1 + net/ipv4/tcp_rt/Kconfig | 7 + net/ipv4/tcp_rt/Makefile | 3 + net/ipv4/tcp_rt/core.c | 377 +++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_rt/output.c | 368 +++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_rt/tcp_rt.h | 115 +++++++++++++++ 8 files changed, 874 insertions(+) create mode 100644 net/ipv4/tcp_rt/Kconfig create mode 100644 net/ipv4/tcp_rt/Makefile create mode 100644 net/ipv4/tcp_rt/core.c create mode 100644 net/ipv4/tcp_rt/output.c create mode 100644 net/ipv4/tcp_rt/tcp_rt.h diff --git a/MAINTAINERS b/MAINTAINERS index c7270d3..7a0514b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -142,6 +142,7 @@ M: Cambda Zhu M: xuanzhuo S: Maintained F: net/ipv4/tcp_rt.c +F: net/ipv4/tcp_rt/ 3C59X NETWORK DRIVER M: Steffen Klassert diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 1a93cdf..b2263d2 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -766,3 +766,5 @@ config TCP_RT This is used for account opening kernel framework, more work needs tcprt module support. + +source "net/ipv4/tcp_rt/Kconfig" diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index e0d13d6..746d41d 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_TCP_RT) += tcp_rt.o +obj-$(CONFIG_TCP_RT_MODULE) += tcp_rt/ obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ xfrm4_output.o xfrm4_protocol.o diff --git a/net/ipv4/tcp_rt/Kconfig b/net/ipv4/tcp_rt/Kconfig new file mode 100644 index 0000000..867fe288 --- /dev/null +++ b/net/ipv4/tcp_rt/Kconfig @@ -0,0 +1,7 @@ +config TCP_RT_MODULE + tristate "TCP-RT module" + default m + depends on TCP_RT + help + This module depends on TCP-RT framework, collect the request + information on tcp and output by relay. diff --git a/net/ipv4/tcp_rt/Makefile b/net/ipv4/tcp_rt/Makefile new file mode 100644 index 0000000..86e4e8e --- /dev/null +++ b/net/ipv4/tcp_rt/Makefile @@ -0,0 +1,3 @@ +TARGET = tcp_rt +obj-$(CONFIG_TCP_RT_MODULE) = $(TARGET).o +$(TARGET)-objs += output.o core.o diff --git a/net/ipv4/tcp_rt/core.c b/net/ipv4/tcp_rt/core.c new file mode 100644 index 0000000..a6313ef --- /dev/null +++ b/net/ipv4/tcp_rt/core.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "tcp_rt.h" + +static int ports_number; +static int ports_peer_number; + +static int ports[PORT_MAX_NUM]; +static int ports_peer[PORT_MAX_NUM]; + +static int log_buf_num = 8; +static int real_buf_num = 2; + +static int check = 1; +static int check_interval = 60; + +module_param(check, int, 0644); +MODULE_PARM_DESC(check, "whether check the init_cwnd is ok"); + +module_param(check_interval, int, 0644); +MODULE_PARM_DESC(check_interval, "how many seconds later do stats"); + +module_param_array(ports, int, &ports_number, 0644); +MODULE_PARM_DESC(ports, "port array of port_number size"); + +module_param_array(ports_peer, int, &ports_peer_number, 0644); +MODULE_PARM_DESC(ports_peer, "peer ports array"); + +module_param(log_buf_num, int, 0644); +MODULE_PARM_DESC(log_buf_num, "the num of buffers for every cpu log buffer. unit is 256k. just work when module load."); +module_param(real_buf_num, int, 0644); +MODULE_PARM_DESC(real_buf_num, "the num of buffers for every cpu real buffer. unit is 16k. just work when module load."); + +struct timer_list tcp_rt_timer; + +static void tcp_rt_timer_handler(struct timer_list *t) +{ + if (check) { + int i = 0; + + for (i = 0; i < ports_number; i++) + tcp_rt_timer_output(i, ports[i], "L"); + + for (i = 0; i < ports_peer_number; i++) + tcp_rt_timer_output(i, ports_peer[i], "P"); + } + mod_timer(&tcp_rt_timer, jiffies + check_interval * HZ); +} + +static void tcp_rt_sk_send_data_peer(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + switch (rt->stage_peer) { + case TCPRT_STAGE_PEER_RESPONSE: + tcp_rt_log_printk(sk, LOG_STATUS_P, false, check); + /* fall through */ + + case TCPRT_STAGE_PEER_NONE: + ktime_get_real_ts64(&rt->start_time); + rt->request_num += 1; + rt->upload_data = 0; + rt->stage_peer = TCPRT_STAGE_PEER_REQUEST; + + rt->last_total_retrans = tp->total_retrans; + break; + + default: + break; + } +} + +static void tcp_rt_sk_send_data_local(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + u64 this_us; + + switch (rt->stage) { + case TCPRT_STAGE_REQUEST: + this_us = tcp_clock_us(); + + rt->stage = TCPRT_STAGE_RESPONSE; + + rt->server_time = this_us - rt->lrcvtime_us; + rt->upload_time = rt->lrcvtime_us - rt->frcvtime_us; + rt->upload_data = tp->rcv_nxt - rt->start_rcv_nxt; + + /* because recv_data is after rcv_nxt update, so + * record the value at here. + */ + rt->start_rcv_nxt = tp->rcv_nxt; + + break; + + case TCPRT_STAGE_RESPONSE: + break; + + case TCPRT_STAGE_NONE: + break; + } +} + +static void tcp_rt_sk_recv_data_peer(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + switch (rt->stage_peer) { + case TCPRT_STAGE_PEER_REQUEST: + rt->stage_peer = TCPRT_STAGE_PEER_RESPONSE; + rt->upload_data = tp->snd_nxt - rt->start_seq; + rt->start_seq = tp->snd_nxt; + return; + + default: + break; + } +} + +static void tcp_rt_sk_recv_data_local(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + switch (rt->stage) { + case TCPRT_STAGE_RESPONSE: + if (after(tp->snd_nxt, tp->snd_una + 1)) { + /* there are some bytes not acked. + * but new request is coming, + * so the skb also be the acked skb for snd_una, + * so update the end_time + */ + ktime_get_real_ts64(&rt->end_time); + } + tcp_rt_log_printk(sk, LOG_STATUS_R, false, check); + + /* fall through */ + + case TCPRT_STAGE_NONE: + ktime_get_real_ts64(&rt->start_time); + rt->end_time = rt->start_time; + + rt->frcvtime_us = tcp_clock_us(); + rt->lrcvtime_us = rt->frcvtime_us; + + rt->start_seq = tp->snd_nxt; + + rt->last_total_retrans = tp->total_retrans; + rt->last_update_seq = tp->snd_una; + + rt->server_time = 0; + rt->upload_time = 0; + rt->upload_data = 0; + + rt->rcv_reorder = 0; + + rt->stage = TCPRT_STAGE_REQUEST; + + rt->request_num += 1; + if (!RB_EMPTY_ROOT(&tp->out_of_order_queue)) + rt->rcv_reorder = 1; + break; + + case TCPRT_STAGE_REQUEST: + rt->lrcvtime_us = tcp_clock_us(); + if (!RB_EMPTY_ROOT(&tp->out_of_order_queue)) + rt->rcv_reorder = 1; + break; + } +} + +static void tcp_rt_sk_release_peer(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + switch (rt->stage_peer) { + case TCPRT_STAGE_PEER_NONE: + case TCPRT_STAGE_PEER_REQUEST: + return; + + case TCPRT_STAGE_PEER_RESPONSE: + tcp_rt_log_printk(sk, LOG_STATUS_P, false, check); + return; + } +} + +static void tcp_rt_sk_release_local(const struct sock *sk, struct tcp_rt *rt, + const struct tcp_sock *tp) +{ + switch (rt->stage) { + case TCPRT_TYPE_NONE: + break; + + case TCPRT_STAGE_REQUEST: + /* closed when receiving data */ + tcp_rt_log_printk(sk, LOG_STATUS_N, false, check); + break; + + case TCPRT_STAGE_RESPONSE: + if (tp->snd_nxt <= rt->start_seq + 1) + break; + + if (tp->snd_nxt <= tp->snd_una + 1) { + if (after(tp->snd_una, rt->last_update_seq + 1)) + ktime_get_real_ts64(&rt->end_time); + + tcp_rt_log_printk(sk, LOG_STATUS_R, true, + check); + } else { + /* closed when sending data */ + tcp_rt_log_printk(sk, LOG_STATUS_W, false, + check); + } + + break; + } +} + +static void tcp_rt_sk_release(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct tcp_rt *rt = TCP_SK_RT(sk); + + if (!rt->request_num) + goto free; + + if (rt->type == TCPRT_TYPE_PEER_PORT) + tcp_rt_sk_release_peer(sk, rt, tp); + else + tcp_rt_sk_release_local(sk, rt, tp); + + /* closed, 1 record per connection */ + tcp_rt_log_printk(sk, LOG_STATUS_E, false, check); + +free: + kfree(rt); +} + +static void tcp_rt_sk_pkts_acked(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_rt *rt = TCP_SK_RT(sk); + + if (after(tp->snd_una, rt->last_update_seq + 1)) { + ktime_get_real_ts64(&rt->end_time); + rt->last_update_seq = tp->snd_una; + } +} + +static void tcp_rt_sk_send_data(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_rt *rt = TCP_SK_RT(sk); + + if (rt->type == TCPRT_TYPE_PEER_PORT) + return tcp_rt_sk_send_data_peer(sk, rt, tp); + else + return tcp_rt_sk_send_data_local(sk, rt, tp); +} + +static void tcp_rt_sk_recv_data(struct sock *sk) +{ + struct tcp_rt *rt = TCP_SK_RT(sk); + struct tcp_sock *tp = tcp_sk(sk); + + if (rt->type == TCPRT_TYPE_PEER_PORT) + return tcp_rt_sk_recv_data_peer(sk, rt, tp); + else + return tcp_rt_sk_recv_data_local(sk, rt, tp); +} + +static int tcp_rt_sk_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); + + struct tcp_rt *rt = NULL; + enum tcp_rt_type type = TCPRT_TYPE_NONE; + int i, port; + + port = ntohs(inet_sk(sk)->inet_sport); + + for (i = 0; i < ports_number; i++) { + if (port == ports[i]) { + type = TCPRT_TYPE_LOCAL_PORT; + goto ok; + } + } + + port = ntohs(inet_sk(sk)->inet_dport); + + for (i = 0; i < ports_peer_number; i++) { + if (port == ports_peer[i]) { + type = TCPRT_TYPE_PEER_PORT; + goto ok; + } + } + + return -1; +ok: + rt = kmalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + return -1; + + memset(rt, 0, sizeof(*rt)); + + icsk->icsk_tcp_rt_priv = (void *)rt; + + rt->type = type; + rt->index = i; + + rt->con_start_seq = tp->snd_nxt; + rt->con_rcv_nxt = tp->rcv_nxt; + + /* because recv_data is after rcv_nxt update, so + * record the value at here. + */ + rt->start_rcv_nxt = tp->rcv_nxt; + rt->start_seq = tp->snd_nxt; + ktime_get_real_ts64(&rt->con_start_time); + return 0; +} + +static struct tcp_rt_ops rt_ops __read_mostly = { + .owner = THIS_MODULE, + .init = tcp_rt_sk_init, + .recv_data = tcp_rt_sk_recv_data, + .send_data = tcp_rt_sk_send_data, + .pkts_acked = tcp_rt_sk_pkts_acked, + .release = tcp_rt_sk_release, +}; + +static ssize_t tcp_rt_deactivate(struct file *file, const char __user *buff, + size_t count, loff_t *offset) +{ + pr_info("tcp-rt: deactivate\n"); + tcp_unregister_rt(&rt_ops); + return count; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .write = tcp_rt_deactivate, +}; + +static int __init tcp_rt_module_init(void) +{ + int ret; + + ret = tcp_rt_output_init(log_buf_num, real_buf_num, &fops); + if (ret) + return ret; + + ret = tcp_register_rt(&rt_ops); + if (ret) { + pr_err("tcp-rt register rt failed!\n"); + tcp_rt_output_released(); + return ret; + } + + timer_setup(&tcp_rt_timer, tcp_rt_timer_handler, 0); + tcp_rt_timer.expires = jiffies + 10 * HZ; + add_timer(&tcp_rt_timer); + + pr_info("tcp-rt: module load success\n"); + return 0; +} + +static void __exit tcp_rt_module_fini(void) +{ + del_timer_sync(&tcp_rt_timer); + tcp_rt_output_released(); + pr_info("tcp-rt: module unloaded\n"); +} + +module_init(tcp_rt_module_init); +module_exit(tcp_rt_module_fini); + +MODULE_AUTHOR("xuanzhuo "); +MODULE_AUTHOR("Cambda Zhu "); +MODULE_AUTHOR("Ya Zhao "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP RT"); diff --git a/net/ipv4/tcp_rt/output.c b/net/ipv4/tcp_rt/output.c new file mode 100644 index 0000000..b163c88 --- /dev/null +++ b/net/ipv4/tcp_rt/output.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include "tcp_rt.h" + +static struct rchan *relay_log; +static struct rchan *relay_stats; +static struct dentry *tcprt_dir; +static struct tcp_rt_real statis_local[PORT_MAX_NUM]; +static struct tcp_rt_real statis_peer[PORT_MAX_NUM]; + +#define get_statis_local(rt) (statis_local[(rt)->index]) +#define get_statis_peer(rt) (statis_peer[(rt)->index]) +#define statis_local_inc(rt, item) atomic64_inc(&statis_local[(rt)->index].item) +#define statis_peer_inc(rt, item) atomic64_inc(&statis_peer[(rt)->index].item) +#define statis_local_add(rt, item, val) \ + atomic64_add(val, &statis_local[(rt)->index].item) +#define statis_peer_add(rt, item, val) \ + atomic64_add(val, &statis_peer[(rt)->index].item) + +static int64_t timespec64_dec(struct timespec64 tv1, struct timespec64 tv2) +{ + return (tv1.tv_sec - tv2.tv_sec) * 1000000 + + (tv1.tv_nsec - tv2.tv_nsec) / 1000; +} + +static int ulong_format(char *buf, unsigned long val) +{ + int idx = 0; + int i; + char tmp[80]; + + if (val == 0) { + tmp[idx++] = '0'; + } else { + while (val != 0) { + tmp[idx++] = (val % 10) + '0'; + val /= 10; + } + } + for (i = 0; i < idx; i++) + buf[i] = tmp[idx - i - 1]; + + return idx; +} + +static int ulong_format2(char *buf, unsigned long val) +{ + int idx = 0; + + idx += ulong_format(buf, val); + buf[idx++] = ' '; + return idx; +} + +static int ip_format2(char *buf, u32 addr, char end) +{ + unsigned char *s = (unsigned char *)&addr; + int idx = 0; + + idx += ulong_format(buf + idx, (unsigned long)s[0]); + buf[idx++] = '.'; + idx += ulong_format(buf + idx, (unsigned long)s[1]); + buf[idx++] = '.'; + idx += ulong_format(buf + idx, (unsigned long)s[2]); + buf[idx++] = '.'; + idx += ulong_format(buf + idx, (unsigned long)s[3]); + buf[idx++] = end; + return idx; +} + +#define bufappend(buf, size, v) \ + ulong_format2((buf) + (size), (unsigned long)(v)) + +static int bufheader(char *buf, int size, char flag, + const struct sock *sk) +{ + struct tcp_rt *rt = TCP_SK_RT(sk); + int n = 0; + + buf[n++] = 'V'; + buf[n++] = '6'; + buf[n++] = ' '; + buf[n++] = flag; + buf[n++] = ' '; + + n += bufappend(buf, n, rt->start_time.tv_sec); + n += bufappend(buf, n, rt->start_time.tv_nsec / 1000); + n += ip_format2(buf + n, (u32)(inet_sk(sk)->inet_daddr), ':'); + n += bufappend(buf, n, ntohs(inet_sk(sk)->inet_dport)); + n += ip_format2(buf + n, (u32)(inet_sk(sk)->inet_saddr), ':'); + n += bufappend(buf, n, ntohs(inet_sk(sk)->inet_sport)); + + return n; +} + +void tcp_rt_log_printk(const struct sock *sk, char flag, bool fin, bool check) +{ +#define MAX_BUF_SIZE 512 + + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_rt *rt = TCP_SK_RT(sk); + char buf[MAX_BUF_SIZE]; + int size = 0; + u32 t_rt; + u32 t_seq, t_retrans; + struct timespec64 now; + u32 start_time, mrtt; + + mrtt = tcp_min_rtt(tp) >> 10; + + start_time = rt->start_time.tv_sec; + + switch (flag) { + case LOG_STATUS_R: + t_rt = timespec64_dec(rt->end_time, rt->start_time); + t_seq = tp->snd_nxt - rt->start_seq; + + /* When come from socket closing, the snd_nxt may include fin, + * so add this options. + * In some cases, there may be no fin, but here is also + * reduced by one byte. But one byte has little effect. + */ + if (fin) + --t_seq; + + t_retrans = tp->total_retrans - rt->last_total_retrans; + + size = bufheader(buf, size, flag, sk); + + size += bufappend(buf, size, t_seq); + size += bufappend(buf, size, t_rt); + size += bufappend(buf, size, mrtt); + size += bufappend(buf, size, t_retrans); + size += bufappend(buf, size, rt->request_num); + size += bufappend(buf, size, rt->server_time); + size += bufappend(buf, size, rt->upload_time); + size += bufappend(buf, size, rt->upload_data); + size += bufappend(buf, size, rt->rcv_reorder); + size += bufappend(buf, size, tp->mss_cache); + buf[size++] = '\n'; + + if (check && t_seq > 0) { + statis_local_inc(rt, number); + + statis_local_add(rt, rt, t_rt); + statis_local_add(rt, bytes, t_seq); + statis_local_add(rt, drop, t_retrans); + statis_local_add(rt, packets, + t_seq / tp->mss_cache + 1); + statis_local_add(rt, server_time, rt->server_time); + statis_local_add(rt, upload_time, rt->upload_time); + statis_local_add(rt, upload_data, rt->upload_data); + } + break; + + case LOG_STATUS_W: + ktime_get_real_ts64(&now); + t_rt = timespec64_dec(now, rt->start_time); + + size = bufheader(buf, size, flag, sk); + + size += bufappend(buf, size, tp->snd_nxt - rt->start_seq); + size += bufappend(buf, size, t_rt); + size += bufappend(buf, size, mrtt); + size += bufappend(buf, size, + tp->total_retrans - rt->last_total_retrans); + size += bufappend(buf, size, rt->request_num); + size += bufappend(buf, size, rt->server_time); + size += bufappend(buf, size, rt->upload_time); + size += bufappend(buf, size, tp->snd_nxt - tp->snd_una); + size += bufappend(buf, size, rt->rcv_reorder); + size += bufappend(buf, size, tp->mss_cache); + buf[size++] = '\n'; + + if (check && t_rt > HZ / 10) + statis_local_inc(rt, fail); + break; + + case LOG_STATUS_N: + ktime_get_real_ts64(&now); + t_rt = timespec64_dec(now, rt->start_time); + + size = bufheader(buf, size, flag, sk); + + size += bufappend(buf, size, rt->request_num); + size += bufappend(buf, size, t_rt); + size += bufappend(buf, size, tp->rcv_nxt - rt->start_rcv_nxt); + size += bufappend(buf, size, rt->rcv_reorder); + size += bufappend(buf, size, tp->mss_cache); + buf[size++] = '\n'; + break; + + case LOG_STATUS_E: + size = bufheader(buf, size, flag, sk); + + size += bufappend(buf, size, rt->request_num); + size += bufappend(buf, size, tp->snd_nxt - rt->con_start_seq); + size += bufappend(buf, size, tp->snd_nxt - tp->snd_una); + size += bufappend(buf, size, tp->rcv_nxt - rt->con_rcv_nxt); + size += bufappend(buf, size, tp->total_retrans); + size += bufappend(buf, size, mrtt); + buf[size++] = '\n'; + + if (mrtt > 0) { + statis_local_inc(rt, con_num); + statis_local_add(rt, rtt, mrtt); + } + break; + + case LOG_STATUS_P: + t_seq = rt->upload_data; + t_retrans = tp->total_retrans - rt->last_total_retrans; + + size = bufheader(buf, size, flag, sk); + + size += bufappend(buf, size, t_seq); + size += bufappend(buf, size, t_retrans); + size += bufappend(buf, size, rt->request_num); + size += bufappend(buf, size, mrtt); + size += bufappend(buf, size, tp->mss_cache); + buf[size++] = '\n'; + + if (check) { + statis_peer_inc(rt, number); + statis_peer_inc(rt, con_num); + statis_peer_add(rt, bytes, t_seq); + statis_peer_add(rt, rtt, mrtt); + statis_peer_add(rt, packets, t_seq / tp->mss_cache + 1); + statis_peer_add(rt, drop, t_retrans); + } + break; + } + + if (relay_log) + relay_write(relay_log, buf, size); +} + +void tcp_rt_timer_output(int index, int port, char *flag) +{ + struct tcp_rt_real *r; + struct _tcp_rt_real t; + struct _tcp_rt_real avg = {0}; + int size; + + char buf[MAX_BUF_SIZE]; + + if (*flag == 'L') { + r = statis_local + index; + flag = ""; + } else { + r = statis_peer + index; + } + + t.rt = atomic64_read(&r->rt); + t.number = atomic64_read(&r->number); + t.bytes = atomic64_read(&r->bytes); + t.drop = atomic64_read(&r->drop); + t.fail = atomic64_read(&r->fail); + t.server_time = atomic64_read(&r->server_time); + t.packets = atomic64_read(&r->packets); + t.con_num = atomic64_read(&r->con_num); + t.rtt = atomic64_read(&r->rtt); + t.upload_time = atomic64_read(&r->upload_time); + t.upload_data = atomic64_read(&r->upload_data); + + if (t.number > 0) { + avg.rt = t.rt / t.number; + avg.fail = 1000 * t.fail / t.number; + avg.bytes = t.bytes / t.number; + avg.server_time = t.server_time / t.number; + avg.upload_time = t.upload_time / t.number; + avg.upload_data = t.upload_data / t.number; + if (t.packets > 0) + avg.drop = 1000 * t.drop / t.packets; + } + + if (t.con_num > 0) + avg.rtt = t.rtt / t.con_num; + + size = snprintf(buf, sizeof(buf), + "%llu all %s%u %u %u %u %u %u %u %u %u %u\n", + ktime_get_real_seconds(), flag, port, avg.rt, + avg.server_time, avg.drop, avg.rtt, avg.fail, avg.bytes, + avg.upload_time, avg.upload_data, t.number); + + if (relay_stats) + relay_write(relay_stats, buf, size); + + atomic64_set(&r->rt, 0); + atomic64_set(&r->number, 0); + atomic64_set(&r->bytes, 0); + atomic64_set(&r->drop, 0); + atomic64_set(&r->fail, 0); + atomic64_set(&r->server_time, 0); + atomic64_set(&r->packets, 0); + atomic64_set(&r->con_num, 0); + atomic64_set(&r->rtt, 0); + atomic64_set(&r->upload_time, 0); + atomic64_set(&r->upload_data, 0); +} + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + return debugfs_create_file(filename, mode, tcprt_dir, buf, + &relay_file_operations); +} + +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static struct rchan_callbacks relay_callbacks = { + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +int tcp_rt_output_init(int log_buf_num, int real_buf_num, + const struct file_operations *fops) +{ + tcprt_dir = debugfs_create_dir("tcp-rt", NULL); + if (!tcprt_dir) + return -1; + + if (!debugfs_create_file("deactivate", 0600, tcprt_dir, NULL, fops)) { + debugfs_remove_recursive(tcprt_dir); + pr_err("tcp-rt: register debugfs deactivate fail!\n"); + return -1; + } + + relay_log = relay_open("rt-network-log", NULL, LOG_SUBBUF_SIZE, + log_buf_num, &relay_callbacks, NULL); + if (!relay_log) { + debugfs_remove_recursive(tcprt_dir); + tcprt_dir = NULL; + pr_err("tcp-rt: create relay_log failed!\n"); + return -1; + } + pr_info("tcp-rt: relay_log ready!\n"); + + relay_stats = relay_open("rt-network-real", NULL, STATS_SUBBUF_SIZE, + real_buf_num, &relay_callbacks, NULL); + if (!relay_stats) { + relay_close(relay_log); + relay_log = NULL; + debugfs_remove_recursive(tcprt_dir); + tcprt_dir = NULL; + pr_err("tcp-rt: create relay_stats failed!\n"); + return -1; + } + + pr_info("tcp_rt: relay_stats ready!\n"); + return 0; +} + +void tcp_rt_output_released(void) +{ + relay_close(relay_log); + relay_close(relay_stats); + debugfs_remove_recursive(tcprt_dir); + pr_info("tcp-rt: output released\n"); +} diff --git a/net/ipv4/tcp_rt/tcp_rt.h b/net/ipv4/tcp_rt/tcp_rt.h new file mode 100644 index 0000000..18c382f --- /dev/null +++ b/net/ipv4/tcp_rt/tcp_rt.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Alibaba Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCP_SK_RT(sk) (inet_csk(sk)->icsk_tcp_rt_priv) + +#define LOG_SUBBUF_SIZE 262144 +#define STATS_SUBBUF_SIZE 16384 +#define PORT_MAX_NUM 6 + +#define LOG_STATUS_R 'R' +#define LOG_STATUS_W 'W' +#define LOG_STATUS_N 'N' +#define LOG_STATUS_E 'E' +#define LOG_STATUS_P 'P' + +enum tcp_rt_type { + TCPRT_TYPE_NONE, + TCPRT_TYPE_LOCAL_PORT, + TCPRT_TYPE_LOCAL_PORT_RANG, + TCPRT_TYPE_PEER_PORT, +}; + +enum tcp_rt_stage { + TCPRT_STAGE_NONE, + TCPRT_STAGE_REQUEST, + TCPRT_STAGE_RESPONSE, +}; + +enum tcp_rt_stage_peer { + TCPRT_STAGE_PEER_NONE, + TCPRT_STAGE_PEER_REQUEST, + TCPRT_STAGE_PEER_RESPONSE, +}; + +struct tcp_rt { + enum tcp_rt_type type; + struct timespec64 con_start_time; + + u32 con_start_seq; + u32 con_rcv_nxt; + u32 index; + u32 request_num; + + /* task item */ + struct timespec64 start_time; + struct timespec64 end_time; + + u64 frcvtime_us; + u64 lrcvtime_us; + + u32 start_seq; + u32 start_rcv_nxt; + + u32 last_total_retrans; + u32 last_update_seq; + + int server_time; + int upload_time; + u32 upload_data; + + u8 rcv_reorder; + + enum tcp_rt_stage stage; + enum tcp_rt_stage_peer stage_peer; +}; + +struct tcp_rt_real { + atomic64_t rt; + atomic64_t number; + atomic64_t drop; + atomic64_t bytes; + atomic64_t server_time; + atomic64_t fail; + atomic64_t packets; + atomic64_t rtt; + atomic64_t upload_time; + atomic64_t upload_data; + atomic64_t con_num; +}; + +struct _tcp_rt_real { + u32 rt; + u32 number; + u32 drop; + u32 bytes; + u32 server_time; + u32 fail; + u32 packets; + u32 rtt; + u32 upload_time; + u32 upload_data; + u32 con_num; +}; + +int tcp_rt_output_init(int log_buf_num, int real_buf_num, + const struct file_operations *fops); +void tcp_rt_output_released(void); +void tcp_rt_log_printk(const struct sock *sk, char flag, bool fin, bool check); +void tcp_rt_timer_output(int index, int port, char *flag); -- 1.8.3.1