網絡設備之監測連接狀態

通常網絡設備會定時地檢測設備是否處於可傳遞狀態。當狀態發生變化時,會調用netif_carrier_on或者netif_carrier_off來通知內核;

從網上設備插拔網線或者另一端的設備關閉或禁止,都會導致連接狀態改變;

netif_carrier_on—-設備驅動監測到設備傳遞信號時調用

netif_carrier_off—-設備驅動監測到設備丟失信號時調用

上述兩個狀態改變函數均會調用linkwatch_fire_event將事件加入到事件隊列進行調度;

相關函數的調用關係如下:

複製代碼

 1 /**
 2  * netif_carrier_on(off)兩個函數均會調用linkwatch_fire_event
 3  * (netif_carrier_on | netif_carrier_off) 
 4  *           |------------>|--->linkwatch_fire_event
 5  *
 6  * linkwatch_fire_event
 7  *    |-->linkwatch_urgent_event
 8  *    |-->linkwatch_add_event
 9  *    |-->linkwatch_schedule_work-->linkwatch_event-->__linkwatch_run_queue
10  *                                                         |---->linkwatch_do_dev
11  */

複製代碼

 

當監測到設備傳遞信號時函數netif_carrier_on會被調用,並調用linkwatch_fire_event函數將設備加入到事件處理隊列進行處理;

複製代碼

 1 /**
 2  *    netif_carrier_on - set carrier
 3  *    @dev: network device
 4  *
 5  * Device has detected that carrier.
 6  */
 7 /* 監測到設備傳遞信號時調用 */
 8 void netif_carrier_on(struct net_device *dev)
 9 {
10     /* 清除nocarrier標記 */
11     if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
12         /* 設備尚未註冊,則返回 */
13         if (dev->reg_state == NETREG_UNINITIALIZED)
14             return;
15         /* 增加狀態改變次數 */
16         atomic_inc(&dev->carrier_changes);
17         /* 加入事件處理隊列進行處理 */
18         linkwatch_fire_event(dev);
19         /* 若設備正在運行 */
20         if (netif_running(dev))
21             /* 啓動軟件狗 */
22             __netdev_watchdog_up(dev);
23     }
24 }

複製代碼

 

當監測到設備信號丟失時函數netif_carrier_off會被調用,並調用linkwatch_fire_event函數將設備加入到事件處理隊列進行處理;

複製代碼

 1 /**
 2  *    netif_carrier_off - clear carrier
 3  *    @dev: network device
 4  *
 5  * Device has detected loss of carrier.
 6  */
 7 /* 監測到設備丟失信號時調用 */
 8 void netif_carrier_off(struct net_device *dev)
 9 {
10     /* 設置nocarrier狀態 */
11     if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
12         /* 設備尚未註冊,則返回 */
13         if (dev->reg_state == NETREG_UNINITIALIZED)
14             return;
15         /* 增加設備改變狀態 */
16         atomic_inc(&dev->carrier_changes);
17         /* 加入事件處理隊列進行處理 */
18         linkwatch_fire_event(dev);
19     }
20 }

複製代碼

 

linkwatch_fire_event函數將設備加入到事件隊列,並且進行事件調度,調度中會根據是否爲緊急事件做不同處理;

複製代碼

 1 /* 加入事件隊列處理 */
 2 void linkwatch_fire_event(struct net_device *dev)
 3 {
 4     /* 判斷是否是緊急處理的事件 */
 5     bool urgent = linkwatch_urgent_event(dev);
 6 
 7     /* 設置待處理事件標記 */
 8     if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
 9         /* 添加事件到事件列表 */
10         linkwatch_add_event(dev);
11     } 
12     /* 設備以前已經設置了pending標記,不是緊急事件,直接返回 */
13     else if (!urgent)
14         return;
15 
16     /* 事件調度 */
17     linkwatch_schedule_work(urgent);
18 }

複製代碼

 

linkwatch_urgent_event判斷是否是否需要緊急處理;

複製代碼

 1 /* 是否需要緊急處理的事件 */
 2 static bool linkwatch_urgent_event(struct net_device *dev)
 3 {
 4     /* 設備未運行,非緊急 */
 5     if (!netif_running(dev))
 6         return false;
 7 
 8     /* 設備的索引號與連接索引號不等,緊急 */
 9     if (dev->ifindex != dev_get_iflink(dev))
10         return true;
11 
12     /* 設備作爲team port,緊急 */
13     if (dev->priv_flags & IFF_TEAM_PORT)
14         return true;
15     /* 連接與否 && 發送隊列排隊規則改變與否 */
16     return netif_carrier_ok(dev) &&    qdisc_tx_changing(dev);
17 }

複製代碼

 

linkwatch_add_event將設備加入到事件處理鏈表;

複製代碼

 1 /* 添加事件 */
 2 static void linkwatch_add_event(struct net_device *dev)
 3 {
 4     unsigned long flags;
 5 
 6     spin_lock_irqsave(&lweventlist_lock, flags);
 7     /* 若未添加,則添加設備到事件列表 */
 8     if (list_empty(&dev->link_watch_list)) {
 9         list_add_tail(&dev->link_watch_list, &lweventlist);
10         dev_hold(dev);
11     }
12     spin_unlock_irqrestore(&lweventlist_lock, flags);
13 }

複製代碼

 

linkwatch_schedule_work對事件處理進行調度,緊急事件立即執行,非緊急事件延後執行;

複製代碼

 1 /* 調度事件處理工作隊列 */
 2 static void linkwatch_schedule_work(int urgent)
 3 {
 4     unsigned long delay = linkwatch_nextevent - jiffies;
 5 
 6     /* 已經設置了緊急標記,則返回 */
 7     if (test_bit(LW_URGENT, &linkwatch_flags))
 8         return;
 9 
10     /* Minimise down-time: drop delay for up event. */
11     /* 需要緊急調度 */
12     if (urgent) {
13         /* 之前設置了,則返回 */
14         if (test_and_set_bit(LW_URGENT, &linkwatch_flags))
15             return;
16         /* 未設置緊急,則立即執行 */
17         delay = 0;
18     }
19 
20     /* If we wrap around we'll delay it by at most HZ. */
21     /* 如果大於1s則立即執行 */
22     if (delay > HZ)
23         delay = 0;
24 
25     /*
26      * If urgent, schedule immediate execution; otherwise, don't
27      * override the existing timer.
28      */
29     /* 如果設置了緊急標記,則立即執行 */
30     if (test_bit(LW_URGENT, &linkwatch_flags))
31         mod_delayed_work(system_wq, &linkwatch_work, 0);
32     /* 未設置緊急標記,則按照delay執行 */
33     else
34         schedule_delayed_work(&linkwatch_work, delay);
35 }

複製代碼

 

__linkwatch_run_queue完成對事件調度隊列中設備的處理;

複製代碼

 1 /*
 2     @urgent_only--1-未到達下一次調度時間
 3                            0-已到達下次調度時間
 4 */
 5 static void __linkwatch_run_queue(int urgent_only)
 6 {
 7     struct net_device *dev;
 8     LIST_HEAD(wrk);
 9 
10     /*
11      * Limit the number of linkwatch events to one
12      * per second so that a runaway driver does not
13      * cause a storm of messages on the netlink
14      * socket.  This limit does not apply to up events
15      * while the device qdisc is down.
16      */
17     /* 已達到調度時間 */
18     if (!urgent_only)
19         linkwatch_nextevent = jiffies + HZ;
20     /* Limit wrap-around effect on delay. */
21     /*
22         未到達調度時間,並且下一次調度在當前時間的1s以後 
23         那麼設置調度時間是當前時間
24     */
25     else if (time_after(linkwatch_nextevent, jiffies + HZ))
26         linkwatch_nextevent = jiffies;
27 
28     /* 清除緊急標識 */
29     clear_bit(LW_URGENT, &linkwatch_flags);
30 
31     spin_lock_irq(&lweventlist_lock);
32     list_splice_init(&lweventlist, &wrk);
33 
34     /* 遍歷鏈表 */
35     while (!list_empty(&wrk)) {
36 
37         /* 獲取設備 */
38         dev = list_first_entry(&wrk, struct net_device, link_watch_list);
39 
40         /* 從鏈表移除設備 */
41         list_del_init(&dev->link_watch_list);
42 
43         /* 未到達調度時間 &&  不需要緊急處理  */
44         if (urgent_only && !linkwatch_urgent_event(dev)) {
45             /* 添加到鏈表尾部 */
46             list_add_tail(&dev->link_watch_list, &lweventlist);
47             /* 繼續處理 */
48             continue;
49         }
50         spin_unlock_irq(&lweventlist_lock);
51         /* 處理設備 */
52         linkwatch_do_dev(dev);
53         spin_lock_irq(&lweventlist_lock);
54     }
55 
56     /* 鏈表有未處理事件,則以非緊急狀態調度隊列 */
57     if (!list_empty(&lweventlist))
58         linkwatch_schedule_work(0);
59     spin_unlock_irq(&lweventlist_lock);
60 }

複製代碼

 

linkwatch_do_dev完成對某個設備的狀態改變處理;

複製代碼

 1 /* 處理某個設備的狀態改變 */
 2 static void linkwatch_do_dev(struct net_device *dev)
 3 {
 4     /*
 5      * Make sure the above read is complete since it can be
 6      * rewritten as soon as we clear the bit below.
 7      */
 8     smp_mb__before_atomic();
 9 
10     /* We are about to handle this device,
11      * so new events can be accepted
12      */
13     /* 清除pending標記 */
14     clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
15 
16     rfc2863_policy(dev);
17 
18     /* 如果設備啓動狀態 */
19     if (dev->flags & IFF_UP) {
20         /* 鏈路連接 */
21         if (netif_carrier_ok(dev))
22             /* 啓用排隊規則 */
23             dev_activate(dev);
24         /* 否則*/
25         else
26             /* 關閉排隊規則 */
27             dev_deactivate(dev);
28 
29         /* 設備狀態改變處理 */
30         netdev_state_change(dev);
31     }
32     dev_put(dev);
33 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章