乙太網驅動的流程淺析(一) Ifconfig主要流程 Author:張昺華 Email:[email protected] Time:2019年3月23日星期六 此文也在我的個人公眾號以及《Linux內核之旅》上有發表: "乙太網驅動流程淺析(一) ifconfig主要流程" 很喜歡一群人在研究技術, ...
乙太網驅動的流程淺析(一)-Ifconfig主要流程
Author:張昺華
Email:[email protected]
Time:2019年3月23日星期六
此文也在我的個人公眾號以及《Linux內核之旅》上有發表:乙太網驅動流程淺析(一)-ifconfig主要流程
很喜歡一群人在研究技術,一起做有意思的東西,一起分享技術帶給我們的快樂,也希望中國有更多的人熱愛技術,喜歡一起研究、分享技術,然後可以一起用我們的技術來做一些好玩的東西,可以為這個社會創造一些東西來改善人們的生活。
如下是本人調試過程中的一點經驗分享,乙太網驅動架構畢竟涉及的東西太多,如下僅僅是針對載入流程和圍繞這個問題產生的分析過程和驅動載入流程部分,並不涉及乙太網協議層的數據流程分析。
【硬體環境】 Imx6ul
【Linux kernel版本】 Linux4.1.15
【乙太網phy】 Realtek8201f
一個乙太網的案例來講述Ifconfig
1. 問題描述
【問題】
機器通過usb方式下載了mac地址後,發現乙太網無法正常使用,敲命令 ifconfig eth0 up出現:ifconfig: SIOCSIFFLAGS: No such device,而對於沒有下載乙太網mac address的機器表現均正常。調試過程中發現在乙太網控制器代碼中加入一些printk,不正常的機器又正常了,列印的位置不同,機器的乙太網有時會正常,有時會異常,十分詭異。
2. 原因分析
【根本原因】
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。
那為什麼下載了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也不會創建
那為什麼不下載的又正常呢?
不下載mac address,會執行步驟5 ,步驟5中調用了函數eth_hw_addr_random
剛好滿足了150ms的時序要求,所以才可以正常
跟入代碼eth_hw_addr_random看下
繼續看:
最終調用了kernel提供的獲取隨機數的一個函數,這塊代碼比較多就不繼續追下去了。
所以這塊步驟五的代碼剛剛好好在這個硬體條件下,恰巧滿足了150ms的reset時序要求,所以乙太網才可以正常。
3. 乙太網流程分析跟蹤
3.1 Ifconfig主要流程
回歸主題,根據這個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
4. 網址分享
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
```