最近、誰かが私に、PREROUTINGのNFHOOKポイントの機能で重要なセクションを保護するためにspin_lock / unlockの代わりにspin_lock / unlock_bhを使用する必要がある理由を尋ねました。
この問題に直面して、私は少し混乱しています。spin_lockファミリーに関しては、多くの一連のインターフェイスがあります。
- spin_lock / spin_unlock
- spin_lock_bh / spin_unlock_bh
- spin_lock_irq / spin_unlock_irq
- spin_lock_irqsave / spin_unlock_irqrestore
- …
非常に多くある理由は、閉じた捕捉を防ぐために、同じCPUの優先度の高いシーケンスとリエントリーデッドロックによってクリティカルエリアが中断されることを意味します。
しかし、理論だけでなく、説得力のある具体的な事例を提示する必要があります。
実際、プロセスコンテキストがPREROUTING関数を呼び出す場合のみを指定する必要があります。
- プロセスコンテキストC1は、PREROUTING関数でspin_lock(Lx)を呼び出して、クリティカルセクションに入ります。
- 重要なセクションは終了しておらず、C1で実行されているCPUが中断され、softirqがnet_rx_actionを実行するようにスケジュールされています。
- ソフト割り込みコンテキストC2でPREROUTING関数を入力し、spin_lock(Lx)を呼び出してクリティカルセクションに入ろうとします。
- C1がスピンロックLxを取得したため、C2は回転を開始し、C1がLxを解放するのを待ちます。
- C1はC2に取って代わられ、C2はすでに回転しているため、適切にデッドロックされています。
しかし、問題は、どのような状況でプロセスコンテキストがPREROUTINGに到達できるかということです。?
2015年のこの頃だったことを思い出し、記事を書きました
。https://blog.csdn.net/dog250/article/details/48770481
この記事のケースは、データパケットの受信がプロセスのコンテキストで実行され、データパケットの受信が行われるシナリオです。プロセスのPREROUTINGポイントを通過している必要があります。
記事の説明を抜粋させてください。
ローカルマシンに接続されたTCPパケットは、最終的にループバックのxmit送信機能に到達します。この機能は、CPUでのソフト割り込み処理をスケジュールし、次の割り込みが終了した後に実行をスケジュールするだけです。これは、現在の可能性が高いです。送信プロセスのコンテキストでは、つまり、送信プロセスはそのコンテキストで送信操作を実行し、この時点でソフト割り込みはそのコンテキストを借用して受信操作をトリガーしました。
ただし、「これは現在の送信プロセスのコンテキストで実行される可能性が高い」という意味の問題があります。これは厳密ではないと感じているので、今日はこの問題を詳しく調べたいと思います。
- ループバックネットワークカードの送受信ロジックが同じプロセスコンテキストで実行されるのはなぜですか?
このためには、ループバックを介してローカルで通信にpingを実行するときにスタックを出力する必要があります。
#!/usr/local/bin/stap -g
function dump()
%{
dump_stack();
%}
probe kernel.function("icmp_rcv")
{
dump();
//print_backtrace(); // 这个不知为何不好使..
}
ping後の結果は次のとおりです。
[34197.319729] [<ffffffff8159a145>] ? icmp_rcv+0x5/0x380
[34197.319732] [<ffffffff81561b84>] ? ip_local_deliver_finish+0xb4/0x1f0
[34197.319735] [<ffffffff81561e69>] ip_local_deliver+0x59/0xd0
[34197.319738] [<ffffffff81561ad0>] ? ip_rcv_finish+0x350/0x350
[34197.319741] [<ffffffff815617fd>] ip_rcv_finish+0x7d/0x350
[34197.319744] [<ffffffff81562196>] ip_rcv+0x2b6/0x410
[34197.319747] [<ffffffff81561780>] ? inet_del_offload+0x40/0x40
[34197.319752] [<ffffffff815267b2>] __netif_receive_skb_core+0x582/0x7d0
[34197.319755] [<ffffffff81526a18>] __netif_receive_skb+0x18/0x60
[34197.319757] [<ffffffff815276ee>] process_backlog+0xae/0x180
[34197.319760] [<ffffffff81526ed2>] net_rx_action+0x152/0x240
[34197.319765] [<ffffffff8107e02f>] __do_softirq+0xef/0x280
[34197.319768] [<ffffffff81646b1c>] call_softirq+0x1c/0x30
[34197.319769] <EOI> [<ffffffff81017155>] do_softirq+0x65/0xa0
[34197.319777] [<ffffffff8107d924>] local_bh_enable+0x94/0xa0
[34197.319780] [<ffffffff81566a00>] ip_finish_output+0x1f0/0x7d0
[34197.319783] [<ffffffff81567cff>] ip_output+0x6f/0xe0
[34197.319786] [<ffffffff81566810>] ? ip_fragment+0x8b0/0x8b0
[34197.319789] [<ffffffff81565971>] ip_local_out_sk+0x31/0x40
[34197.319791] [<ffffffff81568746>] ip_send_skb+0x16/0x50
[34197.319793] [<ffffffff815687b3>] ip_push_pending_frames+0x33/0x40
[34197.319797] [<ffffffff81590fbe>] raw_sendmsg+0x59e/0x620
[34197.319802] [<ffffffff810af1a9>] ? ttwu_do_wakeup+0x19/0xd0
[34197.319805] [<ffffffff8159f604>] inet_sendmsg+0x64/0xb0
[34197.319811] [<ffffffff8150cc90>] sock_sendmsg+0xb0/0xf0
[34197.319814] [<ffffffff8150d201>] SYSC_sendto+0x121/0x1c0
[34197.319817] [<ffffffff8150e221>] ? __sys_recvmsg+0x51/0x90
[34197.319820] [<ffffffff8150dc8e>] SyS_sendto+0xe/0x10
[34197.319823] [<ffffffff81645189>] system_call_fastpath+0x16/0x1b
ハハ、真実が出た!2015年の私の分析は間違っていました:
送信プロセスはそのコンテキストで送信操作を実行し、この時点でソフト割り込みはそのコンテキストを借用して受信操作をトリガーしました。
「コンテキストを借用」しているわけではありませんが、実際には、このコンテキストでアクティブに呼び出されるのはnet_rx_actionです。
呼び出しロジックは次のとおりです。
ip_output_finish
rcu_read_lock_bh
...
dev_queue_xmit
loopback_xmit
netif_rx
enqueue_to_backlog # 这里将skb入队列
raise_softirq_irqoff(NET_RX_SOFTIRQ)
...
...
...
...
...
rcu_read_unlock_bh # unlock操作触发进程上下文中处理接收操作
local_bh_enable
do_softirq
__do_softirq
net_rx_action # 这里对队列中的skb进行处理
...
ip_rcv_finish
icmp_rcv
...
...
...
...
...
...
ip_output_finish return
さて、これは、プロセスコンテキストがパケット受信ロジックを実行する非常に明確なケースです。つまり、次のようになります。
- ソフト割り込み関数net_rx_actionはプロセスのコンテキストで実行される可能性があるため、デッドロックを防ぐために、重要なセクションを_bhバージョンのスピンロックで保護する必要があります。
ロック解除プロセスで多くのことを行うrcu_read_unlock_bhと同様に、カーネルにはさらに多くのことがあります。
- Spin_unlockはスケジュールをトリガーし、タスクの切り替えを引き起こす可能性があります。
- spin_unlock_bhは、do_softirqをトリガーしてソフト割り込みルーチンを実行する場合があります。
- release_sockは、sk_backlog_rcvを実行してから、パッケージコレクションを処理する場合があります。
- …
これは補正効果です。ロック操作はロック解除操作間の動作の一部を禁止するため、ロック解除時の動作が遅れていることを可能な限り補正する必要があるため、すぐに実行してみてください。このデザインは非常に巧妙です。
さらに、プロセスコンテキストがデータパケット受信ロジックを実行する一般的なケースがあります。つまり、TUN / TAPネットワークカードがプロセスコンテキストからtun_get_userを呼び出し、次にnetif_rx_niを直接呼び出してパケットを受信します。
ループバックネットワークカードによってデータパケットを送受信する奇妙で興味深いプロセスを見てみましょう。
- 送信ロジックはまだ戻っておらず、受信ロジックが最初に戻ります。
これは何を意味するのでしょうか?不明ですが、このマシンを接続する過程で不可解な問題が発生した場合は、ここからトラブルシューティングを開始できます。
浙江文州の革靴は濡れているので、雨でも太りません。