SlideShare a Scribd company logo
Ethernetの受信処理 
Takuya ASADA<syuu@cloudius-systems.com>
rcu read lock 
receive_queue lock 
nf_hooks nf_iterate 
rcu read lock 
tcp_rcv_established 
tcp_v4_do_rcv 
nf_hook_slow 
nf_hook_thresh 
___napi_schedule 
__napi_schedule 
e1000_intr 
handle_irq 
do_IRQ 
common_interrupt 
interrupt 
get_rps_cpu 
sock_queue_rcv_skb 
ip_queue_rcv_skb 
__udp_queue_rcv_skb 
udp_queue_rcv_skb 
__udp4_lib_rcv 
ip_local_deliver_finish 
NF_HOOK 
ip_defrag 
ip_rcv_finish 
NF_HOOK 
ip_rcv 
__netif_recive_skb 
process_backlog 
net_rx_action 
__do_softirq 
netif_receive_skb 
do_softirq 
e1000_receive_skb 
e1000_clean_rx_irq 
e1000_clean 
net_rx_action 
__do_softirq 
do_softirq 
rps flow 
table 
backlog lock 
backlog 
enqueue_to_backlog 
sk->sk_data_ready 
fib_lookup 
ip_route_input_slow 
ip_route_input_common 
fib 
ip_mkroute_input 
___napi_schedule 
rps_trigger_softirq 
handle_irq 
do_IRQ 
common_interrupt 
interrupt 
arch_send_call_function_single_ipi 
generic_exec_single 
__smp_call_function_single 
net_rps_action_and_irq_enable 
___napi_schedule process_backlog 
NF_HOOK_THRESH 
ip_route_input_noref 
rt_hash_table 
ip_local_deliver 
ipq lock 
ipq_put ip_frag_queue ip_find 
udp_rcv 
udp_table 
__udp4_lib_lookup_skb 
tcp_v4_rcv 
sk->sk_data_ready 
tcp_hashinfo 
__inet_lookup_skb 
read 
sys_read 
sys_read 
vfs_read 
file->f_op->read 
sock_aio_read 
do_sock_read 
__sock_recvmsg_nosec 
inet_recvmsg 
sk_wait_data tcp_recvmsg 
udp_recvmsg 
__skb_recv_datagram 
wait_for_packet 
finish_wait 
sk_receiv 
e_queue 
sk_backlog
Traditional 
Network Stack 
Process(User) 
Process(Kernel) 
SW Intr Handler 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
パケット受信 
user 
buffer 
socket 
queue 
input 
queue 
パケット 
ユーザ空間へコピー 
netif_rx()
旧来のパケット受信処理 
Process(User) 
Process(Kernel) 
SW Intr Handler 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
パケット受信 
user 
buffer 
socket 
queue 
input 
queue 
パケット 
ユーザ空間へコピー 
ハードウェア割り込み 
↓ 
受信キューにキュー 
イング 
↓ 
ソフトウェア割り込 
みスケジュール 
13年6月7日金曜日
旧来のパケット受信処理 
• 1パケット受信するたびに割り込みを 
受けて処理を行っている 
• 64byte frameの最大受信可能数: 
• GbE:約1.5Mpps(150万) 
• 10GbE:約15Mpps(1500万) 
13年6月7日金曜日
1. 割り込みが多すぎる 
Process(User) 
Process(Kernel) 
SW Intr Handler 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
パケット受信 
user 
buffer 
socket 
queue 
input 
queue 
パケット 
ユーザ空間へコピー 
13年6月7日金曜日
割り込みが多すぎる 
• NICの性能向上によって、一定時間に 
NICが処理できるパケット数が飛躍的に 
増加 
• 1パケット毎に割り込みが来ると、通 
信量が多いときにコンテキストスイッ 
チ回数が増えすぎ性能が劣化 
13年6月7日金曜日
割り込みを無効にする? 
• ポーリング方式 
• NICの割り込みを禁止し、代わりにクロック割り込み 
を用いて定期的に受信キューをチェック 
• デメリット:レイテンシが上がる・定期的にCPUを起 
こす必要がある 
• ハイブリッド方式 
• 通信量が多く連続してパケット処理を行っている時の 
み割り込みを無効化してポーリングで動作 
13年6月7日金曜日
NAPI(ハイブリッド方式) 
Process(User) 
Process(Kernel) 
SW Intr Handler 
パケット受信 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
割り込み無効化 
user 
buffer 
socket 
queue 
パケット 
ユーザ空間へコピー 
パパケケッットト 
パケットが無くなる 
まで繰り返し 
ハードウェア割り込み 
↓ 
割り込み無効化& 
ポーリング開始 
↓ 
パケットが無くなっ 
たら割り込み有効化 
13年6月7日金曜日
NICの割り込みハンドラと 
NAPI(e1000_intr) 
sd = &_get_cpu_var(softnet_data) 
if (napi_schedule_prep(&adapter->napi)) 
__napi_schedule(&adapter->napi) 
→ list_add_tail(&napi->poll_list, sd->poll_list) 
__raise_softirq_irqoff(NET_RX_SOFTIRQ) 
// 割り込みを再開しない!
受信ソフト割り込み 
(net_rx_action) 
sd = &_get_cpu_var(softnet_data) 
foreach (napi : sd->poll_list) 
napi->poll(napi->weight)
NAPI poll処理 
(e1000_clean) 
Interrupt Coalescing 
• NICがOS負荷を考慮して割り込みを間 
引く 
• パケット数個に一回割り込む、 
或いは一定期間待ってから割り込む 
• デメリット:レイテンシが上がる 
while (weight̶) 
skb <- RX queue 
napi_gro_receive(skb) 
→ netif_receive_skb(skb) 
→ ip_rcv() 
if (RX queue.empty) 
napi_complete(napi) 
→ list_del(&napi->poll_list) 
e1000_irq_enable() 
13年6月7日金曜日
Interrupt Coalescingの効果 
• Intel 82599(ixgbe)でInterrupt Coalescing無効、 
有効(割り込み頻度自動調整)で比較 
• MultiQueue, GRO, LRO等は無効化 
• iperfのTCPモードで計測 
interrupts throughput packets CPU%(sy+si) 
無効 
有効 
46687 int/s 7.82 Gbps 660386 pkt/s 97.6% 
7994 int/s 8.24 Gbps 711132 pkt/s 79.6% 
13年6月7日金曜日
2.プロトコル処理が重い 
Process(User) 
Process(Kernel) 
SW Intr Handler 
パケット受信 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
割り込み無効化 
user 
buffer 
socket 
queue 
パケット 
ユーザ空間へコピー 
パパケケッットト 
パケットが無くなる 
まで繰り返し 
13年6月7日金曜日
プロトコル処理が重い 
• 特に小さなパケットが大量に届く場合にプ 
ロトコル処理でCPU時間を大量に使ってしま 
う 
• パケット数分プロトコルスタックが呼び出 
される 
例:64byte frameの場合 
→理論上の最大値は1500万回/s 
13年6月7日金曜日
LRO 
(Large Receive Offload) 
• NICが受信したTCPパケットを結合し、 
大きなパケットにしてからOSへ渡す 
• プロトコルスタックの呼び出し回数を 
削減 
• LinuxではソフトウェアによるLROが実 
装されている(GRO) 
13年6月7日金曜日
LROが無い場合 
To network stack 
seq 10000 seq 10001 seq 10002 seq 10003 
←1500bytes→ 
• パケット毎にネットワークスタックを 
実行 
13年6月7日金曜日
LROが有る場合 
To network stack 
big one packet 
seq 10000 seq 10001 seq 10002 seq 10003 
←1500bytes→ 
• パケットを結合してからネットワークスタックを 
実行、ネットワークスタックの実行回数を削減 
13年6月7日金曜日
Functions — Intel® 82599 10 GbE Controller 
LRO on ixgbe(RSC) 
First packet in the RSC 
Header Payload-1 CRC Header Payload-2 CRC Header Rx Payload-3 CRC Header Payload-4 CRC 
Packets 
Large Rx 
“Packet” Header 
Payload-1 Payload-2 Payload-3 Payload-4 
Large Rx 
Buffers 
Large Rx 
Descriptors 
NEXTP 
Data Length = 
whole buffer size 
NEXTP 
Data Length = 
whole buffer size 
Figure 7.41. RSC Functionality (No Header Split) 
Last packet in the RSC 
Header Payload-1 Payload-2 Payload-3 
Large Receive example 
while using Advanced 
receive descriptors 
(SRRCTL.DESCTYPE = 1) 
Payload-4 
EOP 
Data Length = 
partial buffer size
GROの仕組み 
• napi_complete()が来るかMAX_GRO_SKBSに 
達するまでnapi->gro_listにskbを繋いでいく 
• リスト上に繋いだskbが同じflowの隣り合ったパ 
ケットかチェック 
• くっつけられるところまで一つのskbにくっつけ 
て、netif_receive_skbに渡す
GROの効果 
• Intel 82599(ixgbe)で比較 
• MultiQueueは無効化 
• iperfのTCPモードで計測 
• ethtool -K ix0 gro off 
packets network stack 
called count throughput CPU%(sy+si) 
無効 
有効 
632139 pkt/s 632139 call/s 7.30 Gbps 97.6% 
712387 pkt/s 47957 call/s 8.25 Gbps 79.6% 
13年6月7日金曜日
3.複数のCPUでパケット処理したい 
cpu0 
Process(User) 
Process(Kernel) 
SW Intr Handler 
パケット受信 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
割り込み無効化 
user 
buffer 
socket 
queue 
パケット 
ユーザ空間へコピー 
パパケケッットト 
パケットが無くなる 
まで繰り返し 
cpu1 
Process(User) 
Process(Kernel) 
SW Intr Handler 
パケット受信 
ソフトウェア割り込みスケジュール 
ハードウェア割り込み 
HW Intr Handler 
ユーザ 
プログラム 
システムコール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
割り込み無効化 
user 
buffer 
socket 
queue 
パケット 
ユーザ空間へコピー 
パパケケッットト 
パケットが無くなる 
まで繰り返し 
13年6月7日金曜日
ソフト割り込みが 
1つのコアに偏る 
13年6月7日金曜日
何故偏る? 
ソフト割り込みはNICの割り込みがかかったCPUへ 
スケジュールされる 
↓ 
ポーリングからプロトコルスタックの実行まで 
ソフト割り込み内で実行される 
↓ 
NICの割り込みがかかっているCPUだけに 
負荷がかかる 
13年6月7日金曜日
ソフト割り込みが1つのコ 
アに偏って性能が出ない 
• memcachedなどショートパケットを大 
量に捌くワークロードで顕在化 
• ソフトウェア割り込みを実行している 
CPUがボトルネックになり、性能がス 
ケールしなくなる 
13年6月7日金曜日
解決方法 
• パケットを複数のCPUへ分散させてから 
プロトコル処理する仕組みがあれば良い 
• 但し、TCPには順序保証が有るので並列 
に処理されるとパケットの並べ直し(リ 
オーダ)が発生してパフォーマンスが落 
ちる 
13年6月7日金曜日
TCP Reordering 
1 2 3 4 5 6 
1 2 3 4 5 6 
protocol 
processing 
user buffer 
• シーケンスナンバー通りの順序でパケットが 
着信していれば順にバッファへコピーしてい 
くだけでよいが… 
13年6月7日金曜日
TCP Reordering 
1 2 4 5 3 6 
reorder 
queue 
3 4 5 
1 2 3 4 5 6 
protocol 
processing 
user buffer 
• 順序が乱れているとパケットの並べ直し 
(リオーダ)作業が必要になる 
13年6月7日金曜日
解決方法(続) 
• 1つのフローは1つのCPUで処理され 
る方が都合が良い 
13年6月7日金曜日
RSS 
(Receive Side Scaling) 
• CPUごとに別々の受信キューを持つNIC 
(MultiQueue NICと呼ばれる) 
• 受信キューごとに独立した割り込みを持つ 
• 同じフローに属するパケットは同じキューへ、 
異なるフローに属するパケットはなるべく別のキュ 
ーへ分散 
→パケットヘッダのハッシュ値を計算する事により 
宛先キューを決定 
13年6月7日金曜日
MSI-X割り込み 
• PCI Expressでサポート 
• デバイスあたり2048個のIRQを持てる 
• それぞれのIRQの割り込み先CPUを選べ 
る 
→1つのNICがCPUコア数分のIRQを持 
てる 
13年6月7日金曜日
おさらい:MultiQueue NICの割り込み 
47: 7602 0 0 0 0 3 23 0 PCI-MSI-edge p1p1-TxRx-0 
48: 0 7602 0 0 0 0 13 12 PCI-MSI-edge p1p1-TxRx-1 
49: 12 0 7605 0 0 0 10 0 PCI-MSI-edge p1p1-TxRx-2 
50: 0 12 0 7602 3 0 10 0 PCI-MSI-edge p1p1-TxRx-3 
51: 0 0 12 0 7602 3 10 0 PCI-MSI-edge p1p1-TxRx-4 
52: 0 0 0 20 0 7602 13 0 PCI-MSI-edge p1p1-TxRx-5 
53: 0 0 0 0 12 0 7612 3 PCI-MSI-edge p1p1-TxRx-6 
54: 3 0 0 0 0 13 10 7602 PCI-MSI-edge p1p1-TxRx-7 
55: 0 2 0 0 0 0 2 0 PCI-MSI-edge p1p1 
CPU毎に送受信キュー、キュー毎に 
MSI-X割り込みを持つ 
正しいsmp_affinityは固定的に決まっ 
ている 
複数CPUへ散らしてはならない
RSSによる 
パケット振り分け 
NIC 
cpu0 cpu1 cpu2 cpu3 
ハッシュ計算 
パケット着信 
パパケパケッケットットト 
hash queue 
ディスパッチ 
参照 
受信処理 
RX 
Queue 
#0 
RX 
Queue 
#1 
RX 
Queue 
#2 
RX 
Queue 
#3 
受信処理 
割り込み 
■ 
■ 
0 
1 
13年6月7日金曜日
キュー選択の手順 
indirection_table[64] = initial_value 
input[12] = 
{src_addr, dst_addr, src_port, dst_port} 
key = toeplitz_hash(input, 12) 
index = key & 0x3f 
queue = indirection_table[index] 
13年6月7日金曜日
RSS導入前 
13年6月7日金曜日
RSS導入後 
13年6月7日金曜日
RPS 
• RSS非対応のオンボードNICをうまくつかってサー 
バの性能を向上させたい 
• ソフトでRSSを実装してしまおう 
• ソフト割り込みの段階でパケットを各CPUへばら 
まく 
• CPU間割り込みを使って他のCPUを稼動させる 
• RSSのソフトウエアによるエミュレーション 
13年6月7日金曜日
cpu0 cpu1 cpu2 cpu3 
ユーザ 
プログラム 
システム 
コール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
ディスパッチ 
ハッシュ計算 
パケット受信 
ソフトウェア割り込み 
割り込み無効化 
user 
buffer 
socket 
queue 
ハードウェア割り込み 
パケット 
ユーザ空間へコピー 
パパケケッットト 
ユーザ 
プログラムuser 
ソケット 
受信処理 
プロトコル処理 
buffer 
socket 
queue 
backlog 
#1 
hash queue 参照0 
1 
■ 
■CPU間 
割り込み 
backlog 
#2 
backlog 
#3 
13年6月7日金曜日
RPSの使い方 
# echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus 
# echo 4096 > /sys/class/net/eth0/queues/rx-0/ 
rps_flow_cnt 
13年6月7日金曜日
RPSでリモートCPUへキュー 
(netif_receive_skb_internal) 
cpu = get_rps_cpu(skb) 
→ skb_flow_dissect(skb, &keys) 
hash = __flow_hash_3words(keys.dst, keys.src, keys.ports) 
map = rxqueue->rps_map 
cpu = map->cpus[(hash * map->len) >> 32] 
enqueue_to_backlog(skb, cpu) 
→ sd = &per_cpu(softnet_data, cpu) 
__skb_queue_tail(&sd->input_pkt_queue, skb) 
rps_ipi_queued(sd) 
&__get_cpu_var(softnet_data)->rps_ipi_list = sd 
net_rx_action 
napi->poll 
process_backlog 
net_rps_action_and_irq_enable 
smp_call_function_single_async
RPS導入前 
13年6月7日金曜日
RPS導入後 
13年6月7日金曜日
RPS netperf result 
netperf benchmark result on lwn.net: 
e1000e on 8 core Intel 
Without RPS: 90K tps at 33% CPU 
With RPS: 239K tps at 60% CPU 
foredeth on 16 core AMD 
Without RPS: 103K tps at 15% CPU 
With RPS: 285K tps at 49% CPU 
13年6月7日金曜日
RFS 
• プロセス追跡機能をRPSに追加 
13年6月7日金曜日
RFS 
フローに割り当てら 
れたキューが宛先プ 
ロセスのCPUと異な 
るとオーバヘッドが 
発生する 
13年6月7日金曜日
RFS 
ハッシュテーブルの 
設定値を変更する 
事でCPUを一致さ 
せる事ができる 
13年6月7日金曜日
RFSの使い方 
# echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus 
# echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt 
# echo 32768 > /proc/sys/net/core/rps_sock_flow_entries 
13年6月7日金曜日
RFSの実装 
• ソフト割り込みコンテキストからパケット届け先 
のプロセスが動いているCPUなんて分からない 
• システムコールコンテキストからパケットヘッダ 
のハッシュ値なんて分からない 
• パケットヘッダのハッシュ値とプロセスが動いて 
いるCPU番号の対応表を更新し続ける必要があ 
る
cpu0 cpu1 cpu2 cpu3 
ユーザ 
プログラム 
システム 
コール 
ソケット 
受信処理 
プロセス起床 
プロトコル処理 
ディスパッチ 
ハッシュ計算 
パケット受信 
ソフトウェア割り込み 
割り込み無効化 
user 
buffer 
socket 
queue 
ハードウェア割り込み 
パケット 
ユーザ空間へコピー 
パパケケッットト 
ユーザ 
プログラムuser 
ソケット 
受信処理 
プロトコル処理 
buffer 
socket 
queue 
backlog 
#1 
hash queue 参照0 
1 
■ 
■CPU間 
割り込み 
backlog 
#2 
backlog 
#3 
13年6月7日金曜日
ソフト割り込みコンテキストから 
ハッシュ値をソケットに保存 
(tcp_v4_do_rcv) 
sock_rps_save_rxhash(sk, skb) 
→ sk->sk_rxhash = skb->hash
システムコールコンテキストから 
CPU番号をテーブルに保存 
(inet_recvmsg) 
sock_rps_record_flow(sk) 
→ idx = sk->sk_rxhash & 
rps_sock_flow_table->mask; 
rps_sock_flow_table->ents[idx] = 
raw_smp_processor_id()
受信処理の真っ最中に 
配送先CPUを変更すると…? 
• 変更前CPUと変更後CPUで同時に1つのフロー 
の受信処理が実行される 
• 変更後CPUの方が空いていて処理が早かった場 
合、受信順序が前後する可能性がある 
• TCP Reordering発生 → 遅くなる
無難なタイミングで 
宛先CPU変更 
• システムコール側のテーブル 
(rps_sock_flow_table)とパケットディスパッ 
チに使うテーブル(rxqueue->rps_flow_table) 
を別に持つ 
• 変更元CPUのbacklogに積み残しが無いタイミ 
ングで切り替えを行う
RFS netperf result 
netperf benchmark result on lwn.net: 
e1000e on 8 core Intel 
No RFS or RPS 104K tps at 30% CPU 
No RFS (best RPS config): 290K tps at 63% CPU 
RFS 303K tps at 61% CPU 
RPC test tps CPU% 50/90/99% usec latency StdDev 
No RFS or RPS 103K 48% 757/900/3185 4472.35 
RPS only: 174K 73% 415/993/2468 491.66 
RFS 223K 73% 379/651/1382 315.61 
13年6月7日金曜日
Accelerated RFS 
• RFSをMultiQueue NICでも実現するため 
のNICドライバ拡張 
• Linux kernelはプロセスの実行中CPUを 
NICドライバに通知 
• NICドライバは通知を受けてフローのキ 
ュー割り当てを更新 
13年6月7日金曜日
Receive Side Scalingの制限 
• 32bitのハッシュ値をそのまま使用して 
いればハッシュ衝突しにくいが、 
Indirection Tableが小さいので少ないビッ 
ト数でindex値をマスクしている 
→フローが多い時にハッシュ衝突する 
• Accelerated RFSには不向き 
13年6月7日金曜日
• Disabling RSS on the fly is not allowed, and the 82599 must be reset after RSS is disabled. 
• When RSS is disabled, packets are assigned an RSS output index = zero. 
When multiple request queues are enabled in RSS mode, un-decodable packets are assigned an RSS 
output index = zero. The 32-bit tag (normally a result of the hash function) equals zero. 
Receive Side Scalingの制限 
Parsed receive packet 
RSS hash 
7 LS 
bits 
32 
Packet Descriptor 
7 
128フローしか 
識別出来ない 
RSS Disable or (RSS 
& not decodable) 
Redirection Table 
128 x 4 
4 
0 
4 
RSS output index 
32bitのハッシュ値のうち 
4bitしか使ってない 
フローが多いとハッシュ衝突する為、特定フローを 
Figure 7.10. RSS Block Diagram 
特定CPUへキューするのには向いていない
Flow Steering 
• フローとキューの対応情報を記憶 
4tuple:キュー番号のような形式で設定 
• RSSのような明確な共通仕様は無いが、 
各社の10GbEに実装されている 
• Accelerated RFSはFlow Steeringを前提と 
している 
13年6月7日金曜日
Intel Ethernet 
Flow Director 
• Accelerated RFSガン無視 
• 32kのハッシュテーブルの先にリンクドリスト 
• 2つのFilter mode 
• Signature Mode:ハッシュ値 
最大32k個 
• Perfect Match Mode:ヘッダの完全マッチ(dst-ip, dst-port, 
src-ip, src-port, protocol) 
最大8k個
following figure shows a block diagram of the flow director filters. Received flows are identified buckets by a hash function on the relevant tuples as defined by the FDIR...M registers. Each bucket organized in a linked list indicated by the hash lookup table. Buckets can have a variable length while 
last filter in each bucket is indicated as a last. There is no upper limit for a linked list length during 
Flow Director 
programming; however, a received packet that matches a filter that exceeds the FDIRCTRL.Max-Length 
reported to software (see Section 7.1.2.7.5). 
Logic AND of Rx Packet tuples with 
the Flexible filters Mask registers 
~350 
Hash 
15 bit output 
15 bit address 
Bucket Valid First Filter PTR 
Bucket Valid First Filter PTR 
. . . 
. . . 
Flow ID Fields in “Perfect Match mode” 
Hash (Signature) 
15 bit output Flow ID Field in “Signature mode” 
32K Filter Action 
Hash Lookup Table 
Shares the Rx 
packet buffer memory space 
Addr 
0 
1 
2 
. . . 
M 
Bucket Valid First Filter PTR 
Bucket Valid First Filter PTR 
Hash-Index = 0 
Flow ID fields 
Filter Action 
Collision flag 
Next Filter PTR 
Hash-Index = 1 
Flow ID fields 
Filter Action 
Collision flag 
Next Filter PTR 
Hash-Index = N 
Flow ID fields 
. . . Filter Action 
Collision flag 
Next Filter PTR 
Hash-Index = N+1 
Flow ID fields 
Filter Action . . . 
Collision flag 
Next Filter PTR 
Max recommended linked list length 
(FDIRCTRL.Max-Length) 
Hash-Index = 0 
Flow ID fields 
Filter Action 
Collision flag 
Next Filter PTR 
Hash-Index = 1 
Flow ID fields 
Collision flag 
Next Filter PTR 
. . . 
Bucket Valid First Filter PTR 
Bucket M (linked list M) 
Bucket 0 (linked list 0) 
‘too long’ 
Linked list 
Flexible Filters table - Shares the Rx packet buffer memory space
Linuxでの利用例#1(自 
動) 
送信処理 
プロトコル 
スタック 
ソケット 
システムコール 
proce 
ss 
ドライバ 
Txq 
NIC 
Flow 
Director 
Filters 
フィルタ 
更新 
プロセスコンテキストからのパケット送出時に送信 
元CPUとパケットヘッダを用いてフィルタを更新
Linuxでの利用例#2(手 
動) 
• ethtoolからフィルタをマニュアル追加 
ethtool -K ntuple on 
ethtool -U eth0 flow-type udp4 src-ip 0xa0a0a000 
src-ip-mask 0xFFFFFF00 dst-ip 0 dst-ip-mask 0 src-port 
9 src-port-mask 0 dst-port 9 
ethtool -U eth0 flow-type tcp4 vlan 100 vlan-mask 
0xF000 action 8 
• フィルタの表示 
ethtool -u eth0

More Related Content

Ethernetの受信処理

  • 2. rcu read lock receive_queue lock nf_hooks nf_iterate rcu read lock tcp_rcv_established tcp_v4_do_rcv nf_hook_slow nf_hook_thresh ___napi_schedule __napi_schedule e1000_intr handle_irq do_IRQ common_interrupt interrupt get_rps_cpu sock_queue_rcv_skb ip_queue_rcv_skb __udp_queue_rcv_skb udp_queue_rcv_skb __udp4_lib_rcv ip_local_deliver_finish NF_HOOK ip_defrag ip_rcv_finish NF_HOOK ip_rcv __netif_recive_skb process_backlog net_rx_action __do_softirq netif_receive_skb do_softirq e1000_receive_skb e1000_clean_rx_irq e1000_clean net_rx_action __do_softirq do_softirq rps flow table backlog lock backlog enqueue_to_backlog sk->sk_data_ready fib_lookup ip_route_input_slow ip_route_input_common fib ip_mkroute_input ___napi_schedule rps_trigger_softirq handle_irq do_IRQ common_interrupt interrupt arch_send_call_function_single_ipi generic_exec_single __smp_call_function_single net_rps_action_and_irq_enable ___napi_schedule process_backlog NF_HOOK_THRESH ip_route_input_noref rt_hash_table ip_local_deliver ipq lock ipq_put ip_frag_queue ip_find udp_rcv udp_table __udp4_lib_lookup_skb tcp_v4_rcv sk->sk_data_ready tcp_hashinfo __inet_lookup_skb read sys_read sys_read vfs_read file->f_op->read sock_aio_read do_sock_read __sock_recvmsg_nosec inet_recvmsg sk_wait_data tcp_recvmsg udp_recvmsg __skb_recv_datagram wait_for_packet finish_wait sk_receiv e_queue sk_backlog
  • 3. Traditional Network Stack Process(User) Process(Kernel) SW Intr Handler ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 パケット受信 user buffer socket queue input queue パケット ユーザ空間へコピー netif_rx()
  • 4. 旧来のパケット受信処理 Process(User) Process(Kernel) SW Intr Handler ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 パケット受信 user buffer socket queue input queue パケット ユーザ空間へコピー ハードウェア割り込み ↓ 受信キューにキュー イング ↓ ソフトウェア割り込 みスケジュール 13年6月7日金曜日
  • 5. 旧来のパケット受信処理 • 1パケット受信するたびに割り込みを 受けて処理を行っている • 64byte frameの最大受信可能数: • GbE:約1.5Mpps(150万) • 10GbE:約15Mpps(1500万) 13年6月7日金曜日
  • 6. 1. 割り込みが多すぎる Process(User) Process(Kernel) SW Intr Handler ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 パケット受信 user buffer socket queue input queue パケット ユーザ空間へコピー 13年6月7日金曜日
  • 7. 割り込みが多すぎる • NICの性能向上によって、一定時間に NICが処理できるパケット数が飛躍的に 増加 • 1パケット毎に割り込みが来ると、通 信量が多いときにコンテキストスイッ チ回数が増えすぎ性能が劣化 13年6月7日金曜日
  • 8. 割り込みを無効にする? • ポーリング方式 • NICの割り込みを禁止し、代わりにクロック割り込み を用いて定期的に受信キューをチェック • デメリット:レイテンシが上がる・定期的にCPUを起 こす必要がある • ハイブリッド方式 • 通信量が多く連続してパケット処理を行っている時の み割り込みを無効化してポーリングで動作 13年6月7日金曜日
  • 9. NAPI(ハイブリッド方式) Process(User) Process(Kernel) SW Intr Handler パケット受信 ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 割り込み無効化 user buffer socket queue パケット ユーザ空間へコピー パパケケッットト パケットが無くなる まで繰り返し ハードウェア割り込み ↓ 割り込み無効化& ポーリング開始 ↓ パケットが無くなっ たら割り込み有効化 13年6月7日金曜日
  • 10. NICの割り込みハンドラと NAPI(e1000_intr) sd = &_get_cpu_var(softnet_data) if (napi_schedule_prep(&adapter->napi)) __napi_schedule(&adapter->napi) → list_add_tail(&napi->poll_list, sd->poll_list) __raise_softirq_irqoff(NET_RX_SOFTIRQ) // 割り込みを再開しない!
  • 11. 受信ソフト割り込み (net_rx_action) sd = &_get_cpu_var(softnet_data) foreach (napi : sd->poll_list) napi->poll(napi->weight)
  • 12. NAPI poll処理 (e1000_clean) Interrupt Coalescing • NICがOS負荷を考慮して割り込みを間 引く • パケット数個に一回割り込む、 或いは一定期間待ってから割り込む • デメリット:レイテンシが上がる while (weight̶) skb <- RX queue napi_gro_receive(skb) → netif_receive_skb(skb) → ip_rcv() if (RX queue.empty) napi_complete(napi) → list_del(&napi->poll_list) e1000_irq_enable() 13年6月7日金曜日
  • 13. Interrupt Coalescingの効果 • Intel 82599(ixgbe)でInterrupt Coalescing無効、 有効(割り込み頻度自動調整)で比較 • MultiQueue, GRO, LRO等は無効化 • iperfのTCPモードで計測 interrupts throughput packets CPU%(sy+si) 無効 有効 46687 int/s 7.82 Gbps 660386 pkt/s 97.6% 7994 int/s 8.24 Gbps 711132 pkt/s 79.6% 13年6月7日金曜日
  • 14. 2.プロトコル処理が重い Process(User) Process(Kernel) SW Intr Handler パケット受信 ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 割り込み無効化 user buffer socket queue パケット ユーザ空間へコピー パパケケッットト パケットが無くなる まで繰り返し 13年6月7日金曜日
  • 15. プロトコル処理が重い • 特に小さなパケットが大量に届く場合にプ ロトコル処理でCPU時間を大量に使ってしま う • パケット数分プロトコルスタックが呼び出 される 例:64byte frameの場合 →理論上の最大値は1500万回/s 13年6月7日金曜日
  • 16. LRO (Large Receive Offload) • NICが受信したTCPパケットを結合し、 大きなパケットにしてからOSへ渡す • プロトコルスタックの呼び出し回数を 削減 • LinuxではソフトウェアによるLROが実 装されている(GRO) 13年6月7日金曜日
  • 17. LROが無い場合 To network stack seq 10000 seq 10001 seq 10002 seq 10003 ←1500bytes→ • パケット毎にネットワークスタックを 実行 13年6月7日金曜日
  • 18. LROが有る場合 To network stack big one packet seq 10000 seq 10001 seq 10002 seq 10003 ←1500bytes→ • パケットを結合してからネットワークスタックを 実行、ネットワークスタックの実行回数を削減 13年6月7日金曜日
  • 19. Functions — Intel® 82599 10 GbE Controller LRO on ixgbe(RSC) First packet in the RSC Header Payload-1 CRC Header Payload-2 CRC Header Rx Payload-3 CRC Header Payload-4 CRC Packets Large Rx “Packet” Header Payload-1 Payload-2 Payload-3 Payload-4 Large Rx Buffers Large Rx Descriptors NEXTP Data Length = whole buffer size NEXTP Data Length = whole buffer size Figure 7.41. RSC Functionality (No Header Split) Last packet in the RSC Header Payload-1 Payload-2 Payload-3 Large Receive example while using Advanced receive descriptors (SRRCTL.DESCTYPE = 1) Payload-4 EOP Data Length = partial buffer size
  • 20. GROの仕組み • napi_complete()が来るかMAX_GRO_SKBSに 達するまでnapi->gro_listにskbを繋いでいく • リスト上に繋いだskbが同じflowの隣り合ったパ ケットかチェック • くっつけられるところまで一つのskbにくっつけ て、netif_receive_skbに渡す
  • 21. GROの効果 • Intel 82599(ixgbe)で比較 • MultiQueueは無効化 • iperfのTCPモードで計測 • ethtool -K ix0 gro off packets network stack called count throughput CPU%(sy+si) 無効 有効 632139 pkt/s 632139 call/s 7.30 Gbps 97.6% 712387 pkt/s 47957 call/s 8.25 Gbps 79.6% 13年6月7日金曜日
  • 22. 3.複数のCPUでパケット処理したい cpu0 Process(User) Process(Kernel) SW Intr Handler パケット受信 ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 割り込み無効化 user buffer socket queue パケット ユーザ空間へコピー パパケケッットト パケットが無くなる まで繰り返し cpu1 Process(User) Process(Kernel) SW Intr Handler パケット受信 ソフトウェア割り込みスケジュール ハードウェア割り込み HW Intr Handler ユーザ プログラム システムコール ソケット 受信処理 プロセス起床 プロトコル処理 割り込み無効化 user buffer socket queue パケット ユーザ空間へコピー パパケケッットト パケットが無くなる まで繰り返し 13年6月7日金曜日
  • 24. 何故偏る? ソフト割り込みはNICの割り込みがかかったCPUへ スケジュールされる ↓ ポーリングからプロトコルスタックの実行まで ソフト割り込み内で実行される ↓ NICの割り込みがかかっているCPUだけに 負荷がかかる 13年6月7日金曜日
  • 25. ソフト割り込みが1つのコ アに偏って性能が出ない • memcachedなどショートパケットを大 量に捌くワークロードで顕在化 • ソフトウェア割り込みを実行している CPUがボトルネックになり、性能がス ケールしなくなる 13年6月7日金曜日
  • 26. 解決方法 • パケットを複数のCPUへ分散させてから プロトコル処理する仕組みがあれば良い • 但し、TCPには順序保証が有るので並列 に処理されるとパケットの並べ直し(リ オーダ)が発生してパフォーマンスが落 ちる 13年6月7日金曜日
  • 27. TCP Reordering 1 2 3 4 5 6 1 2 3 4 5 6 protocol processing user buffer • シーケンスナンバー通りの順序でパケットが 着信していれば順にバッファへコピーしてい くだけでよいが… 13年6月7日金曜日
  • 28. TCP Reordering 1 2 4 5 3 6 reorder queue 3 4 5 1 2 3 4 5 6 protocol processing user buffer • 順序が乱れているとパケットの並べ直し (リオーダ)作業が必要になる 13年6月7日金曜日
  • 29. 解決方法(続) • 1つのフローは1つのCPUで処理され る方が都合が良い 13年6月7日金曜日
  • 30. RSS (Receive Side Scaling) • CPUごとに別々の受信キューを持つNIC (MultiQueue NICと呼ばれる) • 受信キューごとに独立した割り込みを持つ • 同じフローに属するパケットは同じキューへ、 異なるフローに属するパケットはなるべく別のキュ ーへ分散 →パケットヘッダのハッシュ値を計算する事により 宛先キューを決定 13年6月7日金曜日
  • 31. MSI-X割り込み • PCI Expressでサポート • デバイスあたり2048個のIRQを持てる • それぞれのIRQの割り込み先CPUを選べ る →1つのNICがCPUコア数分のIRQを持 てる 13年6月7日金曜日
  • 32. おさらい:MultiQueue NICの割り込み 47: 7602 0 0 0 0 3 23 0 PCI-MSI-edge p1p1-TxRx-0 48: 0 7602 0 0 0 0 13 12 PCI-MSI-edge p1p1-TxRx-1 49: 12 0 7605 0 0 0 10 0 PCI-MSI-edge p1p1-TxRx-2 50: 0 12 0 7602 3 0 10 0 PCI-MSI-edge p1p1-TxRx-3 51: 0 0 12 0 7602 3 10 0 PCI-MSI-edge p1p1-TxRx-4 52: 0 0 0 20 0 7602 13 0 PCI-MSI-edge p1p1-TxRx-5 53: 0 0 0 0 12 0 7612 3 PCI-MSI-edge p1p1-TxRx-6 54: 3 0 0 0 0 13 10 7602 PCI-MSI-edge p1p1-TxRx-7 55: 0 2 0 0 0 0 2 0 PCI-MSI-edge p1p1 CPU毎に送受信キュー、キュー毎に MSI-X割り込みを持つ 正しいsmp_affinityは固定的に決まっ ている 複数CPUへ散らしてはならない
  • 33. RSSによる パケット振り分け NIC cpu0 cpu1 cpu2 cpu3 ハッシュ計算 パケット着信 パパケパケッケットットト hash queue ディスパッチ 参照 受信処理 RX Queue #0 RX Queue #1 RX Queue #2 RX Queue #3 受信処理 割り込み ■ ■ 0 1 13年6月7日金曜日
  • 34. キュー選択の手順 indirection_table[64] = initial_value input[12] = {src_addr, dst_addr, src_port, dst_port} key = toeplitz_hash(input, 12) index = key & 0x3f queue = indirection_table[index] 13年6月7日金曜日
  • 37. RPS • RSS非対応のオンボードNICをうまくつかってサー バの性能を向上させたい • ソフトでRSSを実装してしまおう • ソフト割り込みの段階でパケットを各CPUへばら まく • CPU間割り込みを使って他のCPUを稼動させる • RSSのソフトウエアによるエミュレーション 13年6月7日金曜日
  • 38. cpu0 cpu1 cpu2 cpu3 ユーザ プログラム システム コール ソケット 受信処理 プロセス起床 プロトコル処理 ディスパッチ ハッシュ計算 パケット受信 ソフトウェア割り込み 割り込み無効化 user buffer socket queue ハードウェア割り込み パケット ユーザ空間へコピー パパケケッットト ユーザ プログラムuser ソケット 受信処理 プロトコル処理 buffer socket queue backlog #1 hash queue 参照0 1 ■ ■CPU間 割り込み backlog #2 backlog #3 13年6月7日金曜日
  • 39. RPSの使い方 # echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus # echo 4096 > /sys/class/net/eth0/queues/rx-0/ rps_flow_cnt 13年6月7日金曜日
  • 40. RPSでリモートCPUへキュー (netif_receive_skb_internal) cpu = get_rps_cpu(skb) → skb_flow_dissect(skb, &keys) hash = __flow_hash_3words(keys.dst, keys.src, keys.ports) map = rxqueue->rps_map cpu = map->cpus[(hash * map->len) >> 32] enqueue_to_backlog(skb, cpu) → sd = &per_cpu(softnet_data, cpu) __skb_queue_tail(&sd->input_pkt_queue, skb) rps_ipi_queued(sd) &__get_cpu_var(softnet_data)->rps_ipi_list = sd net_rx_action napi->poll process_backlog net_rps_action_and_irq_enable smp_call_function_single_async
  • 43. RPS netperf result netperf benchmark result on lwn.net: e1000e on 8 core Intel Without RPS: 90K tps at 33% CPU With RPS: 239K tps at 60% CPU foredeth on 16 core AMD Without RPS: 103K tps at 15% CPU With RPS: 285K tps at 49% CPU 13年6月7日金曜日
  • 45. RFS フローに割り当てら れたキューが宛先プ ロセスのCPUと異な るとオーバヘッドが 発生する 13年6月7日金曜日
  • 46. RFS ハッシュテーブルの 設定値を変更する 事でCPUを一致さ せる事ができる 13年6月7日金曜日
  • 47. RFSの使い方 # echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus # echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt # echo 32768 > /proc/sys/net/core/rps_sock_flow_entries 13年6月7日金曜日
  • 48. RFSの実装 • ソフト割り込みコンテキストからパケット届け先 のプロセスが動いているCPUなんて分からない • システムコールコンテキストからパケットヘッダ のハッシュ値なんて分からない • パケットヘッダのハッシュ値とプロセスが動いて いるCPU番号の対応表を更新し続ける必要があ る
  • 49. cpu0 cpu1 cpu2 cpu3 ユーザ プログラム システム コール ソケット 受信処理 プロセス起床 プロトコル処理 ディスパッチ ハッシュ計算 パケット受信 ソフトウェア割り込み 割り込み無効化 user buffer socket queue ハードウェア割り込み パケット ユーザ空間へコピー パパケケッットト ユーザ プログラムuser ソケット 受信処理 プロトコル処理 buffer socket queue backlog #1 hash queue 参照0 1 ■ ■CPU間 割り込み backlog #2 backlog #3 13年6月7日金曜日
  • 51. システムコールコンテキストから CPU番号をテーブルに保存 (inet_recvmsg) sock_rps_record_flow(sk) → idx = sk->sk_rxhash & rps_sock_flow_table->mask; rps_sock_flow_table->ents[idx] = raw_smp_processor_id()
  • 52. 受信処理の真っ最中に 配送先CPUを変更すると…? • 変更前CPUと変更後CPUで同時に1つのフロー の受信処理が実行される • 変更後CPUの方が空いていて処理が早かった場 合、受信順序が前後する可能性がある • TCP Reordering発生 → 遅くなる
  • 53. 無難なタイミングで 宛先CPU変更 • システムコール側のテーブル (rps_sock_flow_table)とパケットディスパッ チに使うテーブル(rxqueue->rps_flow_table) を別に持つ • 変更元CPUのbacklogに積み残しが無いタイミ ングで切り替えを行う
  • 54. RFS netperf result netperf benchmark result on lwn.net: e1000e on 8 core Intel No RFS or RPS 104K tps at 30% CPU No RFS (best RPS config): 290K tps at 63% CPU RFS 303K tps at 61% CPU RPC test tps CPU% 50/90/99% usec latency StdDev No RFS or RPS 103K 48% 757/900/3185 4472.35 RPS only: 174K 73% 415/993/2468 491.66 RFS 223K 73% 379/651/1382 315.61 13年6月7日金曜日
  • 55. Accelerated RFS • RFSをMultiQueue NICでも実現するため のNICドライバ拡張 • Linux kernelはプロセスの実行中CPUを NICドライバに通知 • NICドライバは通知を受けてフローのキ ュー割り当てを更新 13年6月7日金曜日
  • 56. Receive Side Scalingの制限 • 32bitのハッシュ値をそのまま使用して いればハッシュ衝突しにくいが、 Indirection Tableが小さいので少ないビッ ト数でindex値をマスクしている →フローが多い時にハッシュ衝突する • Accelerated RFSには不向き 13年6月7日金曜日
  • 57. • Disabling RSS on the fly is not allowed, and the 82599 must be reset after RSS is disabled. • When RSS is disabled, packets are assigned an RSS output index = zero. When multiple request queues are enabled in RSS mode, un-decodable packets are assigned an RSS output index = zero. The 32-bit tag (normally a result of the hash function) equals zero. Receive Side Scalingの制限 Parsed receive packet RSS hash 7 LS bits 32 Packet Descriptor 7 128フローしか 識別出来ない RSS Disable or (RSS & not decodable) Redirection Table 128 x 4 4 0 4 RSS output index 32bitのハッシュ値のうち 4bitしか使ってない フローが多いとハッシュ衝突する為、特定フローを Figure 7.10. RSS Block Diagram 特定CPUへキューするのには向いていない
  • 58. Flow Steering • フローとキューの対応情報を記憶 4tuple:キュー番号のような形式で設定 • RSSのような明確な共通仕様は無いが、 各社の10GbEに実装されている • Accelerated RFSはFlow Steeringを前提と している 13年6月7日金曜日
  • 59. Intel Ethernet Flow Director • Accelerated RFSガン無視 • 32kのハッシュテーブルの先にリンクドリスト • 2つのFilter mode • Signature Mode:ハッシュ値 最大32k個 • Perfect Match Mode:ヘッダの完全マッチ(dst-ip, dst-port, src-ip, src-port, protocol) 最大8k個
  • 60. following figure shows a block diagram of the flow director filters. Received flows are identified buckets by a hash function on the relevant tuples as defined by the FDIR...M registers. Each bucket organized in a linked list indicated by the hash lookup table. Buckets can have a variable length while last filter in each bucket is indicated as a last. There is no upper limit for a linked list length during Flow Director programming; however, a received packet that matches a filter that exceeds the FDIRCTRL.Max-Length reported to software (see Section 7.1.2.7.5). Logic AND of Rx Packet tuples with the Flexible filters Mask registers ~350 Hash 15 bit output 15 bit address Bucket Valid First Filter PTR Bucket Valid First Filter PTR . . . . . . Flow ID Fields in “Perfect Match mode” Hash (Signature) 15 bit output Flow ID Field in “Signature mode” 32K Filter Action Hash Lookup Table Shares the Rx packet buffer memory space Addr 0 1 2 . . . M Bucket Valid First Filter PTR Bucket Valid First Filter PTR Hash-Index = 0 Flow ID fields Filter Action Collision flag Next Filter PTR Hash-Index = 1 Flow ID fields Filter Action Collision flag Next Filter PTR Hash-Index = N Flow ID fields . . . Filter Action Collision flag Next Filter PTR Hash-Index = N+1 Flow ID fields Filter Action . . . Collision flag Next Filter PTR Max recommended linked list length (FDIRCTRL.Max-Length) Hash-Index = 0 Flow ID fields Filter Action Collision flag Next Filter PTR Hash-Index = 1 Flow ID fields Collision flag Next Filter PTR . . . Bucket Valid First Filter PTR Bucket M (linked list M) Bucket 0 (linked list 0) ‘too long’ Linked list Flexible Filters table - Shares the Rx packet buffer memory space
  • 61. Linuxでの利用例#1(自 動) 送信処理 プロトコル スタック ソケット システムコール proce ss ドライバ Txq NIC Flow Director Filters フィルタ 更新 プロセスコンテキストからのパケット送出時に送信 元CPUとパケットヘッダを用いてフィルタを更新
  • 62. Linuxでの利用例#2(手 動) • ethtoolからフィルタをマニュアル追加 ethtool -K ntuple on ethtool -U eth0 flow-type udp4 src-ip 0xa0a0a000 src-ip-mask 0xFFFFFF00 dst-ip 0 dst-ip-mask 0 src-port 9 src-port-mask 0 dst-port 9 ethtool -U eth0 flow-type tcp4 vlan 100 vlan-mask 0xF000 action 8 • フィルタの表示 ethtool -u eth0