上一篇讲的是内核配置成NAPI的情况,那也是绝大多数内核使用的配置
现在讲讲内核不配置成NAPI时的情况
一、no NAPI 数据结构
不配置NAPI的时候,网络设备不使用自己的napi_struct结构,
所有网络设备驱动都使用同一个napi_struct,即cpu私有变量 __get_cpu_var(softnet_data).backlog
每当收到数据包时,网络设备驱动会把 __get_cpu_var(softnet_data).backlog挂到 __get_cpu_var(softnet_data).poll_list上面。
所以软中断里net_rx_action遍历cpu私有变量 __get_cpu_var(softnet_data).poll_list时,
上面挂的napi_struct只有一个
二、内核启动时的准备工作
也是在 net_dev_init中,初始化了cpu私有变量的napi_struct,即所有网络设备驱动使用的napi_struct
__init net_dev_init()
{
//每个CPU都有一个私有变量 _get_cpu_var(softnet_data)
// _get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue); // 不配置NAPI时,才使用这个接收队列
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog; // poll钩子函数初始化
queue->backlog.weight = weight_p; //
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在软中断上挂网络接收handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在软中断上挂网络发送handler
}
三、中断里接受以太网包
TSEC的接收中断处理函数
gfar_receive
{
gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
#ifdef CONFIG_GFAR_NAPI
// test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
// 在软中断里调用 net_rx_action 会检查状态 napi_struct.state
if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED;
gfar_write(&priv->regs->imask, tempval);
// 将当前net_device的 napi_struct.poll_list 挂到
// CPU私有变量 &__get_cpu_var(softnet_data).poll_list 上,并触发软中断
// 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
// napi_struct.poll()钩子函数,即 gfar_poll()
__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}
gfar_clean_rx_ring
-->gfar_process_frame
-- > 初始化了skb->dev,这样在软中断里才能判断这个数据包来自哪里
--> RECEIVE(skb) // 调用 netif_rx(skb)
#ifdef CONFIG_GFAR_NAPI
#define RECEIVE(x) netif_receive_skb(x)
#else
#define RECEIVE(x) netif_rx(x)
#endif
netif_rx(skb)
{
queue = &__get_cpu_var(softnet_data);
__skb_queue_tail(&queue->input_pkt_queue, skb); //将skb放到接收队列(在net_dev_init初始化)中
napi_schedule(&queue->backlog); //将cpu私有变量的的napi_struct挂到cpu私有变量的poll_list上
//test_and_set napi_struct.state为 NAPI_STATE_SCHED
//触发网络接收软中断
}
软中断net_rx_action中调用poll钩子函数
虽说软中断里也遍历cpu私有变量的poll_list,事实上poll_list现在只挂一个napi_struct结构
即cpu私有变量的backlog成员(它在net_dev_init中初始化),所以现在调用的poll钩子函数就是process_backlog了
static int process_backlog(struct napi_struct *napi, int quota)
{
struct softnet_data *queue = &__get_cpu_var(softnet_data);
napi->weight = weight_p;
do {
struct sk_buff *skb;
struct net_device *dev;
local_irq_disable();
skb = __skb_dequeue(&queue->input_pkt_queue); //从接收队列中取出skb,
if (!skb) { //这些skb是在netif_rx中进入队列的
__napi_complete(napi);
local_irq_enable();
break;
}
local_irq_enable();
dev = skb->dev;
netif_receive_skb(skb); //进入协议协议栈
dev_put(dev);
} while (++work return work;
}
现在讲讲内核不配置成NAPI时的情况
一、no NAPI 数据结构
不配置NAPI的时候,网络设备不使用自己的napi_struct结构,
所有网络设备驱动都使用同一个napi_struct,即cpu私有变量 __get_cpu_var(softnet_data).backlog
每当收到数据包时,网络设备驱动会把 __get_cpu_var(softnet_data).backlog挂到 __get_cpu_var(softnet_data).poll_list上面。
所以软中断里net_rx_action遍历cpu私有变量 __get_cpu_var(softnet_data).poll_list时,
上面挂的napi_struct只有一个
二、内核启动时的准备工作
也是在 net_dev_init中,初始化了cpu私有变量的napi_struct,即所有网络设备驱动使用的napi_struct
__init net_dev_init()
{
//每个CPU都有一个私有变量 _get_cpu_var(softnet_data)
// _get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue); // 不配置NAPI时,才使用这个接收队列
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog; // poll钩子函数初始化
queue->backlog.weight = weight_p; //
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在软中断上挂网络接收handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在软中断上挂网络发送handler
}
三、中断里接受以太网包
TSEC的接收中断处理函数
gfar_receive
{
gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
#ifdef CONFIG_GFAR_NAPI
// test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
// 在软中断里调用 net_rx_action 会检查状态 napi_struct.state
if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED;
gfar_write(&priv->regs->imask, tempval);
// 将当前net_device的 napi_struct.poll_list 挂到
// CPU私有变量 &__get_cpu_var(softnet_data).poll_list 上,并触发软中断
// 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
// napi_struct.poll()钩子函数,即 gfar_poll()
__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}
gfar_clean_rx_ring
-->gfar_process_frame
-- > 初始化了skb->dev,这样在软中断里才能判断这个数据包来自哪里
--> RECEIVE(skb) // 调用 netif_rx(skb)
#ifdef CONFIG_GFAR_NAPI
#define RECEIVE(x) netif_receive_skb(x)
#else
#define RECEIVE(x) netif_rx(x)
#endif
netif_rx(skb)
{
queue = &__get_cpu_var(softnet_data);
__skb_queue_tail(&queue->input_pkt_queue, skb); //将skb放到接收队列(在net_dev_init初始化)中
napi_schedule(&queue->backlog); //将cpu私有变量的的napi_struct挂到cpu私有变量的poll_list上
//test_and_set napi_struct.state为 NAPI_STATE_SCHED
//触发网络接收软中断
}
软中断net_rx_action中调用poll钩子函数
虽说软中断里也遍历cpu私有变量的poll_list,事实上poll_list现在只挂一个napi_struct结构
即cpu私有变量的backlog成员(它在net_dev_init中初始化),所以现在调用的poll钩子函数就是process_backlog了
static int process_backlog(struct napi_struct *napi, int quota)
{
struct softnet_data *queue = &__get_cpu_var(softnet_data);
napi->weight = weight_p;
do {
struct sk_buff *skb;
struct net_device *dev;
local_irq_disable();
skb = __skb_dequeue(&queue->input_pkt_queue); //从接收队列中取出skb,
if (!skb) { //这些skb是在netif_rx中进入队列的
__napi_complete(napi);
local_irq_enable();
break;
}
local_irq_enable();
dev = skb->dev;
netif_receive_skb(skb); //进入协议协议栈
dev_put(dev);
} while (++work return work;
}