NETDEV 网络设备子系统——帧发送过程简析

文章详细分析了Linux内核3.18版本中,基于高通EMAC驱动的网络设备数据发送过程。从__netdev_start_xmit开始,经过dev_queue_xmit、__dev_xmit_skb、sch_direct_xmit和qdisc_run等函数,深入探讨了帧如何通过队列管理、调度和传输,特别关注了无队列设备和有队列设备的处理差异。整个过程揭示了网络数据在内核中的流动路径。
摘要由CSDN通过智能技术生成

以高通emac驱动为示例(kernel 3.18),简要分析网络设备子系统数据发送过程;先逆向查找调用栈,在总结整个过程(主要关注帧的传输,流动的过程)。

由3532行可知,mac驱动中的ndo_start_xmit是在__netdev_start_xmit函数中被调用的,而该函数又被netdev_start_xmit封装了一层;接着查找该函数被谁调用; 

 从第3行和第4行可知,主要是在net/core/dev.c中的xmit_one函数和xmit_list函数调用netdev_start_xmit函数;接着查看xmit_onexmit_list的调用者; 

 由此可见,也在net/core/dev.c中,分别是dev_hard_start_xmitdev_hard_start_xmit_list函数;在往上查找调用者;

可见这两个函数主要都是被__dev_queue_xmit函数和sch_direct_xmit函数调用;接着再分别看看_这两个函数的调用者;

 由此可知__dev_queue_xmit函数最终被dev_queue_xmitdev_queue_xmit_acceldev_queue_xmit_list三个函数封装,到此既是网络设备子系统的最边界了;而sch_direct_xmit函数则是被__dev_xmit_skbqdisc_restart两个函数调用;接下来分别看下__dev_queue_xmit函数和__dev_xmit_skb函数以及qdisc_restart函数的实现;

 从注释可知,该函数是将缓冲区(skb)排队,然后传输到网络设备;

 由3023行可知,__dev_xmit_skb函数被此函数给调用了,条件是该网络设备有队列

在3056行和3059行能分别看到dev_hard_start_xmitdev_hard_start_xmit_list函数被调用;由3027行的注释可知,调用这两个中之一的函数的最早的前提条件是,这个是没有队列的网络设备虚拟网络设备,如loopback隧道之类的虚拟网络设备),其次是这个设备已经up;而以太网是有队列的网络设备,所以主要调用的是__dev_xmit_skb函数,接着看看__dev_xmit_skb函数的实现;

 __dev_xmit_skb函数的实现,主要关注2886行和2906行 的条件分支;其中2886行的条件是: 

  1. 该队列设置了旁通标志;(TCQ_F_CAN_BYPASS
  2. 该队列中没有待发送的数据;(! qdisc_qlen()
  3. 该队列没有再运行;(disc_run_begin()

2906行的条件则是:

  1. 该队列没有再运行;(disc_run_begin()

 在这个2886行的分支里,能看到2896行调用了sch_direct_xmit函数,且当该函数返回值大于0时,会调用__qdisc_run函数;__qdisc_run函数在2906行的分支里也被调用了,所以现在需要对sch_direct_xmit函数和__qdisc_run函数的实现进行简要分析;

 从该函数的注释可知,主要作用是尽可能传输多个skb,当队列非空是返回值大于0,当队列为空或者处于throttle状态时返回值为0;从162行的分支可知,当skb有效,且发送队列不是处于停止状态时,才会调用dev_hard_start_xmit函数或者dev_hard_start_xmit_list函数来发送数据;

接着看下__qdisc_run函数的实现:

由244行可知__qdisc_run在循环调用qdisc_restart函数,而qdisc_restart函数则是将skb从队列取出,在调用sch_direct_xmit函数来传输skb;从246行到248行的注释可知,当超过配额或者需要切换到另一个cpu时,会执行__net_schedule函数触发软中断;由此因此需要看看__net_schedule函数的NET_TX_SOFTIRQ软中断处理函数net_tx_action

从3543行可知,这便是主要传输数据的分支,从3552行可知其会循环取出存在的队列,然后获取队列的锁成功时,便会调用qdisc_run函数,而当获取队列的锁失败时,则会重新调度;

由109行可知qdisc_run函数是__qdisc_run函数封装了一层的函数;接着到了看dev_hard_start_xmitdev_hard_start_xmit_list函数的实现了;

从2687行和2691行可知,dev_hard_start_xmit函数是循环调用xmit_one来传输skb;从2717行可知dev_hard_start_xmit_list函数则是直接封装了xmit_list函数;接下来看看xmit_onexmit_list函数的实现;

 从2644行可知,xmit_one函数直接调用了netdev_start_xmit函数;xmit_list函数也是一样,在2675行直接调用netdev_start_xmit函数;不过它们在调用该函数前都有通过dev_queue_xmit_nit函数将skb复制到其他网络节流器(tap)中;到此帧的整个传输过程已简析完,现在总结下正向的过程:

  1. dev_queue_xmit
  2. __dev_queue_xmit
  3. __dev_xmit_skb      (dev_hard_start_xmit (无队列规则的虚拟设备))
  4. __qdisc_run
  5. qdisc_restart            (__netif_schedule  ->  net_tx_action  ->  qdisc_run -> __qdisc_run)
  6. sch_direct_xmit
  7. dev_hard_start_xmit
  8. xmit_one
  9. netdev_start_xmit
  10. __netdev_start_xmit
  11. ndo_start_xmit          (emac_start_xmit  高通emac)

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值