Q. LAN 経由での FreeBSD から Windows へのデータ転送が、その逆に比べ
   圧倒的に遅いです。Windows 同士だとこのような現象は起りません。
A. 一般的な解は、一般的な要因に依存するため、何とも判りませんが、次
   のような場合が Winsock2 の場合にあり得ることは判っています。
   一部の NIC では、NIC のせいなのか NIC Driver のせいなのかは不明で
   すが、Windows で使った場合に、時々 packet の取りこぼしが発生します。
   これは、NIC が用意している Recieve Buffer の大きさが、Winsock2 の
   Recieve Buffer よりも小さい場合、Winsock2 が NIC から packet を
   吸い出す前に NIC の Recieve Buffer が溢れてしまうために起るのでは
   ないか、と推測されます。
   Winsock2 の挙動を tcpdump などで見ると判るのですが、Windows 同士の
   通信において、Winsock は送信において次のようなルールに従っているよ
   うに見えます(これは、そう「見える」と言っているだけであって、そう
   「なっている」という意味で言っているのではありません。Winsock2 の
   実装がどうなっているのかは、そのソースコードを見ることができない以
   上、全く不明です)。
     I) 2つのマシン A,B があって、A->B というデータ転送しかない場合、
        A というマシンは
        Send, Send, Ack 待ち
        というパターンを忠実に守っているように見える。
    II) 2つのマシン A,B があって、双方向に転送すべきデータがある場合、
        A というマシンは
        Send(これには直前の Send に対する Ack を含んでいる),
        Data 待ち(これには直前の Send に対する Ack が含まれている)
        を繰り返す、というパターンを忠実に守っているように見える。
   III) I と II を切り替える際には 0.1 sec の wait が入る。
   もし、Windows 側の NIC のせいでこの現象が起っている場合は、
   Windows->FreeBSD 方向の通信には、この障害は出ていないはずです。
   (別の障害は出ているかも知れませんが :)
   さて、問題の解決方法ですが、ようするに上記の動きを FreeBSD 側で真
   似てやれば問題は解決します。具体的には TCP/IP の Send Buffer Size
   をどうにかして「IP Packet 2つ分」まで減らしてやれば、問題は解決す
   るはずなのです。そうすれば Winsock2 がそれを全て取り込み、Ack を返
   してくるまで先には進めません。
   「IP Packet 2つ分」が何バイトなのかを知るには、基本的には tcpdump
   を使うのが手だと思います。tcpdump は root であれば FreeBSD で使う
   ことができますし、
     WinDump <URL:http://windump.polito.it/>
   を使えば Windows 側で tcpdump を取ることもできます。これらを使って、
   mss の値を獲得します。これが「IP Packet 1つ分」の大きさだと思って
   ください。これを2倍すれば目的の値になります。
   あるいは、ethernet を使っている場合は、mss は大抵、536byte か 
   1460byte のどちらかです。ですので、その丁度2倍 1072byte かあるいは
   2920byte が「IP Packet 2つ分だ」という事ができます。ですので、
   2920 を使ってみて、駄目だったら 1072 を使ってみる、という手もあり
   ます。
   で、ここから先は FreeBSD の管理戦略にかなり依存します。
   1) システム全体を特定の Windows マシンに合わせる、という場合:
      この場合、 sysctl コマンドを使うと良いでしょう。root になって
        $ sysctl -w net.inet.tcp.sendspace=xxxx
      (xxxx は上記の「IP Packet 2つ分」の大きさ) を設定してみましょう。
      多分これでパフォーマンスは向上するはずです。
      ちなみに、
        $ sysctl -w net.inet.tcp.delayed_ack=0
      を設定すると Windows -> FreeBSD 方向の通信も改善される場合がある、
      という話があるそうです。
   2) Windows マシン毎に設定を変える場合:
      残念ながらマシン毎に FreeBSD の設定を変える場合、1 の場合のような
      汎用的戦略はありません。各アプリケーションが、それぞれ、自分が通信
      している相手を認識し、その適切な値をどこかのテーブルから lookup し
      なくてはいけませんが、そのようなサポートのないソフトもあるからです。
      もし、Samba に関して、というのであれば、smb.conf の中の
      [global] section の最後に
        include = /usr/local/etc/smb.conf.global.%m
      のような1行を加えておき、
        /usr/local/etc/smb.conf.global.<その問題の Windows マシン名>
      というファイル中で
        socket options = SO_SNDBUF=xxxx TCP_NODELAY
        (xxxx は上記の「IP Packet 2つ分」の大きさ) 
      を設定するとよいでしょう。上記の include 文は失敗しても smbd の
      起動に影響はありません。従って、特に特別な設定をする必要がない
      target に対しては
        /usr/local/etc/smb.conf.global.<その問題の Windows マシン名>
      というファイルを作成する必要はありません。このあたりは
        <URL:http://www.dd.iij4u.or.jp/~okuyamak/Documents/tuning.japanese.html>
      を参考にして下さい。
      この方法は Winsock2 についてしかチェックできていません。従って、
      Winsock1.1 の場合、あるいは将来できるであろう、Winsock2.1 (あるいは
      Winsock3) でうまく行くのかどうかは判りません。
      もし、現在すでにある程度パフォーマンスが良い場合、SO_SNDBUF の値を
      mss の整数倍にすることで、効率が向上する可能性があります。ですので、
      パフォーマンスを重視するマシンに対しては、そのマシン専用の
        /usr/local/etc/smb.conf.global.<その問題の Windows マシン名>
      ファイルを作った方がいいかも知れません。
      smb.conf には %a(Architecture) というオプションがあります。マルチ
      ブートマシンがある場合は smb.conf を
        include = /usr/local/etc/smb.conf.global.%m
        include = /usr/local/etc/smb.conf.global.%a.%M
      のように変更して、%a と %M で Client OS と Machine Name を指定して
      やると各 OS ごとの設定も可能になります。
グループ名: winsock