阅读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邻居项初始化