网络子系统21_传输接收软中断的触发
//参考 深入理解linux网络技术内幕
//设备接收数据的调度 //在由驱动程序提供的数据接收中断中: // 1.非napi设备使用netif_rx向上传递skb // 2.napi设备使用netif_rx_schedule //设备发送数据的调度 // 调度只针对具有队列规则的设备,在qdisc_run中如果无法获得xmit_lock锁,则通过规则队列重新入队skb,在net_tx_action中,获取queue_lock失败,调度设备 // 对于不使用队列规则的设备,直接通过dev_queue_xmit调用驱动的hard_start_xmit完成传输 //调度发生的路径 // dev_queue_xmit->qdisc_run->netif_schedule // net_tx_action->netif_schedule
//调度设备发送数据,将设备链接到per-cpu的softnet_data->output_queue中,触发软中断 1.1 static inline void netif_schedule(struct net_device *dev) { //如果设备没有关闭传输 if (!test_bit(__LINK_STATE_XOFF, &dev->state)) //执行实际的调度设备操作 __netif_schedule(dev); } //netif_schedule->__netif_schedule 1.2 static inline void __netif_schedule(struct net_device *dev) { //设备没有被调度 if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { unsigned long flags; struct softnet_data *sd; //关本cpu中断 local_irq_save(flags); sd = &__get_cpu_var(softnet_data); //将设备挂在到softnet_data的output_queue上 dev->next_sched = sd->output_queue; sd->output_queue = dev; //触发传输软中断 raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } }
//napi、非napi接收数据时的调度区别

//非napi设备接收中断中使用,将skb向上传输
2.1 int netif_rx(struct sk_buff *skb)
{
int this_cpu;
struct softnet_data *queue;
unsigned long flags;
//处理netpoll
if (skb->dev->netpoll_rx && netpoll_rx(skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
//更新数据接收到的时间戳
if (!skb->stamp.tv_sec)
net_timestamp(&skb->stamp);
//关中断
local_irq_save(flags);
this_cpu = smp_processor_id();
queue = &__get_cpu_var(softnet_data);//获取当前cpu的softnet_data
__get_cpu_var(netdev_rx_stat).total++;//增加收到数据包的统计变量
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {//接收队列有空闲空间
if (queue->input_pkt_queue.qlen) {
if (queue->throttle)//队列处于拥塞状态
goto drop;
enqueue:
dev_hold(skb->dev);
__skb_queue_tail(&queue->input_pkt_queue, skb);//挂载skb到非NAPI设备专用的输入队列
local_irq_restore(flags);//开中断
return queue->cng_level;//返回拥塞等级
}
if (queue->throttle)//输入队列中没有skb,但是上次被标记为拥塞
queue->throttle = 0;//重新开启数据接收
netif_rx_schedule(&queue->backlog_dev);//运行到此处,说明输入队列中的skb个数为0,调度积压设备,入列skb
goto enqueue;
}
//当发生拥塞时,会暂停数据的接收,直到input_pkt_queue为空之后,才重新开启数据的接收
if (!queue->throttle) {
queue->throttle = 1;
__get_cpu_var(netdev_rx_stat).throttled++;
}
drop:
__get_cpu_var(netdev_rx_stat).dropped++;
local_irq_restore(flags);
kfree_skb(skb);
return NET_RX_DROP;
}
//napi设备接收中断中使用,链接设备到softnet->poll_list上
3.1 static inline void netif_rx_schedule(struct net_device *dev)
{
if (netif_rx_schedule_prep(dev))//检测设备是否已经被调度
__netif_rx_schedule(dev);//调度设备
}
3.2 static inline void __netif_rx_schedule(struct net_device *dev)
{
unsigned long flags;
local_irq_save(flags);
dev_hold(dev);
list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);//将设备链接到softnet_data->poll_list上
if (dev->quota < 0)
dev->quota += dev->weight;//分配设备此次调度的配置
else
dev->quota = dev->weight;
__raise_softirq_irqoff(NET_RX_SOFTIRQ);//触发软中断
local_irq_restore(flags);
}
最后更新:2017-04-03 15:22:11