内核如何维护网卡设备的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标志位
总结:
dev->state
下的__LINK_STATE_NOCARRIER
是 carrier是否OK 的唯一判断标准。
1 2 3 4
| 4390 static inline bool netif_carrier_ok(const struct net_device *dev) 4391 { 4392 return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); 4393 }
|
carrier on函数解析
总结:netif_carrier_on
主要依次做下面三个操作:
__LINK_STATE_NOCARRIER
: 设置netdev->state
上的__LINK_STATE_NOCARRIER
标志位。这个标志位很关键,后续判断carrier 是否 OK,就看这个标志位。
如果是从无到有这个标志位,则执行下面两步,否则就可以 return 结束了。
- 计数: 增加网卡设备下的
dev->carrier_up_count
的计数次数。
- 发送事件消息给linkwatch:让他做后续处理。激发动作有两部分
- 如果是网线插好了状态, 会启动一个网卡的看门狗,这个看门狗是负责检测tx队列是否’HUNG’了, 如果’HUNG’了就调用网卡对应的处理函数
ndo_tx_timeout
, 做一些应急补救,比如对网卡队列复位等操作。
1 2 3 4 5 6 7 8 9 10 11 12
| 581 void netif_carrier_on(struct net_device *dev) 582 { 583 if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 584 if (dev->reg_state == NETREG_UNINITIALIZED) 585 return; 586 atomic_inc(&dev->carrier_up_count); 587 linkwatch_fire_event(dev); 588 if (netif_running(dev)) 589 netdev_watchdog_up(dev); 590 } 591 } 592 EXPORT_SYMBOL(netif_carrier_on);
|
carrier off函数解析
跟netif_carrier_on
函数对称,唯一差别是不会处理看门狗的部分。
1 2 3 4 5 6 7 8 9 10
| 600 void netif_carrier_off(struct net_device *dev) 601 { 602 if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 603 if (dev->reg_state == NETREG_UNINITIALIZED) 604 return; 605 atomic_inc(&dev->carrier_down_count); 606 linkwatch_fire_event(dev); 607 } 608 } 609 EXPORT_SYMBOL(netif_carrier_off);
|
link watch函数
link watch时间激发函数
link watch事件在被激发后,会设置一个标志位__LINK_STATE_LINKWATCH_PENDING
然后通过linkwatch_add_event
, 把 link watch事件相关的 netdev 设备挂载到一个全局链表上。
1 2 3 4 5 6 7 8 9 10 11
| 291 void linkwatch_fire_event(struct net_device *dev) 292 { 293 bool urgent = linkwatch_urgent_event(dev); 294 295 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { 296 linkwatch_add_event(dev); 297 } else if (!urgent) 298 return; 299 300 linkwatch_schedule_work(urgent); 301 }
|
linkwatch_add_event
将 netdev 增加到链表中
1 2 3 4 5 6 7 8 9 10 11
| 124 static void linkwatch_add_event(struct net_device *dev) 125 { 126 unsigned long flags; 127 128 spin_lock_irqsave(&lweventlist_lock, flags); 129 if (list_empty(&dev->link_watch_list)) { 130 list_add_tail(&dev->link_watch_list, &lweventlist); 131 netdev_hold(dev, &dev->linkwatch_dev_tracker, GFP_ATOMIC); 132 } 133 spin_unlock_irqrestore(&lweventlist_lock, flags); 134 }
|
link watch的workqueue
link watch 的workqueue 处理函数,把dev从链表上摘下来,逐个调用linkwatch_do_dev
处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 194 static void __linkwatch_run_queue(int urgent_only) 195 { ... 224 spin_lock_irq(&lweventlist_lock); 225 list_splice_init(&lweventlist, &wrk); 226 227 while (!list_empty(&wrk) && do_dev > 0) { 229 230 dev = list_first_entry(&wrk, struct net_device, link_watch_list); ... 243 linkwatch_do_dev(dev); ... 246 } ... 254 }
|
link watch的最终处理逻辑
在linkwatch_do_dev
里, 才执行真正的网口设备状态处理。
- 清除
__LINK_STATE_LINKWATCH_PENDING
标志位,在这个标准位在事件被激发时候,被设置到netdevice上。
- 更新 netdev设备状态
dev->operstate
: 核心函数rfc2863_policy
,后续详解
- 网卡状态变换通知:如果网卡是 UP 状态下, 出现 carrier 状态变换,网卡active/deactive,,并通知响应的回调模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 166 static void linkwatch_do_dev(struct net_device *dev) 167 { ... 177 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); 178 179 rfc2863_policy(dev); 180 if (dev->flags & IFF_UP) { 181 if (netif_carrier_ok(dev)) 182 dev_activate(dev); 183 else 184 dev_deactivate(dev); 185 186 netdev_state_change(dev); 187 } ... 192 }
|
网卡的状态维护核心函数
两个核心函数rfc2863_policy
和default_operstate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 74 static void rfc2863_policy(struct net_device *dev) 75 { 76 unsigned int operstate = default_operstate(dev); 77 78 if (operstate == READ_ONCE(dev->operstate)) 79 return; 80 81 switch(dev->link_mode) { 82 case IF_LINK_MODE_TESTING: 83 if (operstate == IF_OPER_UP) 84 operstate = IF_OPER_TESTING; 85 break; 86 87 case IF_LINK_MODE_DORMANT: 88 if (operstate == IF_OPER_UP) 89 operstate = IF_OPER_DORMANT; 90 break; 91 case IF_LINK_MODE_DEFAULT: 92 default: 93 break; 94 } 95 96 WRITE_ONCE(dev->operstate, operstate); 97 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 36 static unsigned int default_operstate(const struct net_device *dev) 37 { 38 if (netif_testing(dev)) 39 return IF_OPER_TESTING; 40 41 /* Some uppers (DSA) have additional sources for being down, so 42 * first check whether lower is indeed the source of its down state. 43 */ 44 if (!netif_carrier_ok(dev)) { 45 struct net_device *peer; 46 int iflink; 47 48 /* If called from netdev_run_todo()/linkwatch_sync_dev(), 49 * dev_net(dev) can be already freed, and RTNL is not held. 50 */ 51 if (dev->reg_state <= NETREG_REGISTERED) 52 iflink = dev_get_iflink(dev); 53 else 54 iflink = dev->ifindex; 55 56 if (iflink == dev->ifindex) 57 return IF_OPER_DOWN; 58 59 ASSERT_RTNL(); 60 peer = __dev_get_by_index(dev_net(dev), iflink); 61 if (!peer) 62 return IF_OPER_DOWN; 63 64 return netif_carrier_ok(peer) ? IF_OPER_DOWN : 65 IF_OPER_LOWERLAYERDOWN; 66 } 67 68 if (netif_dormant(dev)) 69 return IF_OPER_DORMANT; 70 71 return IF_OPER_UP; 72 }
|
回调函数 (待细看)
这部分代码要涉及到 netdev的callback链和 rtnetlin 消息通知。
是网卡设备状态变换后的消息通知和处理工作。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1530 void netdev_state_change(struct net_device *dev) 1531 { 1532 if (dev->flags & IFF_UP) { 1533 struct netdev_notifier_change_info change_info = { 1534 .info.dev = dev, 1535 }; 1536 1537 call_netdevice_notifiers_info(NETDEV_CHANGE, 1538 &change_info.info); 1539 rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL, 0, NULL); 1540 } 1541 } 1542 EXPORT_SYMBOL(netdev_state_change);
|