NAPI:
首先说一下它出现的原因:
接收通过中断来进行,当系统有一个处理大流量的高速接口时, 会一直有更多的报文来处理. 在这种情况下没有必要中断处理器; 时常从接口收集新报文是足够的.
使用的条件:
接口必须能够存储几个报文( 要么在接口卡上, 要么在内存内 DMA 环).
接口应当能够禁止中断来接收报文, 却可以继续因成功发送或其他事件而中断.
在net/core/dev.c中
接受对应的中断注册
open_softirq(NET_RX_SOFTIRQ,net_rx_action);
还有一个net_tx_action,这个是唤醒队列发送的,上次没细说,现在也不说了。呵呵!
在NAPI中,中断收到数据包后调用__napi_schedule调度软中断,然后软中断处理函数中会调用注册的poll回掉函数中调用netif_receive_skb将数据包发送到3层,没有进行任何的软中断负载均衡。
在非NAPI中,中断收到数据包后调用netif_rx,这个函数会将数据包保存到input_pkt_queue,然后调度软中断,这里为了兼 容NAPI的驱动,他的poll方法默认是process_backlog,最终这个函数会从input_pkt_queue中取得数据包然后发送到3 层。
前面已经说过,软中断处理函数net_rx_action调用设备的poll方法(默认为process_backlog),而process_backlog函数将进一步调用netif_receive_skb()将数据包传上协议栈,如果设备自身注册了poll函数,也将调用netif_receive_skb()函数
之前有个表提到了netif_rx(),一般在100M或一下网卡中用这个。
我当前的1000M网卡用的是napi。
netif_receive_skb
netif_rx:
____napi_schedule(sd, &sd->backlog);//这里会调度net_rx_action.所谓的非napi,还是有napi的存在 --》process_backlog
process_backlog:
这个函数重要的工作,就是出队,然后调用netif_receive_skb()将数据包交给上层,这与上一节讨论的poll是一样的。这也是为什么, 在网卡驱动的编写中,采用中断技术,要调用netif_rx,而采用轮询技术,要调用netif_receive_skb啦!
linux网络设备层次:
dev_queue_xmit()
不贴代码了,简单分析,
虽然函数名有个queue,不过不一定用到队列。这个由struct netdev_queue中的struct netdev_queue *_tx;决定。_tx[n]->qdisc->enqueue是否为空。如果你没有实现ndo_select_queue(),那么_tx[]和sk对应由sk->sk_tx_queue_mapping(位图关系)决定。
当然用queue会先把skb入队再调度,反之直接发送。
发送时通过调用ops->ndo_start_xmit(skb, dev);这就到了上面的说的网络设备接口层。
写实际的网卡驱动主要是设备功能层和网络设备媒介层。
而我的虚拟网卡主要是网络协议接口层和网络设备接口层,对实际网卡的应用。
RPS:
Receive packet steering简称rps,是google贡献给linux kernel的一个patch,主要的功能是解决多核情况下,网络协议栈的软中断的负载均衡。这里的负载均衡也就是指能够将软中断均衡的放在不同的cpu核心上运行。
简介在这里:
http://lwn.net/Articles/362339/
linux现在网卡的驱动支持两种模式,一种是NAPI,一种是非NAPI模式,这两种模式的区别,我前面的blog都有介绍,这里就再次简要的介绍下。
在NAPI中,中断收到数据包后调用__napi_schedule调度软中断,然后软中断处理函数中会调用注册的poll回掉函数中调用netif_receive_skb将数据包发送到3层,没有进行任何的软中断负载均衡。
在非NAPI中,中断收到数据包后调用netif_rx,这个函数会将数据包保存到input_pkt_queue,然后调度软中断,这里为了兼容NAPI的驱动,他的poll方法默认是process_backlog,最终这个函数会从input_pkt_queue中取得数据包然后发送到3层。
通过比较我们可以看到,不管是NAPI还是非NAPI的话都无法做到软中断的负载均衡,因为软中断此时都是运行在在硬件中断相应的cpu上。也就是说如果始终是cpu0相应网卡的硬件中断,那么始终都是cpu0在处理软中断,而此时cpu1就被浪费了,因为无法并行的执行多个软中断。
google的这个patch的基本原理是这样的,根据数据包的源地址,目的地址以及目的和源端口(这里它是将两个端口组合成一个4字节的无符数进行计算的,后面会看到)计算出一个hash值,然后根据这个hash值来选择软中断运行的cpu,从上层来看,也就是说将每个连接和cpu绑定,并通过这个hash值,来均衡软中断在多个cpu上。
这个介绍比较简单,我们来看代码是如何实现的。
它这里主要是hook了两个内核的函数,一个是netif_rx主要是针对非NAPI的驱动,一个是netif_receive_skb这个主要是针对NAPI的驱动,这两个函数我前面blog都有介绍过,想了解可以看我前面的blog,现在这里我只介绍打过patch的实现。
在看netif_rx和netif_receive_skb之前,我们先来看这个patch中两个重要的函数get_rps_cpu和enqueue_to_backlog,我们一个个看。
先来看相关的两个数据结构,首先是netdev_rx_queue,它表示对应的接收队列,因为有的网卡可能硬件上就支持多队列的模式,此时对应就会有多个rx队列,这个结构是挂载在net_device中的,也就是每个网络设备最终都会有一个或者多个rx队列。这个结构在sys文件系统中的表示类似这样的/sys/class/net/<device>/queues/rx-<n> 几个队列就是rx-n.