閱讀555 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go 網絡子係統32_網橋設備的開啟與關閉
  下一篇:go 生產者消費者問題-轉自維基百科