1
2
3
4
5
6
|
struct
softnet_data
{
struct
sk_buff_head input_pkt_queue;
//旧接口的输入队列
struct
list_head poll_list;
//有需要处理报文的NAPI设备
struct
napi_struct backlog;
//虚拟的NAPI设备 backlog
};
|
1
|
DECLARE_PER_CPU(
struct
softnet_data,softnet_data);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
static
int
__init net_dev_init(
void
)
{
int
i, rc = -ENOMEM;
/*
* Initialise the packet receive queues.
*/
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;
/*初始化pool 链表头*/
INIT_LIST_HEAD(&queue->poll_list);
/*把backlog的轮询函数初始化为 process_backlog
该函数来处理传统接口使用的输入队列的报文*/
queue->backlog.poll = process_backlog;
/*backlog 轮询函数一次可以处理的报文上限个数*/
queue->backlog.weight = weight_p;
queue->backlog.gro_list = NULL;
queue->backlog.gro_count = 0;
}
}
|
1
2
3
4
5
6
7
8
9
10
|
struct
napi_struct
{
struct
list_head poll_list;
//挂到softnet_data的pool_list上
unsigned
long
state;
//NAPI的调度状态
int
weight;
//一次轮询的最大处理报文数
int
(*poll)(
struct
napi_struct *,
int
);
//轮询函数
struct
net_device *dev;
//指向关联的网络设备
struct
list_head dev_list;
//对应的网络设备上关联的NAPI链表节点
/*其他字段是gso功能用,这里先不讨论*/
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void
netif_napi_add(
struct
net_device *dev,
struct
napi_struct *napi,
int
(*poll)(
struct
napi_struct *,
int
),
int
weight)
{
INIT_LIST_HEAD(&napi->poll_list);
napi->poll = poll;
napi->weight = weight;
/*把NAPI加入到网络设备相关联的NAPI链表上去。*/
list_add(&napi->dev_list, &dev->napi_list);
napi->dev = dev;
/*绑定时设置NAPI是已调度状态,禁用该NAPI,以后手动的来清除该标识来使
能NAPI.*/
set_bit(NAPI_STATE_SCHED, &napi->state);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
static
inline
void
napi_disable(
struct
napi_struct *n)
{
/*先设置NAPI状态为DISABLE*/
set_bit(NAPI_STATE_DISABLE, &n->state);
/*循环的等待NAPI被调度完成,变成可用的,设置成SCHED状态*/
while
(test_and_set_bit(NAPI_STATE_SCHED, &n->state))
msleep(1);
/*清除DISABLE状态*/
clear_bit(NAPI_STATE_DISABLE, &n->state);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void
__napi_schedule(
struct
napi_struct *n)
{
unsigned
long
flags;
/*链表操作必须在关闭本地中断的情况下操作,防止硬中断抢占*/
local_irq_save(flags);
/*把NAPI加入到本地CPU的softnet_data 的pool_list 链表上*/
list_add_tail(&n->poll_list,
&__get_cpu_var(softnet_data).poll_list);
/*调度收包软中断*/
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
local_irq_restore(flags);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
void
__napi_complete(
struct
napi_struct *n)
{
BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
BUG_ON(n->gro_list);
/*把NAPI从softnet_data的pool_list上摘除下来*/
list_del(&n->poll_list);
/*使能NAPI,允许它下次可以被再次调度*/
smp_mb__before_clear_bit();
clear_bit(NAPI_STATE_SCHED, &n->state);
}
|