Author:张昺华
Email:920052390@qq.com
Time:2019年3月23日星期六
html
此文也在个人我的公众号以及《Linux内核之旅》上有发表:以太网驱动流程浅析(一)-ifconfig主要流程linux
很喜欢一群人在研究技术,一块儿作有意思的东西,一块儿分享技术带给咱们的快乐,也但愿中国有更多的人热爱技术,喜欢一块儿研究、分享技术,而后能够一块儿用咱们的技术来作一些好玩的东西,能够为这个社会创造一些东西来改善人们的生活。
以下是本人调试过程当中的一点经验分享,以太网驱动架构毕竟涉及的东西太多,以下仅仅是针对加载流程和围绕这个问题产生的分析过程和驱动加载流程部分,并不涉及以太网协议层的数据流程分析。git
【问题】架构
机器经过usb方式下载了mac地址后,发现以太网没法正常使用,敲命令 ifconfig eth0 up出现:ifconfig: SIOCSIFFLAGS: No such device,而对于没有下载以太网mac address的机器表现均正常。调试过程当中发如今以太网控制器代码中加入一些printk,不正常的机器又正常了,打印的位置不一样,机器的以太网有时会正常,有时会异常,十分诡异。dom
reset时序问题致使,phy reset的时间不知足时序要求。以下图,若是硬件接了reset引脚,应知足时序要求在reset保持10ms有效电平后,还必须维持至少150ms才能够访问phy register,也就是reset要在B点以后才能够正常经过MDC/MDIO来访问phy register。若是是不使用硬件reset,使用软件reset方式,那也要至少在A点,也就是在reset维持10ms有效电平后,再维持3.5个clk才能正常访问phy register。
socket
那为何下载了mac地址后才异常呢?不下载的又正常呢?模块化
freescale控制器获取mac address流程以下:
1)模块化参数设置,若是没有跳到步骤2;
2)device tree中设置,若是没有跳到步骤3;
3)from flash / fuse / via platform data,若是没有跳到步骤4;
4)FEC mac registers set by bootloader===》即靠usb方式下载mac address ,若是没有跳到步骤5;
5)靠kernel算一个随机数mac address出来,而后写入mac函数
那为何下载了mac地址后才异常呢?
下了mac后,会执行步骤4,不会执行步骤5,此时目前的代码不知足150ms的时序要求,没法访问phy register,
致使phy_id获取不到,所以phy_device也不会建立.net
那为何不下载的又正常呢?
不下载mac address,会执行步骤5 ,步骤5中调用了函数eth_hw_addr_random
恰好知足了150ms的时序要求,因此才能够正常
3d
跟入代码eth_hw_addr_random看下
继续看:
最终调用了kernel提供的获取随机数的一个函数,这块代码比较多就不继续追下去了。
因此这块步骤五的代码刚恰好好在这个硬件条件下,恰巧知足了150ms的reset时序要求,因此以太网才能够正常。
回归主题,根据这个ifconfig失败的现象,咱们追踪一下code:
ifconfig: SIOCSIFFLAGS: No such device,既然出现了这个问题log,咱们就从应用层的log入手,首先咱们使用strace命令来追踪下系统调用,以便于咱们追踪内核代码实现。
strace ifconfig eth0 up跟踪一下
能够发现主要是ioctl的操做,SIOCSIFFLAGS,而后咱们须要了解下这个宏的意思,说白了就是设置各类flag,靠ioctl第三个参数把所须要的动做flag传入,好比说此时要对eth0进行up动做,那么就传入IFF_UP,例如:
struct ifreq ifr;
咱们看这些主要是想知道为何会打印这个log:
ifconfig: SIOCSIFFLAGS: No such device
那么内核中又是对ioctl作了什么动做呢?由于strace命令让咱们知道了系统调用调用函数,咱们能够在kernel中直接搜索SIOCSIFFLAGS,或者去以太网驱动net目录下直接搜索更快。最终我搜到了,路径是:net/ipv4/devinet.c
咱们能够看到内核的宏定义:
查看devinet.c的代码,咱们找到了那个宏,也就是作devinet_ioctl函数中,这也就是应用层的ioctl最终的实现函数,而后咱们在里面加一些打印,
经过打印结果咱们能够确认是这个函数devinet_ioctl为应用层的ioctl的实现函数,由于你在kernel中搜SIOCSIFFLAGS宏的话会有不少地方出现的,因此咱们须要确认咱们找的函数
没问题:
看到这里返回值ret是-19,那么咱们继续顺着追踪下去,上代码:
net/core/dev.c
继续追踪:net/core/dev.c
所以咱们能够看到返回值-19就是以下代码产生的
所以咱们须要追踪__dev_open函数,继续看代码:
经过调试,好比说加打印,或者是经验咱们能够推断出是这里返回的-19,那么这个ndo_open又是在哪里回调的呢?
咱们能够看到ops这个结构的结构体
struct net_device dev
const struct net_device_ops ops = dev->netdev_ops;
这里熟悉驱动的朋友应该能够猜到这在在freescale的以太网控制器驱动中必定有它的实现
net_device_ops就是kernel提供给drvier操做net_device的一些操做方法,具体实现天然由相应厂商的driver本身去实现。
路径:drivers/net/Ethernet/freescale/fec_main.c
咱们能够在这个fec_enet_open函数中加入dump_stack来看下整个调用状况
咱们打出kernel的dump_stack信息来看:
这个调用过程就是应用层ioctl一直到kernel最底层fec_enet_open的过程。
应用代码这样:
整体流程:kill() -> kill.S -> swi陷入内核态 -> 从sys_call_table查看到sys_kill -> ret_fast_syscall -> 回到用户态执行kill()下一行代码
Ioctl《==ret_fast_syscall 《==SyS_ioctl《==do_vfs_ioctl《==vfs_ioctl《==sock_ioctl《==
devinet_ioctl《==dev_change_flags《==__dev_change_flags《==__dev_open《==fec_enet_open
我附上每一个函数的代码:
若是你们想看系统调用流程的话,参考这篇,我就不作这块的说明了:
Linux系统调用(syscall)原理
http://gityuan.com/2016/05/21/syscall/
Arm Linux系统调用流程详细解析
https://www.cnblogs.com/cslunatic/p/3655970.html
http://stackoverflow.com/questions/5308090/set-ip-address-using-siocsifaddr-ioctl
http://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.commtrf2/ioctl_socket_control_operations.htm
https://lkml.org/lkml/2017/2/3/396
linux PHY驱动
http://www.latelee.org/programming-under-linux/linux-phy-driver.html
Linux PHY几个状态的跟踪
http://www.latelee.org/programming-under-linux/linux-phy-state.html
第十六章PHY -基于Linux3.10
https://blog.csdn.net/shichaog/article/details/44682931
```