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;
}