WiresharkでMTU超えの大きすぎるパケットがキャプチャーされてる!
Linux(CentOS on Let's note)上でRawソケットでキャプチャーをしていたら,64,294バイト(約64KB)とかMTUを軽く超えるパケットが送信されているのを発見!
いやいや,そんなバカな,と思いつつ,Wiresharkでも確認してみたらやっぱりありました.MTUを超えた大きなパケットが.
MTUを超えるサイズのパケットはIP層でフラグメントされてMTUサイズ以下にされるはずなのにおかしい.
RawソケットもWiresharkもIP層の下でパケットをキャプチャしているはずだから,フラグメント後のパケットをキャプチャーできるはずなのに・・・.
原因について,いろいろ調べてみると,分かりました.
「GSO(Generic Segmentation Offload)」でした.
パケットのフラグメントという,わりと重い処理をハードウェアであるNICにオフロードすることでCPU負荷を減らすあれです.
GSOが有効になっているかどうかは,ethtoolコマンドで確認できるようです.
$ ethtool -k eth0 Features for eth0: rx-checksumming: on tx-checksumming: on scatter-gather: on tcp-segmentation-offload: off udp-fragmentation-offload: off generic-segmentation-offload: on generic-receive-offload: off large-receive-offload: off rx-vlan-offload: on tx-vlan-offload: on ntuple-filters: off receive-hashing: off
試しに,
ethtool -K eth0 gso off
で,GSOをオフってみると,WiresharkのキャプチャーでのMTU超えのパケットがすっかりなくなった!
やったー!
GSOって,普通のノートPCのNICにもちゃんと実装されているもんなんですね.
--- 参考サイト ---
Seeing 25,000 byte packets in Wireshark Super packets Unleash Networks Blog
http://www.unleashnetworks.com/blog/?p=307
LinuxでNICのオフロード機能のON/OFF - id:rx7(@namikawa)の技術メモ - 技術日記
http://tech.g.hatena.ne.jp/rx7/20130425/p1
【レポート】FreeBSD 10ギガビットネットワーク高速通信の秘密 (3) ハードウェアオフロードによる処理の高速化 | 開発・SE | マイナビニュース
http://news.mynavi.jp/articles/2008/10/29/bsdcon5/002.html
モーツァルトと同時代の作曲家:ボッケリーニのOctet
モーツァルトやハイドンと同じ時代の作曲家,ルイジ・ボッケリーニさんです.
暖かみのある曲の雰囲気で,わりと好きかも.
そんなボッケリーニさんのVn,Va,Vc,Cbの弦楽とFl,Fg,Hrの編成の曲です.
たくさん曲書いているみたいなので,探せばいい曲出てきそう.演奏会にも使えるかも?
Boccherini / Octet (Notturno) in G major, Op. 38 No. 4 (G. 470) - YouTube
http://www.youtube.com/watch?v=wINglWDcV8E
qemu-kvmの仮想ハードディスクイメージへの読み書き・その1
ゲストOSが仮想HDDイメージにデータを書き込むまでのqemu-kvmの内部動作を追ってみる.
ゲストOSがI/O命令を実行すると,CPUの仮想化支援機能が命令をトラップし,ゲストモードからカーネルモードに移行(VMExit)する.
KVMカーネルモジュール内でVMExit要因の判定を行い,I/O命令の場合はユーザプロセスのqemu-kvmのハードウェアエミュレートが必要になるためカーネルモードからユーザモードへ移行する.
qemu-kvm側では,VMExit要因に応じてエミュレートを行なって,必要であればゲストOS(と言うか仮想マシン)のCPUコンテキストを書き換えたり,割り込みを挿入したり,メモリにデータを書き込んだり,必要な処理を行った後,再びKVMカーネルモジュールにお願いして,ゲストOSの実行を再開する.
というのが,おおまかな流れ.
とりあえず,KVMからqemu-kvmへ処理が戻ったあたりから追って見る.
各仮想CPUの実行は,qemu_kvm_cpu_thread_fn()で行われる.
この関数で実行されているkvm_cpu_exec()のdo-whileループ内で,KVMで仮想CPUの実行する,VMExitで戻ってきたらI/Oイベントを処理する,また仮想CPUを実行する・・・,をずっと繰り返してる.
[qemu-kvm1.2.0]
[cpus.c]
732 static void *qemu_kvm_cpu_thread_fn(void *arg) 733 { ... 755 while (1) { 756 if (cpu_can_run(env)) { 757 r = kvm_cpu_exec(env); 758 if (r == EXCP_DEBUG) { 759 cpu_handle_guest_debug(env); 760 } 761 } 762 qemu_kvm_wait_io_event(env); 763 } ...
[kvm-all.c]
1550 int kvm_cpu_exec(CPUArchState *env) 1551 { .... 1562 do { .... 1580 run_ret = kvm_vcpu_ioctl(env, KVM_RUN, 0); .... 1598 switch (run->exit_reason) { 1599 case KVM_EXIT_IO: 1601 kvm_handle_io(run->io.port, 1602 (uint8_t *)run + run->io.data_offset, 1603 run->io.direction, 1604 run->io.size, 1605 run->io.count); .... 1608 case KVM_EXIT_MMIO: 1610 cpu_physical_memory_rw(run->mmio.phys_addr, 1611 run->mmio.data, 1612 run->mmio.len, 1613 run->mmio.is_write); .... 1616 case KVM_EXIT_IRQ_WINDOW_OPEN: .... 1620 case KVM_EXIT_SHUTDOWN: .... 1625 case KVM_EXIT_UNKNOWN: .... 1630 case KVM_EXIT_INTERNAL_ERROR: .... 1633 default: .... 1638 } while (ret == 0);
今回はI/Oを追うので,VMExitの要因が「KVM_EXIT_IO」だった時に実行されるkvm_handle_io()を追う.
I/Oポートへの読み込みか,書き込みかを判定し,読み書きサイズでスイッチしてる.
[kvm-all.c]
1431 static void kvm_handle_io(uint16_t port, void *data, int direction, int size, 1432 uint32_t count) 1433 { 1434 int i; 1435 uint8_t *ptr = data; 1436 1437 for (i = 0; i < count; i++) { 1438 if (direction == KVM_EXIT_IO_IN) { 1439 switch (size) { 1440 case 1: 1441 stb_p(ptr, cpu_inb(port)); 1442 break; 1443 case 2: .... 1449 } 1450 } else { 1451 switch (size) { 1452 case 1: 1453 cpu_outb(port, ldub_p(ptr)); 1454 break; 1443 case 2: .... 1461 } 1462 } ....
I/Oポートに対して,読み込みだった場合はioport_read(),書き込みだった場合はioport_write()が呼ばれ,各ポートのアドレスごとに関数テーブルに登録された関数が呼び出される.
[ioport.c]
60 static uint32_t ioport_read(int index, uint32_t address) 61 { 62 static IOPortReadFunc * const default_func[3] = { 63 default_ioport_readb, 64 default_ioport_readw, 65 default_ioport_readl 66 }; 67 IOPortReadFunc *func = ioport_read_table[index][address]; 68 if (!func) 69 func = default_func[index]; 70 return func(ioport_opaque[address], address); 71 } 72 73 static void ioport_write(int index, uint32_t address, uint32_t data) 74 { 75 static IOPortWriteFunc * const default_func[3] = { 76 default_ioport_writeb, 77 default_ioport_writew, 78 default_ioport_writel 79 }; 80 IOPortWriteFunc *func = ioport_write_table[index][address]; 81 if (!func) 82 func = default_func[index]; 83 func(ioport_opaque[address], address, data); 84 }
この先は,各仮想ハードウェア(仮想HDD)ごとの処理に移るはず.
次回に続く.
無料ダイナミックDNSサービス
ddo.jpが無料ユーザ9ヶ月制限がかかってしまったので,乗り換え検討.
無料ダイナミックDNS(DDNS)サービス - ieServer.Net
http://ieserver.net/
ドメイン取るなら お名前.com - ドメイン取得 年間180円〜
http://onamae.com/
リアルタイムOSについての参考になる記事
技術の広場 - 高田広章のリアルタイムOS論---目次:ITpro
http://itpro.nikkeibp.co.jp/article/COLUMN/20060921/248659/?ST=ittrend
技術の広場 - 高田広章のリアルタイムOS論【第2回】(3):ITpro
http://itpro.nikkeibp.co.jp/article/COLUMN/20050908/220793/
LinuxのTCPのウィンドウサイズの設定値
Linux 3.3.4でのデフォルトの設定値をメモ.
数値は[min, default, max]の意味.
この値にから,TCPのウィンドウサイズは,デフォルトは16KBから始まって,4KBから4MBの間で調整される.
これより,TCPを用いて通信を行う場合,最大で4MBのバースト転送が発生することがわかる.
$ cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
$ cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 4194304
参考サイト
kernel/システムパラメタ - Linux Tips
http://linux.mini13i.com/?kernel%2F%A5%B7%A5%B9%A5%C6%A5%E0%A5%D1%A5%E9%A5%E1%A5%BF
Note - ソケットバッファのチューニング
http://www.anarg.jp/personal/t-tugawa/note/linux/sockbuf.html