Chinaunix首页 | 论坛 | 博客
  • 博客访问: 940689
  • 博文数量: 55
  • 博客积分: 400
  • 博客等级: 一等列兵
  • 技术积分: 2779
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-30 13:08
个人简介

一个人的差异在于业余时间

文章分类

全部博文(55)

文章存档

2019年(1)

2018年(1)

2017年(1)

2016年(2)

2015年(20)

2014年(10)

2013年(7)

2012年(12)

2011年(1)

分类: 嵌入式

2012-11-25 22:22:35

    虽然以前做网卡和swi芯片驱动,但是也仅限于注册中断而已,大部分还是参考别人的框架,那时候是tasklet机制,开始接触和了解NAPI要从调试网络性能说起,现在项目用的内核比较新是2.6.32的,而我这里要分析是3.1.1的大同小异.下面说一下NAPI需要的条件:
    驱动可以继续使用老的 2.4 内核的网络驱动程序接口,NAPI 的加入并不会导致向前兼容性的丧失,但是 NAPI 的使用至少要得到下面的保证:

A. 要使用 DMA 的环形输入队列(也就是 ring_dma,这个在 2.4 驱动中关于 Ethernet 的部分有详细的介绍),或者是有足够的内存空间缓存驱动获得的包。

B. 在发送/接收数据包产生中断的时候有能力关断 NIC 中断的事件处理,并且在关断 NIC 以后,并不影响数据包接收到网络设备的环形缓冲区(以下简称 rx-ring)处理队列中。

    这里本人比较懒,没有打字,可以参考《深入理解linux网络内幕》章节. 而网上搜集的资料大部分也都来自一篇ibm工作人员的文档,但是我觉得写的还行,我这里主要说流程和框架,而不在于某个细节的具体优化工作. 下面就贴出来基本框架图:

   

这里首先看中断例程里主要做了什么工作,这里我随意在源码里找了一个例子:

/*

 * rx/tx dma interrupt handler

 */

static irqreturn_t XXX_isr(int irq, void *dev_id)

{

struct net_device *dev;

struct bcm_enet_priv *priv;


dev = dev_id;

priv = netdev_priv(dev);

        /***/

       .......


napi_schedule(&priv->napi);


return IRQ_HANDLED;

}

注册中断例程:

ret = request_irq(priv->irq_rx, bcm_enet_isr_dma,XXX,  dev->name, dev);

这里我们看napi_schedule()具体做了什么工作:

/**

 * napi_schedule - schedule NAPI poll

 * @n: napi context

 *

 * Schedule NAPI poll routine to be called if it is not already

 * running.

 */

static inline void napi_schedule(struct napi_struct *n)

{

if (napi_schedule_prep(n))

__napi_schedule(n);

}

最后展开是调用了它:

/* Called with irq disabled */

static inline void ____napi_schedule(struct softnet_data *sd,

     struct napi_struct *napi)

{

list_add_tail(&napi->poll_list, &sd->poll_list);

__raise_softirq_irqoff(NET_RX_SOFTIRQ);

}

它的作用就是网卡的数据链表添加到poll_list里,然后开启软中断.  这里比较有意思的是:
raise_softirq_irqoff(NET_RX_SOFTIRQ);我们具体看一下:

/*
 * This function must run with irqs disabled!
 */
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);

/*
 * If we're in an interrupt or softirq, we're done
 * (this also catches softirq-disabled code). We will
 * actually run the softirq once we return from
 * the irq or softirq.
 *
 * Otherwise we wake up ksoftirqd to make sure we
 * schedule the softirq soon.
 */
if (!in_interrupt())
wakeup_softirqd();
}
/*
 * we cannot loop indefinitely here to avoid userspace starvation,
 * but we also don't want to introduce a worst case 1/HZ latency
 * to the pending events, so lets the scheduler to balance
 * the softirq load for us.
 */
static void wakeup_softirqd(void)
{
/* Interrupts are disabled: no need to stop preemption */
struct task_struct *tsk = __this_cpu_read(ksoftirqd);

if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk);
}
它的工作就是唤醒ksoftirqd内核线程触发do_softirq,然后调用net_rx_action.
这里还需要特别说明的是关于XXX_poll这个函数,作为驱动或内核开发者,可以自己写一个,也可以用系统默认的poll函数,那它就是process_backlog.    
  看net/core/dev.c中我们知道它的初始化时在static int __init net_dev_init(void)中,
  sd->backlog.poll = process_backlog;
sd->backlog.weight = weight_p;
sd->backlog.gro_list = NULL;
sd->backlog.gro_count = 0;
而sd->backlog的调用是在netif_rx中调用enqueue_to_backlog而间接调用. 也就是原来的netif_rx的处理方法和现在NAPI达到了一个和谐的统一,它也会走软中断处理的流程. 如果有自己写的poll函数,一般都会在具体驱动初始化的时候已经对poll赋值,看net_rx_action中对poll的调用:
work = n->poll(n, weight);
接下来的工作就是把skb里数据包传递给上层那么这里需要一个接口函数,那么它就是netif_receive_skb
 

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