totofugaのブログ

ネットワークとかc言語、perlの話。

小規模時のtcp_probeについて

輳制御を確認してグラフ化するのに最近tcp_probeを使用しています。

iperfとかで試す時には良いのですが、少ないパケットを送って

cat /proc/net/tcpprobe

等試すと全く何も表示されません。 (full=1オプションをつけてもダメ。。)

このままだと小規模で使えないので調べてみると

readで指定したバッファサイズ以上パケットがこないと処理をかえさなくなってました。 ということで通常のreadと同じようにパケットがあれば即返すように変更。 ついでにheadがbuf超えた時も怪しそうだったので修正。。。

diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 3d063eb..309ff0a 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -85,6 +85,9 @@ struct tcp_log {
 
 static inline int tcp_probe_used(void)
 {
+       if ( tcp_probe.tail > tcp_probe.head )  {
+               return (tcp_probe.head + bufsize - tcp_probe.tail) & (bufsize - 1);
+       }
        return (tcp_probe.head - tcp_probe.tail) & (bufsize - 1);
 }
 
@@ -207,17 +210,18 @@ static ssize_t tcpprobe_read(struct file *file, char __user *buf,
        if (!buf)
                return -EINVAL;
 
-       while (cnt < len) {
+       while (cnt == 0 || tcp_probe.head != tcp_probe.tail) {
                char tbuf[256];
                int width;
 
                /* Wait for data in buffer */
                error = wait_event_interruptible(tcp_probe.wait,
                                                 tcp_probe_used() > 0);
-               if (error)
-                       break;
-
+        if (error)
+            return error;
+        
                spin_lock_bh(&tcp_probe.lock);
+        /* double-checked locking */
                if (tcp_probe.head == tcp_probe.tail) {
                        /* multiple readers race? */
                        spin_unlock_bh(&tcp_probe.lock);
@@ -225,20 +229,20 @@ static ssize_t tcpprobe_read(struct file *file, char __user *buf,
                }
 
                width = tcpprobe_sprint(tbuf, sizeof(tbuf));
-
-               if (cnt + width < len)
-                       tcp_probe.tail = (tcp_probe.tail + 1) & (bufsize - 1);
-
                spin_unlock_bh(&tcp_probe.lock);
 
                /* if record greater than space available
                   return partial buffer (so far) */
-               if (cnt + width >= len)
+               if (cnt + width > len)
                        break;
 
+        tcp_probe.tail = (tcp_probe.tail + 1) & (bufsize - 1);
                if (copy_to_user(buf + cnt, tbuf, width))
                        return -EFAULT;
                cnt += width;
+
+               if (cnt ==  len) 
+                       break;
        }
 
        return cnt == 0 ? error : cnt;

tcp_probeについての使い方はこの辺りを参考にしてください https://wiki.linuxfoundation.org/networking/tcpprobe:tcpprobe

tcp_probeが入っていない場合はkernelのビルド時 CONFIG_NET_TCPPROBE=mに設定してください。