1. Ethtool中的link状态
这里我们以igb网络为例,来说明ethtool中的link状态:
1.1 Net-tools中ethtool
首先从ethtool中, 发出一个ETHTOOL_GLINK的消息到kernel中:
1.2 Kernel中ethtool
net/core/dev_ioctl.c->dev_ethtool 系统ioctl的入口
这里可以看到,link状态取决于: netif_running && ethtool_ops->get_link
1.2.1 netif_running()
__LINK_STATE_START是怎么来的?,搜索代码可以找到_dev_open中有如下代码:
_dev_open则是在调用ifconfig up 的流程中调用的,所以 netif_running类似于管理状态的概念,
执行ifconfig up ,则dev-state->__LINK_STATE_START被标记,ifconfig down, dev-state->__LINK_STATE_START标记被清除
1.2.2 ethtool_ops->get_link
这里进入igb网卡的流程…
这里有一个netif_carrier_ok(netdev): 这里最终是网卡link_up状态中设置的,见后面。
get_link_status: 针对不同的前端media模式, 对link状态的读取不同。
1.2.3 igb_has_link
bool igb_has_link(struct igb_adapter *adapter)
这里对于copper,link状态在外部,由外部上报LSC中断,系统觉得是准确的,所以这里如果外部中断已经上报了,不需要再去读取link状态了。
如下是对应copper和!copper 2种链路方式的读取,最终将读取到网卡的link状态
这里netif_carrier_on 在哪个流程里调用呢?? -- 这里是在网卡驱动的link检查流程中设置的:
1.2.4 link LSC 中断上报
igb_msix_other(int irq, void *data) //这里是other中断
1.2.5 igb_watchdog_task
//在网卡probe加载后,启动timer,timer中启动的work_queue
igb_watchdog_task
link = igb_has_link(adapter); //读取Link状态
if (link) //检查到link 状态
if (!netif_carrier_ok(netdev)) //如果原来没有link up
printk() // dmesg打印出link up的信息
netif_carrier_on(netdev); //设置网卡的link up信息
--- 这里igb是一个特例,不是直接判断carrier_ok的值, 可以参考ixgbe。
综上:
ethtool link状态为: ifconfig up && ethtool->get_link的状态(网卡硬件的物理状态)
netif_carrier_ok: 保存当前网卡的物理link状态到netdev中。
2. ifconfig中running
net-tool 中: 可以看到状态: IFF_RUNNING…
Kernel 中
netif_oper_up
这个状态在哪里设置的呢?
igb_watchdog_task
link: netif_carrier_on(netdev);
linkwatch_fire_event(dev); //通知其它进程关注事件
linkwatch_fire_event
--> linkwatch_schedule_work
--> schedule: linkwatch_work
--> rfc2863_policy(dev); //这个状态是为MIB状态
--> IF_OPER_UP -- 最终这里将状态设置起来
综上:
Ifconfig Running 状态为网卡link事件通过一系列的通知链,最终设置的一个mib状态, 只是ifconfig 将这个状态读取出来。
3. 一体机光转电口卡link 失败
问题描述:
使用的网卡为: E1000_DEV_ID_82580_FIBER 0x150F
在插上SFP光电转化口后启动(或者动态加载驱动):
1. 在插出/插入SFP光模块上的网线的时候, 能够监测到link事件。
2. 在有些环境中,可以将SFP 带着网线一起拔出,这时link事件检查不到。继续插上也检查不到。
3. 在有些环境,只有将SFP上网线拔出后(能够监测到link事件)才能够拔出SFP, 再插上SFP,再接上网线后,无法Link。
对比未插上SFP光模块启动的时候: 拔插网线/光模块都没有问题。
问题分析:
1. 在驱动加载的时候,这2种情况下, 驱动对硬件前端的识别情况不同:
插入SFP光转电: 前端media_type 识别为: e1000_media_type_copper ,对前端的处理Porwerup
igb_power_up_phy_copper(&adapter->hw);
在这种模式下,网卡驱动通过i2c对外界MDI来做negonation。
未插入SFP时候: media_type识别为: internal_serdes, 在这种模式下
igb_power_up_serdes_link_82575(&adapter->hw);
因为本地网卡MAC已经是FIRBBER, 所以协商的话直接设置本地PCS寄存器就可以了
带SFP加载驱动, 识别为copper, copper的前端为SPF光模块,通过MDI线连接。 在当前的环境中, 拔出SFP后,系统没有监测到media_type的变化, 中断未上报,只报了一些对i2c_read ERR的日志。
检查系统中其实是有check_swap的流程中的,只是在读eeprom的时候,当前网卡都没有走入case中,未将该状态设置起来:
该状态将在watch_dog_task中将检查,其核心是检查:
#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
检查到media_chagen后, 会调用网卡的reset流程,在该流程中,重新读取media的状态。
注: 在测试从 serdes -> copper 模式的时候,reset的流程总是发生crash, 考虑到在serdes模式下,热插拔是正常的, 就不检查这个流程了。 所以最终的修改如下:
其实这个问题就是: 光模块暴拔的问题…
//这里的判断表示: 如果不是fiber口,media_type 不是copper(fiber插入光转电口), 可以自动dect link变化,不需要下面增加的dect link流程.
如果发现当前控制器状态为: SERDESD -- 表示希望看到SERDESD 出现, 表面当前Fiber未发生link 变化。
4 ifconfig 逻辑
if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
return (-1);
ife->flags = ifr.ifr_flags;
然后判断 flags:输出 runing状态
if (ptr->flags & IFF_RUNNING)
strcat(flags,_("RUNNING,"));
if (ptr->flags & IFF_UP)
strcat(flags,_("UP,"));
内核dev.c获取flag 返回给ioctl
unsigned int dev_get_flags(const struct net_device *dev)
{
unsigned int flags;
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING |
IFF_LOWER_UP |
IFF_DORMANT)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev)) {
if (netif_oper_up(dev))
flags |= IFF_RUNNING;
if (netif_carrier_ok(dev))
flags |= IFF_LOWER_UP;
if (netif_dormant(dev))
flags |= IFF_DORMANT;
}
return flags;
}
static inline bool netif_oper_up(const struct net_device *dev)
{
return (dev->operstate == IF_OPER_UP ||
dev->operstate == IF_OPER_UNKNOWN /* backward compat */);
}
static inline bool netif_carrier_ok(const struct net_device *dev)
{
return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}
5, ifconfig up
dev_open->__dev_open
6. kni 获取link
kni ea1000_phy_has_link_generic ret_val=0 phy_status=31049 success=0