Linux网卡驱动程序对ethtool的支持和实现
Linux 的一个显著特点就是其强大的网络功能,Linux 几乎支持所有的网络协议,并在这些协议基础上提供了丰富的应用。对 Linux 网络管理的重要性不言而喻,这些管理依赖于网络工具,比如最常用的 ifconfig,route,ip,ethtool 等,其中 ethtool 提供了强大的网卡及网卡驱动管理能力,其具体的实现框架和网络驱动程序及网络硬件关系紧密,容易修改和扩展,能够为 Linux 网络开发人员和管理人员提供对网卡硬件,驱动程序和网络协议栈的设置,查看以及及调试等功能。
从典型的以太网控制器说起
网卡工作在 OSI 网络体系的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为 PHY。数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。以太网卡中数据链路层的芯片称之为 MAC 控制器。很多网卡的这两个部分是做到一起的。他们之间的关系是 PCI 总线接 MAC 总线,MAC 接 PHY,PHY 接网线(当然也不是直接接上的,还有一个变压装置)。
一般地,一个典型的以太网控制器的基本结构如图 1 所示:
图 1. 一个典型的符合 IEEE802.3 标准的的以太网控制器结构图
数据链路层 MAC 是 Media Access Control 的缩写,即媒体访问控制子层协议。该协议位于 OSI 七层协议中数据链路层的下半部分,主要负责控制与连接物理层的物理介质。在发送数据的时候,MAC 协议可以事先判断是否可以发送数据,如果可以发送将给数据加上一些控制信息,最终将数据以及控制信息以规定的格式发送到物理层;在接收数据的时候,MAC 协议首先判断输入的信息并是否发生传输错误,如果没有错误,则去掉控制信息发送至 LLC 层。以太网 MAC 由 IEEE-802.3 以太网标准定义。
物理层 PHY 是物理接口收发器,它实现物理层。包括 MII/GMII(介质独立接口)子层、PCS(物理编码子层)、PMA(物理介质附加)子层、 PMD(物理介质相关)子层、MDI 子层。
MII 即媒体独立接口 , “媒体独立”表明在不对 MAC 硬件重新设计或替换的情况下,任何类型的 PHY 设备都可以正常工作。包括分别用于发送器和接收器的两条独立信道。每条信道都有自己的数据、时钟和控制信号。MII 数据接口总共需要 16 个信号,包括 TX_ER,TXD<3:0>,TX_EN,TX_CLK,COL,RXD<3:0>,RX_EX,RX_CLK,CRS,RX_DV 等。
RMII (Reduced Media Independant Interface ) 是简化的 MII 接口 ,在数据的收发上它比 MII 接口少了一倍的信号线,所以它一般要求是 50 兆的总线时钟 。RMII 一般用在多端口的交换机,它不是每个端口安排收、发两个时钟,而是所有的数据端口公用一个时钟用于所有端口的收发 ,这里就节省了不少的端口数目。RMII 的一个端口要求 7 个数据线 ,比 MII 少了一倍,所以交换机能够接入多一倍数据的端口。和 MII 一样,RMII 支持 10 兆和 100 兆的总线接口速度 。
GMII(Gigabit MII) 是千兆网的 MII 接口,这个也有相应的 RGMII 接口,表示简化了的 GMII 接口。GMII 采用 8 位接口数据,工作时钟 125MHz,因此传输速率可达 1000Mbps 。同时兼容 MII 所规定的 10/100 Mbps 工作方式。
MII 管理接口是个双信号接口,一个是时钟信号 MDC,另一个是数据信号 MDIO。通过管理接口,上层能监视和控制 PHY 的寄存器。PHY 里面的部分寄存器是 IEEE 定义的,这样 PHY 把自己的目前的状态反映到寄存器里面,MAC 通过管理接口不断的读取 PHY 的状态寄存器以得知目前 PHY 的状态,例如连接速度,双工的能力等。当然也可以通过管理接口设置 PHY 的寄存器达到控制的目的,例如流控的打开关闭,自协商模式还是强制模式等,这也是 ethtool 的工作原理。
MDIO/MDC,即 PHY 管理接口串行通信总线,该总线由 IEEE 通过以太网标准 IEEE 802.3 的若干条款加以定义。MDIO 是一种简单的双线串行接口,将管理器件 ( 如 MAC 控制器、微处理器 ) 与具备管理功能的收发器 ( 如多端口吉比特以太网收发器或 10GbE XAUI 收发器 ) 相连接,从而控制收发器并从收发器收集状态信息。可收集的信息包括链接状态、传输速度与选择、断电、低功率休眠状态、TX/RX 模式选择、自动协商控制、环回模式控制等。除了拥有 IEEE 要求的功能之外,收发器厂商还可添加更多的信息收集功能。
MDC 则是管理数据的时钟输入,最高速率可达 8.3MHz。MDIO 是管理数据的输入输出双向接口,数据是与 MDC 时钟同步的。MDIO 的工作流程为:
- MDIO 接口在没有传输数据的空闲状态(IDLE)数据线 MDIO 处于高阻态。
- MDIO 出现一个 2bit 的开始标识码 (01) 一个读 / 写操作开始。
- MDIO 出现一个 2bit 数据来标识是读操作 (10) 还是写操作 (01)。
- MDIO 出现一个 5bit 数据标识 PHY 的地址。
- MDIO 出现一个 5bitPHY 寄存器地址。
- MDIO 需要 2 个时钟的访问时间。
- MDIO 串行读出 / 写入 16bit 的寄存器数据。
- MDIO 恢复成 IDLE 状态,同时 MDIO 进入高阻状态。
注:以上内容部分摘选自互联网。
Linux 设备驱动程序中对 ethtool 的支持
目前几乎所有的网卡驱动程序都有对 ethtool 的支持,其框架如图 2 所示,ethtool 框架包含内核空间和用户空间两部分:用户空间的部分负责将 ethtool 命令发送到内核,并接收命令的执行结果;内核空间的部分根据相应的命令字,通过 MDIO/MDC 读写 MII 寄存器,实现对网卡的管理,并把执行结果传回用户空间。由于 Linux 网络驱动程序是一个复杂而庞大的体系,这里只介绍驱动程序中对 MII 寄存器的定义,对 MDIO/MDC 的支持以及驱动程序中实现如何实现 ethtool 功能部分。
图 2.ethtool 在 Linux 中的实现框架
IEEE 802.3 规定的 MII 寄存器
关于 MII/GMII 接口 PHY 寄存器的定义在 802.3 的 22.2.4 Management functions. 章节中,如该章节中的 Table 22 – 6 和 Table 22 – 7(即本文的图 3 和图 4,均出自 http://standards.ieee.org/getieee802/download/802.3-2008_section2.pdf)所示,
图 3. IEEE802.3 定义的 MII 管理寄存器集
可以看到寄存器分为基本集和扩展集,基本集的定义因 GMII 和 MII 而不同,对于 MII, 基本集包括寄存器 0 控制寄存器和 1 状态寄存器,而对于 GMII;基本集包括寄存器 0、1 和 15。控制寄存器 0 和状态寄存器 1 的定义如图 3 所示:
图 4. IEEE802.3 定义的寄存器 0 控制寄存器和 1 状态寄存器
图 4. IEEE802.3 定义的寄存器 0 控制寄存器和 1 状态寄存器
对寄存器 0 和寄存器 1 的读写可以实现对网卡的管理,清单 1 列出了部分 PHY 管理寄存器以及控制寄存器和状态寄存器的各个 bit 的定义。
清单 1,/kernel/drivers/net/Mii.h, 定义 PHY 管理寄存器
#define MII_BMCR 0x00 /* Basic mode control register */ #define MII_BMSR 0x01 /* Basic mode status register */ #define MII_PHYSID1 0x02 /* PHYS ID 1 */ #define MII_PHYSID2 0x03 /* PHYS ID 2 */ #define MII_ADVERTISE 0x04 /* Advertisement control reg */ #define MII_LPA 0x05 /* Link partner ability reg */ #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ ... /* Basic mode control register. */ #define BMCR_RESV 0x003f /* Unused... */ #define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ #define BMCR_CTST 0x0080 /* Collision test */ #define BMCR_FULLDPLX 0x0100 /* Full duplex */ #define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ #define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ #define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ #define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ #define BMCR_SPEED100 0x2000 /* Select 100Mbps */ #define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ #define BMCR_RESET 0x8000 /* Reset the DP83840 */ /* Basic mode status register. */ #define BMSR_ERCAP 0x0001 /* Ext-reg capability */ #define BMSR_JCD 0x0002 /* Jabber detected */ #define BMSR_LSTATUS 0x0004 /* Link status */ #define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ #define BMSR_RFAULT 0x0010 /* Remote fault detected */ #define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ #define BMSR_RESV 0x00c0 /* Unused... */ #define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ #define BMSR_100FULL2 0x0200 /* Can do 100BASE-T2 HDX */ #define BMSR_100HALF2 0x0400 /* Can do 100BASE-T2 FDX */ #define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ #define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ #define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ #define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ #define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
通过 MDC/MDIO 读写 MII 寄存器的具体实现
在本文的前面部分介绍过 MDC/MDIO 的工作流程,网卡驱动程序中的 MDIO 读写函数 mdio_read 和 mdio_write,也就是清单 3 中的函数指针的具体实现是在各个网卡的驱动程序文件中完成的,都遵从 IEEE802.3 MDIO 的帧格式。典型的帧格式是第 22 条款中定义的格式:
图 5.IEEE802.3 条款 22 定义的 MDIO 帧格式
域 | 长度(bit) | 说明 |
---|---|---|
ST | 2bits | 01b |
OP | 2bits | 操作码,写为 01b,读为 10b |
PHYADR | 5bits | PHY ID |
REGADR | 5bits | 寄存器地址 |
TA | 2 bits | 状态转换域,读操作为 X0b, 写操作为 10b |
DATA | 16 bits | 数据 |
在驱动程序中实现 ethtool 功能
在 kernel/include/linux/ethtool.h 定义了结构体 ethtool_ops,这个结构体的所有成员都是函数指针类型,定义了 ethtool 可以实现的功能,该结构体成员变量较多,在这里就不列出代码清单;同时,在结构体 net_device 中也有成员变量 ethtool_ops 如清单 2 所示,
清单 2,kernel/include/linux/NetDevice.h, net_device 中成员变量 ethtool_ops
struct net_device { ... const struct ethtool_ops *ethtool_ops; ... }
网卡驱动程序需要初始化 ethtool_ops 并且实现其定义的函数功能,从而实现对 ethtool 的支持,以 Dm9000.c 为例。
清单 3,kernel/drivers/net/Dm9000.c,DM9000 驱动程序对 ethtool 的支持
static const struct ethtool_ops dm9000_ethtool_ops = { .get_drvinfo = dm9000_get_drvinfo, .get_settings= dm9000_get_settings, .set_settings = dm9000_set_settings, .get_msglevel = dm9000_get_msglevel, .set_msglevel = dm9000_set_msglevel, .nway_reset= dm9000_nway_reset, .get_link = dm9000_get_link, .get_eeprom_len = dm9000_get_eeprom_len, .get_eeprom = dm9000_get_eeprom, .set_eeprom= dm9000_set_eeprom, .get_rx_csum= dm9000_get_rx_csum, .set_rx_csum= dm9000_set_rx_csum, .get_tx_csum= ethtool_op_get_tx_csum, .set_tx_csum= dm9000_set_tx_csum, }; ... ndev->ethtool_ops = &dm9000_ethtool_ops; ...
清单 3 中的各个函数在 DM9000 的驱动程序中均有实现,比如如果需要查看当前网络的连接情况,可以通过 dm9000_get_link 获得,函数的具体实现如清单 4:
清单 4,dm9000_get_link
static u32 dm9000_get_link(struct net_device *dev) { board_info_t *dm = to_dm9000_board(dev); u32 ret; if (dm->flags & DM9000_PLATF_EXT_PHY) ret = mii_link_ok(&dm->mii); else ret = dm9000_read_locked(dm, DM9000_NSR) & NSR_LINKST ? 1 : 0; return ret; } kernel/drivers/net/Mii.c int mii_link_ok (struct mii_if_info *mii) { /* first, a dummy read, needed to latch some MII phys */ mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS) return 1; return 0; }
可以看到最终的实现还是通过 MDIO/MDC 读取 PHY 寄存器得到。
除了管理网卡的命令,ethtool 还有还有其它扩展的功能,ethtool 的框架十分有利于新功能的扩展,开发人员可以在这个框架里加入自己想要的功能来实现对除了网卡管理的其它功能,事实上,现在的 ethtool 已经提供了一些其它的功能,比如用来实现网卡 Firmware 的刷写和更新,对网络驱动程序日志的控制等,这些新功能对于调试程序,修正错误是十分有利的。
清单 5, 部分 ethtool 的扩展功能:firmware 更新和修改日志级别
ethtool -f|--flash DEVNAME FILENAME ethtool -s|--change DEVNAME msglvl %d
使用 ethtool 配置和管理网卡
上一节主要介绍了 ethtool 实现的基础和方法,本节将主要介绍 ethtool 的一些用法,主要集中在 ethtool 在配置和管理网卡方面的用法。
了解 ethtool 用法最好的方法是查看 ethtool 的帮助信息“ethtool -h” 或者 “man ethtool”,由于帮助信息很多,这里就不一一列出了,将会举例一些实际的应用例子代替。
实例 1,利用 ethtool 来查看网卡接口 eth4 的信息
清单 6,查看网卡的接口信息
root@IMMV2-DEV4:~# ethtool eth4 Settings for eth4: Supported ports: [ TP ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supports auto-negotiation: Yes Advertised link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Advertised auto-negotiation: Yes Speed: 100Mb/s Duplex: Full Port: Twisted Pair PHYAD: 1 Transceiver: internal Auto-negotiation: on Supports Wake-on: g Wake-on: g Link detected: yes
实例 2,关闭网卡的自动协商并且查看修改结果。
清单 7,关闭网卡的自动协商并且查看修改结果
root@IMMV2-DEV4:~# ethtool -s eth4 autoneg off root@IMMV2-DEV4:~# ethtool eth4 Settings for eth4: Supported ports: [ TP ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supports auto-negotiation: Yes Advertised link modes: Not reported Advertised auto-negotiation: No Speed: 100Mb/s Duplex: Full Port: Twisted Pair PHYAD: 1 Transceiver: internal Auto-negotiation: off Supports Wake-on: g Wake-on: g Link detected: yes
实例 3,关闭网卡的自动协商并且修改网卡的速率为 10Mb/s
清单 8,关闭网卡的自动协商并修改网卡速率为 10Mb/s
root@IMMV2-DEV4:~# ethtool -s eth4 autoneg off speed 10 root@IMMV2-DEV4:~# ethtool eth4 Settings for eth4: Supported ports: [ TP ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supports auto-negotiation: Yes Advertised link modes: Not reported Advertised auto-negotiation: No Speed: 10Mb/s Duplex: Full Port: Twisted Pair PHYAD: 1 Transceiver: internal Auto-negotiation: off Supports Wake-on: g Wake-on: g Link detected: yes
ethtool 的其它功能可以根据其帮助信息规定的语法来实现,这里就不一一列举。
扩展 ethtool
根据 NIC 的一些特性,可以扩展 ethtool 来完成对网卡特殊功能的支持,一个典型的扩展应用就是增加 ethtool 对 SideBand 的支持功能,有关 SideBand 的介绍可以参考 IBM developerWorks 《浅谈 NCSI 及其在 Linux 上的实现
》。图 6 是一个通过添加自定义的 cmd 和对应的实现函数来完成 SideBand 的 select_channel,enable_channel 及 disable_channel 等功能的框图。以 select_channel 为例,可以通过如下的步骤来实现。
图 6, 扩展 ethtool 的 sideband 管理功能
- ethtool 的在用户空间和内核空间同时添加命令字 ETHTOOL_SELCHANNEL;
- 在 ethtool.ops 中添加与 ETHTOOL_SELCHANNEL 相对应的执行函数 ethtool_select_channel;
- 在 dev_ethtool 函数中实现 ethtool_select_channel() 功能,这个函数的功能是利用协议栈的包发送接口向 NIC 的 mac 层发送包装后的 NCSI 命令协议包,并且接受相应的回应 , 类似的对于 ethtool_enable_channel(),ethtool_disable_channel 都可以按照相同的方法来扩展,可以看出 ethtool 框架的扩展性是很好的,有利于开发人员根据实际需要来量身定制。
总结
ethtool 是一个 Linux 下功能强大的网络管理工具,本文首先介绍了这个工具的实现原理和方法,重点介绍了 IEEE802.3.22 中的 MII 管理寄存器和 MDIO/MDC 标准以及 Linux 网络驱动程序中对 ethtool 的支持,然后实例说明了利用这个工具管理网卡的方法,最后介绍了在 ethtool 框架上扩展 SideBand 管理的实例,可以作为广大开发人员的一个参考。