記錄一次USB虛擬網(wǎng)絡(luò)問題排查
背景介紹
項目是用yocto構(gòu)建的,在升級kernel及yocto后(見http://www.rzrgm.cn/ma-yangbiao/p/19149251), 發(fā)現(xiàn)某些功能不能正常工作。
問題介紹
目標(biāo)機(jī)是某個不常用的x86平臺,該x86 盒子通過USB連接MDM9150, 在該x86盒子上跑某個應(yīng)用程序過程中,發(fā)現(xiàn)沒有像預(yù)期一樣建立和初始化USB虛擬網(wǎng)口custom_usb0。
調(diào)試
由于qxdm log在該目標(biāo)機(jī)上受限。加入一些文本打印,重新編譯運(yùn)行,進(jìn)一步調(diào)試發(fā)現(xiàn)是因為我們一個管理網(wǎng)絡(luò)的服務(wù)ioctl返回錯誤,這個ioctl是自己客制化的驅(qū)動代碼里負(fù)責(zé)處理的,
于是把相關(guān)的驅(qū)動編譯成模塊然后手動加載進(jìn)內(nèi)核,其中現(xiàn)象如下
$ sudo insmod ./custom_usb.ko
insmod: ERROR: could not insert module custom_usb.ko: Unknown symbol in module
$dmesg | tail
[ 183.497776] custom_usb: Unknown symbol usbnet_suspend (err -2)
[ 183.497778] custom_usb: Unknown symbol usbnet_start_xmit (err -2)
[ 183.497793] custom_usb: Unknown symbol usbnet_stop (err -2)
[ 183.497797] custom_usb: Unknown symbol usbnet_disconnect (err -2)
[ 183.497801] custom_usb: Unknown symbol usbnet_probe (err -2)
[ 183.497804] custom_usb: Unknown symbol usbnet_resume (err -2)
這幾個符號都來自linux kernel 驅(qū)動代碼,具體在kernel/drivers/net/usb/usbnet.c,由CONFIG_USB_USBNET決定是否編譯,但是在我的defconfig中CONFIG_USB_USBNET已經(jīng)被顯示設(shè)置位y了。
于是在我的defconfig把CONFIG_USB_USBNET=m,重新編譯后待會手動加載看看是不是報錯,
同時該文件中有幾處打印用的netdev_dbg,為了方便調(diào)試有2種方法運(yùn)行時看到打印信息,
- 把netdev_dgb改成netdev_warn
- 改CONFIG_DYNAMIC_DEBUG=y,這是為了打開編譯時動態(tài)打印開關(guān),運(yùn)行時還需要打開運(yùn)行時調(diào)試開關(guān):echo 'file drivers/net/usb/usbnet.c +p' > /sys/kernel/debug/dynamic_debug/control,這樣即使是netdev_dbg也能打印到dmesg
我選擇方法1,
再次編譯完執(zhí)行insmod usbnet.ko發(fā)現(xiàn)沒有錯誤,再手動insmod custom_usb.ko也沒有錯誤.
說明代碼沒問題,回過頭來在usbnet.c-> usbnet_init()里加一句打印 pr_err("usbnet_init called\n");
我的defconfig里再設(shè)置CONFIG_USB_USBNET=y,重新編譯更新,再次重啟發(fā)現(xiàn)沒有這句打印, 有也就是說這個usbnet壓根沒有自動加載。
于是檢查最終的defconfig檢查一下,發(fā)現(xiàn)最終的.config里CONFIG_USB_USBNET=m
經(jīng)過檢查我的kernel recipe bbapend代碼,有這么一段值得懷疑
do_preconfigure_prepend () {
cat ${WORKDIR}/custom/defconfig >> ${WORKDIR}/defconfig
}
如果我更新后,還有別的部分更新那就會覆蓋我的配置。
于是嘗試刪除上述這三行,同時加入這2行,
SRC_URI += "file://custom/defconfig.cfg"
KERNEL_FEATURES += " custom/defconfig.cfg"
再次編譯,查看.config.還是不起作用CONFIG_USB_USBNET=m.
閱讀源碼,Kconfig并沒有提及CONFIG_USB_USBNET是否有依賴項,查看代碼CONFIG_USB_USBNET實(shí)際是屬于CONFIG_USB_NET_DRIVERS的一個子模塊,所以嘗試也配置
CONFIG_USB_NET_DRIVERS=y
CONFIG_DYNAMIC_DEBUG=y
再次編譯,這次可以看到最終的.config里已經(jīng)成功使能CONFIG_DYNAMIC_DEBUG=y了。說明kernel升級后這些配置項與舊版本里的配置不再一樣。
再重新燒image,重新嘗試,在解決了其它問題后功能終于正常。
總結(jié)
經(jīng)學(xué)習(xí) Yocto 的內(nèi)核配置流程是:從 BSP 提供的 defconfig 開始,按順序合并 KERNEL_FEATURES 和 SRC_URI 中的 .cfg 片段, 運(yùn)行 merge_config.sh + olddefconfig 生成最終 .config。
usbnet的功能
最后分享一下,這次debug過程中加深了對usbnet的功能的認(rèn)識,
usbnet.c 是一個通用的 USB 網(wǎng)絡(luò)驅(qū)動核心,定義了一個結(jié)構(gòu)體 struct usbnet,它包含struct net_device, struct usb_interface等指針,就像膠水一樣把usb操作與網(wǎng)絡(luò)的操作net_device聯(lián)系在一起
它實(shí)現(xiàn)了一個“USB 網(wǎng)絡(luò)設(shè)備”的抽象:負(fù)責(zé)處理 USB 傳輸、數(shù)據(jù)包隊列,以及與內(nèi)核的 net_device 的集成交互。通過usb虛擬網(wǎng)口收發(fā)數(shù)據(jù)等通用邏輯放在這里。
它被許多小型驅(qū)動程序共享:例如 cdc_ether、asix、r8152、rmnet_usb 等都依賴于 usbnet 作為基礎(chǔ),這些特定廠商的驅(qū)動只需重寫/寫少量部分即可。它將通用邏輯與硬件特性分離:核心部分負(fù)責(zé)收發(fā)(TX/RX)管理等通用邏輯,而其它特定的驅(qū)動則通過重寫鉤子函數(shù)來實(shí)現(xiàn)設(shè)備特定的行為。
它還導(dǎo)出了一些輔助函數(shù),供其他驅(qū)動調(diào)用。
最終效果是:當(dāng)你插入一個 USB 網(wǎng)卡時,內(nèi)核可以將其識別為 usb0;而通過少量的驅(qū)動特定代碼,也可以顯示為例如 rmnet_usb0 等。
具體解讀
模塊初始化/退出
usbnet_init():注冊通用的 USB 驅(qū)動框架,準(zhǔn)備好供子驅(qū)動調(diào)用。
usbnet_exit():注銷驅(qū)動,釋放資源。
設(shè)備探測與移除
usbnet_probe():當(dāng)匹配的 USB 設(shè)備插入時調(diào)用,分配并初始化 struct usbnet 和 struct net_device。
usbnet_disconnect():設(shè)備拔出時清理資源。
網(wǎng)絡(luò)設(shè)備操作
usbnet_open() / usbnet_stop():啟動或停止接口,提交/取消接收 URB。
usbnet_start_xmit():把上層網(wǎng)絡(luò)棧傳下來的數(shù)據(jù)包封裝成 URB,提交給 USB core。
usbnet_change_mtu()、usbnet_set_mac_address():常見的 net_device 操作。
數(shù)據(jù)收發(fā)路徑
發(fā)送 (TX):start_xmit() → 構(gòu)造 URB → usb_submit_urb() → 設(shè)備。
接收 (RX):URB 完成回調(diào) → rx_complete() → 調(diào)用 netif_rx() 把包交給內(nèi)核協(xié)議棧。
提供 rx_fixup() / tx_fixup() 鉤子,供子驅(qū)動修正報文格式。
電源管理與錯誤恢復(fù)
處理 USB suspend/resume。提供 usbnet_tx_timeout() watchdog,避免傳輸卡死。

浙公網(wǎng)安備 33010602011771號