DPDK之PMD原理

PMD是Poll Mode Driver的縮寫,即基於用戶態的輪詢機制的驅動。本文將介紹PMD的基本原理。

在不考慮vfio的情況下,PMD的結構圖如下:

圖1. PMD結構圖

雖然PMD是在用戶態實現設備驅動,但還是依賴於內核提供的策略。其中uio模塊,是內核提供的用戶態驅動框架,而igb_uio是DPDK kit中擁有與uio交互,bind指定網卡的內核模塊。

當使用DPDK腳本dpdk-devbind來bind網卡時,會通過sysfs與內核交互,讓內核使用指定驅動來匹配網卡。具體的行爲向/sys/bus/pci/devices/(pci id)/driver_override寫入指定驅動名稱,或者向/sys/bus/pci/drivers/igb_uio(驅動名稱)/new_id寫入要綁定網卡的PCI ID。前者是配置設備,讓其選擇驅動。後者是是配置驅動,讓其支持新的PCI設備。按照內核的文檔https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci,這兩個動作都會促使驅動bind新設備。但是在dpdk-devbind腳本中,還是通過向/sys/bus/pci/drivers/igb_uio(驅動名稱)/bind寫入pci id來保證bind。—— 也許是處於兼容性考慮吧。

當使用igb_uio bind指定設備後,內核會調用igb_uio註冊的struct pci_driver的probe函數,即igbuio_pci_probe。在這個函數中,設置PCI的一些操作(如設置PCI BAR、DMA等),不是重點,那是驅動工程師的職責:) 對於PMD來說,重點是與UIO的交互。

1. 調用igbuio_setup_bars,設置uio_info的uio_mem和uio_port。

圖2. igbuio_setup_bars

2. 設置uio_info的其他成員

圖3. igb_uio設置uio_info

3. 調用uio_register_device,註冊uio設備。

查看/dev目錄,可以發現對應的uioX設備,如下圖:

圖4. uio設備

打開uio設備

這時,應用層已經可以使用uio設備了。DPDK的應用層代碼,會打開uioX設備。在函數pci_uio_alloc_resource中,

圖5. 打開uio設備

當open對應的uio設備時,對應的內核操作爲uio_open,其又會調用igb_uio的open函數,流程圖如下:

圖6. open uio設備

igb_uio的默認中斷模式爲RTE_INTR_MODE_MSIX,在igbuio_pci_enable_interrupts的關鍵代碼如下:

圖7. 設置中斷信息

圖8. 註冊中斷

當打開uio設備時,igb_uio註冊了一箇中斷。這時大家應該有個疑問,PMD不是用戶態輪詢設備嗎?爲什麼還要申請中斷,註冊中斷處理函數呢?這是因爲,即使應用層可以通過uio來實現設備驅動,但是設備的某些事件還是需要內核進行響應,然後通知應用層。當然,現在的中斷處理已經非常簡單了。

圖9. igb_uio中斷處理函數

其中的關鍵步驟是調用uio_event_notify。

圖10. uio_event_notify

這個函數很簡單:1. 增加uio設備的“事件”數; 2. 喚醒在idev->wait等待隊列中的task;3. 使用信號異步通知async_queue隊列中的進程;目前DPDK沒有使用異步IO的方式,所有對於DPDK的PMD來說,只有前兩個語句有用。

uio模塊除了實現了上面的“事件”通知,還支持了mmap方法,用於將註冊的uio設備的“內存空間”映射到應用空間。其mmap的函數爲uio_mmap,關鍵代碼如下:

圖11.uio_mmap

至此,uio已經可以讓PMD的應用層訪問設備的大部分資源了。接下來,要轉過去看看PMD的應用層。

當DPDK的app啓動時,會進行EAL初始化,如下圖:

圖12. 應用層uio初始化

在pci_uio_alloc_resource中,主要是打開dpdk要管理的uio設備

圖13. 打開ui設備

同時,DPDK還需要把PCI設備的BAR映射到應用層。在pci_uio_map_resource函數中,除了調用上圖中的pci_uio_alloc_resource,還會調用pci_uio_map_resource_by_index做資源映射。

圖14. pci_uio_map_resource

下面就是PMD的應用層的驅動實現了。以最簡單的e1000驅動爲例,在其初始化函數eth_igb_dev_init中,

圖15. 註冊e1000的中斷處理函數

從圖11和圖12的代碼中,可以看出當uio設備有事件時,由eth_igb_interrupt_handler負責處理,實現了用戶態的中斷處理。

圖16. eth_igb_interrupt_handler

eth_igb_interrupt_handler非常簡單,只是處理設備的狀態變化事件,如link status。

接下來,就是最重要的了,PMD如何讀取網卡數據。DPDK的應用代碼,會調用rte_eth_rx_burst讀取數據報文。

圖17.rte_eth_rx_burst

在這個函數中,會調用驅動dev->rx_pkt_burst來做實際的操作。以e1000爲例,即eth_igb_recv_pkts。

圖18 eth_igb_recv_pkts

圖19 eth_igb_recv_pkts

這裏的實現很簡單。如果網卡接收buffer的描述符表示已經完成一個報文的接收(有E1000_RXD_STAT_DD標誌),則rte_mbuf_raw_alloc一個mbuf,進行處理。如果沒有報文,直接跳出循環。

對應RTC模型的DPDK應用來說,就是不斷的調用rte_eth_rx_burst去“問”網卡是否有新的報文。如果有,就取走所有的報文或達到參數nb_pkts的上限。然後進行報文處理,處理完畢,再次循環。

以上就是PMD的大體流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章