您的位置:首页 > 运维架构 > Linux

Linux napi与netif简要分析

2016-02-02 11:00 1251 查看
、linux网络接收函数与软中断的关系及初始化。

关于软中断的原理就不依依介绍了,本节主要介绍网络数据处理对软中断的使用(关于软中断的工作原理可参看 linux中断底半部之 softirq 原理与代码分析)。

网络数据处理的软中断的注册是在函数net_dev_init进行初始化,主要是通过调用函数open_softirq,将rx、tx的处理函数注册到数组softirq_vec中相应软中断号对应的action指针中。这样,当设备产生中断接收一个网络数据时,在中断处理函数中通过调用函数raise_softirq_irqoff将NET_RX_SOFTIRQ对应的flag位置1,这样在中断函数处理结束,通过函数irq_exit中调用invoke_softirq,实现软中断处理。

open_softirq(NET_TX_SOFTIRQ, net_tx_action);

open_softirq(NET_RX_SOFTIRQ, net_rx_action);

以上就是网络协议栈中对于软中断的使用与初始化。

二、LINUX NAPI与netif_rx之间的关系

NAPI:混合使用中断与轮询,而不是使用单一的中断驱动机制,这样就提高了系统的性能。当设备产生一个数据接收中断后,新机制的软中断处理函数就会轮询设备的入口队列,直到入口队列中没有数据了,再开启中断,好多芯片厂家会同时使用DMA与NAPI机制,通过DMA机制收取switch接收到的数据,然后再通过NAPI机制,由协议栈再对数据进行处理。

Netif_rx:单一的中断驱动处理机制,每产生一个设备接收中断,就会在中断结束时唤醒该软中断,调用该软中断的处理函数。

由于NAIP相对于netif_rx是新的机制,所以需要做兼容,而对于现有设备来说netif_rx机制也是需要的。因为对于现在的网关设备来说都会支持vlan功能,而好多厂家的vlan实现中,会在原有接口的基础上,虚拟出一个接口作为vlan接口。既然虚拟出了接口,那也就有相应的数据收发流程,而对于虚拟接口来说收发数据时是没有相应的中断的,此时可以使用函数netif_rx就可以实现数据从真实接口处理转变为虚拟接口的处理,而且虚拟接口的处理也是走的标准的协议栈,所以netif_rx机制也还是有用处的。

由于这两种机制都有存在的理由,那就需要进行兼容。那如何进行兼容呢?

1、数据队列的考虑

由于NAPI设备都有自己的私有队列,所以其队列处理函数就要有相应的设备驱动程序来进行处理。

对于netif来说,使用的是内核统一的数据帧接收队列,而其处理函数也是唯一的。

2、如何将网络设备与软中断联系起来,

对于netif_rx来说各设备不需要与softnet_data联系,直接调用netif_rx的软中断处理函数,从内核接收队列中取出数据包,然后交给协议栈处理函数即可;

而不同的napi设备的私有队列是不同的,则其接收处理函数就应该不同。

下面分析内核的实现流程。

首先看sftnet_data的定义,

struct softnet_data {

struct Qdisc *output_queue;

struct sk_buff_head input_pkt_queue;//不使用napi设备存储入口数据的队列

struct list_head poll_list;//napi链表,将所有需要软中断的napi设备链接在一起

struct sk_buff *completion_queue;//对于已不需要的数据,可以移到该队列中,被释放调用

struct napi_struct backlog;//不使用napi设备的入口数据处理函数。

};

该结构体兼容了napi与netiif,对于napi设备,需要将napi设备链接到poll_list中;对于不支持napi的设备,可以用input_pkt_queue与backlog实现入口数据的存储与处理。

对于napi设备,其对应的数据结构体如下:

struct napi_struct {

/* The poll_list must only be managed by the entity which

* changes the state of the NAPI_STATE_SCHED bit. This means

* whoever atomically sets that bit can add this napi_struct

* to the per-cpu poll_list, and whoever clears that bit

* can remove from the list right before clearing the bit.

*/

struct list_head poll_list;//链接到softnet_data->poll_list

unsigned long state;

int weight;//一次可以从队列中取出的数据个数

int (*poll)(struct napi_struct *, int);//poll处理函数,负责从队列中取出数据,并调用函数netif_receive_skb将数据交给协议栈进行处理

#ifdef CONFIG_NETPOLL

spinlock_t poll_lock;

int poll_owner;

#endif

unsigned int gro_count;

struct net_device *dev;//对应的网络设备

struct list_head dev_list;//将该结构体链接到网络设备的list链表中。

struct sk_buff *gro_list;

struct sk_buff *skb;

};

通过poll_list就实现了 将该napi结构与softnet_data的关联。

三、实现及调用

1、napi

对于napi设备,可以通过调用netif_napi_add实现napi结构的初始化以及与设备的关联。而调用napi_schedule,实现将napi添加到当前cpu对应的softnet_data->poll_list链表中,然后置位NET_RX_SOFTIRQ,实现软中断的调用。

对于每一个napi设备,如果使用软中断,则需要调用以上两个函数

2、netif_rx

对于netif_rx,由于其是统一的处理,所以该机制已经默认写入到dev的初始化函数中,在net_dev_init初始化函数中,对于每一个cpu,会执行以下代码段

for_each_possible_cpu(i) {

struct softnet_data *queue;

queue = &per_cpu(softnet_data, i);

skb_queue_head_init(&queue->input_pkt_queue);//入口队列初始化,

queue->completion_queue = NULL;

INIT_LIST_HEAD(&queue->poll_list);//napi链表初始化

/*netif_rx的napi结构初始化*/

queue->backlog.poll = process_backlog;//netif_rx入口队列的处理函数。

queue->backlog.weight = weight_p;

queue->backlog.gro_list = NULL;

queue->backlog.gro_count = 0;

}

关于netif_rx的调用,如果想使用netif_rx的napi机制时,只需要将softnet_data->backlog添加到链表softnet_data->poll_list中,并置位NET_RX_SOFTIRQ,实现软中断的调用即可,所以其调用函数依然是napi_schedule。

至此,完成napi 与netif_rx的分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: