1. 程式人生 > >乙太網驅動的流程淺析(一)-Ifconfig主要流程【原創】

乙太網驅動的流程淺析(一)-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

```

E