前言: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_init
和obj_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...