// 数据帧接收软中断处理函数
// 调用路径:do_softirq->net_rx_action
// 函数主要任务:
// 1.在关中断情况下检查调度队列poll_list是否有设备接收到入口帧,然后开中断
// 2.单次运行处理的入口帧不能超过netdev_max_backlog=300,运行时间不能超过1个jiffies
// 3.依次遍历调度队列poll_list
// 4.如果设备配额以用完,或者设备驱动的poll函数表明仍然有入口数据帧
// 4.1 将设备重新添加poll_list尾部,重新分配配额,准备接受下一次调度
// 5.如果单次运行没有调度完所有接收到入口帧的设备,则再次设置接受软中断调度标志,退出
// 注:接收软中断中,poll_list上挂载的npai设备,或者非napi设备使用的积压设备。
3.2 static void net_rx_action(struct softirq_action *h)
{
struct softnet_data *queue = &__get_cpu_var(softnet_data);
unsigned long start_time = jiffies;
int budget = netdev_max_backlog;
//关本地中断
local_irq_disable();
//有设备有输入流量
while (!list_empty(&queue->poll_list)) {
struct net_device *dev;
//执行时间超过了一个1 jiffies
//或者本次软中断处理的输入流量已经超过了netdev_max_backlog,退出软中
if (budget <= 0 || jiffies - start_time > 1)
goto softnet_break;
local_irq_enable();
//获得设备指针
dev = list_entry(queue->poll_list.next,
struct net_device, poll_list);
//如果设备的配额已经用完,或者执行一次poll之后,此设备依然有待处理的流量
if (dev->quota <= 0 || dev->poll(dev, &budget)) {
local_irq_disable();
//将设备从list上删除,添加到list尾部
list_del(&dev->poll_list);
list_add_tail(&dev->poll_list, &queue->poll_list);
//给设备重新分配配额,已weight作为一次分配的量,范围从1024~64
if (dev->quota < 0)
dev->quota += dev->weight;
else
dev->quota = dev->weight;
} else {
dev_put(dev);
local_irq_disable();
}
}
out:
local_irq_enable();
return;
softnet_break:
__get_cpu_var(netdev_rx_stat).time_squeeze++;
//重新调度接收软中断
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
goto out;
}
// 发送软中断处理函数
// 一次处理output_queue中所有等待传输的设备
// 调用路径:do_softirq->net_tx_action
// 函数主要功能:
// 1.释放本cpu所有已经完成传输的skb
// 2.关中断的情况获取本cpu的传输设备队列output_queue, 其中的设备有数据需要进行传输
// 3.清除设备传输调度标志,表明设备可以接收下一次传输调度
// 4.获取设备队列锁queue_lock,通过队列规则接口,进行设备数据传输
// 5.如果获取queue_lock失败,说明设备当前正在进行传输,重调度设备
// 注:发送软中断中,output_queue挂载的设备均为有队列规则设备,没有队列规则的设备不会在软中断进行传输,因为其没有传输调度的概念
3.3 static void net_tx_action(struct softirq_action *h)
{
struct softnet_data *sd = &__get_cpu_var(softnet_data);
//如果完成队列非空
if (sd->completion_queue) {
struct sk_buff *clist;
//关本cpu中断
local_irq_disable();
//取下本cpu的完成队列
clist = sd->completion_queue;
sd->completion_queue = NULL;
local_irq_enable();
while (clist) {
struct sk_buff *skb = clist;
clist = clist->next;
//释放对应的skb
__kfree_skb(skb);
}
}
//如果输出队列非空
if (sd->output_queue) {
struct net_device *head;
//将输出队列取下
local_irq_disable();
head = sd->output_queue;
sd->output_queue = NULL;
local_irq_enable();
while (head) {
struct net_device *dev = head;
//net_device 通过next_sched链接到output_queue上
head = head->next_sched;
//一个内存屏障
smp_mb__before_clear_bit();
//设备准备好进行传输,清除设备调度标志,说明,当设备执行传输时,属于非调度状态,可以被重新调度。
clear_bit(__LINK_STATE_SCHED, &dev->state);
//获取设备的输出队列锁
if (spin_trylock(&dev->queue_lock)) {
//通过出口队列规则,调度设备
qdisc_run(dev);
spin_unlock(&dev->queue_lock);
} else {
//说明队列正在进行传输,重新调度设备
netif_schedule(dev);
}
}
}
}
网络子系统20_传输接收软中断
最新推荐文章于 2022-07-23 17:44:18 发布