内核如何维护网卡设备的RUNNING 状态
流程概述
主要几个部分:
- 网卡驱动有个看门狗:不同网卡驱动实现可能不太一样 ,功能负责监控网卡硬件上网线的状态, 当网线状态变换的时候,会激发内核的 carrier 处理函数。
- 内核两个通用的处理函数:
netif_carrier_on
和netif_carrier_off
。这个函数会- 设置或者清除
netdev->state
上的__LINK_STATE_NOCARRIER
标志位。 - 发送事件消息给linkwatch,做后续处理.
- 如果是网线插好了状态, 会启动一个通用的看门狗,这个看门狗是负责检测tx队列是否’HUNG’了, 如果’HUNG’了就调用网卡对应的处理函数
ndo_tx_timeout
, 做一些应急补救,比如对网卡队列复位等操作。这里的看门狗跟网卡驱动里的看门狗还不是同一个看门狗。具体差别待研究。
- 设置或者清除
- linkwath模块:linkwatch本身是个workqueue 队列,对接受到的消息按照,分为紧急和非紧急两类。紧急的决定立即处理,非紧急的则挂到一个链表里,等定时器超时后,再集中处理。所有事件处理,最终都交给
linkwatch_do_dev(struct net_device *dev)
函数进行处理。 该函数更新netdev->operate
标志位。同时调用通用的dev_activate
或者dev_deactivate
对网卡做网卡队列进行处理。 我们这里重点关注跟网卡状态位有管的部分,忽略跟网卡队列的处理。
这里有两个重要函数rfc2863_policy
和default_operstate
后面我们重点介绍。
carrier on 和 off 函数
netif_carrier_on
和netif_carrier_off
: 内核里的两个通用的处理函数,功能基本对称
carrier on详解
`netif_carrier_on`主要依次做下面三个操作
+ `__LINK_STATE_NOCARRIER`: 设置`netdev->state`上的`__LINK_STATE_NOCARRIER`标志位。这个标志位很关键,后续判断carrier 是否 OK,就看这个标志位。
如果是从无到有这个标志位,则执行下面两步,否则就跳过。
+ 增加网卡设备下的`dev->carrier_up_count`的计数次数。
+ 发送事件消息给linkwatch:让他做后续处理。激发动作有两部分
+ '__LINK_STATE_LINKWATCH_PENDING':设置这个标志位
+ 如果是网线插好了状态, 会启动一个网卡的看门狗,这个看门狗是负责检测tx队列是否'HUNG'了, 如果'HUNG'了就调用网卡对应的处理函数`ndo_tx_timeout`, 做一些应急补救,比如对网卡队列复位等操作。
1 | 4390 static inline bool netif_carrier_ok(const struct net_device *dev) |
1 | 581 void netif_carrier_on(struct net_device *dev) |
1 | 600 void netif_carrier_off(struct net_device *dev) |
link watch函数
入口函数
1 | 291 void linkwatch_fire_event(struct net_device *dev) |
将 netdev 增加到链表中
1 | 124 static void linkwatch_add_event(struct net_device *dev) |
workqueue 的处理函数,dev从链表上摘下来,逐个调用linkwatch_do_dev
处理。
1 | 194 static void __linkwatch_run_queue(int urgent_only) |
link watch
1 | 166 static void linkwatch_do_dev(struct net_device *dev) |
两个状态维护函数
1 | 74 static void rfc2863_policy(struct net_device *dev) |
1 | 36 static unsigned int default_operstate(const struct net_device *dev) |
回调函数 (待细看)
这部分代码要涉及到 netdev的callback链和 rtnetlin 消息通知。
是网卡设备状态变换后的消息通知和处理工作。
1 | 1530 void netdev_state_change(struct net_device *dev) |