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
```