网络子系统20_传输接收软中断

  1. 云栖社区>
  2. 博客>
  3. 正文

网络子系统20_传输接收软中断

亦侠 2013-09-30 20:20:00 浏览715
展开阅读全文
//	数据帧接收软中断处理函数
//	调用路径:do_softirq->net_rx_action
//	函数主要任务:
//		1.在关中断情况下检查调度队列poll_list是否有设备接收到入口帧,然后开中断
//		2.单次运行处理的入口帧不能超过netdev_max_backlog=300,运行时间不能超过1个jiffies
//		3.依次遍历调度队列poll_list
//		4.如果设备配额以用完,或者设备驱动的poll函数表明仍然有入口数据帧
//			4.1 将设备重新添加poll_list尾部,重新分配配额,准备接受下一次调度
//		5.如果单次运行没有调度完所有接收到入口帧的设备,则再次设置接受软中断调度标志,退出

//	注:接收软中断中,poll_list上挂载的npai设备,或者非napi设备使用的积压设备。
3.2 static void net_rx_action(struct softirq_action *h)
{
	struct softnet_data *queue = &__get_cpu_var(softnet_data);
	unsigned long start_time = jiffies;
	int budget = netdev_max_backlog;

	//关本地中断
	local_irq_disable();

	//有设备有输入流量
	while (!list_empty(&queue->poll_list)) {
		struct net_device *dev;

		//执行时间超过了一个1 jiffies
		//或者本次软中断处理的输入流量已经超过了netdev_max_backlog,退出软中
		if (budget <= 0 || jiffies - start_time > 1)
			goto softnet_break;

		local_irq_enable();
		//获得设备指针
		dev = list_entry(queue->poll_list.next,
				 struct net_device, poll_list);

		//如果设备的配额已经用完,或者执行一次poll之后,此设备依然有待处理的流量
		if (dev->quota <= 0 || dev->poll(dev, &budget)) {
			local_irq_disable();
			//将设备从list上删除,添加到list尾部
			list_del(&dev->poll_list);
			list_add_tail(&dev->poll_list, &queue->poll_list);
			//给设备重新分配配额,已weight作为一次分配的量,范围从1024~64
			if (dev->quota < 0)
				dev->quota += dev->weight;
			else
				dev->quota = dev->weight;
		} else {
			dev_put(dev);
			local_irq_disable();
		}
	}
out:
	local_irq_enable();
	return;

softnet_break:
	__get_cpu_var(netdev_rx_stat).time_squeeze++;
	//重新调度接收软中断
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
	goto out;
}


//	发送软中断处理函数
//		一次处理output_queue中所有等待传输的设备
//	调用路径:do_softirq->net_tx_action

//	函数主要功能:
//		1.释放本cpu所有已经完成传输的skb
//		2.关中断的情况获取本cpu的传输设备队列output_queue, 其中的设备有数据需要进行传输
//		3.清除设备传输调度标志,表明设备可以接收下一次传输调度
//		4.获取设备队列锁queue_lock,通过队列规则接口,进行设备数据传输
//		5.如果获取queue_lock失败,说明设备当前正在进行传输,重调度设备

//	注:发送软中断中,output_queue挂载的设备均为有队列规则设备,没有队列规则的设备不会在软中断进行传输,因为其没有传输调度的概念
3.3 static void net_tx_action(struct softirq_action *h)
{
	struct softnet_data *sd = &__get_cpu_var(softnet_data);
	//如果完成队列非空
	if (sd->completion_queue) {
		struct sk_buff *clist;

		//关本cpu中断
		local_irq_disable();
		//取下本cpu的完成队列
		clist = sd->completion_queue;
		sd->completion_queue = NULL;
		local_irq_enable();

		while (clist) {
			struct sk_buff *skb = clist;
			clist = clist->next;
			//释放对应的skb
			__kfree_skb(skb);
		}
	}
	//如果输出队列非空
	if (sd->output_queue) {
		struct net_device *head;
		//将输出队列取下
		local_irq_disable();
		head = sd->output_queue;
		sd->output_queue = NULL;
		local_irq_enable();
		while (head) {
			struct net_device *dev = head;
			//net_device 通过next_sched链接到output_queue上
			head = head->next_sched;
			//一个内存屏障
			smp_mb__before_clear_bit();
			//设备准备好进行传输,清除设备调度标志,说明,当设备执行传输时,属于非调度状态,可以被重新调度。
			clear_bit(__LINK_STATE_SCHED, &dev->state);

			//获取设备的输出队列锁
			if (spin_trylock(&dev->queue_lock)) {
				//通过出口队列规则,调度设备
				qdisc_run(dev);
				spin_unlock(&dev->queue_lock);
			} else {
				//说明队列正在进行传输,重新调度设备
				netif_schedule(dev);
			}
		}
	}
}

网友评论

登录后评论
0/500
评论
亦侠
+ 关注