網絡子係統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