NAPI 技术在 Linux 网络驱动上的应用和完善.docx

上传人:b****4 文档编号:3966727 上传时间:2023-05-06 格式:DOCX 页数:22 大小:93.60KB
下载 相关 举报
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第1页
第1页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第2页
第2页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第3页
第3页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第4页
第4页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第5页
第5页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第6页
第6页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第7页
第7页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第8页
第8页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第9页
第9页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第10页
第10页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第11页
第11页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第12页
第12页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第13页
第13页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第14页
第14页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第15页
第15页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第16页
第16页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第17页
第17页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第18页
第18页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第19页
第19页 / 共22页
NAPI 技术在 Linux 网络驱动上的应用和完善.docx_第20页
第20页 / 共22页
亲,该文档总共22页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

NAPI 技术在 Linux 网络驱动上的应用和完善.docx

《NAPI 技术在 Linux 网络驱动上的应用和完善.docx》由会员分享,可在线阅读,更多相关《NAPI 技术在 Linux 网络驱动上的应用和完善.docx(22页珍藏版)》请在冰点文库上搜索。

NAPI 技术在 Linux 网络驱动上的应用和完善.docx

NAPI技术在Linux网络驱动上的应用和完善

NAPI技术在Linux网络驱动上的应用和完善

  前言:

  NAPI是Linux上采纳的一种提高网络处理效率的技术,它的核心概念确实是不采纳中断的方式读取数据,而代之以第一采纳中断唤醒数据接收的服务程序,然后POLL的方法来轮询数据,〔类似于底半〔bottom-half〕处理模式〕;从我们在实验中所得到的数据来看,在随着网络的接收速度的增加,NIC触发的中断能做到不断减少,目前NAPI技术差不多在网卡驱动层和网络层得到了广泛的应用,驱动层次上差不多有E1000系列网卡,RTL8139系列网卡,3c50X系列等主流的网络适配器都采纳了那个技术,而在网络层次上,NAPI技术差不多完全被应用到了闻名的netif_rx函数中间,同时提供了专门的POLL方法--process_backlog来处理轮询的方法;依照实验数据说明采纳NAPI技术能够大大改善短长度数据包接收的效率,减少中断触发的时刻;由于RTL8139CP是一种应用比较广泛的网络适配器,因此本文以其为例,说明了NAPI技术在网络适配器上的应用和差不多原理。

  然而NAPI存在一些比较严峻的缺陷:

而关于上层的应用程序而言,系统不能在每个数据包接收到的时候都能够及时地去处理它,而且随着传输速度增加,累计的数据包将会耗费大量的内存,通过实验说明在Linux平台上那个问题会比在FreeBSD上要严峻一些;另外采纳NAPI所造成的另外一个问题是关于大的数据包处理比较困难,缘故是大的数据包传送到网络层上的时候耗费的时刻比短数据包长专门多〔即使是采纳DMA方式〕,因此正如前面所说的那样,NAPI技术适用于对高速率的短长度数据包的处理,在本文的末尾提出了NAPI的改善方法,和实验数据。

  使用NAPI先决条件:

  驱动能够连续使用老的2.4内核的网络驱动程序接口,NAPI的加入并可不能导致向前兼容性的丧失,然而NAPI的使用至少要得到下面的保证:

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

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

  NAPI对数据包到达的事件的处理采纳轮询方法,在数据包达到的时候,NAPI就会强制执行dev->poll方法。

而和不象往常的驱动那样为了减少包到达时刻的处理延迟,通常采纳中断的方法来进行。

  应当注意的是,通过测试假如DECTulip系列〔DE21x4x芯片〕以及NationalSemi的部分网卡芯片,的测试说明假如把从前中断处理的部分都改换用设备的POLL方法去执行,那么会造成轻微的延迟,因此在进行MII〔介质无关〕的操作上就需要一些小小的诀窍,详见mii_check_media的函数处理流程,本文不做详细讨论。

  在下面显示的例子表示了在8139中如何把处理过程放在dev的poll方法中,把所有的原先中断应该处理的过程放在了POLL方法里面,篇幅起见,我们只介绍接收的POLL方法。

  在下面的8139CP驱动程序介绍中说明了能够把在中断程序中所做的任何情况放在POLL方法中去做,因此不同的NIC在中断中所要处理的状态和事件是不一样的。

  关于所有的NIC设备,以下两种类型的NIC接收事件寄存器响应机制:

  COR机制:

当用户程序读状态/事件寄存器,读完成的时候寄存器和NIC的rx-ring中表示的状态队列将被清零,natsemi和sunbmac的NIC会如此做,在这种情形下,必须把NIC所有往常的中断响应的处理部分都移动到POLL方法中去。

  COW机制:

用户程序写状态寄存器的时候,必须对要写的位先写1清0,如下面要介绍的8139CP确实是如此的类型,大多数的NIC都属于这种类型,而且这种类型对NAPI响应得最好,它只需要把接收的数据包处理部分放置在POLL方法中,而接收事件的状态处理部分放在原先的中断操纵程序中,我们等下将要介绍的8139CP类型网卡确实是属于这种类型。

  C.有防止NIC队列中排队的数据包冲突的能力。

  当关断发送/接收事件中断的时候,NAPI将在POLL中被调用处理,由于POLL方法的时候,NIC中断差不多不能通知包到达,那么那个时候在假如在完成轮询,同时中断打开以后,会赶忙有一个NIC中断产生,从而触发一次POLL事件,这种在中断关断时刻到达的包我们称为"rotting";如此就会在POLL机制和NIC中断之间产生一个竞争,解决的方法确实是利用网卡的接收状态位,连续接收环形队列缓冲rx-ring中的数据,直到没有数据接收以后,才使能中断。

  锁定和防冲突机制:

  -1.SMP的保证机制:

保证同时只有一个处理器调用网络设备的POLL方法,因为我们将在下面看到同时只有一个处理器能够对调用netif_rx_schedule挂在POLL队列中的NIC设备调用POLL方法。

  -2.网络核心层〔netcore〕调用设备驱动程序使用循环方式发送数据包,在设备驱动层接收数据包的时候完全无锁的接收,而网络核心层那么同样要保证每次只有一个处理器能够使用软中断处理接收队列。

  -3.在多个处理器对NIC的rx-ring访问的时刻只能发生在对循环队列调用关闭〔close〕和挂起〔suspend〕方法的时候〔在那个时刻会试图清除接收循环队列〕

  -4.数据同步的问题〔关于接收循环队列来说〕,驱动程序是不需要考虑的网络层上的程序差不多把这些情况做完了。

  -5.假如没有把全部的部分交给POLL方法处理,那么NIC中断仍旧需要使能,接收链路状态发生变化和发送完成中断仍旧和往常的处理步骤一样,如此处理的假设是接收中断是设备负载最大的的情形,因此并不能说如此一定正确。

  下面的部分将详细介绍在接收事件中调用设备的POLL方法。

  

  NAPI提供的重要函数和数据结构和函数:

  核心数据结构:

  structsoftnet_data结构内的字段确实是NIC和网络层之间处理队列,那个结构是全局的,它从NIC中断和POLL方法之间传递数据信息。

其中包含的字段有:

  structsoftnet_data

  {

  intthrottle;/*为1表示当前队列的数据包被禁止*/

  intcng_level;/*表示当前处理器的数据包处理拥塞程度*/

  intavg_blog;/*某个处理器的平均拥塞度*/

  structsk_buff_headinput_pkt_queue;/*接收缓冲区的sk_buff队列*/

  structlist_headpoll_list;/*POLL设备队列头*/

  structnet_deviceoutput_queue;/*网络设备发送队列的队列头*/

  structsk_buffcompletion_queue;/*完成发送的数据包等待开释的队列*/

  structnet_devicebacklog_dev;/*表示当前参与POLL处理的网络设备*/

  };

  核心API:

  1.netif_rx_schedule(dev)

  那个函数被中断服务程序调用,将设备的POLL方法添加到网络层次的POLL处理队列中去,排队同时预备接收数据包,在使用之前需要调用netif_rx_reschedule_prep,同时返回的数为1,同时触发一个NET_RX_SOFTIRQ的软中断通知网络层接收数据包。

  2.netif_rx_schedule_prep(dev)

  确定设备处于运行,而且设备还没有被添加到网络层的POLL处理队列中,在调用netif_rx_schedule之前会调用那个函数。

  3.netif_rx_complete(dev)

  把当前指定的设备从POLL队列中清除,通常被设备的POLL方法调用,注意假如在POLL队列处于工作状态的时候是不能把指定设备清除的,否那么将会出错。

 

  如何在8139CP使用NAPI:

  

  从POLL方法的本质意义上来说就在于尽量减少中断的数目,专门在于大量的小长度的数据包的时候,减少中断,以达到不要让整个操作系统花费太多的时刻在中断现场的爱护和复原上,以便把赢得的时刻用来在我网络层上的处理数据的传输,例如在下面介绍的8139CP中断的处理过程中,目的就在于尽快把产生中断的设备挂在poll_list,同时关闭接收中断,最后直截了当调用设备的POLL方法来处理数据包的接收,直到收到数据包收无可收,或者是达到一个时刻片内的调度完成。

  RTL8139C+的数据接收环形缓冲队列:

  RTL8139C+的接收方式是一种全新的缓冲方式,能显著的降低CPU接收数据造成的花费,适合大型的服务器使用,适合IP,TCP,UDP等多种方式的数据下载,以及连接IEEE802.1P,802.1Q,VLAN等网络形式;在8139CP中分别有64个连续的接收/发送描述符单元,对应三个不同的环形缓冲队列--一个是高优先级传输描述符队列,一个是一般优先级传输符描述队列,一个是接收符描述队列,每个环形缓冲队列右64个4个双字的连续描述符组成,每个描述符有4个连续的双字组成,每个描述符的开始地址在256个字节的位置对齐,接收数据之前,软件需要预先分配一个DMA缓冲区,一样关于传输而言,缓冲区最大为8Kbyte同时把物理地址链接在描述符的DMA地址描述单元,另外还有两个双字的单元表示对应的DMA缓冲区的接收状态。

  在/driver/net/8139CP.C中关于环形缓冲队列描述符的数据单元如下表示:

  structcp_desc{u32opts1;/*缓冲区状态操纵符,包含缓冲区大小,缓冲区传输启动位*/u32opts2;/*专门用于VLAN部分*/u64addr;/*缓冲区的DMA地址*/};

  8139CP的NIC中断:

 

  staticirqreturn_t

  cp_interrupt(intirq,void*dev_instance,structpt_regs*regs)

  {

  structnet_device*dev=dev_instance;

  structcp_private*cp=dev->priv;

  u16status;

  /*检查rx-ring中是否有中断到达*/

  status=cpr16(IntrStatus);

  if(!

status||(status==0xFFFF))

  returnIRQ_NONE;

  if(netif_msg_intr(cp))

  printk(KERN_DEBUG"%s:

intr,status%04xcmd%02xcpcmd%04x\n",

  dev->name,status,cpr8(Cmd),cpr16(CpCmd));

  /*清除NIC中断操纵器的内容*/

  cpw16(IntrStatus,status&~cp_rx_intr_mask);

  spin_lock(&cp->lock);

  /*接收状态寄存器表示有数据包到达*/

  if(status&(RxOK|RxErr|RxEmpty|RxFIFOOvr)){

  /*把当前的产生中断的NIC设备挂在softnet_data中的POLL队列上,等待网络上层上的应用程序处理*/

  if(netif_rx_schedule_prep(dev)){

  /*关闭接收中断使能*/

  cpw16_f(IntrMask,cp_norx_intr_mask);

  __netif_rx_schedule(dev);

  }

  }

  /*发送中断的处理过程以及8139C+的专门软中断的处理过程,那个地点我们不关怀*/

  if(status&(TxOK|TxErr|TxEmpty|SWInt))

  cp_tx(cp);

  /*假如发生链路变化的情形,需要检查介质无关接口〔MII〕的载波状态同样也发生变化,

  否那么就要预备重新启动MII接口*/

  if(status&LinkChg)

  mii_check_media(&cp->mii_if,netif_msg_link(cp),FALSE);

  /*假如PCI总线发生错误,需要对8139C+的设备重新复位*/

  if(status&PciErr){

  u16pci_status;

  pci_read_config_word(cp->pdev,PCI_STATUS,&pci_status);

  pci_write_config_word(cp->pdev,PCI_STATUS,pci_status);

  printk(KERN_ERR"%s:

PCIbuserror,status=%04x,PCIstatus=%04x\n",

  dev->name,status,pci_status);

  /*TODO:

resethardware*/

  }

  spin_unlock(&cp->lock);

  returnIRQ_HANDLED;

  }

  

  把NIC挂在POLL队列(poll_list)上

  在8139CP的中断程序能够看到__netif_rx_schedule的调用方式,它把NIC设备挂在softnet_data结构中的poll_list队列上,以便及时的返回中断,让专门数据包处理bottom-half部分来进行处理,我们先来看一下__netif_rx_schedule的内部工作流程。

  

  staticinlinevoid__netif_rx_schedule(structnet_device*dev)

  {

  unsignedlongflags;

  local_irq_save(flags);

  dev_hold(dev);

  /*把当前NIC设备挂在POLL〔poll_list〕队列中,等待唤醒软中断以后进行轮询*/

  list_add_tail(&dev->poll_list,&__get_cpu_var(softnet_data).poll_list);

  /*确定当前该设备所要预备接收的包大小*/

  if(dev->quota<0)

  dev->quota+=dev->weight;

  else

  dev->quota=dev->weight;

  /*启动软中断,在表示所有中断的状态字irq_cpustat_t中关于软中断字段__softirq_pending中,

  把关于网络轮循接收软中断位置1,等待调度时机来临时候运行该中断的句柄net_rx_action。

*/

  __raise_softirq_irqoff(NET_RX_SOFTIRQ);

  local_irq_restore(flags);

  }

  

  由__netif_rx_schedule启动的软中断的处理过程分析

  软中断事件触发前差不多在此设备子系统初始化时刻调用subsys_initcall(net_dev_init)在软中断操纵台上被激活,挂在任务队列tasklet上预备在任务调度schedule的时刻运行它了,那个里面最要紧的部分是调用了8139C+网络设备的POLL方法〔dev->poll〕,从网络设备的rx-ring队列中获得数据,本来它应当放在网络设备中断服务程序中执行的,按照我们前面说明的那样,POLL方法以空间换取时刻的机制把它放在软中断部分来执行轮循机制〔采纳类似老的Bottom-half机制也能够达到同样成效,而且更加容易明白得一些〕在每次进行进程调度的时候就会执行网络设备软中断,轮询rx-ring对NIC进行数据的接收。

  软中断的处理过程:

  

  staticvoidnet_rx_action(structsoftirq_action*h)

  {

  structsoftnet_data*queue=&__get_cpu_var(softnet_data);

  unsignedlongstart_time=jiffies;

  intbudget=netdev_max_backlog;/*表示队列的最大长度*/

  /*锁定当前线程,多处理器的情形之下不能被其他处理器中断处理*/

  preempt_disable();

  local_irq_disable();

  /*检查POLL队列〔poll_list〕上是否有设备在预备等待轮询取得数据*/

  while(!

list_empty(&queue->poll_list)){

  structnet_device*dev;

  /*那个地点保证执行当前的POLL过程的时刻不超过一个时刻片,如此不至于被软中断占用太多的时刻,

  如此在一次调度的时刻内执行完毕当前的POLL过程,budget表示一个时刻片内最大数据传输的"块数",

  块的意思为每个POLL所完成sk_buff数量,每块中间的sk_buff数量为dev->quota决定,在8139CP驱动中,

  budget为300,而quota为16表示每给时刻片最多能够接收到4.8K的sk_buff数量*/

  if(budget<=0||jiffies-start_time>1)

  gotosoftnet_break;

  local_irq_enable();

  /*从公共的softnet_data数据结构中的轮循队列上获得等待轮循的设备结构*/

  dev=list_entry(queue->poll_list.next,

  structnet_device,poll_list);

  /*调用设备的POLL方法从NIC上的RingBuffer中读入数据*/

  if(dev->quota<=0||dev->poll(dev,&budget)){

  /*完成一次POLL过程的数据的接收,重新定义设备接收数据的"配额"

  〔事实上确实是sk_buff缓冲区的数量,每次调用POLL方法的时候能够创建同时最

  多能够向上层提交的sk_buff缓冲区数目,那个参数专门重要在高速处理的时候有需要慎重优化那个数值,

  在有大量数据接收的情形下,需要增加该数值〕*/

  local_irq_disable();

  list_del(&dev->poll_list);

  list_add_tail(&dev->poll_list,&queue->poll_list);

  if(dev->quota<0)

  dev->quota+=dev->weight;

  else

  dev->quota=dev->weight;

  }else{

  /*发生了错误的数据接收状况,或者没有完成"规定"配额的数据接收,同时没有新的数据进来,

  那个也可能表示差不多完成了传输的过程,调用__netif_rx_complete把网络设备从POLL队列上清除

  〔介绍POLL过程的时候详细介绍〕*/

  dev_put(dev);

  local_irq_disable();

  }

  }

  out:

  local_irq_enable();

  preempt_enable();

  return;

  softnet_break:

  __get_cpu_var(netdev_rx_stat).time_squeeze++;

  __raise_softirq_irqoff(NET_RX_SOFTIRQ);

  gotoout;

  }  

  

  8139CP驱动中的轮询方法

  dev->poll方法:

  那个方法通常被网络层在向驱动的接收循环队列猎取新的数据包时刻调用,而驱动的接收循环队列中能够向网络层交付的包数量那么在dev->quota字段中表示,我们来看8139cp中POLL的原型:

  staticintcp_rx_poll(structnet_device*dev,int*budget)

  参数budget的上层任务所需要底层传递的数据包的数量,那个数值不能超过netdev_max_backlog的值。

  总而言之,POLL方法被网络层调用,只负责按照网络层的要求值〔"预算"值〕提交对应数量的数据包。

8139CP的POLL方法注册通常在设备驱动程序模块初始化(调用probe)的时候进行,如下:

  

  staticintcp_init_one(structpci_dev*pdev,conststructpci_device_id*ent)

  {

  ……

  dev->poll=cp_rx_poll;

  ……

  }

  

  

  

  设备的POLL方法正如前所说的是被网络层上的软中断net_rx_action调用,我们现在来看具体的流程:

  staticintcp_rx_poll(structnet_device*dev,int*budget)

  {

  structcp_private*cp=netdev_priv(dev);

  unsignedrx_tail=cp->rx_tail;

  /*设定每次进行调度的时候从设备发送到网络层次最大的数据包的大小*/

  unsignedrx_work=dev->quota;

  unsignedrx;

  rx_status_loop:

  rx=0;

  /*重新打开NIC中断,在cp_interrupt中断句柄中中断关闭了,现在POLl差不多开始处理环行缓冲队列中的数据,

  因此中断能够打开,预备接收新的数据包*/

  cpw16(IntrStatus,cp_rx_intr_mask);

  while

(1){/*POLL循环的开始*/

  u32status,len;

  dma_addr_tmapping;

  structsk_buff*skb,*new_skb;

  structcp_desc*desc;

  uns

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工作范文 > 制度规范

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2