網絡 -- 以太網(PAUSE)流量控制

關鍵詞:

以太網  數據鏈路層  PAUSE幀  流量控制

摘  要:

PAUSE操作實現了一種簡單的停-等式流量控制機制,可以防止瞬時過載導致緩衝區溢出時不必要的幀丟失。

以太網流控的引入

硬件成本和數據幀處理速度的限制,緩衝擁塞造成的丟幀率大於鏈路傳輸中位出錯率,因此必須解決緩衝擁塞造成的丟幀問題。

如何查看哪一層丟幀了?1000M網卡設備使用UDP發送數據給10M/100M網卡,會不會出現緩衝區溢出導致大量丟幀?)

假如沒有以太網流控功能,可能會造成哪些問題?

對於以太網來說,Rx端口將會持續的接收到包,但是沒有足夠的空間臨時存儲這些包。Rx端口僅僅是簡單的忽略這些輸入數據包。Ethernet和TCP/IP協同合作重發這些包。然而,判決包已被丟棄,請求這些重新發送,最後發送包將耗費時間

以太網流量控制技術

IEEE802.3x標準定義了一種新方法,在全雙工環境中去實現流量控制(PAUSE

流量控制是作爲一種減少接收包緩衝區溢出的可能性的方法來實現的,這種溢出會導致接收包的丟失,並允許對網絡擁塞級別進行本地控制。這可以通過向發射站發送接收站中幾乎滿的接收緩衝區狀態的指示來實現

IEEE802.3z標準定義了非對稱流量控制的具體操作(ASM_DIR)。

非對稱流控制的實現允許一個鏈路夥伴發送流控制數據包,同時允許忽略它們的接收。例如,不需要響應暫停幀。

以太網流量控制-PAUSE

在IEEE802.3協議中規定中,PAUSE幀是一種控制幀,用於控制數據流停止發送,在MAC 發送側產生,在MAC接收側解析並執行。當此端設備輸入數據量過大,無法及時處理時會在此端發送側MAC產生PAUSE幀,發給對端,要求對端在一定時間內停止發送數據。

在全雙工MAC控制框架下,流量控制機制是通過PAUSE功能實現的。如果某個端口要停止幀的接收,可以發送一個帶有時間參數的PAUSE幀,參數指示全雙工鏈路對方在開始發送數據前需要等待的時間,收到PAUSE幀的設備通過簡單的解析,就可以確定停止發送的時長。當鏈路對方接收到PAUSE幀後,在參數指定的時間內停止發送數據。當指定時間超出,或原擁塞端口重新發出操作參數爲0的PAUSE,鏈路對方從暫停的位置繼續發送數據幀。

PAUSE幀操作實現了一種簡單的停-等式流量控制機制。可以防止瞬時過載導致緩衝區溢出時不必要的幀丟失。

  • PAUSE幀位於網絡協議中的哪一層?

PAUSE幀位於網絡報文協議中的數據鏈路層(詳細點講應該是數據鏈路層中的MAC控制子層)。

IEEE802.3 將數據鏈路層分爲三層:LLCMAC控制子層(可選)和MAC介質訪問控制子層

MAC控制子層規定了通用的全雙工流量控制結構。

交換控制電路要防止緩衝區溢出,可以利用MAC控制子層來控制以太網介質訪問控制子層的操作。當已用緩衝區容量達到一個預先設定的閾值時,端口向全雙工鏈路對方發出停止發送數據的請求,這個請求通過MAC控制子層產生的控制幀實現。

同樣,端口可以接收由其他站點MAC控制子層產生的控制幀,控制幀夾在客戶數據幀流中發送,接收方會根據幀的內容將控制幀分離出來,提交到MAC控制子層中的流量控制模塊,流量控制模塊解析控制幀的內容,提取幀中的控制參數,根據控制參數決定暫停發送的時間。

PAUSE幀格式如下:

目的地址(組播) 源地址    類型     操作碼    操作參數    保留     …   校驗序列

(6字節)         (6字節)  (2字節)   (2字節)   (2字節)   (42字節)  …  (4字節)

0180-C200-0001   xxxx     0x8808   0x0001     時間參數                   CRC

MAC控制幀(PAUSE幀)是符合IEEE802.3協議的以太網幀,可以通過其唯一的類型域標識符(0x8808)識別。MAC控制幀在網絡上的發送和接收與數據幀類似,除了前導碼和幀開始符外,長度爲以太網幀的最小幀長度(64字節)。MAC控制幀的數據域內,前兩個字節標識了MAC控制的操作碼,表示幀請求的控制功能。目前協議只定義了一種操作代碼,即PAUSE操作,操作碼爲0x0001。操作碼後是操作所需的參數,參數只用了數據字段的2個字節,數據字段中其餘位將填充0。

PAUSE幀各個字段的定義如下:

目的地址:協議規定PAUSE的目的地址爲保留的組播地址0x01-80-C2-00-00-01。

源地址:發送PAUSE幀端口的48位MAC地址。

類型:MAC控制幀(PAUSE幀)是符合IEEE802.3協議的以太網幀,可以通過其唯一的類型域標識符(0x8808)識別。

操作碼:恆爲0x0001。其實,PAUSE幀是MAC控制幀的一種,其他類型的MAC控制幀使用不同的opcode值,此處不做詳細說明。後面會談到和PAUSE類似的PFC幀,PFC幀中該域的取值是0x0101。

操作參數:2字節的暫停時間參數。它是PAUSE發送方請求對方停止發送數據幀的時間長度,通常爲0xFFFF,時間度量單位是以當前傳輸速率傳輸512位數據所用的時間,接收方實際暫停的時間爲操作參數字段內容與以當前傳輸速率傳輸512位數據所用時間的乘積。

校驗序列(FCS):4個字節的循環冗餘校驗序列(CRC)字段。CRC 是一種數學算法,MAC發送側(MAC_TX)創建每個幀時都將運行它。MAC接收側(MAC_RX)接收到幀時以preamble, SFD, DA, SA, Length/Type, DATA and Pading,作爲輸入數據進行 CRC 計算,計算結果與接收幀的FCS字段比較,結果相同表示幀有效,結果不同接收方認爲發生了錯誤,進而將幀丟棄。

使用wireshark軟件分析PAUSE幀:

如圖所示,左側爲本端芯片,右側爲對端芯片。

MAC0和MAC1都包含發送側tx和接收側rx。左側芯片內部mac上游模塊A與mac0發送側有流控信號fc_rdy。信號高表示模塊A無法及時處理輸入數據,需要進行流控。爲了方便突出重點,圖中省略了PCS以及serdes等模塊。

具體流程處理如下:

1~2步:對端mac1發送數據給mac0接收側,進行發送到模塊A

3步:模塊A無法即使處理輸入的數據,需要減少數據輸入,從而將fc_rdy拉高。

4步:mac0發送側tx發現流控信號fc_rdy爲高,產生pause幀,發送給mac1接收側。只要fc_rdy爲高,mac0發送側tx每隔一段時間發送一個pause幀,間隔時間由配置寄存器控制。間隔時長計算由計數器counting計算。Pause幀內停止發送數據的時間由另外一個配置寄存器控制。只要fc_rdy爲高期間,mac0發送側不發送正常數據。

5步:mac1接收側rx接受到pause報文後,提取pause幀內包含的暫停時間,控制發送側tx停止發送數據

6~7~8:mac1停止發送數據後,模塊A處理完之前的數據後將fc_rdy拉低,表示mac1可以繼續發送數據了。

9:步:第9步分2種情況。

情況1:fc_rdy拉低,並且counting在計數沒有到一個間隔週期,此時發送pause幀,但是幀內暫停時間爲0. Mac1接受到pause幀後,控制tx控制立即開始發送數據。

情況2:fc_rdy拉低的同時,counting正好計數到一個間隔週期,此時不發送pause幀。等到上一個pause幀的暫停時間到達後,mac1發送側tx繼續發送數據。

pause幀處理協議強制要求:

pause的產生髮送過程不能中斷一個完整的數據報文。即在第4步中,fc_rdy拉高後,首先mac0 tx側需要判斷當前是否正常數據報文在傳輸。如果有,則需要在當前數據報文傳輸完成後才能發送pause幀。也就是說在發送過程中,只能在完整數據報文的間隙插入pause幀。

新的pause報文暫停時間會覆蓋上一個暫停時間。對mac1來說,當mac1接收到新的pause幀後,暫停時間以最新時間爲準。

流控功能分析

在使用ethtool工具查看網卡信息中,有如下字段可以查看當前流控功能:

(假如部分設備沒有自帶ethtool工具?ethtool源碼可以通過Linux內核官網上下載:https://www.kernel.org/pub/software/network/ethtool/)

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool eth0
Settings for eth0:
    Supported ports: [ TP ]
    Supported link modes:   10baseT/Half 10baseT/Full
                            100baseT/Half 100baseT/Full
                            1000baseT/Full
    Supported pause frame use: Symmetric
    Supports auto-negotiation: Yes
    Advertised link modes:  10baseT/Half 10baseT/Full
                            100baseT/Half 100baseT/Full
                            1000baseT/Full
    Advertised pause frame use: Symmetric
    Advertised auto-negotiation: Yes
    Speed: 100Mb/s
    Duplex: Full
    Port: Twisted Pair
    PHYAD: 1
    Transceiver: internal
    Auto-negotiation: on
    MDI-X: off (auto)
    Supports Wake-on: pumbg
    Wake-on: g
    Current message level: 0x00000007 (7)
                   drv probe link
    Link detected: yes

    其中“Advertised pause frame use”表示當前流控的模式,直接通過ethtool源碼找到什麼情況下會打印“Symmetric”。

fprintf(stdout, "       %s pause frame use: ", prefix);
if (ethtool_link_mode_test_bit(
ETHTOOL_LINK_MODE_Pause_BIT, mask)) {
fprintf(stdout, "Symmetric");
if (ethtool_link_mode_test_bit(
ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
fprintf(stdout, " Receive-only");
fprintf(stdout, "\n");
} else {
if (ethtool_link_mode_test_bit(
ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
fprintf(stdout, "Transmit-only\n");
else
fprintf(stdout, "No\n");
}

    根據源碼,可以得出一個結論:

ETHTOOL_LINK_MODE_Pause_BIT

ETHTOOL_LINK_MODE_Asym_Pause_BIT

輸出結果

0

0

No

0

1

Symmetric Transmit-only

1

0

Symmetric

1

1

Symmetric Receive-only

    源碼中涉及到兩個宏定義如下:

    ETHTOOL_LINK_MODE_Pause_BIT         = 13,      /* IEEE802.3x標準定義流控 */

    ETHTOOL_LINK_MODE_Asym_Pause_BIT    = 14,      /* IEEE802.3z標準定義非對稱流控 */

    可能會有人問兩個宏定義具體是什麼意思?在上文寫流控技術的引入時,有提到:

IEEE802.3x標準定義了在全雙工環境中去實現流量控制。

流量控制是作爲一種減少接收包緩衝區溢出的可能性的方法來實現的,這種溢出會導致接收包的丟失,並允許對網絡擁塞級別進行本地控制。這可以通過向發射站發送接收站中幾乎滿的接收緩衝區狀態的指示來實現。

IEEE802.3z標準定義了非對稱流量控制的具體操作。

非對稱流控制的實現允許一個鏈路夥伴發送流控制數據包,同時允許忽略它們的接收。例如,不需要響應暫停幀。

在802.3標準文檔中,有相關的描述,如下圖:

https://img-blog.csdn.net/20150411183651243

從描述上看到,802.3標準文檔中的描述和ethtool源碼是能對應上的。“PAUSE”是標準流控控制,“ASM_DIR”是非對稱流控控制。關於本地設備(網卡)和對端設備(交換機)的詳細關係如下:

從圖中可以明白,流控必須需要本地和對端都支持纔行,否則只有一側流控功能開啓,是不生效的。其次,假如兩端都開啓了“PAUSE”功能,則都支持流控發送和接收。如果本地端沒有開啓“PAUSE”但開啓了“ASM_DIR”功能,則需要判斷對端是否都開啓了“PAUSE”和“ASM_DIR”,若是,則本地端只能發送流控、對端只能接收流控。

再來看I211網卡手冊中的PHY芯片寄存器截圖:

表明當前網卡對應PHY的硬件上是支持標準流控和非對稱流控功能的。也可以通過讀PHY寄存器上的這兩個位數據,確認當前的流控模式。

再看I211網卡手冊中的MAC寄存器截圖:

同樣的,驅動中也有對流控功能的相關描述,以I211網卡爲例,網卡驅動對應“driver/net/Ethernet/inetl/igb/”目錄下,流控功能相關描述如下:

       /* Two bits in the Auto Negotiation Advertisement Register

         * (Address 4) and two bits in the Auto Negotiation Base

         * Page Ability Register (Address 5) determine flow control

         * for both the PHY and the link partner.  The following

         * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,

         * 1999, describes these PAUSE resolution bits and how flow

         * control is determined based upon these settings.

         * NOTE:  DC = Don't Care

         *

         *   LOCAL DEVICE  |   LINK PARTNER

        * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution

        *-------|---------|-------|---------|--------------------

        *   0   |    0    |  DC   |   DC    | e1000_fc_none

        *   0   |    1    |   0   |   DC    | e1000_fc_none

        *   0   |    1    |   1   |    0    | e1000_fc_none

        *   0   |    1    |   1   |    1    | e1000_fc_tx_pause

        *   1   |    0    |   0   |   DC    | e1000_fc_none

        *   1   |   DC    |   1   |   DC    | e1000_fc_full

        *   1   |    1    |   0   |    0    | e1000_fc_none

        *   1   |    1    |   0   |    1    | e1000_fc_rx_pause

         *

         * Are both PAUSE bits set to 1?  If so, this implies

         * Symmetric Flow Control is enabled at both ends.  The

         * ASM_DIR bits are irrelevant per the spec.

         *

         * For Symmetric Flow Control:

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   1   |   DC    |   1   |   DC    | E1000_fc_full

         *

         */

完整的流控初始化源碼:

/**

 *  igb_config_fc_after_link_up - 建立連接後的流控設置

 *  @hw: 指向HW結構的指針

 *  檢查連接後的自動連接狀態,確保全雙工和傳輸速度不是被強制設置的

 *  如果鏈接必須被強制設置,那麼流控也必須被強制設置

 *  如果自動協商是被開啓的,並且沒有失敗,那麼我們會根據鏈接夥伴狀態進流控行配置

 **/

s32 igb_config_fc_after_link_up(struct e1000_hw *hw)

{

    struct e1000_mac_info *mac = &hw->mac;

    s32 ret_val = 0;

    u32 pcs_status_reg, pcs_adv_reg, pcs_lp_ability_reg, pcs_ctrl_reg;

    u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;

    u16 speed, duplex;


    /* Check for the case where we have fiber media and auto-neg failed

     * so we had to force link.  In this case, we need to force the

     * configuration of the MAC to match the "fc" parameter.

     * 檢查以下情況:當有光纖介質同時自適配失敗,我們就不得不強制設置鏈接狀態

     * 在這種情況下,我們也必須要強制配置MAC的“流控”參數

     */

    if (mac->autoneg_failed) {

        if (hw->phy.media_type == e1000_media_type_internal_serdes)

            ret_val = igb_force_mac_fc(hw);

    } else {

        if (hw->phy.media_type == e1000_media_type_copper)

            ret_val = igb_force_mac_fc(hw);

    }


    if (ret_val) {

        hw_dbg("Error forcing flow control settings\n");

        goto out;

    }


    /* Check for the case where we have copper media and auto-neg is

     * enabled.  In this case, we need to check and see if Auto-Neg

     * has completed, and if so, how the PHY and link partner has

     * flow control configured.

     * 檢查以下情況:當有銅介質同時自適配使能,

     * 在這種情況下,我們需要檢查自適配是否完成了,

     * 如果已經完成了,那麼PHY和對端的流控需要配置

     */

    if ((hw->phy.media_type == e1000_media_type_copper) && mac->autoneg) {

        /* Read the MII Status Register and check to see if AutoNeg

         * has completed.  We read this twice because this reg has

         * some "sticky" (latched) bits.

         * 讀取 MII 狀態寄存器來檢測自適配是否完成

         * 我們讀取了兩次寄存器,因爲這個寄存器有一點“粘性”

         */

        ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,

                           &mii_status_reg);

        if (ret_val)

            goto out;

        ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,

                           &mii_status_reg);

        if (ret_val)

            goto out;


        if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {

            hw_dbg("Copper PHY and Auto Neg has not completed.\n");

            goto out;

        }


        /* The AutoNeg process has completed, so we now need to

         * read both the Auto Negotiation Advertisement

         * Register (Address 4) and the Auto_Negotiation Base

         * Page Ability Register (Address 5) to determine how

         * flow control was negotiated.

         * 自適配過程已經完成,我們需要讀PHY Register 4和PHY Register 5寄存器

         * 去決定如何配置流控

         */

        ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,

                        &mii_nway_adv_reg);

        if (ret_val)

            goto out;

        ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,

                        &mii_nway_lp_ability_reg);

        if (ret_val)

            goto out;


        /* Two bits in the Auto Negotiation Advertisement Register

         * (Address 4) and two bits in the Auto Negotiation Base

         * Page Ability Register (Address 5) determine flow control

         * for both the PHY and the link partner.  The following

         * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,

         * 1999, describes these PAUSE resolution bits and how flow

         * control is determined based upon these settings.

         * NOTE:  DC = Don't Care

         * PHY Register 4和PHY Register 5寄存器中的

         * “PAUSE”,“ASM_DIR”兩位決定了PHY和對端的流控控制模式

         * 下面的表摘自1999年3月25日的IEEE 802.3ab/D6.0

         * 描述了這些暫停分辨率位以及如何根據這些設置確定流量控制

         * (DC表示忽略,不用在意)

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution

         *-------|---------|-------|---------|--------------------

         *   0   |    0    |  DC   |   DC    | e1000_fc_none

         *   0   |    1    |   0   |   DC    | e1000_fc_none

         *   0   |    1    |   1   |    0    | e1000_fc_none

         *   0   |    1    |   1   |    1    | e1000_fc_tx_pause

         *   1   |    0    |   0   |   DC    | e1000_fc_none

         *   1   |   DC    |   1   |   DC    | e1000_fc_full

         *   1   |    1    |   0   |    0    | e1000_fc_none

         *   1   |    1    |   0   |    1    | e1000_fc_rx_pause

         *

         * Are both PAUSE bits set to 1?  If so, this implies

         * Symmetric Flow Control is enabled at both ends.  The

         * ASM_DIR bits are irrelevant per the spec.

         * 如果PHY和對端的PAUSE位都爲1?

         * 那麼,“ASM_DIR”這一位(非對稱流量控制)就沒用了

         * 在兩端都開啓標準流量控制位的時候,“ASM_DIR”位可以忽略。

         *

         * For Symmetric Flow Control:

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   1   |   DC    |   1   |   DC    | E1000_fc_full

         *

         */

        if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&

            (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {

            /* Now we need to check if the user selected RX ONLY

             * of pause frames.  In this case, we had to advertise

             * FULL flow control because we could not advertise RX

             * ONLY. Hence, we must now check to see if we need to

             * turn OFF  the TRANSMISSION of PAUSE frames.

             * 在PHY和對端都開啓“PAUSE”位時,

             * 我們需要判斷用戶是否希望設置爲e1000_fc_rx_pause模式

             * 但在這種情況下,我們不得不將流控設置爲e1000_fc_full,

             * 因此,我們必須檢查暫停幀的傳輸是否被關閉了

             */

            if (hw->fc.requested_mode == e1000_fc_full) {

                hw->fc.current_mode = e1000_fc_full;

                hw_dbg("Flow Control = FULL.\n");

            } else {

                hw->fc.current_mode = e1000_fc_rx_pause;

                hw_dbg("Flow Control = RX PAUSE frames only.\n");

            }

        }

        /* For receiving PAUSE frames ONLY.

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   0   |    1    |   1   |    1    | e1000_fc_tx_pause

         */

        else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&

              (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&

              (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&

              (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {

            hw->fc.current_mode = e1000_fc_tx_pause;

            hw_dbg("Flow Control = TX PAUSE frames only.\n");

        }

        /* For transmitting PAUSE frames ONLY.

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   1   |    1    |   0   |    1    | e1000_fc_rx_pause

         */

        else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&

             (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&

             !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&

             (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {

            hw->fc.current_mode = e1000_fc_rx_pause;

            hw_dbg("Flow Control = RX PAUSE frames only.\n");

        }

        /* Per the IEEE spec, at this point flow control should be

         * disabled.  However, we want to consider that we could

         * be connected to a legacy switch that doesn't advertise

         * desired flow control, but can be forced on the link

         * partner.  So if we advertised no flow control, that is

         * what we will resolve to.  If we advertised some kind of

         * receive capability (Rx Pause Only or Full Flow Control)

         * and the link partner advertised none, we will configure

         * ourselves to enable Rx Flow Control only.  We can do

         * this safely for two reasons:  If the link partner really

         * didn't want flow control enabled, and we enable Rx, no

         * harm done since we won't be receiving any PAUSE frames

         * anyway.  If the intent on the link partner was to have

         * flow control enabled, then by us enabling RX only, we

         * can at least receive pause frames and process them.

         * This is a good idea because in most cases, since we are

         * predominantly a server NIC, more times than not we will

         * be asked to delay transmission of packets than asking

         * our link partner to pause transmission of frames.

         * 根據IEEE標準,此時應該禁用流控。

         * 但是我們希望可以遺留一個開關,與上述表格無關,可以強制的進行設置,

         * 這就是我們想要做的事情。

         * 如果當前PHY設置了“PAUSE”,“ASM_DIR”但是對端沒有設置任何功能,

         * 那麼我們也可以設置爲只啓用“RX”流控功能。

         * 這麼做是非常安全的:

         * 1.對端連接夥伴沒有設置流控,那麼我們只啓用RX,不會對對端造成任何傷害

         * 2.如果對端連接夥伴想要使能流控,那麼我們至少可以接收暫停幀並處理它們,

         *   這是一個好主意。因爲在大多數情況下,我們主要是一個服務器,

         *   我們會被要求延遲包的傳輸,而不是讓對端暫停傳輸

         */

        else if ((hw->fc.requested_mode == e1000_fc_none) ||

             (hw->fc.requested_mode == e1000_fc_tx_pause) ||

             (hw->fc.strict_ieee)) {

            hw->fc.current_mode = e1000_fc_none;

            hw_dbg("Flow Control = NONE.\n");

        } else {

            hw->fc.current_mode = e1000_fc_rx_pause;

            hw_dbg("Flow Control = RX PAUSE frames only.\n");

        }


        /* Now we need to do one last check...  If we auto-

         * negotiated to HALF DUPLEX, flow control should not be

         * enabled per IEEE 802.3 spec.

         * 現在我們需要做最後一次檢查,如果自協商到是半雙工模式,

         * 那麼根據IEEE標準,流控不能被開啓

         */

        ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);

        if (ret_val) {

            hw_dbg("Error getting link speed and duplex\n");

            goto out;

        }


        if (duplex == HALF_DUPLEX)

            hw->fc.current_mode = e1000_fc_none;


        /* Now we call a subroutine to actually force the MAC

         * controller to use the correct flow control settings.

         * 現在我們可以調用函數來強制設置MAC的流控

         */

        ret_val = igb_force_mac_fc(hw);

        if (ret_val) {

            hw_dbg("Error forcing flow control settings\n");

            goto out;

        }

    }

    /* Check for the case where we have SerDes media and auto-neg is

     * enabled.  In this case, we need to check and see if Auto-Neg

     * has completed, and if so, how the PHY and link partner has

     * flow control configured.

     * 檢查是否 SerDes 介質同時自協商是否使能,

     * 在這種情況下,我們需要檢查自協商是否完成,

     * 如果已經完成,那麼PHY和對端的流控需要配置

     */

    if ((hw->phy.media_type == e1000_media_type_internal_serdes)

        && mac->autoneg) {

        /* Read the PCS_LSTS and check to see if AutoNeg

         * has completed.

         * 讀取PCS_LSTS位判斷自協商是否完成

         */

        pcs_status_reg = rd32(E1000_PCS_LSTAT);


        if (!(pcs_status_reg & E1000_PCS_LSTS_AN_COMPLETE)) {

            hw_dbg("PCS Auto Neg has not completed.\n");

            return ret_val;

        }


        /* The AutoNeg process has completed, so we now need to

         * read both the Auto Negotiation Advertisement

         * Register (PCS_ANADV) and the Auto_Negotiation Base

         * Page Ability Register (PCS_LPAB) to determine how

         * flow control was negotiated.

         * 自協商已經完成,那麼我們需要讀取PCS_ANADV、PCS_LPAB位來協商如何設置流控模式

         */

        pcs_adv_reg = rd32(E1000_PCS_ANADV);

        pcs_lp_ability_reg = rd32(E1000_PCS_LPAB);


        /* Two bits in the Auto Negotiation Advertisement Register

         * (PCS_ANADV) and two bits in the Auto Negotiation Base

         * Page Ability Register (PCS_LPAB) determine flow control

         * for both the PHY and the link partner.  The following

         * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,

         * 1999, describes these PAUSE resolution bits and how flow

         * control is determined based upon these settings.

         * NOTE:  DC = Don't Care

         * PHY Register 4和PHY Register 5寄存器中的

         * “PAUSE”,“ASM_DIR”兩位決定了PHY和對端的流控控制模式

         * 下面的表摘自1999年3月25日的IEEE 802.3ab/D6.0

         * 描述了這些暫停分辨率位以及如何根據這些設置確定流量控制

         * (DC表示忽略,不用在意)

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution

         *-------|---------|-------|---------|--------------------

         *   0   |    0    |  DC   |   DC    | e1000_fc_none

         *   0   |    1    |   0   |   DC    | e1000_fc_none

         *   0   |    1    |   1   |    0    | e1000_fc_none

         *   0   |    1    |   1   |    1    | e1000_fc_tx_pause

         *   1   |    0    |   0   |   DC    | e1000_fc_none

         *   1   |   DC    |   1   |   DC    | e1000_fc_full

         *   1   |    1    |   0   |    0    | e1000_fc_none

         *   1   |    1    |   0   |    1    | e1000_fc_rx_pause

         *

         * Are both PAUSE bits set to 1?  If so, this implies

         * Symmetric Flow Control is enabled at both ends.  The

         * ASM_DIR bits are irrelevant per the spec.

         * 如果PHY和對端的PAUSE位都爲1?

         * 那麼,“ASM_DIR”這一位(非對稱流量控制)就沒用了

         * 在兩端都開啓標準流量控制位的時候,“ASM_DIR”位可以忽略。

         *

         * For Symmetric Flow Control:

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   1   |   DC    |   1   |   DC    | e1000_fc_full

         *

         */

        if ((pcs_adv_reg & E1000_TXCW_PAUSE) &&

            (pcs_lp_ability_reg & E1000_TXCW_PAUSE)) {

            /* Now we need to check if the user selected Rx ONLY

             * of pause frames.  In this case, we had to advertise

             * FULL flow control because we could not advertise Rx

             * ONLY. Hence, we must now check to see if we need to

             * turn OFF the TRANSMISSION of PAUSE frames.

             * 在PHY和對端都開啓“PAUSE”位時,

             * 我們需要判斷用戶是否希望設置爲e1000_fc_rx_pause模式

             * 但在這種情況下,我們不得不將流控設置爲e1000_fc_full,

             * 因此,我們必須檢查暫停幀的傳輸是否被關閉了

             */

            if (hw->fc.requested_mode == e1000_fc_full) {

                hw->fc.current_mode = e1000_fc_full;

                hw_dbg("Flow Control = FULL.\n");

            } else {

                hw->fc.current_mode = e1000_fc_rx_pause;

                hw_dbg("Flow Control = Rx PAUSE frames only.\n");

            }

        }

        /* For receiving PAUSE frames ONLY.

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   0   |    1    |   1   |    1    | e1000_fc_tx_pause

         */

        else if (!(pcs_adv_reg & E1000_TXCW_PAUSE) &&

              (pcs_adv_reg & E1000_TXCW_ASM_DIR) &&

              (pcs_lp_ability_reg & E1000_TXCW_PAUSE) &&

              (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) {

            hw->fc.current_mode = e1000_fc_tx_pause;

            hw_dbg("Flow Control = Tx PAUSE frames only.\n");

        }

        /* For transmitting PAUSE frames ONLY.

         *

         *   LOCAL DEVICE  |   LINK PARTNER

         * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result

         *-------|---------|-------|---------|--------------------

         *   1   |    1    |   0   |    1    | e1000_fc_rx_pause

         */

        else if ((pcs_adv_reg & E1000_TXCW_PAUSE) &&

             (pcs_adv_reg & E1000_TXCW_ASM_DIR) &&

             !(pcs_lp_ability_reg & E1000_TXCW_PAUSE) &&

             (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) {

            hw->fc.current_mode = e1000_fc_rx_pause;

            hw_dbg("Flow Control = Rx PAUSE frames only.\n");

        } else {

            /* Per the IEEE spec, at this point flow control

             * should be disabled.

             */

            hw->fc.current_mode = e1000_fc_none;

            hw_dbg("Flow Control = NONE.\n");

        }


        /* Now we call a subroutine to actually force the MAC

         * controller to use the correct flow control settings.

         * 現在我們可以調用函數來強制設置MAC的流控

         */

        pcs_ctrl_reg = rd32(E1000_PCS_LCTL);

        pcs_ctrl_reg |= E1000_PCS_LCTL_FORCE_FCTRL;

        wr32(E1000_PCS_LCTL, pcs_ctrl_reg);


        ret_val = igb_force_mac_fc(hw);

        if (ret_val) {

            hw_dbg("Error forcing flow control settings\n");

            return ret_val;

        }

    }


out:

    return ret_val;

}
  • ethtool工具設置流控功能具體流程追蹤

ethtool是一個非常強大的網絡工具,可以通過ethtool排查很多網卡的狀態等信息,進而分析當前的網絡問題。

本文原本是主要分析流控相關驅動的,但是因爲調試的時候發現,流控功能好像並沒有被開啓,即使用了ethtool工具查看並設置當前的流控,也沒有效果,因此嘗試追蹤一下源碼,找到流控對應的調用關係,並排查問題。

首先,ethtool通過-a參數選擇開啓/關閉流控,源碼如下(節段部分):

    { "-a|--show-pause", 1, do_gpause, "Show pause options" },

    { "-A|--pause", 1, do_spause, "Set pause options",

      "     [ autoneg on|off ]\n"

      "     [ rx on|off ]\n"

      "     [ tx on|off ]\n" },

在ethtool源碼中,調用do_gpause函數查看流控功能,調用do_spause函數設置流控功能。繼續追蹤do_spause函數,源碼如下:

static int do_spause(struct cmd_context *ctx)

{

…

    /* 獲取pause參數 */

    parse_generic_cmdline(ctx, &gpause_changed,

                  cmdline_pause, ARRAY_SIZE(cmdline_pause));

…

    /* 讀取當前硬件pause設置 */

    epause.cmd = ETHTOOL_GPAUSEPARAM;

    err = send_ioctl(ctx, &epause);

…

    /* 比較當前硬件pause設置是否與傳入參數相同,相同則退出 */

    do_generic_set(cmdline_pause, ARRAY_SIZE(cmdline_pause), &changed);

    if (!changed) {

        fprintf(stderr, "no pause parameters changed, aborting\n");

        return 78;

    }

…

    /* 硬件pause設置 */

    epause.cmd = ETHTOOL_SPAUSEPARAM;

    err = send_ioctl(ctx, &epause);

…

}

    其中send_ioctl函數會最終通過ioctl方式與驅動中的network device ioctl之間調用。Seng_ioctl函數源碼如下:

int send_ioctl(struct cmd_context *ctx, void *cmd)

{

    ctx->ifr.ifr_data = cmd;

    return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);

}

    send_ioctl函數中的ioctl最終會調用network device ioctl,在驅動中,network device ioctl對應dev_ioctl函數,源碼如下(net/core/dev_ioctl.c):

int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)

{

…

    case SIOCETHTOOL:

        dev_load(net, ifr.ifr_name);

        rtnl_lock();

        ret = dev_ethtool(net, &ifr);

        rtnl_unlock();

…

}

    dev_ioctl函數會根據“SIOCETHTOOL”選擇調用dev_ethtool函數,源碼如下(net/core/ethtool.c):

int dev_ethtool(struct net *net, struct ifreq *ifr)

{

…

    if (dev->ethtool_ops->begin) {            /* ethtool初始化 */

        rc = dev->ethtool_ops->begin(dev);

        if (rc  < 0)

            return rc;

    }

…

    case ETHTOOL_SPAUSEPARAM:

        rc = ethtool_set_pauseparam(dev, useraddr);

        break;

…

    if (dev->ethtool_ops->complete)          /* ethtool操作完成 */

        dev->ethtool_ops->complete(dev);

    if (old_features != dev->features)       /* 如果發生變化,設備更改特性*/

        netdev_features_change(dev);

}

    繼續追蹤ethtool_set_pauseparam函數,源碼如下(net/core/ethtool.c):

static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)

{

    struct ethtool_pauseparam pauseparam;

…

    if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))

        return -EFAULT;

    return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);

}

    ethtool_ops->set_pauseparam會在對應的網卡驅動中去配置,以I211驅動爲例,對應源碼如下(driver/net/Ethernet/intel/igb/igb_ethtool.c):

static const struct ethtool_ops igb_ethtool_ops  = {

    .get_pauseparam     = igb_get_pauseparam,

    .set_pauseparam     = igb_set_pauseparam,

}

    經過漫漫長路,終於從ethtool源碼追蹤到了I211網卡驅動中的設置流控函數。在igb_set_pauseparam函數中,會判斷當前是否是自動適配模式,如果是自動匹配模式,直接重啓網卡設備(不需要手動設置流控)。如果不是自動匹配模式,則判斷ethtool傳入的“rx_pause”、“tx_pause”等流控參數,最終調用igb_force_mac_fc設置對應的寄存器。

static int igb_set_pauseparam(struct net_device *netdev,

                  struct ethtool_pauseparam *pause)

{

…

    if (adapter->fc_autoneg == AUTONEG_ENABLE) {

        hw->fc.requested_mode = e1000_fc_default;

        if (netif_running(adapter->netdev)) {

            igb_down(adapter);

            igb_up(adapter);

        } else {

            igb_reset(adapter);

        }

    } else {

        if (pause->rx_pause && pause->tx_pause)

            hw->fc.requested_mode = e1000_fc_full;

        else if (pause->rx_pause && !pause->tx_pause)

            hw->fc.requested_mode = e1000_fc_rx_pause;

        else if (!pause->rx_pause && pause->tx_pause)

            hw->fc.requested_mode = e1000_fc_tx_pause;

        else if (!pause->rx_pause && !pause->tx_pause)

            hw->fc.requested_mode = e1000_fc_none;


        hw->fc.current_mode = hw->fc.requested_mode;

        retval = ((hw->phy.media_type == e1000_media_type_copper) ?

              igb_force_mac_fc(hw) : igb_setup_link(hw));

    }

…

}

    igb_force_mac_fc函數就不繼續追蹤了,實際就是在函數中讀取和設置流控寄存器。

/**

 *  igb_force_mac_fc - Force the MAC's flow control settings

 *  @hw: pointer to the HW structure

 *

 *  Force the MAC's flow control settings.  Sets the TFCE and RFCE bits in the

 *  device control register to reflect the adapter settings.  TFCE and RFCE

 *  need to be explicitly set by software when a copper PHY is used because

 *  autonegotiation is managed by the PHY rather than the MAC.  Software must

 *  also configure these bits when link is forced on a fiber connection.

 **/

s32 igb_force_mac_fc(struct e1000_hw *hw)

{

    u32 ctrl;

    s32 ret_val = 0;

 printk("%s(%d)\n", __func__, __LINE__);

    ctrl = rd32(E1000_CTRL);


    /* Because we didn't get link via the internal auto-negotiation

     * mechanism (we either forced link or we got link via PHY

     * auto-neg), we have to manually enable/disable transmit an

     * receive flow control.

     *

     * The "Case" statement below enables/disable flow control

     * according to the "hw->fc.current_mode" parameter.

     *

     * The possible values of the "fc" parameter are:

     *      0:  Flow control is completely disabled

     *      1:  Rx flow control is enabled (we can receive pause

     *          frames but not send pause frames).

     *      2:  Tx flow control is enabled (we can send pause frames

     *          frames but we do not receive pause frames).

     *      3:  Both Rx and TX flow control (symmetric) is enabled.

     *  other:  No other values should be possible at this point.

     */

    hw_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode);


    switch (hw->fc.current_mode) {

    case e1000_fc_none:

        ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));

        break;

    case e1000_fc_rx_pause:

        ctrl &= (~E1000_CTRL_TFCE);

        ctrl |= E1000_CTRL_RFCE;

        break;

    case e1000_fc_tx_pause:

        ctrl &= (~E1000_CTRL_RFCE);

        ctrl |= E1000_CTRL_TFCE;

        break;

    case e1000_fc_full:

        ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE);

        break;

    default:

        hw_dbg("Flow control param set incorrectly\n");

        ret_val = -E1000_ERR_CONFIG;

        goto out;

    }


 printk("%s(%d) hw->fc.current_mode = %u, hw->fc.requested_mode=%u\n", __func__, __LINE__, hw->fc.current_mode, hw->fc.requested_mode);

    wr32(E1000_CTRL, ctrl);


out:

    return ret_val;

}

查看流控狀態

  • 查看/設置本地設備流控狀態

可以通過“ethtool –a”命令查看流控

參數說明:

ethtool -a|--show-pause DEVNAME     Show pause options

功能使用:

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool -a eth0

Pause parameters for eth0:

Autonegotiate:       on          /* 自動協商狀態(假如自動協商開啓,流控模式由硬件自動協商) */

RX:        on              /* 流控接收狀態 */

TX:         on              /* 流控發送狀態 */

       通過“ethtool –A”命令設置流控

參數說明:

ethtool -A|--pause DEVNAME     Set pause options

              [ autoneg on|off ]          /* 啓動/關閉自協商模式 */

              [ rx on|off ]               /* 啓動/關閉流控接收 */

              [ tx on|off ]               /* 啓動/關閉流控發送 */

功能使用:

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool -A eth0 autoneg off  /* 關閉自協商 */

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool -A eth0 rx off       /* 關閉流控接收 */

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool -A eth0 tx off       /* 關閉流控發送 */

       通過PHY寄存器也可以獲取當前的流控狀態,可以通過“mdio”工具查看PHY上的流控狀態。(mdio工具是在用戶層訪問smi/mdio總線,讀寫phy芯片寄存器的通用代碼。Linux內核2.6以上通用,自行編譯,源碼鏈接:https://blog.csdn.net/Ivan804638781/article/details/98882865)

語法說明:

mdio:

read operation: mdio reg_addr

write operation: mdio reg_addr value

For example:

mdio eth0 1

mdio eth0 0 0x12

功能使用:

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ./mdio eth0 4  /* Auto-Neg寄存器 */

read phy addr: 0x1  reg: 0x4   value : 0xde1

通過“mdio”工具查看遠端設備的流控狀態

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ./mdio eth0 5  /* Copper Link Partner寄存器 */

read phy addr: 0x1  reg: 0x5   value : 0xcde1

以I211網卡爲例,在《i210-ethernet-controller-datasheet》數據手冊上有如下流控寄存器描述:

流控低地址。流量控制數據包由802.3X定義爲一個唯一的組播地址,或者一個帶有指示暫停的EtherType字段的站地址。完整的流控制組播地址爲:0x01_80_C2_00_00_01;

流控高地址。該寄存器包含48位流控制以太網地址的上位。這個寄存器只有下面的16位有意義。

以太網幀類型(0x8808代表PAUSE幀)。此寄存器包含硬件匹配的類型字段,用於識別流控制包。只有寄存器的後16位纔有意義。

流控暫停時間參數。TTV字段中的16位值被插入到傳輸幀中(無論是XOFF幀還是其他任何暫停幀值)。它以64字節的插槽時間爲單位計數。如果軟件需要發送一個XON幀,它必須在啓動PAUSE幀之前將TTV設置爲0x0。

流控低水位閥值。此寄存器包含用於確定何時發送XON包的接收閾值。完整的寄存器以字節爲單位反映閾值。較低的四位必須被編程爲0x0(16字節粒度)。軟件必須設置XONE來支持傳輸XON幀。每次硬件通過接收高閾值(變得更滿),然後通過接收低閾值並啓用XONE (1b),硬件傳輸一個XON幀。XONE被設置時,RTL字段應該被編程爲至少0x3(至少48字節)。流量控制接收/傳輸是通過自動協商過程協商的功能。當手動配置I210時,流控制操作由CTRL.RFCE和CTRL.TFCE位確定。

流控高水位閥值。此寄存器包含用於確定何時發送XOFF包的接收閾值。完整的寄存器以字節爲單位反映閾值。這個值必須小於分配給接收包緩衝區的最大字節數(RXPBSIZE.RXPbsize),並且較低的四位必須被編程爲0x0(16字節粒度)。RTH的值也應該大於FCRTL0.RTL(流控低水位閥值)。每當接收FIFO達到RTH所指示的滿度時,如果啓用了流控制幀的傳輸,硬件就會傳輸一個暫停幀。流量控制接收/傳輸是通過自動協商過程協商的功能。當手動配置I210時,流控制操作由CTRL.RFCE和CTRL.TFCE位確定。

流控刷新閥值。此寄存器指示啓用傳輸流控制時流控制計數器的閾值(CTRL.TFCE = 1b)。當計數器達到這個值時,暫停狀態的條件仍然有效(緩衝區滿度高於低閾值),將向鏈接夥伴發送暫停(XOFF)幀。如果該字段包含零值,則禁用流控制刷新

流控狀態。該寄存器描述流量控制機的狀態。

分析驅動中的流控寄存器

通過“ethtool -d”命令查看當前網卡寄存器。

ethtool -d|--register-dump DEVNAME  Do a register dump

        [ raw on|off ]

        [ file FILENAME ]

    查看驅動中的流控寄存器:

[root@fe0fdb76-b761-11e9-b107-0014101e89e7 ~]# ethtool -d eth0 | grep FC

0x00028: FCAL        (Flow control address low)       0x00C28001

0x0002C: FCAH        (Flow control address high)      0x00000100

0x00170: FCTTV       (Flow control tx timer value)    0x0000FFFF

0x02160: FCRTL       (Flow control rx threshold low)  0x80007A50

0x02168: FCRTH       (Flow control rx threshold high) 0x00007A60

0x02460: FCRTV       (Flow control refresh threshold) 0x00000000

讀取到的寄存器值爲:0x00C28001

流控低地址。完整的流控制組播地址爲:0x01_80_C2_00_00_01。

讀取到的寄存器值爲:0x00000100

流控高地址。低16位有效,完整的流控制組播地址爲:0x01_80_C2_00_00_01。

讀到的寄存器值爲:0x00008808

以太網幀類型。流控幀的Type類型爲0x8808。

讀到的寄存器值爲:0x0000FFFF

流控暫停時間參數。當前流控時間參數65535,時間度量單位是以當前傳輸速率傳輸512位數據所用的時間。流控暫停時間爲:65535*傳輸512位數據所用的時間。

讀到的寄存值爲:0x80007A50

流控低水位閥值。第31位爲XON使能位,開啓。第4~16位表示當前的流控低閥值,第0~3位必須被編程爲0x0(16字節粒度)。即當前的低水位閥值爲31312字節。(I210默認分配一個36kb的片上數據包緩衝區。緩衝區用於存儲數據包,直到它們被轉發到主機。實際芯片上分配的接收緩衝區可以控制RXPBSIZE寄存器。)

當前I211讀到的RXPBSIZE寄存值爲:0x000000A2

(網卡驅動中的rx_ring(接收描述符緩衝區)?rx_buffer(接收數據緩衝區)?之間的關係)

讀到的寄存器值爲:0x00007A60

流控高水位閥值。第4~17位表示當前流控高閥值,第0~3位必須被編程爲0x0(16字節粒度)。即當前的高水位閥值爲31328字節。

流量控制設置:高水位線必須足夠低,以適應一個完整的框架(或用於早期接收)以上的Rx先進先出。將其設置爲:- 90%的Rx FIFO大小,以及完整的Rx FIFO大小減去早期接收大小(對於ERT支持的部件,假設ERT設置爲E1000_ERT_2048),或完整的Rx FIFO大小減去一個完整幀

文字讀起來比較難懂,直接看代碼。在I211對應的網卡驅動中,設置流控高水位閥值的代碼如下:

void igb_reset(struct igb_adapter *adapter)

{

…

    /* Repartition Pba for greater than 9k mtu

     * To take effect CTRL.RST is required.

     */

    switch (mac->type) {

    case e1000_i210:

    case e1000_i211:

    default:

        pba = E1000_PBA_34K;

        break;

    }

…

    /* flow control settings */

    /* The high water mark must be low enough to fit one full frame

     * (or the size used for early receive) above it in the Rx FIFO.

     * Set it to the lower of:

     * - 90% of the Rx FIFO size, or

     * - the full Rx FIFO size minus one full frame

     */

    hwm = min(((pba << 10) * 9 / 10),

            ((pba << 10) - 2 * adapter->max_frame_size));


    fc->high_water = hwm & 0xFFFFFFF0; /* 16-byte granularity */

    fc->low_water = fc->high_water - 16;

    fc->pause_time = 0xFFFF;

    fc->send_xon = 1;

    fc->current_mode = fc->requested_mode;

…

}

讀到的寄存器值爲:0x00000000

流控刷新閥值。表示當前禁用流控制刷新

讀到的寄存器值爲:0x00000004

    流控狀態。第2位爲1,表示當前流控低於低水位閥值。

驅動中其他和流控相關的寄存器

流控功能首先硬件需要支持,通常流控幀的發送是需要硬件做的。但是驅動這邊可以控制開啓/關閉硬件的流控功能。

具體體現在驅動中會配置流控相關的寄存器。

以JD4000設備爲例,網卡型號爲I211,對應的網卡驅動爲igb。

在I211數據手冊中,定義了下列字段:

•CTRL.RFCE字段用於啓用接收流控包並對其進行響應 

•CTRL.TFCE字段用於啓用流控包的傳輸

•流控制地址低/高(FCAL/H) - 6字節流控制多播地址

•流量控制類型(FCT) 16位字段,表示流量控制類型

•流量控制位在設備控制(CTRL)寄存器-啓用流量控制模式

•丟棄暫停幀(DPF),在RCTL中傳遞MAC控制幀(PMCF)——控制控制數據包轉發到主機

•流量控制接收閾值高(FCRTH0) -一個13位高字段,表示接收緩衝區已滿。

•DMA合併接收閾值高(FCRTC) -當DMA合併和Tx緩衝區爲空時,表示一個13位高字段表示接收緩衝區已滿。

•流量控制接收閾值低(FCRTL0) -一個13位低字段,表示接收緩衝區的空值。

•流量控制傳輸定時器值(FCTTV) -一組16位定時器值,包括在傳輸暫停幀。

•流控制刷新閾值(FCRTV) - 16位暫停刷新閾值

•RXPBSIZE。Rxpbsize字段用於控制接收包緩衝區的大小

FAQ

pause frame的目的地址決定了它不被bridge轉發,意思就是它不能經過交換機嗎?

PAUSE幀的目的地址是0180c2000001,而不是發送到A和B的mac地址!交換機並不是不轉發,而是延時轉發,pause幀中有2個字節用來代表停止發送時間的,過了這個時間,交換機還是會繼續轉發的!

Pause幀爲什麼會成對出現(PAUSE幀是不是一定是成對出現的)?因爲在發送PAUSE_XOFF幀後,對端就不會再發送報文了,得等到對端的暫時時間結束(文中提到的0x1FFF,8191*傳輸512bit所需時間),或者收到PAUSE_XON幀後,纔會繼續發送。這應該就是PAUSE幀成對出現的原因。

交換機在進入流控狀態後會往對端發pause幀(假設工作在全雙工),查了很多資料沒找到發送pause幀的時間間隔是多少。是不停地發呢還是一秒固定發幾個?哪位高人指點一下。

總結

合理設定發出流量控制請求時機。由於全雙工鏈路存在傳播延遲和響應延遲,發出PAUSE幀後,對方不能立即停止數據幀的發送。發送方必須考慮在發出PAUSE幀後能夠接收的最大數據量,避免緩衝區溢出。以太網交換控制電路支持10M/100M鏈路傳送,最壞情況下需要考慮的因素包括:

(1)在發送PAUSE幀前正在發送一個最長數據幀(1 536字節);

(2)發送PAUSE幀花費的時間爲512位鏈路傳輸時間;

(3)發送幀間隔爲96位鏈路傳輸時間;

(4)對方在解析PAUSE幀的同時,正開始發送一個最長數據幀 (1 536字節);

(5)接收幀間隔爲96位鏈路傳輸時間;

(6)鏈路的往返傳輸延遲時間。

根據上述因素,流量控制請求發出以後,還需要大約3.2K字節的數據等待接收與緩存,PAUSE幀才能終止鏈路對方數據幀的發送。在設計緩衝容量和選擇流量控制閾值時,需要考慮這些延遲因素,有效地實現流量控制機制,防止幀丟失。+

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章