net_rx_action()

net_rx_action()

 

/*
net_rx_action()会从两个地方取帧数据:
(1)非NAPI设备的中断处理流程把帧放入本地cpu私有数softnet_data 的接收队列中
(2)调用NAPI设备驱动的poll方法会直接从设备内存(或设备驱动程序接收环)中取出帧
*/
static void net_rx_action(struct softirq_action *h)
{
   /*取得本地cpu 的softnet_data 的poll_list  链表
     在里面放着所有需要轮询接收数据包的设备
   */
	struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
   
	 /*设置软中断处理程序一次允许的最大执行时间为2个jiffies*/
	unsigned long time_limit = jiffies + 2;
	 
	int budget = netdev_budget;//轮询配额 当数据包数超过此值 进行轮询接收 此值初始为300
	void *have;

	//禁止本地cpu中断
	local_irq_disable();

   /*
    list的更新:
    (1)在执行具体的poll函数中若此时的设备已经没有数据则poll中会调用__napi_complete()从链表删除此设备list得到更新
    (2)在执行具体的poll函数中若处理的数据超过了设备的权重weight 则在while的后面在此设备没有被禁止的情下将此设备放到链表的尾部list得到更新
   */
	while (!list_empty(list)) 
	{  
		struct napi_struct *n;
		int work, weight;

		 //限制轮询处理包个数以及处理的时间
		if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
			goto softnet_break;
		
		//开启本地cpu中断
		local_irq_enable();

	    //取得等待轮询设备的结构
		n = list_entry(list->next, struct napi_struct, poll_list);

		have = netpoll_poll_lock(n);

		//获得权重 处理此设备n的最大帧数
		weight = n->weight;

		/* This NAPI_STATE_SCHED test is for avoiding a race
		 * with netpoll's poll_napi().  Only the entity which
		 * obtains the lock and sees NAPI_STATE_SCHED set will
		 * actually make the ->poll() call.  Therefore we avoid
		 * accidently calling ->poll() when NAPI is not scheduled.
		 */
		work = 0;
		if (test_bit(NAPI_STATE_SCHED, &n->state)) 
		{
		    //对于NAPI设备如e1000驱动此处为poll为e1000_clean() 进行轮询处理数据
		    //对于非NAPI设备此处poll为process_backlog() 看net_dev_init()中的初始化
			work = n->poll(n, weight);
			trace_napi_poll(n);
		}

		//返回值work需要小于等于weight  work返回的是此设备处理的帧个数不能超过weight
 		WARN_ON_ONCE(work > weight);

		budget -= work;

        //禁止本地中断
		local_irq_disable();

		/* Drivers must not modify the NAPI state if they
		 * consume the entire weight.  In such cases this code
		 * still "owns" the NAPI instance and therefore can
		 * move the instance around on the list at-will.
		 */ 
		//消耗完了此设备的权重
		if (unlikely(work == weight)) 
		{   
		    //若此时设备被禁止了  则从链表删除此设备
			if (unlikely(napi_disable_pending(n)))
			{
			    //开启本地cpu中断
				local_irq_enable();
				napi_complete(n);
				//关闭本地cpu中断
				local_irq_disable();
			} 
			else//否则将设备放到链表的最后面
				list_move_tail(&n->poll_list, list);
		}

		netpoll_poll_unlock(have);
	}
out:
	//开启本地cpu中断 
	local_irq_enable();

#ifdef CONFIG_NET_DMA
	/*
	 * There may not be any more sk_buffs coming right now, so push
	 * any pending DMA copies to hardware
	 */
	dma_issue_pending_all();
#endif

	return;

softnet_break:
	__get_cpu_var(netdev_rx_stat).time_squeeze++;
	
	 //在这里触发下一次软中断处理
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
	goto out;
}