// 1.积压设备的目的:
// napi设备,非napi设备同一处理接口,linux为所有不使用napi的驱动程序,提供了一个虚拟设备,叫做积压设备。
// 2.与积压设备有关的数据结构:
// 2.1 每个cpu都有一个积压设备,保存在softnet_data->backlog_dev
// 2.2 每个cpu都有一个积压设备的输入队列,保存在softnet_data->input_pkt_queue,所有非napi设备的入口skb,均挂载到此队列。
// 2.3 内核为积压设备的提供poll函数,process_backlog
// 积压设备的poll函数
// 调用路径:net_rx_action->process_backlog
// 参数:
// backlog_dev,积压设备
// budget,函数此次运行可以处理的skb配额
// 返回值:
// <0 , 表明设备还有待处理的入口帧,此时设备仍然在poll_list中,且设备处于关中断状态
// =0 , 表明设备已经处理完所有的入口帧,此时设备已经从poll_list中删除,可接收重新调度,设备中断已开启
// 函数主要任务:
// 1.函数运行不能超过1个jiffies,处理的skb不能超过配额
// 2.在关中断的情况下,从softnet->input_pkt_queue取下入口skb,后开中断
// 2.1 如果有入口skb
// 2.1.1 通过netif_receive_skb向上层协议传递skb
// 2.1.2 递减入口设备的引用计数
// 2.2 如果在运行时间不足1个jiffies,并且没有超过配额的情况下,处理完了入口队列的所有skb
// 2.2.1 更新设备的可用配额
// 2.2.2 由于已经没有入口数据,因此将挤压设备从调度队列poll_list上取下来
// 2.2.3 清除积压设备的调度标志,表示设备可以接收下一次调度
// 2.2.4 如果之前入口队列满,被设置了throttle,则在入口队列为空时,清除throttle
// 注:在入口队列满,设置了throttle,之后的入口数据都会被丢弃,直到入口队列为空,清除throttle之后,才开始重新接收入口数据。
1.1 static int process_backlog(struct net_device *backlog_dev, int *budget)
{
int work = 0;
//此次调用可以处理的skb为 设备剩余配额,传入的配置中的较小者
int quota = min(backlog_dev->quota, *budget);
struct softnet_data *queue = &__get_cpu_var(softnet_data);
unsigned long start_time = jiffies;
for (;;) {
struct sk_buff *skb;
struct net_device *dev;
//关闭本cpu中断,因为input_pkt_queue可能会在中断中被修改
local_irq_disable();
//从input_pkt_queue上退出一个skb
skb = __skb_dequeue(&queue->input_pkt_queue);
if (!skb)
goto job_done;
//开中断
local_irq_enable();
dev = skb->dev;
//将skb向上传递
netif_receive_skb(skb);
//递减接收此skb的dev的引用计数
dev_put(dev);
//消费流量个数-1
work++;
//此函数运行时间应该 < 1 jiffies
if (work >= quota || jiffies - start_time > 1)
break;
}
//将设备的配额减去实际消费的流量个数
backlog_dev->quota -= work;
//将打算让设备消费的流量个数中减去实际消费的流量个数
*budget -= work;
return -1;
//表示接收队列为空
job_done:
backlog_dev->quota -= work;
*budget -= work;
//将积压设备从poll_list上删除
list_del(&backlog_dev->poll_list);
smp_mb__before_clear_bit();
//清除积压设备__LINK_STATE_RX_SCHED标识,表示积压设备重新可以接收调度
netif_poll_enable(backlog_dev);
//如果此cpu的输入队列已经关闭,开启输入队列
if (queue->throttle)
queue->throttle = 0;
//开本cpu中断
local_irq_enable();
return 0;
}
// 对积压设备的调度
// 调用路径 : 非napi设备的接收中断->netif_rx
2.1 int netif_rx(struct sk_buff *skb)
{
...
netif_rx_schedule(&queue->backlog_dev);
...
}