网络 -- 以太网(PAUSE)流量控制

关键词:

以太网  数据链路层  PAUSE帧  流量控制

摘  要:

PAUSE操作实现了一种简单的停-等式流量控制机制,可以防止瞬时过载导致缓冲区溢出时不必要的帧丢失。

以太网流控的引入

硬件成本和数据帧处理速度的限制,缓冲拥塞造成的丢帧率大于链路传输中位出错率,因此必须解决缓冲拥塞造成的丢帧问题。

如何查看哪一层丢帧了?1000M网卡设备使用UDP发送数据给10M/100M网卡,会不会出现缓冲区溢出导致大量丢帧?)

假如没有以太网流控功能,可能会造成哪些问题?

对于以太网来说,Rx端口将会持续的接收到包,但是没有足够的空间临时存储这些包。Rx端口仅仅是简单的忽略这些输入数据包。Ethernet和TCP/IP协同合作重发这些包。然而,判决包已被丢弃,请求这些重新发送,最后发送包将耗费时间

以太网流量控制技术

IEEE802.3x标准定义了一种新方法,在全双工环境中去实现流量控制(PAUSE

流量控制是作为一种减少接收包缓冲区溢出的可能性的方法来实现的,这种溢出会导致接收包的丢失,并允许对网络拥塞级别进行本地控制。这可以通过向发射站发送接收站中几乎满的接收缓冲区状态的指示来实现

IEEE802.3z标准定义了非对称流量控制的具体操作(ASM_DIR)。

非对称流控制的实现允许一个链路伙伴发送流控制数据包,同时允许忽略它们的接收。例如,不需要响应暂停帧。

以太网流量控制-PAUSE帧

  • 什么是PAUSE帧

在IEEE802.3协议中规定中,PAUSE帧是一种控制帧,用于控制数据流停止发送,在MAC 发送侧产生,在MAC接收侧解析并执行。当此端设备输入数据量过大,无法及时处理时会在此端发送侧MAC产生PAUSE帧,发给对端,要求对端在一定时间内停止发送数据。

在全双工MAC控制框架下,流量控制机制是通过PAUSE功能实现的。如果某个端口要停止帧的接收,可以发送一个带有时间参数的PAUSE帧,参数指示全双工链路对方在开始发送数据前需要等待的时间,收到PAUSE帧的设备通过简单的解析,就可以确定停止发送的时长。当链路对方接收到PAUSE帧后,在参数指定的时间内停止发送数据。当指定时间超出,或原拥塞端口重新发出操作参数为0的PAUSE,链路对方从暂停的位置继续发送数据帧。

  • PAUSE帧功能

PAUSE帧操作实现了一种简单的停-等式流量控制机制。可以防止瞬时过载导致缓冲区溢出时不必要的帧丢失。

  • PAUSE帧位于网络协议中的哪一层?

PAUSE帧位于网络报文协议中的数据链路层(详细点讲应该是数据链路层中的MAC控制子层)。

IEEE802.3 将数据链路层分为三层:LLCMAC控制子层(可选)和MAC介质访问控制子层

MAC控制子层规定了通用的全双工流量控制结构。

交换控制电路要防止缓冲区溢出,可以利用MAC控制子层来控制以太网介质访问控制子层的操作。当已用缓冲区容量达到一个预先设定的阈值时,端口向全双工链路对方发出停止发送数据的请求,这个请求通过MAC控制子层产生的控制帧实现。

同样,端口可以接收由其他站点MAC控制子层产生的控制帧,控制帧夹在客户数据帧流中发送,接收方会根据帧的内容将控制帧分离出来,提交到MAC控制子层中的流量控制模块,流量控制模块解析控制帧的内容,提取帧中的控制参数,根据控制参数决定暂停发送的时间。

网络 -- 以太网(PAUSE)流量控制_第1张图片

  • PAUSE帧格式

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帧:

网络 -- 以太网(PAUSE)流量控制_第2张图片

  • PAUSE帧处理流程

如图所示,左侧为本端芯片,右侧为对端芯片。

网络 -- 以太网(PAUSE)流量控制_第3张图片

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标准文档中,有相关的描述,如下图:

网络 -- 以太网(PAUSE)流量控制_第4张图片

网络 -- 以太网(PAUSE)流量控制_第5张图片

从描述上看到,802.3标准文档中的描述和ethtool源码是能对应上的。“PAUSE”是标准流控控制,“ASM_DIR”是非对称流控控制。关于本地设备(网卡)和对端设备(交换机)的详细关系如下:

网络 -- 以太网(PAUSE)流量控制_第6张图片

从图中可以明白,流控必须需要本地和对端都支持才行,否则只有一侧流控功能开启,是不生效的。其次,假如两端都开启了“PAUSE”功能,则都支持流控发送和接收。如果本地端没有开启“PAUSE”但开启了“ASM_DIR”功能,则需要判断对端是否都开启了“PAUSE”和“ASM_DIR”,若是,则本地端只能发送流控、对端只能接收流控。

再来看I211网卡手册中的PHY芯片寄存器截图:

网络 -- 以太网(PAUSE)流量控制_第7张图片

表明当前网卡对应PHY的硬件上是支持标准流控和非对称流控功能的。也可以通过读PHY寄存器上的这两个位数据,确认当前的流控模式。

再看I211网卡手册中的MAC寄存器截图:

网络 -- 以太网(PAUSE)流量控制_第8张图片

  • 驱动中的流控支持

同样的,驱动中也有对流控功能的相关描述,以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

网络 -- 以太网(PAUSE)流量控制_第9张图片

  • 查看远端设备流控状态

通过“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》数据手册上有如下流控寄存器描述:

网络 -- 以太网(PAUSE)流量控制_第10张图片

  • Flow Control Address Low - FCAL (0x0028; RO)

流控低地址。流量控制数据包由802.3X定义为一个唯一的组播地址,或者一个带有指示暂停的EtherType字段的站地址。完整的流控制组播地址为:0x01_80_C2_00_00_01;

  • Flow Control Address High - FCAH (0x002C; RO)

流控高地址。该寄存器包含48位流控制以太网地址的上位。这个寄存器只有下面的16位有意义。

  • Flow Control Type - FCT (0x0030; R/W)

以太网帧类型(0x8808代表PAUSE帧)。此寄存器包含硬件匹配的类型字段,用于识别流控制包。只有寄存器的后16位才有意义。

  • Flow Control Transmit Timer Value - FCTTV (0x0170; R/W)

流控暂停时间参数。TTV字段中的16位值被插入到传输帧中(无论是XOFF帧还是其他任何暂停帧值)。它以64字节的插槽时间为单位计数。如果软件需要发送一个XON帧,它必须在启动PAUSE帧之前将TTV设置为0x0。

  • Flow Control Receive Threshold Low - FCRTL0 (0x2160; R/W)

流控低水位阀值。此寄存器包含用于确定何时发送XON包的接收阈值。完整的寄存器以字节为单位反映阈值。较低的四位必须被编程为0x0(16字节粒度)。软件必须设置XONE来支持传输XON帧。每次硬件通过接收高阈值(变得更满),然后通过接收低阈值并启用XONE (1b),硬件传输一个XON帧。XONE被设置时,RTL字段应该被编程为至少0x3(至少48字节)。流量控制接收/传输是通过自动协商过程协商的功能。当手动配置I210时,流控制操作由CTRL.RFCE和CTRL.TFCE位确定。

网络 -- 以太网(PAUSE)流量控制_第11张图片

  • Flow Control Receive Threshold High - FCRTH0 (0x2168; R/W)

流控高水位阀值。此寄存器包含用于确定何时发送XOFF包的接收阈值。完整的寄存器以字节为单位反映阈值。这个值必须小于分配给接收包缓冲区的最大字节数(RXPBSIZE.RXPbsize),并且较低的四位必须被编程为0x0(16字节粒度)。RTH的值也应该大于FCRTL0.RTL(流控低水位阀值)。每当接收FIFO达到RTH所指示的满度时,如果启用了流控制帧的传输,硬件就会传输一个暂停帧。流量控制接收/传输是通过自动协商过程协商的功能。当手动配置I210时,流控制操作由CTRL.RFCE和CTRL.TFCE位确定。

网络 -- 以太网(PAUSE)流量控制_第12张图片

  • Flow Control Refresh Threshold Value - FCRTV (0x2460; R/W)

流控刷新阀值。此寄存器指示启用传输流控制时流控制计数器的阈值(CTRL.TFCE = 1b)。当计数器达到这个值时,暂停状态的条件仍然有效(缓冲区满度高于低阈值),将向链接伙伴发送暂停(XOFF)帧。如果该字段包含零值,则禁用流控制刷新

网络 -- 以太网(PAUSE)流量控制_第13张图片

  • Flow Control Status - FCSTS0 (0x2464; RO)

流控状态。该寄存器描述流量控制机的状态。

网络 -- 以太网(PAUSE)流量控制_第14张图片

分析驱动中的流控寄存器

通过“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
  • Flow Control Address Low - FCAL (0x0028; RO)

读取到的寄存器值为:0x00C28001

流控低地址。完整的流控制组播地址为:0x01_80_C2_00_00_01。

  • Flow Control Address High - FCAH (0x002C; RO)

读取到的寄存器值为:0x00000100

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

  • Flow Control Type - FCT (0x0030; R/W)

读到的寄存器值为:0x00008808

以太网帧类型。流控帧的Type类型为0x8808。

  • Flow Control Transmit Timer Value - FCTTV (0x0170; R/W)

读到的寄存器值为:0x0000FFFF

流控暂停时间参数。当前流控时间参数65535,时间度量单位是以当前传输速率传输512位数据所用的时间。流控暂停时间为:65535*传输512位数据所用的时间。

  • Flow Control Receive Threshold Low - FCRTL0 (0x2160; R/W)

读到的寄存值为:0x80007A50

流控低水位阀值。第31位为XON使能位,开启。第4~16位表示当前的流控低阀值,第0~3位必须被编程为0x0(16字节粒度)。即当前的低水位阀值为31312字节。(I210默认分配一个36kb的片上数据包缓冲区。缓冲区用于存储数据包,直到它们被转发到主机。实际芯片上分配的接收缓冲区可以控制RXPBSIZE寄存器。)

当前I211读到的RXPBSIZE寄存值为:0x000000A2

网络 -- 以太网(PAUSE)流量控制_第15张图片

(网卡驱动中的rx_ring(接收描述符缓冲区)?rx_buffer(接收数据缓冲区)?之间的关系)

  • Flow Control Receive Threshold High - FCRTH0 (0x2168; R/W)

读到的寄存器值为: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;

…

}
  • Flow Control Refresh Threshold Value - FCRTV (0x2460; R/W)

读到的寄存器值为:0x00000000

流控刷新阀值。表示当前禁用流控制刷新

  • Flow Control Status - FCSTS0 (0x2464; RO)

读到的寄存器值为: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)流量控制_第16张图片

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帧才能终止链路对方数据帧的发送。在设计缓冲容量和选择流量控制阈值时,需要考虑这些延迟因素,有效地实现流量控制机制,防止帧丢失。+

你可能感兴趣的:(网络)