超高速なパケットI/Oフレームワーク netmap について

の続きで,最近論文読んだやつのプロジェクトの紹介です.

概要

今の汎用OSは高速なパケットI/Oを考慮してない.
20年前のAPIをそのまま使っている.
ネットワークがどんどん高速になっているので,NICとかOSカーネルのパケット処理がボトルネックになってる. (http://news.mynavi.jp/news/2013/04/04/094/index.html)

こういうの解決するために既存手法がいろいろある.

netmapはその試みのひとつ.

  • ユーザプロセス-NIC間の効率のよいパケットI/Oフレームワーク
  • 汎用的な10Gbps環境で14.88Mppsものスループット
  • 60-65 クロックサイクルでwire - userspace間のデータ移動
  • 標準的なデバイスドライバと比較して10-20倍の性能向上
  • FreeBSDにはもう取り込まれている ([base] Revision 227614)

Usenix ATC'12でベストアワード,SIGCOMM 2011でベストアワードを受賞してるらしい.やばそう.

アーキテクチャ

f:id:y_uuki:20130803161153p:plain

netmapは既存の性能向上手法をいくつか使っている.
NICのパケットバッファの(mmapとかで)メモリマッピング,I/Oバッチング,NICのリングキューとマッチするような送信・受信キューのモデリングなど.

既存のものとは違うのはnatmapを使うユーザプロセスのアプリケーションがOSをクラッシュさせることができないようになっていること.
natmapのクライアントはユーザ空間で動作するため,デバイスレジスタやカーネルメモリポインタにダイレクトアクセスできない.

プログラミングモデルは固定長バッファのリングキューを扱うだけなので非常に単純で,アプリケーションは標準的なシステムコールしか使わない.
(ノンブロッキングioctl()でNICと同期,poll()可能なファイルディスクリプタ)

パフォーマンス

f:id:y_uuki:20130803161216p:plain

netmapはCPUのクロックレートが1GHz以下で10Gbpsの上限に達してる.
pktgen(Linuxのカーネルで動作するパケットジェネレータ)やnetsend(UDPを使ったユーザランドで動作するパケットジェネレータ)よりもかなり高いスループットがでてる.

インタフェース

サンプルコードが書いてあった.
/dev/netmapで開いたディスクリプタに対してioctl()とmmap()でリングキューやバッファのメモリ領域を取得して,NETMAP_xxxなインタフェースでリングに対する操作とバッファの中身を読めるイメージ.

重要なのは,1パケットごとにpollとかするんじゃなくて,リングの複数スロットにまとめて1回のシステムコールを発行するところ.
これにより,パケット単位のシステムコールのオーバヘッドを削減できる.

struct netmap_if *nifp;
struct nmreq req;
int i, len;
char *buf;

fd = open("/dev/netmap", 0);
strcpy(req.nr_name, "ix0"); // register the interface
ioctl(fd, NIOCREG, &req); // offset of the structure
mem = mmap(NULL, req.nr_memsize, PROT_READ|PROT_WRITE, 0, fd, 0);
nifp = NETMAP_IF(mem, req.nr_offset);
for (;;) {
    struct pollfd x[1];
    struct netmap_ring *ring = NETMAP_RX_RING(nifp, 0);

    x[0].fd = fd;
    x[0].events = POLLIN;
    poll(x, 1, 1000);
    for ( ; ring->avail > 0 ; ring->avail--) {
        i = ring->cur;
        buf = NETMAP_BUF(ring, i);
        use_data(buf, ring->slot[i].len);
        ring->cur = NETMAP_NEXT(ring, i);
    }
}

ソースコード

システムコール(ioctl,select/poll)に対するパッチとNICドライバのパッチを除いて,現行バージョンは約2000行程度らしい.
さらに,各デバイスドライバのパッチはそれぞれ500行程度で, デバイスドライバの変更を最小にするために機能のほとんどが各ドライバで共通したコードで実装されている.

デバイスドライバパッチのソースをちょっと読んでみた感じだと,リングキューの初期化処理と同期処理(netmap自前のリングとNIC自体のリング)を書けばよいだけみたいだった. 同期処理はちょっと書くのめんどくさそうだった.

資料

あんまり咀嚼して書けてない.

感想

正直,アーキテクチャについてはよくわからない部分が結構あったけど,サンプルコードみたらだいたいイメージ湧いてきた. もうちょっと既存研究あたったほうがよさそう.

netmapで遊ぼうとおもったけど,持ってるNICがMellanox製で,netmap対応ドライバがまだないからつらい. 自分でドライバパッチ書けってことか…