发送的主要流程
1. 高层任务通过统一的设备无关接口来发起发送动作,dev_queue_xmit(*skb)
2. 在这个接口中,通过判断txq->qdisc->enqueue是否存在来判断是否为接口挂上了tc配置的qos队列,如果有非空的回调接口那么就进入了qos的队列调度中__dev_xmit_skb。事实上除非netdev的txqueuelen=0,这个回调总是存在的。即使你没有主动为netdev配置队列,也会使用默认的pfifo_fast(只要netdev的txqueuelen不是0)。
3. 在__dev_xmit_skb()中,已经知道了所需要使用的qdisc,检查他的状态和队列长度,把skb送入qdisc的队列,进入真正的出队调度__qdisc_run(q)
4. __qdisc_run(q) 唯一任务是出队,qos就是对出队的控制。这时候除非配额到了,或者是有其他任务需要使用cpu了,会一直循环调用qdisc_restart
5. qdisc_restart()每次从qdisc的队列中拿到一个skb,这个skb是tc调度后的幸运儿。有机会真正发送出去,调用netdev的dev_hard_start_xmit()
6. dev_hard_start_xmit()是设备相关接口。netdev的硬件驱动初始化时定义好了设备的操作方法集合,比如ndo_start_xmit负责硬件驱动发包。
7. ndo_start_xmit()以下就是每个网卡本身的sdk驱动,比如atheros的驱动定义在drivers/net/ethernet/atheros/ag71xx_main.c
static const struct net_device_ops ag71xx_netdev_ops = {
...
}
但是发包不是也有软中断 NET_TX么?
原来软中断并非是必须的,就是说发一个包出去不一定非得要触发软中断。
做了个实验,发200k包出去时,观察 /proc/softirqs 的NET_TX 基本没有增长。
在出接口配上tc规则把速率限制到100k,模拟发包压力大的情况,这时候看到 softirq 才会猛增。
绝好的设计,当发包压力很小时,这是理想情况,直接操作buffer到硬件xmit返回再去发送下一个,就根本无须软中断调度。