Linuxネットワークスタックの監視と構成:データの取得





この記事では、Linuxカーネルを実行しているコンピューターでパケットを受信する方法を検討し、パケットがネットワークからユーザー空間アプリケーションに移動する際のネットワークスタックの各コンポーネントの監視と構成も分析します。 ここには多くのソースコードがあります。プロセスを深く理解しないと、Linuxネットワークスタックを構成および監視できないためです。



また、同じトピックに関する図解されたマニュアルを理解することをお勧めします。説明図と追加情報があります。



内容



1. Linuxネットワークスタックの監視と構成に関する一般的なアドバイス

2. 概要

3. 詳細な分析

3.1。 ネットワークデバイスドライバー

3.2。 ソフティールク

3.3。 Linuxネットワークデバイスサブシステム

3.4。 受信パケットステアリング(RPS)

3.5。 受信フローステアリング(RFS)

3.6。 ハードウェアアクセラレーテッド受信フローステアリング(aRFS)

3.7。 netif_receive_skbを使用してネットワークスタックを上に移動する

3.8。 netif_receive_skb

3.9。 プロトコルレベルの登録

3.10。 追加情報

4. 結論



1. Linuxネットワークスタックの監視と構成に関する一般的なアドバイス



ネットワークスタックは複雑であり、あらゆる状況に対応できる普遍的なソリューションはありません。 ネットワークを操作する際のパフォーマンスと正確さがあなたまたはあなたのビジネスにとって重要である場合、システムのさまざまな部分が互いにどのように相互作用するかを理解するために多くの時間、努力、およびお金を投資する必要があります。



理想的には、ネットワークスタックの各レベルでパケット損失を測定する必要があります。 この場合、構成する必要があるコンポーネントを選択する必要があります。 多くの人がgiveめたのは、この時点です。 この仮定は、sysctl設定または/ proc値を繰り返し大量に使用できるという事実に基づいています。 場合によっては、システムに相互接続が浸透し、微妙なニュアンスに満ちている可能性が高いため、有用な監視を実装したり、チューニングを実行したりする場合は、システムの機能を低レベルで処理する必要があります。 それ以外の場合は、デフォルト設定を使用します。 これは、さらなる最適化(およびこれらの設定を追跡するための添付ファイル)が必要になるまでは十分です。



この記事に示されている設定例の多くは、単に説明として使用されているものであり、既定の構成または設定の使用を推奨または推奨するものではありません。 したがって、各設定を適用する前に、重要な変更を検出するために監視する必要があるものをまず考えてください。



マシンにリモートで接続してネットワーク設定を適用することは危険です。 自分へのアクセスを簡単にブロックしたり、ネットワークシステムを削除したりすることもできます。 動作中のマシンには設定を適用せず、最初に可能な限り新しいマシンで設定を実行してから、本番に適用します。



2.概要



デバイスのデータシートのコピーを手元に用意することをお勧めします。 この記事では、igbドライバーによって制御されるIntel I350コントローラーについて説明します。 ここから仕様をダウンロードできます。

パケットが到着からソケット受信バッファに渡す高レベルのパスは次のようになります。



  1. ドライバーがロードされ、初期化されます。
  2. パケットは、ネットワークからネットワークカードに到着します。
  3. パケットは(DMA経由で)カーネルの循環メモリバッファーにコピーされます。
  4. ハードウェア割り込みが生成され、パケットがメモリに表示されたときにシステムに通知されます。
  5. まだ開始していない場合、ドライバはNAPIを呼び出してポーリングループを開始します。
  6. ksoftirqdプロセスは、システムの各CPUで実行されます。 それらはブート時に登録されます。 これらのプロセスは、初期化中にデバイスドライバーによって登録されたポーリングNAPI関数を呼び出して、リングバッファーからパケットをプルします。
  7. ネットワークデータが書き込まれたリングバッファ内のマップされていないメモリ領域はクリアされます。
  8. メモリ(DMA)に直接送信されたデータは、さらに処理するために「skb」の形式でネットワーク層に転送されます。
  9. パケット管理が有効になっている場合、またはネットワークカードに複数の受信キューがある場合、着信ネットワークデータフレームはシステムの複数のCPUに分散されます。
  10. ネットワークデータフレームは、キューからプロトコルレイヤーに渡されます。
  11. プロトコル層はデータを処理します。
  12. プロトコル層によってソケットに接続されたバッファーを受信するためにデータが追加されます。


さらに、このストリーム全体を詳細に検討します。 プロトコル層として、IPおよびUDP層が考慮されます。 情報のほとんどは、他のプロトコル層にも当てはまります。



3.詳細な分析



Linuxカーネルバージョン3.13.0を検討します。 また、コードサンプルとGitHubへのリンクが記事全体で使用されています。



カーネルがパケットを受信する方法を正確に理解することは非常に重要です。 ネットワークドライバーの操作を注意深く読んで理解する必要があるため、後でネットワークスタックの説明を掘り下げるのが容易になります。



ネットワークドライバーとして、igbが検討されます。 かなり一般的なサーバーネットワークカード、Intel I350で使用されます。 それでは、このドライバーを解析することから始めましょう。



3.1。 ネットワークデバイスドライバー



初期化



ドライバーは、ドライバーのロード時にカーネルによって呼び出される初期化関数を登録します。 登録は、module_initマクロを使用して実行されます。

igb初期化関数(igb_init_module)を見つけて、それをdrivers / net / ethernet / intel / igb / igb_main.cのmodule_initに登録できます。 とても簡単です:



/** * igb_init_module –  (routine)   * * igb_init_module —   ,    . *       PCI. **/ static int __init igb_init_module(void) { int ret; pr_info("%s - version %s\n", igb_driver_string, igb_driver_version); pr_info("%s\n", igb_copyright); /* ... */ ret = pci_register_driver(&igb_driver); return ret; } module_init(igb_init_module);
      
      





後で見るように、デバイスの初期化作業の大部分は、pci_register_driverが呼び出されたときに発生します。



PCI初期化



Intel I350ネットワークカードは、 PCI Expressデバイスです。



PCIデバイスは、PCI構成スペースの一連のレジスタを介して自身を識別します



デバイスドライバーがコンパイルされると、マクロMODULE_DEVICE_TABLE( include / module.hから)を使用して、ドライバーが制御できるPCIデバイスの識別子のテーブルをエクスポートします。 以下では、テーブルも構造の一部として登録されていることがわかります。



このテーブルは、デバイスを制御するためにロードするドライバーを決定するためにカーネルによって使用されます。 したがって、オペレーティングシステムは、どのデバイスが接続されており、どのドライバーがユーザーと対話できるかを認識します。



igbドライバーのテーブルとPCIデバイス識別子は、それぞれここでdrivers / net / ethernet / intel / igb / igb_main.cおよびここでdrivers / net / ethernet / intel / igb / e1000_hw.hを見つけることができます:



 static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS), board_82575 }, /* ... */ }; MODULE_DEVICE_TABLE(pci, igb_pci_tbl);
      
      





上で見たように、pci_register_driverはドライバー初期化関数によって呼び出されます。



この関数は、ポインターの構造を登録します。 それらのほとんどは関数ポインターですが、PCデバイス識別子テーブルも登録されています。 カーネルは、ドライバーによって登録された関数を使用してPCIデバイスを起動します。



ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから



 static struct pci_driver igb_driver = { .name = igb_driver_name, .id_table = igb_pci_tbl, .probe = igb_probe, .remove = igb_remove, /* ... */ };
      
      





PCIプローブ機能



PCI IDによってデバイスが識別されると、カーネルは適切なドライバーを選択できます。 各ドライバーは、プローブ機能をカーネルPCIシステムに登録します。 カーネルは、ドライバーがまだ要求していないデバイスに対してこの関数を呼び出します。 ドライバーの1つがデバイスであると主張すると、他のドライバーはもはや尋問されません。 ほとんどのドライバーには、使用するデバイスを準備するために実行される多くのコードが含まれています。 実行される手順は、ドライバーによって大きく異なります。



一般的な手順を次に示します。



  1. PCIデバイスを有効にします。
  2. メモリ領域とI / Oポートを照会します
  3. DMAマスクを構成します。
  4. ドライバーがサポートするethtool機能が登録されています(以下で説明します)。
  5. ウォッチドッグタイマーが実行されています(たとえば、e1000eには、ハードウェアがハングしているかどうかを確認するタイマーがあります)。
  6. このデバイスに固有のその他の手順。 たとえば、ハードウェアトリックのバイパスや解決など。
  7. struct net_device_ops構造体を作成、初期化、および登録します。 デバイスのオープン、ネットワークへのデータ送信、MACアドレスの設定などに必要なさまざまな機能へのポインターが含まれています。
  8. ネットワークデバイスを表す高レベルの構造体net_device構造体を作成、初期化、および登録します。


igbドライバーとigb_probe関数のこれらの手順をいくつか見ていきましょう。



PCI初期化の概要



以下のigb_probe関数のコードは、基本的なPCI構成を実行します。 ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから適応



 err = pci_enable_device_mem(pdev); /* ... */ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); /* ... */ err = pci_request_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM), igb_driver_name); pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); pci_save_state(pdev);
      
      





まず、pci_enable_device_memを使用してデバイスが初期化されます。 デバイスがスリープモードの場合、デバイスが起動し、メモリソースがアクティブになります。



次に、DMAマスクが構成されます。 デバイスは64ビットメモリのアドレスの読み取りと書き込みができるため、DMA_BIT_MASK(64)の助けを借りてdma_set_mask_and_coherentが呼び出されます。



pci_request_selected_regionsを呼び出すことにより、メモリ領域が予約されます。 ドライバがロードされると、 PCI Express Advanced Error Reportingサービスが開始されます。 pci_set_masterを呼び出すと、DMAがアクティブになり、pci_save_stateを呼び出すとPCI設定スペースが保存されます。



ふう。



Linux用の追加のPCIドライバー情報



PCIデバイスの操作に関する詳細な説明はこの記事の範囲外ですが、次の資料を読むことができます。





ネットワークデバイスの初期化



igb_probe関数は、ネットワークデバイスを初期化する重要な仕事をします。 PCIに固有の手順に加えて、ネットワークを操作し、ネットワークデバイスを機能させるためのより一般的な操作を実行します。



  1. struct net_device_opsを登録します。
  2. ethtool操作を記録します。
  3. ネットワークカードからデフォルトのMACアドレスを取得します。
  4. net_deviceプロパティフラグを設定します。
  5. さらに多くのことを行います。


これは後で必要になるので、簡単に説明しましょう。



struct net_device_ops



struct net_device_opsには、デバイスを制御するためにネットワークサブシステムが必要とする多くの重要な操作への関数ポインターが含まれています。 この構造については、記事で複数回言及します。



net_device_ops構造体は、igb_probeのnet_device構造体に付加されます。 ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから適応



 static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { /* ... */ netdev->netdev_ops = &igb_netdev_ops;
      
      





同じファイルで、関数ポインターはnet_device_ops構造体に保存されます。 ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから適応



 static const struct net_device_ops igb_netdev_ops = { .ndo_open = igb_open, .ndo_stop = igb_close, .ndo_start_xmit = igb_xmit_frame, .ndo_get_stats64 = igb_get_stats64, .ndo_set_rx_mode = igb_set_rx_mode, .ndo_set_mac_address = igb_set_mac, .ndo_change_mtu = igb_change_mtu, .ndo_do_ioctl = igb_ioctl, /* ... */
      
      





ご覧のとおり、構造体には、ndo_open、ndo_stop、ndo_start_xmit、ndo_get_stats64など、igbドライバーによって実装される関数のアドレスを含む興味深いフィールドがいくつかあります。 それらのいくつかを以下でより詳細に検討します。



ethtoolを登録する



ethtoolはコマンドライン駆動のプログラムです。 これにより、さまざまなドライバーとハードウェアオプションを受け取り、構成できます。 Ubuntuでは、このプログラムはapt-get install ethtoolのようにインストールできます。



通常、ethtoolはネットワークデバイスから詳細な統計を収集するために使用されます。 他のアプリケーションについては以下で説明します。



プログラムは、 ioctlシステムコールを使用してドライバーと通信します。 デバイスドライバーはethtool操作のために実行される一連の機能を登録し、カーネルはグルーを提供します。



ethtoolがioctlを呼び出すと、カーネルは適切なドライバーによって登録されたethtool構造を見つけ、登録された機能を実行します。 ethtoolドライバー機能の実装は、ドライバーの単純なソフトウェアフラグの変更から、デバイスにレジスタ値を書き込むことによる物理NIC機器の動作の制御まで、何でもできます。



igb_set_ethtool_ops呼び出しを使用して、igbドライバーはethb操作をigb_probeに登録します。



 static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { /* ... */ igb_set_ethtool_ops(netdev);
      
      





igb_set_ethtool_ops関数と共にigbドライバーethtoolコード全体は、 drivers / net / ethernet / intel / igb / igb_igtool.cファイルにあります



ドライバー/ネット/イーサネット/インテル/ igb / igb_ethtool.cから適応



 void igb_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &igb_ethtool_ops); }
      
      





さらに、対応するフィールドで構成されたethtool関数でサポートされているigbドライバーでigb_ethtool_ops構造を見つけることができます。



ドライバー/ネット/イーサネット/インテル/ igb / igb_ethtool.cから適応



 static const struct ethtool_ops igb_ethtool_ops = { .get_settings = igb_get_settings, .set_settings = igb_set_settings, .get_drvinfo = igb_get_drvinfo, .get_regs_len = igb_get_regs_len, .get_regs = igb_get_regs, /* ... */
      
      





各ドライバーは、その裁量により、どのethtool機能が関連しており、どのethtool機能を実装すべきかを決定します。 残念ながら、すべてのドライバーがethtoolのすべての機能を実装しているわけではありません。



get_ethtool_stats関数(実装されている場合)は、ドライバーソフトウェアまたはデバイス自体によって監視される詳細な統計カウンターを作成しますが、非常に興味深いものです。



監視の部分では、ethtoolを使用してこれらの統計を取得する方法を見ていきます。



IRQ



DMAを使用してデータフレームがメモリに書き込まれると、ネットワークカードはデータを処理する準備ができたことをシステムにどのように伝えますか?



通常、カードはデータの到着を知らせる割り込みを生成します。 割り込みには、MSI-X、MSI、Legacy-IRQの3つの一般的なタイプがあります。 すぐにそれらを検討します。 メモリにデータを書き込むときに生成される割り込みは非常に簡単ですが、多数のフレームが到着すると、多数のIRQも生成されます。 割り込みが多いほど、ユーザープロセスなどの高レベルのタスクを処理するために使用できるCPU時間は短くなります。



新しいAPI(NAPI)は、パケットの到着時にネットワークデバイスによって生成される割り込みの数を減らすメカニズムとして作成されました。 それでも、NAPIは中断から完全に救うことはできません。 後で理由を調べます。



ナピ



多くの重要な機能について、 NAPIは従来のデータ収集方法とは異なります。 これにより、デバイスドライバーは、NAPIサブシステムによって呼び出されたポーリング関数を登録して、データフレームを収集できます。



ネットワークデバイスにNAPIドライバーを使用するためのアルゴリズムは次のようになります。



  1. ドライバーにはNAPIが含まれていますが、最初は非アクティブ状態です。
  2. パケットが到着し、ネットワークカードが直接パケットをメモリに送信します。
  3. ネットワークカードは、ドライバーで割り込みハンドラーを起動してIRQを生成します。
  4. ドライバーは、SoftIRQを使用してNAPIサブシステムを起動します(これについては以下で詳しく説明します)。 彼女はパケットの収集を開始し、ドライバーによって実行の別のスレッドで登録されたポーリング関数を呼び出します。
  5. ドライバは、ネットワークカードによる後続の割り込み生成を無効にする必要があります。 これは、NAPIサブシステムがデバイスからの干渉なしにパケットを処理できるようにするために必要です。
  6. すべての作業が完了すると、NAPIサブシステムがオフになり、デバイスによる割り込み生成が再びオンになります。
  7. サイクルはポイント2から繰り返されます。


データフレームを収集するこの方法は、多くのフレームをそれぞれのIRQを同時に生成する必要なしに同時に受信できるため、従来の方法と比較して負荷を軽減しました。



デバイスドライバーはポーリング関数を実装し、netif_napi_addを呼び出してNAPIに登録します。 この場合、ドライバーは重量も設定します。 ほとんどのドライバーは、64の値をハードコードします。それがなぜ正確なのか、さらに詳しく説明します。



通常、ドライバーはドライバーの初期化中にポーリングNAPI関数を登録します。



igbドライバーでNAPIを初期化する



igbドライバーは、呼び出しの長いチェーンでこれを行います。



  1. igb_probeはigb_sw_initを呼び出します。
  2. igb_sw_initはigb_init_interrupt_schemeを呼び出します。
  3. igb_init_interrupt_schemeはigb_alloc_q_vectorsを呼び出します。
  4. igb_alloc_q_vectorsはigb_alloc_q_vectorを呼び出します。
  5. igb_alloc_q_vectorはnetif_napi_addを呼び出します。


その結果、多くの高レベルの操作が実行されます。



  1. MSI-Xがサポートされている場合、pci_enable_msixを呼び出すことで有効になります。
  2. さまざまな設定が計算され、初期化されます。 たとえば、デバイスとドライバーがパケットの送受信に使用する送信および受信キューの数。
  3. igb_alloc_q_vectorは、作成された送信および受信キューごとに1回呼び出されます。
  4. igb_alloc_q_vectorを呼び出すたびに、netif_napi_addも呼び出して、特定のキューのポーリング関数を登録します。 パッケージを収集するためにポーリング関数が呼び出されると、struct napi_structのインスタンスが渡されます。


igb_alloc_q_vectorを見て、コールバックポーリングとそのプライベートデータがログに記録される方法を理解しましょう。



ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから適応



 static int igb_alloc_q_vector(struct igb_adapter *adapter, int v_count, int v_idx, int txr_count, int txr_idx, int rxr_count, int rxr_idx) { /* ... */ /*    q_vector   (rings) */ q_vector = kzalloc(size, GFP_KERNEL); if (!q_vector) return -ENOMEM; /*  NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64); /* ... */
      
      





上記は、NAPIサブシステムを使用して受信および登録するために、igb_poll関数をキューのメモリに配置するためのコードです。 この新しく作成された受信キューに関連付けられたstruct napi_structへのリンクを取得します(&q_vector-> napi)。 キューからパケットを収集するときが来て、NAPIサブシステムによってigb_pollが呼び出されると、このリンクが渡されます。



ドライバからネットワークスタックへのデータストリームを調べるときに、説明したアルゴリズムの重要性を理解します。



ネットワークデバイスを起動する



ネットワークデバイスの起動、パケットの送信、MACアドレスの設定などの一連の関数を登録したnet_device_ops構造を覚えていますか?



ネットワークデバイスがロードされると(たとえば、ifconfig eth0 upを使用して)、net_device_ops構造体のndo_openフィールドに関連付けられている関数が呼び出されます。



ndo_open関数は通常、次のことを行います。



  1. 受信および送信キューにメモリを割り当てます。
  2. NAPIが含まれています。
  3. 割り込みハンドラーを登録します。
  4. ハードウェア割り込みを有効にします。
  5. そして、はるかに。


igbドライバーの場合、igb_openはnet_device_ops構造体のndo_openフィールドに付加された関数を呼び出します。



ネットワークからデータを受信する準備



最新のネットワークカードのほとんどは、DMAを使用してデータをメモリに直接書き込み、オペレーティングシステムがそこからデータを取得して、さらに処理することができます。 ほとんどの場合、これに使用される構造は、リングバッファに基づいて作成されたキューに似ています。



最初に、デバイスドライバーは、OSとともに、ネットワークカードが使用する領域をメモリに予約する必要があります。 次に、カードにメモリの割り当てが通知されます。このメモリには、後で受信されるデータが記録され、ネットワークサブシステムを使用して取得および処理できます。



単純に見えますが、パケットの周波数が非常に高く、1つのCPUがパケットを処理する時間がない場合はどうでしょうか? データ構造は固定サイズのメモリ領域に基づいているため、パケットは破棄されます。



この場合、マルチキューシステムであるReceive Side Scaling(RSS)が役立ちます。



一部のデバイスは、着信パケットを複数の異なるメモリ領域に同時に書き込むことができます。 各エリアは個別のキューを提供します。 これにより、OSはハードウェアレベルで受信データの並列処理に複数のCPUを使用できます。 しかし、すべてのネットワークカードがこれを実行できるわけではありません。



Intel I350-できます。 igbドライバーでこのスキルの証拠を確認します。 ロード後に彼が最初に行うことの1つは、 igb_setup_all_rx_resources関数を呼び出すことです 。 この関数は、受信キューごとに1つの別の関数igb_setup_rx_resourcesを呼び出します。これは、ネットワークカードが着信データを書き込むDMAメモリを編成します。



詳細については、 github.com / torvalds / linux / blob / v3.13 / Documentation / DMA-API-HOWTO.txtをご覧ください



ethtoolを使用して、受信キューの数とサイズを構成できます。 これらのパラメーターを変更すると、処理されたフレームと破棄されたフレームの比率に大きく影響する可能性があります。



データを送信するキューを決定するために、ネットワークカードはヘッダーフィールド(ソース、宛先、ポートなど)でハッシュ関数を使用します。



一部のネットワークカードでは、受信キューの重みを設定できるため、より多くのトラフィックを特定のキューに転送できます。

あまり一般的ではないのは、ハッシュ関数自体を構成する機能です。 設定できる場合は、特定のスレッドを特定のキューに送信したり、ハードウェアレベルでパケットをドロップしたりできます。

以下では、ハッシュ関数がどのように構成されているかを見ていきます。



NAPIを有効にする



ネットワークデバイスが読み込まれると、通常、ドライバーにはNAPIが含まれます。 ドライバーがNAPIを使用してポーリング機能を登録する方法については既に説明しました。 通常、NAPIはデバイスが起動するまで電源を入れません。



オンにするのは非常に簡単です。 napi_enable呼び出しは、NAPIが有効であることをstruct napi_structに通知します。 上記のように、オンにすると、NAPIは非アクティブ状態になります。



igbドライバーの場合、ドライバーのロード後、またはethtoolを使用してカウンターまたはキューのサイズが変更されたときに初期化される各q_vectorに対してNAPIが有効になります。



ドライバー/ネット/イーサネット/インテル/ igb / igb_main.cから適応



 for (i = 0; i < adapter->num_q_vectors; i++) napi_enable(&(adapter->q_vector[i]->napi));
      
      





割り込みハンドラーの登録



NAPIを有効にした後、割り込みハンドラーを登録する必要があります。 デバイスは、MSI-X、MSI、およびレガシー割り込みなど、さまざまな方法で割り込みを生成できます。 したがって、サポートされている方法によって、コードが異なる場合があります。



ドライバーは、このデバイスでサポートされているメソッドを判別し、割り込みを受信したときに実行する対応するハンドラー関数を登録する必要があります。



igbを含む一部のドライバーは、各メソッドのハンドラーを登録しようとしますが、失敗した場合は、次の未試行のメソッドに進みます。



特に複数の受信キューをサポートするネットワークカードの場合は、MSI-X割り込みを使用することをお勧めします。 その理由は、各キューに固有のハードウェア割り込みが割り当てられ、特定のCPUで処理できる(irqbalanceを使用するか、/ proc / irq / IRQ_NUMBER / smp_affinityを変更する)ためです。 すぐにわかるように、割り込みとパケットは同じCPUを処理しています。 したがって、着信パケットは、ハードウェア割り込みのレベルから開始して、ネットワークスタック全体内の異なるCPUによって処理されます。



MSI-Xが使用できない場合、ドライバーはMSI(サポートされている場合)を使用しますが、MSI-Xは従来の割り込みよりも利点があります。 詳細については、英語版ウィキペディアをご覧ください



igbドライバーでは、関数igb_msix_ring、igb_intr_msi、igb_intrはそれぞれ、MSI-X、MSI、およびレガシーの割り込みハンドラーとして機能します。



各メソッドを試行するドライバーコードは、 drivers / net / ethernet / intel / igb / igb_main.cにあります。



 static int igb_request_irq(struct igb_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; int err = 0; if (adapter->msix_entries) { err = igb_request_msix(adapter); if (!err) goto request_done; /*   MSI */ /* ... */ } /* ... */ if (adapter->flags & IGB_FLAG_HAS_MSI) { err = request_irq(pdev->irq, igb_intr_msi, 0, netdev->name, adapter); if (!err) goto request_done; /*    */ /* ... */ } err = request_irq(pdev->irq, igb_intr, IRQF_SHARED, netdev->name, adapter); if (err) dev_err(&pdev->dev, "Error %d getting interrupt\n", err); request_done: return err; }
      
      





ご覧のとおり、ドライバーは最初にMSI-X割り込みにigb_request_msixハンドラーを使用しようとします;失敗すると、MSIに切り替わります。 Request_irqは、igb_intr_msi MSIハンドラーの登録に使用されます。 これが機能しない場合、ドライバーはレガシー割り込みに切り替わります。 Request_irqは、igb_intrの登録に再び使用されます。



igb , , .





. . , igb __igb_open, igb_irq_enable.



:

 static void igb_irq_enable(struct igb_adapter *adapter) { /* ... */ wr32(E1000_IMS, IMS_ENABLE_MASK | E1000_IMS_DRSTA); wr32(E1000_IAM, IMS_ENABLE_MASK | E1000_IMS_DRSTA); /* ... */ }
      
      





. , , , , . .



.





, . .



ethtool -S



ethtool Ubuntu : sudo apt-get install ethtool.

, -S , .



(, ) `ethtool -S`.



 $ sudo ethtool -S eth0 NIC statistics: rx_packets: 597028087 tx_packets: 5924278060 rx_bytes: 112643393747 tx_bytes: 990080156714 rx_broadcast: 96 tx_broadcast: 116 rx_multicast: 20294528 ....
      
      





. , . , , .



, “drop”, “buffer”, “miss” . . , (, ), . , , . , ethtool, .



sysfs



sysfs , , .



, , eth0, cat .



sysfs:



 $ cat /sys/class/net/eth0/statistics/rx_dropped 2
      
      





: collisions, rx_dropped, rx_errors, rx_missed_errors .



, , , . , , — .



, , , .



/proc/net/dev



/proc/net/dev, .



/proc/net/dev, :



 $ cat /proc/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed eth0: 110346752214 597737500 0 2 0 0 0 20963860 990024805984 6066582604 0 0 0 0 0 0 lo: 428349463836 1579868535 0 0 0 0 0 0 428349463836 1579868535 0 0 0 0 0 0
      
      





, sysfs. .



, , . , , , , FIFO.







RSS ( ), ethtool ( , RX-channels).



:



 $ sudo ethtool -l eth0 Channel parameters for eth0: Pre-set maximums: RX: 0 TX: 0 Other: 0 Combined: 8 Current hardware settings: RX: 0 TX: 0 Other: 0 Combined: 4
      
      





( ) .



: .



, , :



 $ sudo ethtool -l eth0 Channel parameters for eth0: Cannot get device channel parameters : Operation not supported
      
      





, ethtool get_channels. , RSS, .





, , sudo ethtool -L.



: , — — .



ethtool -L 8 :



 $ sudo ethtool -L eth0 combined 8
      
      





, 8 :



 $ sudo ethtool -L eth0 rx 8
      
      





: , . .





. , , , ethtool . . , , .



ethtool –g:



 $ sudo ethtool -g eth0 Ring parameters for eth0: Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 512 RX Mini: 0 RX Jumbo: 0 TX: 512
      
      





, 4096 , 512.



4096:



 $ sudo ethtool -G eth0 rx 4096
      
      





: , . .





.



, :





:



 $ sudo ethtool -x eth0 RX flow hash indirection table for eth3 with 2 RX ring(s): 0: 0 1 0 1 0 1 0 1 8: 0 1 0 1 0 1 0 1 16: 0 1 0 1 0 1 0 1 24: 0 1 0 1 0 1 0 1
      
      





— 0 1. 2 0, 3 — 1.



: :



 $ sudo ethtool -X eth0 equal 2
      
      





, , ( , CPU), ethtool –X:



 $ sudo ethtool -X eth0 weight 6 2
      
      





0 6, 1 — 2. , 0.



, , .





ethtool , , RSS.



C ethtool -n , UPD:



 $ sudo ethtool -n eth0 rx-flow-hash udp4 UDP over IPV4 flows use these fields for computing Hash flow key: IP SA IP DA
      
      





eth0, UDP- IPv4 . :



 $ sudo ethtool -N eth0 rx-flow-hash udp4 sdfn
      
      





sdfn . ethtool man.



, ntuple .



ntuple



« ntuple» (ntuple filtering). ( ethtool) , . , , TCP-, , 1.



Intel Intel Ethernet Flow Director . .



, ntuple — , Accelerated Receive Flow Steering (aRFS). ntuple, . aRFS .



, (data locality) (hit rates) CPU . , -, 80:





, ntuple ethtool, , . :



 $ sudo ethtool -k eth0 Offload parameters for eth0: ... ntuple-filters: off receive-hashing: on
      
      





, ntuple-filters off.



ntuple-:



 $ sudo ethtool -K eth0 ntuple on
      
      





, , :



 $ sudo ethtool -u eth0 40 RX rings available Total 0 rules
      
      





. ethtool. , TCP-, 80, 2:



 $ sudo ethtool -U eth0 flow-type tcp4 dst-port 80 action 2
      
      





ntuple . - IP-. man ethtool.



ntuple- ethtool -S [device name]. , Intel fdir_match fdir_miss . .



3.2。 SoftIRQ



, SoftIRQ Linux.



SoftIRQ?



, . , . , . , .



, . SoftIRQ.



SoftIRQ ( CPU), -, SoftIRQ-. - ksoftirqd/0 , SoftIRQ, CPU 0.



(, ) SoftIRQ open_softirq. , SoftIRQ-. , SoftIRQ.



ksoftirqd



SoftIRQ , ksoftirqd .



kernel/softirq.c , , ksoftirqd:



 static struct smp_hotplug_thread softirq_threads = { .store = &ksoftirqd, .thread_should_run = ksoftirqd_should_run, .thread_fn = run_ksoftirqd, .thread_comm = "ksoftirqd/%u", }; static __init int spawn_ksoftirqd(void) { register_cpu_notifier(&cpu_nfb); BUG_ON(smpboot_register_percpu_thread(&softirq_threads)); return 0; } early_initcall(spawn_ksoftirqd);
      
      





struct smp_hotplug_thread, : ksoftirqd_should_run run_ksoftirqd.



kernel/smpboot.c -, (event loop).



kernel/smpboot.c ksoftirqd_should_run, , SoftIRQ. , run_ksoftirqd, , __do_softirq.



__do_softirq



__do_softirq :





CPU softirq si, CPU, .



モニタリング



/proc/softirqs



softirq , /proc/softirqs. , SoftIRQ .



/proc/softirqs, SoftIRQ:



 $ cat /proc/softirqs CPU0 CPU1 CPU2 CPU3 HI: 0 0 0 0 TIMER: 2831512516 1337085411 1103326083 1423923272 NET_TX: 15774435 779806 733217 749512 NET_RX: 1671622615 1257853535 2088429526 2674732223 BLOCK: 1800253852 1466177 1791366 634534 BLOCK_IOPOLL: 0 0 0 0 TASKLET: 25 0 0 0 SCHED: 2642378225 1711756029 629040543 682215771 HRTIMER: 2547911 2046898 1558136 1521176 RCU: 2056528783 4231862865 3545088730 844379888
      
      





, (NET_RX). , CPU , . , Receive Packet Steering / Receive Flow Steering. , : NET_RX, . SoftIRQ NET_RX , .



, , , /proc/softirqs.



, .



3.3。 Linux



SoftIRQ, . .





(netdev) net_dev_init. .



struct softnet_data



net_dev_init struct softnet_data CPU . :





SoftIRQ



net_dev_init SoftIRQ , . :



 static int __init net_dev_init(void) { /* ... */ open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action); /* ... */ }
      
      





, «» ( ) net_rx_action, SoftIRQ NET_RX_SOFTIRQ.





- !



, , DMA. (, MSI-X, , ).





, . , .



MSI-X. , .



drivers/net/ethernet/intel/igb/igb_main.c :

 static irqreturn_t igb_msix_ring(int irq, void *data) { struct igb_q_vector *q_vector = data; /*   ITR,    . */ igb_write_itr(q_vector); napi_schedule(&q_vector->napi); return IRQ_HANDLED; }
      
      





, .



  1. igb_write_itr . . “Interrupt Throttling” ( « », Interrupt Coalescing), CPU. , ethtool IRQ.
  2. napi_schedule, NAPI, . , SoftIRQ, . , .


, . , .



NAPI napi_schedule



, napi_schedule .



, NAPI , . , poll (bootstrapped) . , NAPI , , . NAPI . , NAPI , , .



poll , napi_schedule. -, , __napi_schedule.



net/core/dev.c :



 /** * __napi_schedule –   * @n:    * *       */ void __napi_schedule(struct napi_struct *n) { unsigned long flags; local_irq_save(flags); ____napi_schedule(&__get_cpu_var(softnet_data), n); local_irq_restore(flags); } EXPORT_SYMBOL(__napi_schedule);
      
      





softnet_data, CPU, __get_cpu_var. ____napi_schedule struct napi_struct. .



____napi_schedule, net/core/dev.c :



 /*    IRQ */ static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi) { list_add_tail(&napi->poll_list, &sd->poll_list); __raise_softirq_irqoff(NET_RX_SOFTIRQ); }
      
      





:



  1. struct napi_struct, , poll_list, softnet_data, CPU.
  2. __raise_softirq_irqoff SoftIRQ NET_RX_SOFTIRQ. net_rx_action, , .


, SoftIRQ - net_rx_action NAPI poll.



CPU



, , SoftIRQ, , CPU.



IRQ- , SoftIRQ- CPU, IRQ-. , CPU IRQ: CPU , SoftIRQ NAPI.



, ( Receive Packet Steering ) CPU .







: . NAPI. .



/proc/interrupts, :



 $ cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 0: 46 0 0 0 IR-IO-APIC-edge timer 1: 3 0 0 0 IR-IO-APIC-edge i8042 30: 3361234770 0 0 0 IR-IO-APIC-fasteoi aacraid 64: 0 0 0 0 DMAR_MSI-edge dmar0 65: 1 0 0 0 IR-PCI-MSI-edge eth0 66: 863649703 0 0 0 IR-PCI-MSI-edge eth0-TxRx-0 67: 986285573 0 0 0 IR-PCI-MSI-edge eth0-TxRx-1 68: 45 0 0 0 IR-PCI-MSI-edge eth0-TxRx-2 69: 394 0 0 0 IR-PCI-MSI-edge eth0-TxRx-3 NMI: 9729927 4008190 3068645 3375402 Non-maskable interrupts LOC: 2913290785 1585321306 1495872829 1803524526 Local timer interrupts
      
      





/proc/interrupts, . , CPU. , , , , NAPI. , (interrupt coalescing) , . , .



, /proc/softirqs /proc. .







CPU, .



« » , . , CPU. : , CPU.



, igb, e1000 InterruptThrottleRate. generic ethtool.



IRQ:



 $ sudo ethtool -c eth0 Coalesce parameters for eth0: Adaptive RX: off TX: off stats-block-usecs: 0 sample-interval: 0 pkt-rate-low: 0 pkt-rate-high: 0 ...
      
      





ethtool generic- . , . , , . ethtool: «, , ».



« /» (adaptive RX/TX IRQ coalescing). . , , - (bookkeeping) ( igb).



, .



:



 $ sudo ethtool -C eth0 adaptive-rx on
      
      





ethtool -C . :





.



, . .



, , . include/uapi/linux/ethtool.h , ethtool ( , ).



: . , , . .



IRQ



RSS, , CPU .



CPU. , .



IRQ, , irqbalance. CPU, . irqbalance, --banirq IRQBALANCE_BANNED_CPUS, irqbalance , CPU.



/proc/interrupts . , /proc/irq/IRQ_NUMBER/smp_affinity, , CPU . , , CPU .



: IRQ 8 CPU 0:



 $ sudo bash -c 'echo 1 > /proc/irq/8/smp_affinity'
      
      







SoftIRQ- , SoftIRQ , net_rx_action .



net_rx_action, , , .



net_rx_action



net_rx_action , DMA.



NAPI, CPU, .



NAPI- poll. :



  1. (work budget) ( ),
  2. .


net/core/dev.c :



 while (!list_empty(&sd->poll_list)) { struct napi_struct *n; int work, weight; /*    SoftIRQ -  *      ,    *     1.5/. */ if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) goto softnet_break;
      
      





CPU. budget — , NAPI-, CPU.



, IRQ. , CPU, , SoftIRQ. CPU .



, , , CPU NAPI-. CPU «» .



CPU , net_rx_action budget, CPU . CPU ( sitime si top ), , .



: CPU jiffies , .



NAPI- poll weight



, poll netif_napi_add. , igb :



 /*  NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64);
      
      





NAPI-, 64. , net_rx_action.



net/core/dev.c :



 weight = n->weight; work = 0; if (test_bit(NAPI_STATE_SCHED, &n->state)) { work = n->poll(n, weight); trace_napi_poll(n); } WARN_ON_ONCE(work > weight); budget -= work;
      
      





, NAPI, poll, NAPI ( igb_poll).



poll . work, budget.



:

  1. 64 ( Linux 3.13.0 ),
  2. budget 300.


, :



  1. igb_poll 5 ( , , ),
  2. 2 jiffies.


NAPI



, NAPI . NAPI.





, net_rx_action . , poll, , .



net_rx_action



net_rx_action , NAPI. net/core/dev.c :



 /*      NAPI,   *    .       * «»  NAPI, , ,  *       . */ if (unlikely(work == weight)) { if (unlikely(napi_disable_pending(n))) { local_irq_enable(); napi_complete(n); local_irq_disable(); } else { if (n->gro_list) { /*     *  HZ < 1000,   . */ local_irq_enable(); napi_gro_flush(n, HZ >= 1000); local_irq_disable(); } list_move_tail(&n->poll_list, &sd->poll_list); } }
      
      





, net_rx_action :



  1. (, ifconfig eth0 down).
  2. , , generic receive offload (GRO). ( timer tick rate ) >= 1000, GRO, , . GRO. NAPI- CPU, NAPI-.


poll, . , .





net_rx_action , :









  /*    SoftIRQ - . *      ,    *     1.5/. */ if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) goto softnet_break;
      
      





label softnet_break, - . net/core/dev.c :



 softnet_break: sd->time_squeeze++; __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out;
      
      





struct softnet_data SoftIRQ NET_RX_SOFTIRQ. time_squeeze — , net_rx_action , , . . . NET_RX_SOFTIRQ , . , , , CPU.



(label) out. out , NAPI- , , , NAPI, net_rx_action .



net_rx_action, out : net_rps_action_and_irq_enable. ( Receive Packet Steering ), CPU, .



RPS. , net_rx_action, «» NAPI- poll, .



NAPI- poll



, , . , , .



igb ?



igb_poll



- igb_poll. . drivers/net/ethernet/intel/igb/igb_main.c :



 /** * igb_poll – NAPI Rx polling callback * @napi:   (polling) NAPI * @budget:   ,    **/ static int igb_poll(struct napi_struct *napi, int budget) { struct igb_q_vector *q_vector = container_of(napi, struct igb_q_vector, napi); bool clean_complete = true; #ifdef CONFIG_IGB_DCA if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED) igb_update_dca(q_vector); #endif /* ... */ if (q_vector->rx.ring) clean_complete &= igb_clean_rx_irq(q_vector, budget); /*     ,      */ if (!clean_complete) return budget; /*      ,     */ napi_complete(napi); igb_ring_irq_enable(q_vector); return 0; }
      
      





:





, igb_clean_rx_irq .



igb_clean_rx_irq



igb_clean_rx_irq — , , budget .



:



  1. , . IGB_RX_BUFFER_WRITE (16) .
  2. skb.
  3. , “End of Packet”. , . skb. , .
  4. (layout) .
  5. skb->len .
  6. skb , , , VLAN id . . , csum_error. , UDP TCP, skb CHECKSUM_UNNECESSARY. , . eth_type_trans skb.
  7. skb napi_gro_receive.
  8. .
  9. , .


, .



, . -, , SoftIRQ . -, Generic Receive Offloading (GRO). , napi_gro_receive, .





/proc/net/softnet_stat





, net_rx_action, , , SoftIRQ. struct softnet_data, CPU. /proc/net/softnet_stat, , , . .



Linux 3.13.0 , /proc/net/softnet_stat . net/core/net-procfs.c :



 seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", sd->processed, sd->dropped, sd->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ sd->cpu_collision, sd->received_rps, flow_limit_count);
      
      





. , . squeeze_time net_rx_action, , .



/proc/net/softnet_stat, :



 $ cat /proc/net/softnet_stat 6dcad223 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6f0e1565 00000000 00000002 00000000 00000000 00000000 00000000 00000000 00000000 00000000 660774ec 00000000 00000003 00000000 00000000 00000000 00000000 00000000 00000000 00000000 61c99331 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6794b1b3 00000000 00000005 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6488cb92 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000
      
      





/proc/net/softnet_stat:





, , . .





net_rx_action



net_rx_action , NAPI-, CPU. sysctl net.core.netdev_budget.



: 600.



 $ sudo sysctl -w net.core.netdev_budget=600
      
      





/etc/sysctl.conf , . Linux 3.13.0 300.



Generic Receive Offloading (GRO)



Generic Receive Offloading (GRO) — , Large Receive Offloading (LRO).



, , , « » . CPU. , , . , , . . , .



. - , . LRO .

GRO LRO, .



: - tcpdump , GRO . , tap' , GRO.



GRO ethtool



ethtool , GRO, .



:



 $ ethtool -k eth0 | grep generic-receive-offload generic-receive-offload: on
      
      





generic-receive-offload. GRO:



 $ sudo ethtool -K eth0 gro on
      
      





: , . .



napi_gro_receive



napi_gro_receive GRO ( GRO ) . dev_gro_receive.



dev_gro_receive



, GRO. , : offload-, , GRO. , , , , , GRO. , TCP- , / .



, , net/core/dev.c :



 list_for_each_entry_rcu(ptype, head, list) { if (ptype->type != type || !ptype->callbacks.gro_receive) continue; skb_set_network_header(skb, skb_gro_offset(skb)); skb_reset_mac_len(skb); NAPI_GRO_CB(skb)->same_flow = 0; NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; pp = ptype->callbacks.gro_receive(&napi->gro_list, skb); break; }
      
      





, GRO-, . napi_gro_complete, callback gro_complete , netif_receive_skb.



net/core/dev.c :

 if (pp) { struct sk_buff *nskb = *pp; *pp = nskb->next; nskb->next = NULL; napi_gro_complete(nskb); napi->gro_count--; }
      
      





, napi_gro_receive .



MAX_GRO_SKBS (8) GRO-, gro_list NAPI- CPU .



net/core/dev.c :



 if (NAPI_GRO_CB(skb)->flush || napi->gro_count >= MAX_GRO_SKBS) goto normal; napi->gro_count++; NAPI_GRO_CB(skb)->count = 1; NAPI_GRO_CB(skb)->age = jiffies; skb_shinfo(skb)->gso_size = skb_gro_len(skb); skb->next = napi->gro_list; napi->gro_list = skb; ret = GRO_HELD;
      
      





GRO Linux.



napi_skb_finish



dev_gro_receive napi_skb_finish, , , netif_receive_skb ( GRO MAX_GRO_SKBS).



(Receive Packet Steering (RPS)).



3.4。 Receive Packet Steering (RPS)



, , NAPI- poll? NAPI SoftIRQ, CPU. , CPU, , SoftIRQ- .



, CPU poll.



( Intel I350) . , , . NAPI-. CPU.



Receive Side Scaling (RSS).



Receive Packet Steering (RPS) — RSS. , , . , , RPS , DMA- .



, CPU poll, , , CPU .



RPS , , CPU . (backlog) . backlog (IPI), , . /proc/net/softnet_stat , softnet_data IPI ( received_rps).



, netif_receive_skb RPS CPU.



RPS



RPS ( Ubuntu 3.13.0), , , CPU .



. , :



 /sys/class/net/DEVICE_NAME/queues/QUEUE/rps_cpus
      
      





, eth0 0 /sys/class/net/eth0/queues/rx-0/rps_cpus , , CPU 0 eth0. , RPS .



: RPS CPU, , CPU SoftIRQ `NET_RX`, `si` `sitime` CPU. «» «», , RPS .



3.5. (Receive Flow Steering (RFS))



Receive flow steering (RFS) RPS. RPS CPU, CPU. , RFS, CPU.



RFS



RFS , . RFS - . sysctl net.core.rps_sock_flow_entries.



RFS:



 $ sudo sysctl -w net.core.rps_sock_flow_entries=32768
      
      





. rps_flow_cnt .



: 2048 eth0 0:



 $ sudo bash -c 'echo 2048 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt'
      
      





3.6. (Accelerated Receive Flow Steering (aRFS))



RFS . , CPU . , . ndo_rx_flow_steer, aRFS.



aRFS



, . :



  1. RPS.
  2. RFS.
  3. CONFIG_RFS_ACCEL. , Ubuntu 3.13.0.
  4. ntuple, . , , ethtool.
  5. IRQ , CPU, .


, aRFS , CPU, . ntuple .



3.7. (moving up) netif_receive_skb



, netif_receive_skb, . ( ):





: netif_receive_skb SoftIRQ. top sitime si.



netif_receive_skb sysctl , , backlog-. , , RPS ( backlog-, CPU). , . RPS CPU, .



:



sysctl net.core.netdev_tstamp_prequeue.



:



 $ sudo sysctl -w net.core.netdev_tstamp_prequeue=0
      
      





1. .



3.8. netif_receive_skb



, netif_receive_skb , , RPS. , RPS .



RPS ( )



__netif_receive_skb, - (bookkeeping), __netif_receive_skb_core, .



, __netif_receive_skb_core, RPS, __netif_receive_skb_core.



RPS



, netif_receive_skb , backlog- CPU . get_rps_cpu. net/core/dev.c :



 cpu = get_rps_cpu(skb->dev, skb, &rflow); if (cpu >= 0) { ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); rcu_read_unlock(); return ret; }
      
      





get_rps_cpu RFS aRFS, , enqueue_to_backlog backlog CPU.



enqueue_to_backlog



softnet_data CPU, input_pkt_queue. input_pkt_queue CPU. net/core/dev.c :



 qlen = skb_queue_len(&sd->input_pkt_queue); if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
      
      





input_pkt_queue netdev_max_backlog. , . , , . softnet_data. , CPU, . /proc/net/softnet_stat.



enqueue_to_backlog RPS, netif_rx. netif_rx, netif_receive_skb. RPS netif_rx, backlog' , .



: . netif_receive_skb RPS, netdev_max_backlog , input_pkt_queue.



, input_pkt_queue ( ), . , :





- goto , . net/core/dev.c :



 if (skb_queue_len(&sd->input_pkt_queue)) { enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); input_queue_tail_incr_save(sd, qtail); rps_unlock(sd); local_irq_restore(flags); return NET_RX_SUCCESS; } /* Schedule NAPI for backlog device * We can use non atomic operation since we own the queue lock */ if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) { if (!rps_ipi_queued(sd)) ____napi_schedule(sd, &sd->backlog); } goto enqueue;
      
      







RPS CPU, . , backlog. .



if net/core/dev.c skb_flow_limit :



 if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
      
      





, . . ( RPS).



input_pkt_queue



. /proc/net/softnet_stat. dropped — , input_pkt_queue CPU.



カスタマイズ



netdev_max_backlog



, .



RPS netif_rx, enqueue_to_backlog netdev_max_backlog.



: backlog 3000:



 $ sudo sysctl -w net.core.netdev_max_backlog=3000
      
      





1000.



NAPI backlog poll



NAPI backlog' net.core.dev_weight sysctl. , poll backlog' (. net.core.netdev_budget).



: backlog' poll:



 $ sudo sysctl -w net.core.dev_weight=600
      
      





64.



, backlog' SoftIRQ poll. , .



-



:



 $ sudo sysctl -w net.core.flow_limit_table_len=8192
      
      





4096.



. , .



/proc/sys/net/core/flow_limit_cpu_bitmap, RPS, , CPU .



NAPI backlog-



Backlog- CPU NAPI , . poll, SoftIRQ. , weight.



NAPI .



net_dev_init net/core/dev.c :



 sd->backlog.poll = process_backlog; sd->backlog.weight = weight_p; sd->backlog.gro_list = NULL; sd->backlog.gro_count = 0;
      
      





NAPI-c backlog' weight: . .



process_backlog



process_backlog — , , ( ) backlog' .



backlog- __netif_receive_skb. , RPS. , __netif_receive_skb __netif_receive_skb_core, .



process_backlog NAPI, : NAPI , . ____napi_schedule enqueue_to_backlog, .



, net_rx_action ( ) ( net.core.netdev_budget, ).



__netif_receive_skb_core packet taps



__netif_receive_skb_core . , - packet taps, . – AF_PACKET, libpcap .



tap, , .



packet tap



. net/core/dev.c :



 list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) ret = deliver_skb(skb, pt_prev, orig_dev); pt_prev = ptype; } }
      
      





, pcap, net/packet/af_packet.c .





, , __netif_receive_skb_core . (deliver functions), .



__netif_receive_skb_core net/core/dev.c :



 type = skb->protocol; list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) { if (ptype->type == type && (ptype->dev == null_or_dev || ptype->dev == skb->dev || ptype->dev == orig_dev)) { if (pt_prev) ret = deliver_skb(skb, pt_prev, orig_dev); pt_prev = ptype; } }
      
      





ptype_base - net/core/dev.c :



 struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
      
      





-, ptype_head:



 static inline struct list_head *ptype_head(const struct packet_type *pt) { if (pt->type == htons(ETH_P_ALL)) return &ptype_all; else return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK]; }
      
      





dev_add_pack. .

, .



3.9.



, . IP, .



IP



IP - ptype_base, , .



inet_init, net/ipv4/af_inet.c :



 dev_add_pack(&ip_packet_type);     IP-,   <a href="https://github.com/torvalds/linux/blob/v3.13/net/ipv4/af_inet.c#L1673-L1676">net/ipv4/af_inet.c</a>: static struct packet_type ip_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_IP), .func = ip_rcv, };
      
      





__netif_receive_skb_core deliver_skb ( ), func ( – ip_rcv).



ip_rcv



ip_rcv . , , .



ip_rcv ip_rcv_finish netfilter . , iptables , IP, , .



, netfilter ip_rcv net/ipv4/ip_input.c :



 return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);
      
      





netfilter iptables



netfilter, iptables conntrack.



: NF_HOOK_THRESH , - IP, netfilter , , iptables conntrack.



: netfilter iptables, , SoftIRQ, . , , .



ip_rcv_finish



, netfilter , , ip_rcv_finish. , netfilter'.



ip_rcv_finish . , dst_entry . early_demux , , .



early_demux — , dst_entry. , dst_entry .



, net/ipv4/ip_input.c :



 if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) { const struct net_protocol *ipprot; int protocol = iph->protocol; ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot && ipprot->early_demux) { ipprot->early_demux(skb); /*   iph, skb->head   */ iph = ip_hdr(skb); } }
      
      





, sysctl_ip_early_demux. early_demux. , .



( ), , dst_entry .



, dst_input(skb). , , dst_entry, .



— , ip_local_deliver dst_entry.



early demux IP



early_demux:



 $ sudo sysctl -w net.ipv4.ip_early_demux=0
      
      





1; early_demux .



sysctl 5% early_demux.



ip_local_deliver



IP:



  1. ip_rcv (bookkeeping).
  2. netfilter , callback', .
  3. ip_rcv_finish — callback, .


ip_local_deliver . net/ipv4/ip_input.c :



 /* *  IP-     . */ int ip_local_deliver(struct sk_buff *skb) { /* *  IP-. */ if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish); }
      
      





, netfilter , ip_local_deliver_finish. , netfilter'.



ip_local_deliver_finish



ip_local_deliver_finish , net_protocol , handler .



.



IP



/proc/net/snmp, IP:



 $ cat /proc/net/snmp Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates Ip: 1 64 25922988125 0 0 15771700 0 0 25898327616 22789396404 12987882 51 1 10129840 2196520 1 0 0 0 ...
      
      





. IP. .



IP , -. enum- /proc/net/snmp, , include/uapi/linux/snmp.h :



 enum { IPSTATS_MIB_NUM = 0, /*    ,     - */ IPSTATS_MIB_INPKTS, /* InReceives */ IPSTATS_MIB_INOCTETS, /* InOctets */ IPSTATS_MIB_INDELIVERS, /* InDelivers */ IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ IPSTATS_MIB_OUTPKTS, /* OutRequests */ IPSTATS_MIB_OUTOCTETS, /* OutOctets */ /* ... */
      
      





/proc/net/netstat, IP:



 $ cat /proc/net/netstat | grep IpExt IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets InCsumErrors InNoECTPkts InECT0Pktsu InCEPkts IpExt: 0 0 0 0 277959 0 14568040307695 32991309088496 0 0 58649349 0 0 0 0 0
      
      





/proc/net/snmp, IpExt.



:





IP. , . , IP, , .





UDP, TCP , UDP.



net/ipv4/af_inet.c , - UDP, TCP ICMP IP. net/ipv4/af_inet.c :



 static const struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, .no_policy = 1, .netns_ok = 1, }; static const struct net_protocol udp_protocol = { .early_demux = udp_v4_early_demux, .handler = udp_rcv, .err_handler = udp_err, .no_policy = 1, .netns_ok = 1, }; static const struct net_protocol icmp_protocol = { .handler = icmp_rcv, .err_handler = icmp_err, .no_policy = 1, .netns_ok = 1, };
      
      





inet. net/ipv4/af_inet.c :



 /* *    . */ if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0) pr_crit("%s: Cannot add ICMP protocol\n", __func__); if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0) pr_crit("%s: Cannot add UDP protocol\n", __func__); if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0) pr_crit("%s: Cannot add TCP protocol\n", __func__);
      
      





UDP. , handler UDP udp_rcv. UPD, IP.



UDP



UDP : net/ipv4/udp.c .



udp_rcv



udp_rcv , __udp4_lib_rcv .



__udp4_lib_rcv



__udp4_lib_rcv UDP, UDP-, . .



, , IP, , dst_entry , ( — UDP).



dst_entry, __udp4_lib_rcv :



 sk = skb_steal_sock(skb); if (sk) { struct dst_entry *dst = skb_dst(skb); int ret; if (unlikely(sk->sk_rx_dst != dst)) udp_sk_rx_dst_set(sk, dst); ret = udp_queue_rcv_skb(sk, skb); sock_put(sk); /*   > 0    , *     –protocol  0 */ if (ret > 0) return -ret; return 0; } else {
      
      





early_demux , __udp4_lib_lookup_skb.



:



 ret = udp_queue_rcv_skb(sk, skb); sock_put(sk);
      
      





, :



 /*  .   ,     */ if (udp_lib_checksum_complete(skb)) goto csum_error; UDP_INC_STATS_BH(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); /* * .   UDP-  ,  *   .  . */ kfree_skb(skb); return 0;
      
      





udp_queue_rcv_skb



:



  1. , , , . , - .
  2. , UDP-Lite .
  3. UDP- .


- . . net/ipv4/udp.c :



 if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) goto drop;
      
      





sk_rcvqueues_full



sk_rcvqueues_full backlog' sk_rmem_alloc , , sk_rcvbuf (sk->sk_rcvbuf ):



 /* *      backlog-. *    skb truesize, *       . */ static inline bool sk_rcvqueues_full(const struct sock *sk, const struct sk_buff *skb, unsigned int limit) { unsigned int qsize = sk->sk_backlog.len + atomic_read(&sk->sk_rmem_alloc); return qsize > limit; }
      
      





, .



:



sk->sk_rcvbuf ( sk_rcvqueues_full) sysctl net.core.rmem_max.



:



 $ sudo sysctl -w net.core.rmem_max=8388608
      
      





sk->sk_rcvbuf net.core.rmem_default, sysctl.



:



 $ sudo sysctl -w net.core.rmem_default=8388608
      
      





sk->sk_rcvbuf, setsockopt SO_RCVBUF. setsockopt net.core.rmem_max.



net.core.rmem_max, setsockopt SO_RCVBUFFORCE. , , CAP_NET_ADMIN.



sk->sk_rmem_alloc skb_set_owner_r, . UDP.



sk->sk_backlog.len sk_add_backlog.



udp_queue_rcv_skb



. net/ipv4/udp.c :



 bh_lock_sock(sk); if (!sock_owned_by_user(sk)) rc = __udp_queue_rcv_skb(sk, skb); else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) { bh_unlock_sock(sk); goto drop; } bh_unlock_sock(sk); return rc;
      
      





, . , __udp_queue_rcv_skb. , backlog- sk_add_backlog.



backlog', release_sock .



__udp_queue_rcv_skb



__udp_queue_rcv_skb sock_queue_rcv_skb. , __udp_queue_rcv_skb .



net/ipv4/udp.c :



 rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); /*  ,   ENOMEM   */ if (rc == -ENOMEM) UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,is_udplite); UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS, is_udplite); kfree_skb(skb); trace_udp_fail_queue_rcv_skb(rc, sk); return -1; }
      
      





UDP



UDP:





/proc/net/snmp



/proc/net/snmp, UDP.



 $ cat /proc/net/snmp | grep Udp\: Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors Udp: 16314 0 0 17161 0 0
      
      





IP, , , , .



InDatagrams: , :





NoPorts: , UDP- , .



InErrors: , :





OutDatagrams: , UDP- , IP .



RcvbufErrors: , sock_queue_rcv_skb ; , sk->sk_rmem_alloc sk->sk_rcvbuf.



SndbufErrors: , :





InCsumErrors: , UDP. , , , InCsumErrors InErrors. , InErrors — InCsumErros .



/proc/net/udp



/proc/net/udp, UDP.



 $ cat /proc/net/udp sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops 515: 00000000:B346 00000000:0000 07 00000000:00000000 00:00000000 00000000 104 0 7518 2 0000000000000000 0 558: 00000000:0371 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 7408 2 0000000000000000 0 588: 0100007F:038F 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 7511 2 0000000000000000 0 769: 00000000:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 7673 2 0000000000000000 0 812: 00000000:006F 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 7407 2 0000000000000000 0
      
      





:





net/ipv4/udp.c .





sock_queue_rcv. , :



  1. , . , .
  2. sk_filter Berkeley Packet Filter, .
  3. sk_rmem_schedule , , .
  4. skb_set_owner_r . sk->sk_rmem_alloc.
  5. __skb_queue_tail .
  6. , sk_data_ready , .


, . .



3.10. 追加情報



, .





, . sysctl , RPS . , RPS . .



, , .



, - !



, :



 $ sudo ethtool -T eth0 Time stamping parameters for eth0: Capabilities: software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE) software-receive (SOF_TIMESTAMPING_RX_SOFTWARE) software-system-clock (SOF_TIMESTAMPING_SOFTWARE) PTP Hardware Clock: none Hardware Transmit Timestamp Modes: none Hardware Receive Filter Modes: none
      
      





, , . , .





, SO_BUSY_POLL. , .



: , . igb 3.13.0 . ixgbe — . , ndo_busy_poll struct net_device_ops ( ), SO_BUSY_POLL.



Intel , .



, . , .



sysctl net.core.busy_poll — poll select ( ).



, CPU .



Netpoll:



Linux , . API Netpoll. , kgdb netconsole .



Netpoll . ndo_poll_controller struct net_device_ops, probe.



, Netpoll, , .



__netif_receive_skb_core net/dev/core.c :



 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) { /* ... */ /*      NAPI,  netpoll */ if (netpoll_receive_skb(skb)) goto out; /* ... */ }
      
      





Netpoll Linux, .



Netpoll API struct netpoll netpoll_setup. , API .



Netpoll API, netconsole , Netpoll API, 'include/linux/netpoll.h` .



SO_INCOMING_CPU



SO_INCOMING_CPU Linux 3.19, , .



, CPU , getsockopt SO_INCOMING_CPU. , CPU. CPU.



, , : patchwork.ozlabs.org/patch/408257 .



DMA



DMA — , CPU , . DMA , , CPU.



Linux DMA, . .



DMA, — Intel IOAT DMA engine .



I/O Intel (Intel's I/O Acceleration Technology (IOAT))



Intel I/O AT , . — DMA. dmesg ioatdma , . DMA , — TCP.



Intel IOAT Linux 2.6.18, 3.13.11.10 - , . 3.13.11.10 ioatdma. , .



(Direct cache access (DCA))



, Intel I/O AT — Direct Cache Access (DCA).



( ) CPU. . igb igb_update_dca , igb_update_rx_dca . igb DCA .



DCA, BIOS, , dca .



IOAT DMA



, , ioatdma, sysfs.

memcpy DMA-:



 $ cat /sys/class/dma/dma0chan0/memcpy_count 123205655
      
      





, DMA-:



 $ cat /sys/class/dma/dma0chan0/bytes_transferred 131791916307
      
      





IOAT DMA



IOAT DMA , — copybreak. , DMA .



copybreak DMA:



 $ sudo sysctl -w net.ipv4.tcp_dma_copybreak=2048
      
      





4096.



4.



Linux . ( ). sysctl.conf, , . .



, . , , , .



, .



All Articles