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


網絡子係統7_l2、l3接口

//	調用路徑:
//		1.NAPI設備的poll函數->netif_receive_skb
//		2.積壓設備的process_backlog函數->netif_receive_skb
// 	參數:
//		skb,驅動程序已經去掉了以太幀最後的四字節crc32,skb->mac.raw指向mac頭,skb->data,skb->nh指向l3幀頭

//	函數主要任務:
//		1.處理netpoll銜接點
//		2.更新時間戳,dev的統計信息
//		3.處理bonding銜接點
//		4.向ETH_P_ALL類型的l3協議傳遞skb
//		5.處理bridge銜接點
//		6.通過數據幀標示的l3協議,傳遞skb到正確的l3協議
1.1 int netif_receive_skb(struct sk_buff *skb)
{
	struct packet_type *ptype, *pt_prev;
	int ret = NET_RX_DROP;
	unsigned short type;

#ifdef CONFIG_NETPOLL
	//netpoll機製 
	if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) {
		//返回1,說明netpoll處理了這個skb,則釋放skb,並返回
		kfree_skb(skb);
		return NET_RX_DROP;
	}
#endif
	//設置skb的時間戳
	if (!skb->stamp.tv_sec)
		net_timestamp(&skb->stamp);
	//處理bonding
	//在3.x的版本中,虛擬dev會注冊一個handler,處理bonding
	skb_bond(skb);
	//更新本cpu接收到的數據包個數
	__get_cpu_var(netdev_rx_stat).total++;

	//將l4,l3報頭對應的指針,指向skb->data
	skb->h.raw = skb->nh.raw = skb->data;
	skb->mac_len = skb->nh.raw - skb->mac.raw;

	pt_prev = NULL;

	rcu_read_lock();

	...
	//向所有ETH_P_ALL類型的l3協議,傳遞一份skb
	list_for_each_entry_rcu(ptype, &ptype_all, list) {
		//如果對應的l3協議沒有指定dev,或者l3指定的dev就是接收到本skb的dev
		if (!ptype->dev || ptype->dev == skb->dev) {
			if (pt_prev) 
				//向對應l3傳遞skb
				ret = deliver_skb(skb, pt_prev);
			pt_prev = ptype;
		}
	}


	//處理分流器
	handle_diverter(skb);
	//處理橋接
	if (handle_bridge(&skb, &pt_prev, &ret))
		goto out;
	//獲取skb的l3協議
	type = skb->protocol;
	//hash到l3協議對應的鏈表,會存在協議號相同的l3協議,向所有協議號相同的處理函數傳遞一份skb。
	list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
		if (ptype->type == type &&
		    (!ptype->dev || ptype->dev == skb->dev)) {
			if (pt_prev) 
				//將skb傳遞給對應的l3協議
				ret = deliver_skb(skb, pt_prev);
			pt_prev = ptype;
		}
	}

	//處理遍曆過程中最後一個l3協議
	if (pt_prev) {
		ret = pt_prev->func(skb, skb->dev, pt_prev);
	} else {
		kfree_skb(skb);
		ret = NET_RX_DROP;
	}

out:
	rcu_read_unlock();
	return ret;
}


//	調用路徑:
//		1.鄰居子係統鄰居項output->dev_queue_xmit
//		2.l2幀頭緩存output->dev_queue_xmit
//		3.其他l3協議

//	參數:
//		skb,已經填充好mac頭,l3,l4協議頭,skb->data指向l2幀頭
//	函數主要任務:
//		1.處理skb與驅動能力不匹配的情況
//			1.1 skb->frag_list分片,驅動不能處理frag_list,
//			1.2 skb->frags分散聚集內存,驅動不能處理分散內存
//			1.3 重新分配sk_buff,拷貝所有數據到sk->data中
//		2.處理上層協議與驅動能力不匹配的情況
//			2.1 上層要求硬件計算校驗和,驅動不能提供此能力
//			2.2 由軟件計算校驗和
//		3.關中斷,禁止下半部
//		4.傳輸
//			4.1 驅動程序使用隊列規則 
//				4.1.1 獲取dev->queue_lock	
//				4.1.2 入隊skb
//				4.1.3 通過qdisc_run啟動隊列進行傳輸
//				4.1.4 釋放dev->queue_lock
//			4.2 驅動程序沒有使用隊列規則
//				4.2.1 獲取xmit_lock鎖
//				4.2.2 通過hard_start_xmit進行傳輸
//				4.2.3 釋放xmit_lock鎖

2.1 int dev_queue_xmit(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	struct Qdisc *q;
	int rc = -ENOMEM;
	//1.如果,skb被分片,設備不能處理skb->frag_list,重新分配sk_buff,
	//將skb_shinfo(skb)->frag_list中的數據拷貝到新skb->data
	if (skb_shinfo(skb)->frag_list &&
	    !(dev->features & NETIF_F_FRAGLIST) &&
	    __skb_linearize(skb, GFP_ATOMIC))
		goto out_kfree_skb;

	//2.如果,skb使用分散聚集內存,設備不支持分散聚集IO,或者skb處於高端dma內存,
	//重新分配skb_buff,將skb_shinfo(skb)->frags中的數據拷貝到skb->data
	if (skb_shinfo(skb)->nr_frags &&
	    (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
	    __skb_linearize(skb, GFP_ATOMIC))
		goto out_kfree_skb;

	//在1,2中,隻要有一個成立,就會通過__skb_linearize將skb_shinfo(skb)->frags, 
	//skb_shinfo(skb)->frag_list中的數據拷貝新分配的sk_buff

	//3.l4協議要求使用硬件計算校驗和,但是設備驅動沒有提供校驗和功能,
	//通過軟件方式完成校驗和
	if (skb->ip_summed == CHECKSUM_HW &&
	    (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
	     (!(dev->features & NETIF_F_IP_CSUM) ||
	      skb->protocol != htons(ETH_P_IP))))
	      	if (skb_checksum_help(skb, 0))
	      		goto out_kfree_skb;

	//1.設備驅動使用規則隊列,通過隊列規則完成數據傳輸
	//關軟中斷,獲取dev的隊列規則
	local_bh_disable(); 
	q = rcu_dereference(dev->qdisc);
	//隊列規則具有入隊功能
	if (q->enqueue) {
		//獲取隊列鎖
		spin_lock(&dev->queue_lock);
		rc = q->enqueue(skb, q);
		//在獲取設備隊列鎖之後,啟動規則隊列的數據傳輸
		qdisc_run(dev);

		spin_unlock(&dev->queue_lock);
		rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
		goto out;
	}

	//設備沒有使用隊列規則,直接通過驅動程序完成傳輸
	if (dev->flags & IFF_UP) {
		int cpu = smp_processor_id(); 
		if (dev->xmit_lock_owner != cpu) {
			//獲取xmit_lock
			HARD_TX_LOCK(dev, cpu);
			//如果設備傳輸隊列沒有被關閉
			if (!netif_queue_stopped(dev)) {
				//向ETH_P_ALL類型的l3協議傳遞一份skb
				if (netdev_nit)//netdev_nit表示ETH_P_ALL類型l3協議的個數
					dev_queue_xmit_nit(skb, dev);

				rc = 0;
				//通過驅動程序完成傳輸
				if (!dev->hard_start_xmit(skb, dev)) {
					HARD_TX_UNLOCK(dev);
					goto out;
				}
			}
			//對xmit_lock解鎖
			HARD_TX_UNLOCK(dev);
			if (net_ratelimit())
				printk(KERN_CRIT "Virtual device %s asks to "
				       "queue packet!\n", dev->name);
		} else {

			if (net_ratelimit())
				printk(KERN_CRIT "Dead loop on virtual device "
				       "%s, fix it urgently!\n", dev->name);
		}
	}

	rc = -ENETDOWN;
	local_bh_enable();

out_kfree_skb:
	kfree_skb(skb);
	return rc;
out:
	local_bh_enable();
	return rc;
}

最後更新:2017-04-03 15:22:03

  上一篇:go Hive on CDH4部署、調錯及測試
  下一篇:go 網絡子係統15_arp鄰居項初始化