dpdk内存管理

一、整体结构

1.结构关系

在这里插入图片描述

##2. mmap文件映射关系

结构变量路径size
rte_mem_configrte_config.mem_config/var/run/dpdk/configsizeof(*rte_config.mem_config)
rte_memseg_listrte_config.mem_config->memsegs[0].memseg_arr.data/var/run/dpdk/fbarray_memseg-32768k-0-0need_len * sizeof(struct rte_memseg) + msk_needsz
rte_memzonerte_config.mem_config->memzones.data/var/run/dpdk/fbarray_memzoneneed_len * sizeof(struct rte_memzone) + msk_needsz
hugepage_infointernal_config.hugepage_info[0]/var/run/dpdk/hugepage_infosizeof(internal_config.hugepage_info)
rte_memseg( (struct rte_memseg*) (rte_config.mem_config->memsegs[0].memseg_arr.data + elt_size*idx) )->addr/mnt/huge/fbarray_rtemap_0hugepage_sz (2M\32M\1G…)

上面的rte_memseg_list中保存着的rte_memseg, 以及rte_config.mem_config->memzones,
这两个数据都是用rte_fbarray维护,他所需要的大小计算方法是

mmap_len = calc_data_size(page_sz, elt_sz, len);
//其中,
//page_sz是页大小,la架构是16K,
//elt_sz是数据结构的大小【 sizeof(struct rte_memseg)  、 sizeof(struct rte_memzone) 】
//len 是所需的长度
//fbarray保存一个个结构后,最后的位置有msk用于维护各个的使用情况
static size_t
calc_data_size(size_t page_sz, unsigned int elt_sz, unsigned int len)
{
    size_t data_sz = elt_sz * len;
    size_t msk_sz = calc_mask_size(len);
    return RTE_ALIGN_CEIL(data_sz + msk_sz, page_sz);
}

3.锁的应用

读写函数锁的种类调用位置rte_rwlock_t rte_mem_config::mlock;rte_rwlock_read_lockrte_rwlock_read_unlockrte_rwlock_write_lockrte_rwlock_write_unlock自旋锁rte_memzone_reserve_thread_saferte_memzone_freerte_memzone_lookuprte_eal_memzone_initrte_memzone_walk

二、数据结构解析

1.rte_config

所有的数据都存在全局变量rte_config中,其中内存管理的都在struct rte_mem_config *mem_config结构中。

struct rte_config {
    uint32_t master_lcore;       /**< Id of the master lcore */
    uint32_t lcore_count;        /**< Number of available logical cores. */
    uint32_t numa_node_count;    /**< Number of detected NUMA nodes. */
    uint32_t numa_nodes[RTE_MAX_NUMA_NODES]; /**< List of detected NUMA nodes. */
    uint32_t service_lcore_count;/**< Number of available service cores. */
    enum rte_lcore_role_t lcore_role[RTE_MAX_LCORE]; /**< State of cores. */
    /** Primary or secondary configuration */
    enum rte_proc_type_t process_type;
    /** PA or VA mapping mode */
    enum rte_iova_mode iova_mode;
    /**
     * Pointer to memory configuration, which may be shared across multiple
     * DPDK instances
     */
    struct rte_mem_config *mem_config;
} __attribute__((__packed__));

2.rte_mem_config

2.1 创建过程

在这里插入图片描述

  1. mem_config会保存在“/var/run/dpdk/config”文件中
    2)虚拟地址会先通过eal_get_virtual_area获得进程的匿名映射的基地址,再通过这个基地址映射config文件,得到最后的rte_config.mem_config的地址

2.2 主要结构

mem_config总共有三个重要的结构,其余是一些大小、标志、锁等,这三个重要的结构分别是memzones、memseg_list、malloc_heap
memzone 和memseg都封装在了rte_fbarray的data中
memzone = rte_fbarray->data + idx * rte_fbarray->elt_sz;
memseg = rte_fbarray->data + idx * rte_fbarray->elt_sz;
在这里插入图片描述

3. memzones

在这里插入图片描述

3.1初始化流程

在这里插入图片描述

主进程创建"memzone"的fbarray,配置文件存储在"/var/run/dpdk/fbarray_memzone"。

3.2 创建流程

在这里插入图片描述

1)先通过malloc_heap_alloc找到一块空闲heap 的 elem,
2)然后把memzone的配置 arr = &mcfg->memzones 管理set为使用。

4. memseg

4.1 初始化流程

struct rte_memseg_list {
    RTE_STD_C11
    union {
        void *base_va;
        /**< Base virtual address for this memseg list. */
        uint64_t addr_64;
        /**< Makes sure addr is always 64-bits */
    };
    uint64_t page_sz; /**< Page size for all memsegs in this list. */
    int socket_id; /**< Socket ID for all memsegs in this list. */
    volatile uint32_t version; /**< version number for multiprocess sync. */
    size_t len; /**< Length of memory area covered by this memseg list. */
    unsigned int external; /**< 1 if this list points to external memory */
    unsigned int heap; /**< 1 if this list points to a heap */
    struct rte_fbarray memseg_arr;
};

1)这里是对管理memseg_list的结构进行初始化,
2)memseg是用 struct rte_fbarray数据结构进行维护的,
他的配置信息保存在rte_config.mem_config->memseg[id]->memseg_arr里面,映射在配置文件保存在"/var/run/dpdk/fbarray_memseg-32768k-%d-%d".
3)具体流程是
先对rte_memseg_list结构中的memseg_arr进行初始化,创建配置文件,并映射(流程和rte_fbarray的一致)。
再对rte_config.mem_config->memseg[id]进行初始化,通过eal_get_virtual_area得到后面memseg映射的基地址base_va。后面的映射大页内存的虚拟地址的基地址就是这个地址。

4.2 创建流程

4.2.1 申请多个内存段

int
eal_memalloc_alloc_seg_bulk(struct rte_memseg **ms, int n_segs, size_t page_sz,
int socket, bool exact)

  1. 这个函数先进行参数配置,把参数保存在了wa结构中,其中n_segs是此次要申请的段数,socket是申请所在的numa_id
struct alloc_walk_param {
    struct hugepage_info *hi;
    struct rte_memseg **ms;
    size_t page_sz;
    unsigned int segs_allocated;
    unsigned int n_segs;
    int socket;
    bool exact;
};
  1. 最后调用alloc_seg_walk函数来申请大页内存
    在这里插入图片描述
4.2.2 内存分段申请

1)上面eal_memalloc_alloc_seg_bulk函数中把申请的参数全部保存在了wa结构中,这里提取出来,need就是需要申请映射的次数
2)分need次数申请,最后调用alloc_seg来映射大页内存(下一节说明)。
3)映射的基地址就是 4.1节中说的rte_config.mem_config->memseg[id]->base_va.
在这里插入图片描述

4.2.3 申请内存段

这是巨页内存申请最小单元的函数alloc_seg

  1. 得到巨页的路径 “/mnt/huge/rtemap_%d”
    2)扩大巨页文件大小至alloc_sz,就是巨页大小,loongarch架构是32M
    3)下面是将32M大小的文件,mmap到用户进程空间中,得到虚拟addr
    4)通过rte_mem_virt2iova,读取"/proc/self/pagemap"进程文件,获取addr虚拟地址对应的物理地址
    整个过程完成,得到这个内存段的虚拟地址和物理地址。
    在这里插入图片描述

5. rte_fbarray

这是一个中间数据管理结构,用于维护memzone和memseg这两个数据结构

struct rte_fbarray {
    char name[RTE_FBARRAY_NAME_LEN]; /**< name associated with an array */
    unsigned int count;              /**< number of entries stored */
    unsigned int len;                /**< current length of the array */
    unsigned int elt_sz;             /**< size of each element */
    void *data;                      /**< data pointer */
    rte_rwlock_t rwlock;             /**< multiprocess lock */
};

在这里插入图片描述在这里插入图片描述

  1. rte_fbarray结构中的len是最大个数,其中memzone是RTE_MAX_MEMZONE(2560)个,memseg是下面流程计算所得。
    cur_max_mem = max_type_mem - total_type_mem;
    cur_mem = get_mem_amount(hugepage_sz,cur_max_mem);
    n_segs = cur_mem / hugepage_sz;
    if (eal_memseg_list_init(msl, hugepage_sz, n_segs,
    0, type_msl_idx, false))
  2. elt_sz是memzone和memseg结构体的sizeof。data 是具体的数据,他的映射长度是 calc_data_size计算出来的 elt_sz * len + msk_sz, 每一块都保存一个数据(struct rte_memzone 或者 struct rte_memseg).
  3. data 是具体的数据,他的映射长度是 calc_data_size计算出来的 elt_sz * len + msk_sz, 每一块都保存一个数据(struct rte_memzone 或者 struct rte_memseg)
  4. data的数据是memzone或者memseg,他是通过resize_and_map映射配置文件得到,
    memzone的数据映射文件是 “/var/run/dpdk/fbarray_memzone”
    memseg的数据映射文件是"/var/run/dpdk/fbarray_memseg-32768k-%d-%d"

6.malloc_heap

6.1 封装函数

dpdk中的malloc最终调用malloc_heap_alloc函数,从heap中获取数据,其中heap是

struct malloc_heap *heap = &rte_config.mem_config->malloc_heaps[heap_id];

在这里插入图片描述

6.2 数据结构

在这里插入图片描述

malloc_heap中的数据是用malloc_elem结构储存数据的。调用rte_malloc最终返回的地址是malloc_elem结构的尾地址
(return elem == NULL ? NULL : (void *)(&elem[1]))。

struct malloc_heap {
    rte_spinlock_t lock;
    LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
    struct malloc_elem *volatile first;
    struct malloc_elem *volatile last;
    unsigned int alloc_count;
    unsigned int socket_id;
    size_t total_size;
    char name[RTE_HEAP_NAME_MAX_LEN];
} __rte_cache_aligned;

申请的内存,都是以malloc_elem结构存在,前面是malloc_elem结构的维护数据,后面的内存大小供用户使用。

struct malloc_elem {
    struct malloc_heap *heap;
    struct malloc_elem *volatile prev;
    /**< points to prev elem in memseg */
    struct malloc_elem *volatile next;
    /**< points to next elem in memseg */
    LIST_ENTRY(malloc_elem) free_list;
    /**< list of free elements in heap */
    struct rte_memseg_list *msl;
    volatile enum elem_state state;
    uint32_t pad;
    size_t size;
    struct malloc_elem *orig_elem;
    size_t orig_size;
#ifdef RTE_MALLOC_DEBUG
    uint64_t header_cookie;         /* Cookie marking start of data */
                                    /* trailer cookie at start + size */
#endif
} __rte_cache_aligned;

6.3 申请流程

6.3.1 申请流程总述

malloc_heap_alloc是从malloc heaps中申请内存,
如果当前heaps (rte_config.mem_config->malloc_heaps[heap_id])中没有找到,调用malloc_heap_alloc_on_heap_id,会尝试从hugepage中获取more memseg,然后add到heap memory中。
当heaps中有足够的空间时,调用heap_alloc可以找到适当的malloc element,从该malloc element中得到一定大小的内存,剩余部分重新插入heaps中的free 链。
在这里插入图片描述

6.3.2 从heap中获取

1)当heap中有足够的空间,就会调用heap_alloc函数,先找到find_suitable_element合适的elem,
2) 然后调用malloc_elem_alloc函数,在找到的elem中,
3) 先调用elem_start_pt找到新的elem的起始和终止地址,原则是地址从下向上查找(具体查看new_data_start = RTE_ALIGN_FLOOR((end_pt - size),align);),新的起始地址是原来的elem的结束地址减去申请的大小。
4) 拆分(split_elem)elem,将新申请的插入elem链表中。
在这里插入图片描述

6.3.3 从大页中映射新的memseg

当heap中没有充足空间,就会调用alloc_more_mem_on_socket,最终调用try_expand_heap_primary,来确定需要申请空间的大小alloc_sz,进而得到需要申请大页的个数 n_segs = alloc_sz / pg_sz(即hugepage_sz);
然后调用alloc_pages_on_heap,进入后先映射新的hugepage,添加memseg,然后调用malloc_heap_add_memory把elem添加进到heap(rte_config.mem_config->malloc_heaps[heap_id])中。
在这里插入图片描述

7.rte_ring

7.1结构关系

在这里插入图片描述

大小
rxqsizeof(struct i40e_rx_queue)
rxq->mp外面传入的mempool
rxq->rx_ring描述符个数 * sizeof(union i40e_rx_desc)描述符总共16字节
rxq->sw_ring描述符个数 * sizeof(struct i40e_rx_entry)
struct i40e_rx_entry {
        struct rte_mbuf *mbuf;
};

驱动里面的queue,里面有描述符和buffer两部分,需要申请内存的部分总共有rxq、rxq->rx_ring(描述符)、rxq->sw_ring(存储数据地址的地址)

rxq = rte_zmalloc_socket("i40e rx queue",
                                 sizeof(struct i40e_rx_queue),
                                 RTE_CACHE_LINE_SIZE,
                                 socket_id);
rxq->mp = mp;


len = I40E_MAX_RING_DESC;
len += RTE_PMD_I40E_RX_MAX_BURST;
ring_size = RTE_ALIGN(len * sizeof(union i40e_rx_desc),
                      I40E_DMA_MEM_ALIGN);
rz = rte_eth_dma_zone_reserve(dev, "rx_ring", queue_idx,
                              ring_size, I40E_RING_BASE_ALIGN, socket_id);
rxq->mz = rz;
/* Zero all the descriptors in the ring. */
memset(rz->addr, 0, ring_size);
rxq->rx_ring_phys_addr = rz->iova;
rxq->rx_ring = (union i40e_rx_desc *)rz->addr;

rxq->sw_ring = rte_zmalloc_socket("i40e rx sw ring",
                           sizeof(struct i40e_rx_entry) * len,
                           RTE_CACHE_LINE_SIZE,
                           socket_id);

7.2 sw_ring 获取流程

运行中,收发函数会不断申请mbuf
开始阶段,会申请描述符个mbuf,并写进硬件。
在这里插入图片描述

I40e驱动rxq开始 代码解读

int
i40e_dev_rx_queue_start(struct rte_eth_dev *dev, uint16_t rx_queue_id)
{
        struct i40e_rx_queue *rxq;
        int err;
        struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);

        PMD_INIT_FUNC_TRACE();

        rxq = dev->data->rx_queues[rx_queue_id];
        if (!rxq || !rxq->q_set) {
                PMD_DRV_LOG(ERR, "RX queue %u not available or setup",
                            rx_queue_id);
                return -EINVAL;
        }

        if (rxq->rx_deferred_start)
                PMD_DRV_LOG(WARNING, "RX queue %u is deferred start",
                            rx_queue_id);
//申请rxq中的sw_ring中的mbuf
        err = i40e_alloc_rx_queue_mbufs(rxq);
        if (err) {
                PMD_DRV_LOG(ERR, "Failed to allocate RX queue mbuf");
                return err;
        }

        /* Init the RX tail register. */
        I40E_PCI_REG_WRITE(rxq->qrx_tail, rxq->nb_rx_desc - 1);

        err = i40e_switch_rx_queue(hw, rxq->reg_idx, TRUE);
        if (err) {
                PMD_DRV_LOG(ERR, "Failed to switch RX queue %u on",
                            rx_queue_id);

                i40e_rx_queue_release_mbufs(rxq);
                i40e_reset_rx_queue(rxq);
                return err;
        }
        dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED;

        return 0;
}


int
i40e_alloc_rx_queue_mbufs(struct i40e_rx_queue *rxq)
{
        struct i40e_rx_entry *rxe = rxq->sw_ring;
        uint64_t dma_addr;
        uint16_t i;

        for (i = 0; i < rxq->nb_rx_desc; i++) {
                volatile union i40e_rx_desc *rxd;
//从mempool中申请一个mbuf
                struct rte_mbuf *mbuf = rte_mbuf_raw_alloc(rxq->mp);

                if (unlikely(!mbuf)) {
                        PMD_DRV_LOG(ERR, "Failed to allocate mbuf for RX");
                        return -ENOMEM;
                }

                rte_mbuf_refcnt_set(mbuf, 1);
                mbuf->next = NULL;
                mbuf->data_off = RTE_PKTMBUF_HEADROOM;
                mbuf->nb_segs = 1;
                mbuf->port = rxq->port_id;

//获取mbuf的物理地址
                dma_addr =
                        rte_cpu_to_le_64(rte_mbuf_data_iova_default(mbuf));
//写描述符
                rxd = &rxq->rx_ring[i];
                rxd->read.pkt_addr = dma_addr;
                rxd->read.hdr_addr = 0;
#ifndef RTE_LIBRTE_I40E_16BYTE_RX_DESC
                rxd->read.rsvd1 = 0;
                rxd->read.rsvd2 = 0;
#endif /* RTE_LIBRTE_I40E_16BYTE_RX_DESC */

                rxe[i].mbuf = mbuf;
        }

        return 0;
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值