diff options
-rw-r--r-- | kernel/time.c | 1 | ||||
-rw-r--r-- | net/ipv4/tcp_probe.c | 68 |
2 files changed, 40 insertions, 29 deletions
diff --git a/kernel/time.c b/kernel/time.c index a1439f421d0b..ba18ec4899bd 100644 --- a/kernel/time.c +++ b/kernel/time.c | |||
@@ -452,6 +452,7 @@ struct timespec ns_to_timespec(const s64 nsec) | |||
452 | 452 | ||
453 | return ts; | 453 | return ts; |
454 | } | 454 | } |
455 | EXPORT_SYMBOL(ns_to_timespec); | ||
455 | 456 | ||
456 | /** | 457 | /** |
457 | * ns_to_timeval - Convert nanoseconds to timeval | 458 | * ns_to_timeval - Convert nanoseconds to timeval |
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 61f406f27294..3938d5dbdf20 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <linux/proc_fs.h> | 26 | #include <linux/proc_fs.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/kfifo.h> | 28 | #include <linux/kfifo.h> |
29 | #include <linux/ktime.h> | ||
30 | #include <linux/time.h> | ||
29 | #include <linux/vmalloc.h> | 31 | #include <linux/vmalloc.h> |
30 | 32 | ||
31 | #include <net/tcp.h> | 33 | #include <net/tcp.h> |
@@ -34,43 +36,45 @@ MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>"); | |||
34 | MODULE_DESCRIPTION("TCP cwnd snooper"); | 36 | MODULE_DESCRIPTION("TCP cwnd snooper"); |
35 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
36 | 38 | ||
37 | static int port = 0; | 39 | static int port __read_mostly = 0; |
38 | MODULE_PARM_DESC(port, "Port to match (0=all)"); | 40 | MODULE_PARM_DESC(port, "Port to match (0=all)"); |
39 | module_param(port, int, 0); | 41 | module_param(port, int, 0); |
40 | 42 | ||
41 | static int bufsize = 64*1024; | 43 | static int bufsize __read_mostly = 64*1024; |
42 | MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)"); | 44 | MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)"); |
43 | module_param(bufsize, int, 0); | 45 | module_param(bufsize, int, 0); |
44 | 46 | ||
47 | static int full __read_mostly; | ||
48 | MODULE_PARM_DESC(full, "Full log (1=every ack packet received, 0=only cwnd changes)"); | ||
49 | module_param(full, int, 0); | ||
50 | |||
45 | static const char procname[] = "tcpprobe"; | 51 | static const char procname[] = "tcpprobe"; |
46 | 52 | ||
47 | struct { | 53 | struct { |
48 | struct kfifo *fifo; | 54 | struct kfifo *fifo; |
49 | spinlock_t lock; | 55 | spinlock_t lock; |
50 | wait_queue_head_t wait; | 56 | wait_queue_head_t wait; |
51 | struct timeval tstart; | 57 | ktime_t start; |
58 | u32 lastcwnd; | ||
52 | } tcpw; | 59 | } tcpw; |
53 | 60 | ||
61 | /* | ||
62 | * Print to log with timestamps. | ||
63 | * FIXME: causes an extra copy | ||
64 | */ | ||
54 | static void printl(const char *fmt, ...) | 65 | static void printl(const char *fmt, ...) |
55 | { | 66 | { |
56 | va_list args; | 67 | va_list args; |
57 | int len; | 68 | int len; |
58 | struct timeval now; | 69 | struct timespec tv; |
59 | char tbuf[256]; | 70 | char tbuf[256]; |
60 | 71 | ||
61 | va_start(args, fmt); | 72 | va_start(args, fmt); |
62 | do_gettimeofday(&now); | 73 | /* want monotonic time since start of tcp_probe */ |
74 | tv = ktime_to_timespec(ktime_sub(ktime_get(), tcpw.start)); | ||
63 | 75 | ||
64 | now.tv_sec -= tcpw.tstart.tv_sec; | 76 | len = sprintf(tbuf, "%lu.%09lu ", |
65 | now.tv_usec -= tcpw.tstart.tv_usec; | 77 | (unsigned long) tv.tv_sec, (unsigned long) tv.tv_nsec); |
66 | if (now.tv_usec < 0) { | ||
67 | --now.tv_sec; | ||
68 | now.tv_usec += 1000000; | ||
69 | } | ||
70 | |||
71 | len = sprintf(tbuf, "%lu.%06lu ", | ||
72 | (unsigned long) now.tv_sec, | ||
73 | (unsigned long) now.tv_usec); | ||
74 | len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args); | 78 | len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args); |
75 | va_end(args); | 79 | va_end(args); |
76 | 80 | ||
@@ -78,38 +82,44 @@ static void printl(const char *fmt, ...) | |||
78 | wake_up(&tcpw.wait); | 82 | wake_up(&tcpw.wait); |
79 | } | 83 | } |
80 | 84 | ||
81 | static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk, | 85 | /* |
82 | struct msghdr *msg, size_t size) | 86 | * Hook inserted to be called before each receive packet. |
87 | * Note: arguments must match tcp_rcv_established()! | ||
88 | */ | ||
89 | static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, | ||
90 | struct tcphdr *th, unsigned len) | ||
83 | { | 91 | { |
84 | const struct tcp_sock *tp = tcp_sk(sk); | 92 | const struct tcp_sock *tp = tcp_sk(sk); |
85 | const struct inet_sock *inet = inet_sk(sk); | 93 | const struct inet_sock *inet = inet_sk(sk); |
86 | 94 | ||
87 | if (port == 0 || ntohs(inet->dport) == port || | 95 | /* Only update if port matches */ |
88 | ntohs(inet->sport) == port) { | 96 | if ((port == 0 || ntohs(inet->dport) == port || ntohs(inet->sport) == port) |
97 | && (full || tp->snd_cwnd != tcpw.lastcwnd)) { | ||
89 | printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n", | 98 | printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n", |
90 | NIPQUAD(inet->saddr), ntohs(inet->sport), | 99 | NIPQUAD(inet->saddr), ntohs(inet->sport), |
91 | NIPQUAD(inet->daddr), ntohs(inet->dport), | 100 | NIPQUAD(inet->daddr), ntohs(inet->dport), |
92 | size, tp->snd_nxt, tp->snd_una, | 101 | skb->len, tp->snd_nxt, tp->snd_una, |
93 | tp->snd_cwnd, tcp_current_ssthresh(sk), | 102 | tp->snd_cwnd, tcp_current_ssthresh(sk), |
94 | tp->snd_wnd); | 103 | tp->snd_wnd, tp->srtt >> 3); |
104 | tcpw.lastcwnd = tp->snd_cwnd; | ||
95 | } | 105 | } |
96 | 106 | ||
97 | jprobe_return(); | 107 | jprobe_return(); |
98 | return 0; | 108 | return 0; |
99 | } | 109 | } |
100 | 110 | ||
101 | static struct jprobe tcp_send_probe = { | 111 | static struct jprobe tcp_probe = { |
102 | .kp = { | 112 | .kp = { |
103 | .symbol_name = "tcp_sendmsg", | 113 | .symbol_name = "tcp_rcv_established", |
104 | }, | 114 | }, |
105 | .entry = JPROBE_ENTRY(jtcp_sendmsg), | 115 | .entry = JPROBE_ENTRY(jtcp_rcv_established), |
106 | }; | 116 | }; |
107 | 117 | ||
108 | 118 | ||
109 | static int tcpprobe_open(struct inode * inode, struct file * file) | 119 | static int tcpprobe_open(struct inode * inode, struct file * file) |
110 | { | 120 | { |
111 | kfifo_reset(tcpw.fifo); | 121 | kfifo_reset(tcpw.fifo); |
112 | do_gettimeofday(&tcpw.tstart); | 122 | tcpw.start = ktime_get(); |
113 | return 0; | 123 | return 0; |
114 | } | 124 | } |
115 | 125 | ||
@@ -162,7 +172,7 @@ static __init int tcpprobe_init(void) | |||
162 | if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops)) | 172 | if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops)) |
163 | goto err0; | 173 | goto err0; |
164 | 174 | ||
165 | ret = register_jprobe(&tcp_send_probe); | 175 | ret = register_jprobe(&tcp_probe); |
166 | if (ret) | 176 | if (ret) |
167 | goto err1; | 177 | goto err1; |
168 | 178 | ||
@@ -180,7 +190,7 @@ static __exit void tcpprobe_exit(void) | |||
180 | { | 190 | { |
181 | kfifo_free(tcpw.fifo); | 191 | kfifo_free(tcpw.fifo); |
182 | proc_net_remove(procname); | 192 | proc_net_remove(procname); |
183 | unregister_jprobe(&tcp_send_probe); | 193 | unregister_jprobe(&tcp_probe); |
184 | 194 | ||
185 | } | 195 | } |
186 | module_exit(tcpprobe_exit); | 196 | module_exit(tcpprobe_exit); |