网络子系统8_netpoll机制

//	1.netpoll的作用:
//		用于让内核在网络和I/O子系统尚不能完整可用时,依然能发送和接收数据包,主要用于网络控制台和远程。

//	2.netpoll机制需要驱动程序的支持:
//		使外部通过软件方式调用驱动程序的中断处理程序。
//		大部分poll_controller定义如下:
//			void this_controller(struct net_device *dev)
//			{
//				disable_dev_interrupt(dev);
//				call_interrupt_handler(dev->irq, dev);
//				enable_device_interrupt(dev);
//			}

//	3.netpoll运行条件:
//		3.1 以太网介质
//		3.2 本机l2地址,或者l2广播地址
//		3.3 l3协议为ip协议
//		3.4 ip数据包没有分片,且有效
//		3.5 l4协议为udp协议,且有效

//	4.netpoll在协议栈中的切入点为netif_receive_skb

//	5.static atomic_t trapped;
//	trapped用于指示在netpoll_rx执行之后,netif_receive_skb是否丢弃该封包。

//	函数任务:
//		1. 如果入口封包为arp封包,且开启了trapped模式,则处理入口arp,并返回。
//		2. 检查入口skb是否可以被处理。
//		3. 在关中断的情况下,遍历已经注册的netpoll控制块,向其传递skb。
//	调用路径:netif_receive_skb->netpoll_rx
1.1 int netpoll_rx(struct sk_buff *skb)
{
	int proto, len, ulen;
	struct iphdr *iph;//ip头
	struct udphdr *uh;//udp头
	struct netpoll *np;//netpoll描述符
	struct list_head *p;
	unsigned long flags;
	//netpoll只适用以太网设备
	if (skb->dev->type != ARPHRD_ETHER)
		goto out;

	//trapped被设置,则由netpoll机制处理入口arp
	if (skb->protocol == __constant_htons(ETH_P_ARP) &&
	    atomic_read(&trapped)) {
		arp_reply(skb);
		return 1;
	}

	proto = ntohs(eth_hdr(skb)->h_proto);
	//l3协议需要是ip协议
	if (proto != ETH_P_IP)
		goto out;

	//应该为本机l2地址,或广播地址
	if (skb->pkt_type == PACKET_OTHERHOST)
		goto out;

	//skb没有被其他部分引用
	if (skb_shared(skb))
		goto out;

	iph = (struct iphdr *)skb->data;
	//skb->data - tail之间满足20字节的ip头
	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto out;

	//skb->data - tail之间满足完整的ip头
	if (iph->ihl < 5 || iph->version != 4)
		goto out;
	if (!pskb_may_pull(skb, iph->ihl*4))
		goto out;

	//检查ip报头校验和
	if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
		goto out;

	//检查ip数据包是否完整
	len = ntohs(iph->tot_len);
	if (skb->len < len || len < iph->ihl*4)
		goto out;

	//l4协议需要时udp协议
	if (iph->protocol != IPPROTO_UDP)
		goto out;

	//跨越ip报头以及ip选项
	len -= iph->ihl*4;

	//udp头部
	uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
	ulen = ntohs(uh->len);
	//udp报头中指定长度与实际长度不等
	if (ulen != len)
		goto out;
	//计算udp校验和
	if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr) < 0)
		goto out;

	//关中断的情况下,向控制块传递skb
	spin_lock_irqsave(&rx_list_lock, flags);
	list_for_each(p, &rx_list) {
		//遍历已经注册的netpoll控制块
		np = list_entry(p, struct netpoll, rx_list);
		//比较匹配条件
		if (np->dev && np->dev != skb->dev)
			continue;
		if (np->local_ip && np->local_ip != ntohl(iph->daddr))
			continue;
		if (np->remote_ip && np->remote_ip != ntohl(iph->saddr))
			continue;
		if (np->local_port && np->local_port != ntohs(uh->dest))
			continue;

		spin_unlock_irqrestore(&rx_list_lock, flags);
		//调用控制块的回调函数
		if (np->rx_hook)
			np->rx_hook(np, ntohs(uh->source),
				    (char *)(uh+1),
				    ulen - sizeof(struct udphdr));

		return 1;
	}
	spin_unlock_irqrestore(&rx_list_lock, flags);

out:
	return atomic_read(&trapped);//如果trapped非零,则netif_receive_skb会在netpoll_rx返回后,直接释放skb,跳过后续的执行。
}

//	netpoll使用专用的skb缓存,对入口arp响应。
//	在缓存链表中,获取一个空闲的skb
//	函数主要任务:
//		1.为skb缓存链表补充skb
//		2.如果缓存链表有空闲skb,则更新缓存的数量,返回skb
//		3.如果无法获取空闲的skb
//			3.1 调用netpoll_poll,加快网卡设备的数据接收,希望释放空闲skb
//		4.重复3,直到有空闲skb
//	调用路径:arp_reply->find_skb
2.1 static struct sk_buff * find_skb(struct netpoll *np, int len, int reserve)
{
	int once = 1, count = 0;
	unsigned long flags;
	struct sk_buff *skb = NULL;

	zap_completion_queue();
repeat:
	//skb缓存不足
	if (nr_skbs < MAX_SKBS)
		refill_skbs();
	//分配skb
	skb = alloc_skb(len, GFP_ATOMIC);

	//分配skb失败
	if (!skb) {
		//关中断
		spin_lock_irqsave(&skb_list_lock, flags);
		//从skb缓存链表取一个skb
		skb = skbs;
		//如果获取skb缓存成功
		if (skb)
			skbs = skb->next;//更新skb缓存链表
		skb->next = NULL;
		nr_skbs--;//递减计数器
		spin_unlock_irqrestore(&skb_list_lock, flags);
	}
	//如果获取skb依然失败
	if(!skb) {
		//递增失败次数
		count++;
		//失败次数已经达到最大
		if (once && (count == 1000000)) {
			printk("out of netpoll skbs!\n");
			once = 0;
		}
		//加快网卡设备上的数据接收,以此来释放更多的空闲skb
		netpoll_poll(np);
		goto repeat;
	}
	//设置skb使用者的个数
	atomic_set(&skb->users, 1);
	//预留skb头空间
	skb_reserve(skb, reserve);
	return skb;
}

//	补充空闲skb后备链表
//	调用路径arp_reply->find_skb->refill_skbs
2.3 static void refill_skbs(void)
{
	struct sk_buff *skb;
	unsigned long flags;
	//关中断,获取skb_list_lock
	spin_lock_irqsave(&skb_list_lock, flags);
	//当前可用skb的个数小于最大的skb数
	while (nr_skbs < MAX_SKBS) {
		//分配skb
		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
		if (!skb)
			break;
		//将skb添加到链表
		skb->next = skbs;
		skbs = skb;
		nr_skbs++;//递增skb个数计数器
	}
	spin_unlock_irqrestore(&skb_list_lock, flags);
}

//	加快dev上的数据接收
//	调用路径arp_reply->find_skb->netpoll_poll
2.4 void netpoll_poll(struct netpoll *np)
{
	//netpoll没有指定设备,或者设备已经停止,或者设备没有提供poll控制器,则返回
	if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller)
		return;
	//软件方式调用设备中断处理例程
	np->dev->poll_controller(np->dev);
	//调度设备napi,完成数据接收
	if (np->dev->poll)
		poll_napi(np);

	zap_completion_queue();
}

//	poll_napi调用驱动程序的poll函数,模拟net_rx_action
//	函数主要任务:
//		1.获取该cpu的softnet_data
//		2.通过softnet_data->poll_list获取有入口数据的dev
//		3.通过dev->poll接收数据
//	调用路径arp_reply->find_skb->netpoll_poll->poll_napi
2.5 static void poll_napi(struct netpoll *np)
{
	int budget = 16;
	unsigned long flags;
	struct softnet_data *queue;
	//关中断,获取锁
	spin_lock_irqsave(&netpoll_poll_lock, flags);
	queue = &__get_cpu_var(softnet_data);//获取per-cpu变量
	if (test_bit(__LINK_STATE_RX_SCHED, &np->dev->state) &&//当前设备被调度,说明有数据等待接收
	    !list_empty(&queue->poll_list)) {//本cpu的接收队列上有等待poll的设备
		np->dev->netpoll_rx |= NETPOLL_RX_DROP;//
		atomic_inc(&trapped);//递增trapped,使驱动程序的poll->netif_receive_skb->netpoll_rx之后,netif_receive_skb直接丢弃skb。
		np->dev->poll(np->dev, &budget);//驱动程序提供的poll函数
		atomic_dec(&trapped);
		np->dev->netpoll_rx &= ~NETPOLL_RX_DROP;
	}
	spin_unlock_irqrestore(&netpoll_poll_lock, flags);
}
时间: 2024-12-30 13:05:56

网络子系统8_netpoll机制的相关文章

Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理子系统.网络子系统.虚拟文件系统在这个阶段进行分析的,但是为了让大家对内核有个整体的把握,今天还是简单的介绍一下剩余的几个子系统,我们对这几个子系统的分析,只要了解其作用和部分内容即可,不必深究,等我们写上几个驱动,到时候按照驱动再来分析这几个子系统我们就清晰多了. 在http://www.cnbl

利用CISCO路由器建立企业网络的安全机制

近几年来,计算机网络,特别是互联网的发展非常迅速,ATM以及IP OVER DWDM技术的发展使网络传输的带宽快速增加.而网络安全和管理技术的发展却相对滞后,网络安全的呼声虽高,但真正落到实际的确少之又少.人们往往认为增加企业网络的安全机制需要增加防火墙.认证服务器等设备,但是价格不菲的安全设备,却增加了许多小型企业的生产成本,而事实上,我国广泛使用的CISCO路由器中内嵌IOS中的安全机制以足以满足一般企业互联的需求. 从传统的观点看,路由器的主要功能是将数据报文从一个网络传输到另一个网络.路

关于发布非可控性炎症恶性转化的调控网络及其分子机制重大研究计划集成项目指南的通告

国家自然科学基金重大研究计划遵循"有限目标.稳定支持.集成升华.跨越发展"的总体思路,围绕国民经济.社会发展和科学前沿中的重大战略需求,重点支持我国具有基础和优势的优先发展领域.重大研究计划以专家顶层设计引导和科技人员自由选题申请相结合的方式,凝聚优势力量,形成具有相对统一目标或方向的项目群,通过相对稳定和较高强度的支持,积极促进学科交叉,培养创新人才,实现若干重点领域或重要方向的跨越发展,提升我国基础研究创新能力,为国民经济和社会发展提供科学支撑. 国家自然科学基金委员会(以下简称自

三中全会决定:健全网络突发事件处置机制

摘要: 中共十八届三中全会审议通过的<中共中央关于全面深化改革若干重大问题的决定>今日全文播发.<决定>指出,健全基础管理.内容管理.行业管理以及网络违法犯罪防范和打击等 中共十八届三中全会审议通过的<中共中央关于全面深化改革若干重大问题的决定>今日全文播发.<决定>指出,健全基础管理.内容管理.行业管理以及网络违法犯罪防范和打击等工作联动机制,健全网络突发事件处置机制,形成正面引导和依法管理相结合的网络舆论工作格局. <决定>提出,完善文化管理

网络子系统14_邻居子系统通用接口

//创建一个新的邻居项 //参考 深入理解linux网络技术内幕 // 1.邻居子系统为具体的邻居协议,提供通用的功能接口 // 2.系统中所有的邻居协议被链接在neigh_tables链表中 // 3.neigh_table代表一个具体的邻居协议 // 4.具体邻居协议在运行时的行为,可以通过struct neigh_parms调节, // neigh_params与设备关联,每个邻居协议neigh_table提供一个默认的neigh_params. //注册一个邻居协议到系统中 // 1.与

网络子系统63_路由子系统处理netlink事件

// 路由子系统netlink控制块 // 在ip_rt_init->devinet_init中注册. 1.1 static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { [4] = { .doit = inet_rtm_newaddr, }, [5] = { .doit = inet_rtm_deladdr, }, [6] = { .dumpit = inet_dump_ifaddr, }, [8]

网络子系统70_路由缓存操作

// 刷新路由缓存 // 参数: // delay, 刷新操作的延迟时间 // 函数主要任务: // 1.重新计算路由刷新定时器的到期时间 // 2.如果delay=0,则立即刷新缓存 // 3.激活定时器,使用最近的刷新延迟作为到期时间 1.1 void rt_cache_flush(int delay) { unsigned long now = jiffies; //用户态 int user_mode = !in_softirq(); if (delay < 0) delay = ip_r

网络子系统71_路由缓存垃圾回收

// 同步回收机制 // 返回值: // 0,达到回收目标 // 1,没有达到回收目标 // 注:ip_rt_gc_min_interval = HZ/2 // ip_rt_gc_timeout = RT_GC_TIMEOUT = 300HZ // ip_rt_gc_elasticity = 8*(rt_hash_mask+1)(正常情况下) // ip_rt_gc_elasticity = 1(当绑定缓存到邻居子系统失败时) // 平衡点,gc目标数的动态调整: // 1.当前缓存个数 < 安

网络子系统2_设备子系统相关的初始化

//在socket文件系统注册之后,设备驱动程序注册之前被调用 //start_kernel->rest_init->kernel_thread->init->do_basic_setup->do_initcalls->net_dev_init 2.1 static int __init net_dev_init(void) { ... //proc文件系统中相关项的初始化 if (dev_proc_init()) goto out; //注册网络设备类 if (net