DPDK裝置驅動的匹配和初始化

2021-09-24 13:51:44 字數 3390 閱讀 9231

前言:dpdk使用了uio(使用者空間i/o)的機制,跳過核心態的網絡卡驅動,轉而使用使用者態的收發包驅動,從驅動到記憶體和資料報,繼而到資料報的處理,這是乙個完整的收發包流程。這篇主要介紹裝置驅動的初始化,和收發包的處理。所選**以dpdk-17.02版本為依據。

資料報的驅動初始化是在rte_eal_init()進行的,總體上分為2個階段進行。

我們就按照這個順序進行介紹。

***********************************===

記憶體池的建立使用的介面是rte_mempool_create()。在仔細分析**之前,先說明一下mempool的設計思路:在dpdk-17.02版本中(和2.1等先前版本在初始化略有差異),總體來說,mempool的組織是通過3個部分實現的

接下來,就來具體看看mempool的建立和初始化過程。

先注意一下rte_mempool_create的引數中的兩個-mp_initobj_init,前者負責初始化mempool中配置的私有引數,如在資料報中加入的我們自己的私有結構;後者負責初始化每個mempool物件。我們然後按照mempool的3個關鍵部分展開說明。

rte_build_bug_on((sizeof(struct rte_mempool) & rte_cache_line_mask) != 0);
然後從mempool佇列中取出頭節點,我們建立的mempool結構填充好,就掛接在這個節點上。接下來做一些檢查工作和建立flag的設定。

rte_mempool_calc_obj_size()計算了每個obj的大小,這個obj又是由三個部分組成的,objhdr,elt_size,objtlr,即頭,資料區,尾。在沒有開啟rte_librte_mempool_debug除錯時,沒有尾部分;頭部分的結構為:struct rte_mempool_objhdr,通過這個頭部,mempool中的obj都是鏈結到佇列中的,所以,提供了遍歷obj的方式(儘管很少這麼用)。函式返回最後計算對齊後的obj的大小,為後面分配空間提供依據。

然後分配了乙個mempool佇列條目,為後面掛接在佇列做準備。

te = rte_zmalloc("mempool_tailq_entry", sizeof(*te), 0);

if (te == null)

接下來,就是計算整個mempool頭結構多大,吐槽這裡的命名!

mempool_size = mempool_header_size(mp, cache_size);

mempool_size += private_data_size;

mempool_size = rte_align_ceil(mempool_size, rte_mempool_align);

mempool_size這個名字太有誤導性,這裡指的是計算mempool的頭結構的大小。而不是記憶體池實際的大小。在這裡可以清晰的看出這個mempool頭結構是由三部分組成的。cache計算的是所有核上的cache之和。

然後,分配這個mempool頭結構大小的空間,填充mempool結構體,並把mempool頭結構中的cache位址分配給mempool。初始化這部分cache.

最後就是掛接mempool結構。tailq_insert_tail(mempool_list, te, next);

這部分的建立是在函式rte_mempool_populate_default()中完成的。

首先計算了每個elt的總共的大小

total_elt_sz = mp->header_size + mp->elt_size + mp->trailer_size;
然後計算為這些元素需要分配多大的空間,rte_mempool_xmem_size(n, total_elt_sz, pg_shift);

接著rte_memzone_reserve_aligned()分配空間。

終於到關鍵的一步了,rte_mempool_populate_phys()把元素新增到mempool,實際上就是把申請的空間分給每個元素。

先看到的是這麼一段**:

if ((mp->flags & mempool_f_pool_created) == 0)
這就是建立ring的過程咯,其中的函式rte_mempool_ops_alloc()就是實現。那麼,對應的ops->alloc()在哪註冊的呢?

if ((flags & mempool_f_sp_put) && (flags & mempool_f_sc_get))

rte_mempool_set_ops_byname(mp, "ring_sp_sc", null);

else if (flags & mempool_f_sp_put)

rte_mempool_set_ops_byname(mp, "ring_sp_mc", null);

else if (flags & mempool_f_sc_get)

rte_mempool_set_ops_byname(mp, "ring_mp_sc", null);

else

rte_mempool_set_ops_byname(mp, "ring_mp_mc", null);

就是根據ring的型別,來註冊對應的操作函式,如預設的就是ring_mp_mc,多生產者多消費者模型,其操作函式不難找到:

static const struct rte_mempool_ops ops_mp_mc = ;
接下來,又分配了乙個struct rte_mempool_memhdr *memhdr;結構的變數,就是這個變數管理著mempool的實際記憶體區,它記錄著mempool實際位址區的實體地址,虛擬位址,長度等資訊。

再然後,就是把每個元素對應到mempool池中了:mempool_add_elem()。在其中,把每個元素都掛在了elt_list中,可以遍歷每個元素。最後rte_mempool_ops_enqueue_bulk(mp, &obj, 1);,最終,把元素對應的位址入隊,這樣,mempool中的每個元素都放入了ring中。

建立完成!!!

mempool的常見使用是獲取元素空間和釋放空間。

mempool是dpdk記憶體管理的重要元件,這篇重點介紹了 mempool建立使用的過程,對於系統如何做大頁對映,統一位址並沒有涉及,希望在後面的篇幅中,關注一下大頁的對映和共享記憶體等。再往後,會介紹驅動與收發包等聯絡較大的內容

DPDK的RTE INIT初始化

dpdk 中廣泛使用rte init巨集進行裝置驅動或者rte模組等的初始化工作,其核心是rte init prio巨集,定義在檔案rte common.h中。如下可見,rte init prio巨集的實現,實際為乙個附帶gcc編譯屬性的函式定義。此處用到兩個屬性,constructor和used。...

dpdk記憶體管理 初始化

dpdk的記憶體初始化工作,主要是將hugetlbfs的配置的大記憶體頁,根據其對映的實體地址是否連續 屬於哪個socket等,有效的組織起來,為後續管理提供便利。eal hugepage info init 主要是獲取配置好的hugetlbfs的相關資訊,並將其儲存在struct internal...

字元裝置驅動初始化和銷毀部分

每個字元裝置的驅動都要包含對這個字元裝置的初始化,而這個初始化其實是乙個非常固定的步驟,接著我們就來看看這個初始化怎麼寫。static inline int register chrdev unsigned int major,const char name,const struct file op...