Chinaunix首页 | 论坛 | 博客
  • 博客访问: 282805
  • 博文数量: 72
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 276
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-28 23:52
文章分类

全部博文(72)

文章存档

2017年(20)

2014年(52)

分类: LINUX

2014-10-16 16:59:07

文中涉及的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;
}



阅读(963) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~