通常網絡設備會定時地檢測設備是否處於可傳遞狀態。當狀態發生變化時,會調用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 }