文中涉及的NAPI的接口及相关数据结构请参考
Linux网络子系统中报文的接收及NAPI的实现
linux旧的收包方式提供给驱动的接口netif_rx()
int
netif_rx(struct sk_buff *skb)
{
struct softnet_data *queue;
unsigned long flags;
/*如果接收skb的时间戳没设定,设定接收时间戳*/
if (!skb->tstamp.tv64)
{
net_timestamp(skb);
}
/*禁止本地cpu的中断*/
local_irq_save(flags);
/*取得本地cpu的softnet_data*/
queue = &__get_cpu_var(softnet_data);
/*每个CPU都有一个统计数据,增加统计数据*/
__get_cpu_var(netdev_rx_stat).total++;
/*如果本地CPU的输入队列中的skb 个数小于允许的最多的个数*/
if (queue->input_pkt_queue.qlen <= netdev_max_backlog)
{
/*如果本地cpu的输入队列长度不为0,表示输入队列已经有skb了,
并且特殊的napi backlog 已经挂入了softnet_data 的
pool_list上了*/
if (queue->input_pkt_queue.qlen)
{
enqueue:
/*把skb 放入CPU的输入队列 input_pkt_queue*/
__skb_queue_tail(&queue->input_pkt_queue, skb);
/*使能中断 并 返回*/
local_irq_restore(flags);
return NET_RX_SUCCESS;
}
/*如果输入队列为空,则把 特殊的napi backlog 挂到softnet_data
的 pool_list 上 并返回把skb放入输入队列并返回*/
napi_schedule(&queue->
backlog);
goto enqueue;
}
/*如果本地cpu的输入队列已经满了,则丢弃报文,
并增加丢包计数并返回*/
__get_cpu_var(netdev_rx_stat).dropped++;
local_irq_restore(flags);
kfree_skb(skb);
return NET_RX_DROP;
}
上篇博文里我们已经分析过,Linux网络子系统中管理的收包队列由一个虚拟的NAPI backlog来进行处理。
驱动调用netif_rx把报文放入队列,把虚拟的NAPI backlog加入到pool链表上,调度收包软中断。收包软中断会调用backlog的轮询
函数对队列中的报文进行处理。
该虚拟的NAPI backlog的轮询函数在函数
net_dev_init中被初始化为process_backlog
我们看一下NAPI backlog的轮询函数
process_backlog的实现
参数:
napi : 本地cpu上softnet_data 的backlog .
quota : 一次轮询可以处理的最多报文数。
函数详解:
static int
process_backlog(struct napi_struct *napi, int quota)
{
int work = 0;
/*取得本地CPU上的softnet_data 数据*/
struct softnet_data *queue = &__get_cpu_var(softnet_data);
/*开始计时,一旦允许时间到,就退出轮询*/
unsigned long start_time = jiffies;
napi->weight = weight_p;
/
*循环从softnet_data 的输入队列取报文并处理,直到队列中没有报文了,
或处理的报文数大于了允许的上限值了,
或轮询函数执行时间大于一个jiffies 了
*/
do
{
struct sk_buff *skb;
/*禁用本地中断,要存队列中取skb,防止抢占*/
local_irq_disable();
/*从softnet_data 的输入队列中取得一个skb*/
skb = __skb_dequeue(&queue->input_pkt_queue);
/*如果队列中没有skb,则使能中断并退出轮询*/
if (!skb)
{
/*把napi 从 softnet_data 的 pool_list 链表上摘除*/
__napi_complete(napi);
/*使能本地CPU的中断*/
local_irq_enable();
break;
}
/*skb 已经摘下来了,使能中断*/
local_irq_enable();
/*把skb送到协议栈相关协议模块进行处理,详细处理见后续章节*/
netif_receive_skb(skb);
} while (++work < quota && jiffies == start_time);
/*返回处理报文个数*/
return work;
}
阅读(3707) | 评论(0) | 转发(2) |