網絡子係統6_設備開啟與關閉
// 網絡設備開啟
// 函數主要任務:
// 1.設置dev->state=__LINK_STATE_START
// 2.調用驅動程序的回調函數open
// 3.設置dev->flags |= IFF_UP表示設備開啟
// 4.更新多播列表,
// 5.激活設備
// 6.通知監聽器,設置dev->flags
// 設備開啟之後應該具備的特征:
// 1.dev->state, 表示設備可以進行傳輸接收
// 2.dev->flags,表示設備已經開啟
// 3.設備使用了正確的隊列規則
1.1 int dev_open(struct net_device *dev)
{
int ret = 0;
//檢查設備是否已經開啟
if (dev->flags & IFF_UP)
return 0;
//檢查設備是否存在
if (!netif_device_present(dev))
return -ENODEV;
//設置設備可以進行接收
set_bit(__LINK_STATE_START, &dev->state);
if (dev->open) {
//如果驅動程序提供了open函數,則調用
ret = dev->open(dev);
if (ret)
clear_bit(__LINK_STATE_START, &dev->state);
}
if (!ret) {
//設置開啟標誌
dev->flags |= IFF_UP;
//加載多播地址列表
dev_mc_upload(dev);
//激活設備,使能傳輸隊列
dev_activate(dev);
//向netdev_chain通知有設備開啟
notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
}
return ret;
}
// 加載設備多播地址
// 調用路徑:dev_open->dev_mc_upload
// ip地址,mac地址之間的映射,參照https://blog.csdn.net/hxg130435477/article/details/8049271
1.2 void dev_mc_upload(struct net_device *dev)
{
spin_lock_bh(&dev->xmit_lock);
__dev_mc_upload(dev);
spin_unlock_bh(&dev->xmit_lock);
}
// 調用路徑:dev_open->dev_mc_upload->__dev_mc_upload
// 通過驅動程序的回調函數,設置設備的l2多播地址列表
1.3 static void __dev_mc_upload(struct net_device *dev)
{
//設備應該處於關閉狀態
if (!(dev->flags&IFF_UP))
return;
//設備驅動提供了設置多播地址的方法
if (dev->set_multicast_list == NULL ||
!netif_device_present(dev))
return;
//調用設備提供的設置多播地址的方法
dev->set_multicast_list(dev);
}
// 調用路徑:dev_open->dev_activate
// 函數主要任務:
// 1.更新設備的隊列規則
// 1.1 如果設備沒有使用隊列規則,則dev->qdisc設置為noqueue_disc,防止不正確的使用該驅動的隊列規則
// 1.2 如果設備使用隊列規則:
// 1.2.1 如果設置第一次被激活,則分配新的隊列規則,保存在dev->qdisc_sleeping
// 1.2.2 如果設備非第一次被激活
// 1.2.3 設置dev->qdisc=dev->qdisc_sleeping
// 2.啟動看門狗
1.4 void dev_activate(struct net_device *dev)
{
//設備沒有關聯隊列規則
if (dev->qdisc_sleeping == &noop_qdisc) {
struct Qdisc *qdisc;
//設備存在傳輸隊列
//dev->tx_queue_len的值由設備驅動程序設置
if (dev->tx_queue_len) {
//創建先進先出隊列
qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops);
if (qdisc == NULL) {
printk(KERN_INFO "%s: activation failed\n", dev->name);
return;
}
//將隊列規則添加到dev的隊列規則鏈表頭
//說明一個dev可以有多個隊列規則
write_lock_bh(&qdisc_tree_lock);
list_add_tail(&qdisc->list, &dev->qdisc_list);
write_unlock_bh(&qdisc_tree_lock);
} else {
//如果驅動程序沒有提供tx_queue_len,即=0
//設置默認的隊列
qdisc = &noqueue_qdisc;
}
write_lock_bh(&qdisc_tree_lock);
//設置dev的qdisc_sleeping
dev->qdisc_sleeping = qdisc;
write_unlock_bh(&qdisc_tree_lock);
}
spin_lock_bh(&dev->queue_lock);
//設置qdisc_sleep到qdisc字段
rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping);
if (dev->qdisc != &noqueue_qdisc) {
dev->trans_start = jiffies;
//喚醒看門狗
dev_watchdog_up(dev);
}
spin_unlock_bh(&dev->queue_lock);
}
// 設備關閉
// 函數主要任務:
// 1.向netdev_chain發送消息NETDEV_GOING_DOWN
// 2.deactivate設備,
// 3.清除dev->state的__LINK_STATE_START
// 4.等待設備完成數據接收
// 5.調用驅動程序提供的關閉函數
// 6清除dev->flags中的IFF_UP
// 7.向netdev_chain發送消息NETDEV_DOWN
2.1 int dev_close(struct net_device *dev)
{
//設備已經關閉,則直接返回
if (!(dev->flags & IFF_UP))
return 0;
//向netdev_chain發送設備正在關閉消息
notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
//關閉設備隊列規則
dev_deactivate(dev);
//設置設備關閉傳輸
clear_bit(__LINK_STATE_START, &dev->state);
//內存屏障
smp_mb__after_clear_bit();
//設備還在接收輸入流量
while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) {
//調度當前線程等待
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
//驅動提供了停止函數,則調用
if (dev->stop)
dev->stop(dev);
//清除IFF_UP標誌,表示設備已被關閉
dev->flags &= ~IFF_UP;
//通知netdev_chain已關閉消息
notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
return 0;
}
// 網絡設備的看門狗:
// 1.當網絡設備由於被開啟而激活傳輸時,同時激活設備驅動程序的開門狗機製。
// 2.看門狗的定時周期由驅動程序通過dev->watchdog_time指定
// 3.看門狗的定時器由驅動程序通過dev->watchdog_timer提供
// 調用路徑:dev_activate->dev_watchdog_up
3.1 static void dev_watchdog_up(struct net_device *dev)
{
//在獲取dev->xmit_lock傳輸鎖時設置看門狗定時器
spin_lock_bh(&dev->xmit_lock);
__netdev_watchdog_up(dev);
spin_unlock_bh(&dev->xmit_lock);
}
// 調用路徑dev_activate->dev_watchdog_up->__netdev_watchdog_up
void __netdev_watchdog_up(struct net_device *dev)
{
if (dev->tx_timeout) {
if (dev->watchdog_timeo <= 0)//設備驅動沒有設置看門狗的到期時間
dev->watchdog_timeo = 5*HZ;
if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo))
dev_hold(dev);
}
}
最後更新:2017-04-03 16:49:08