天天看点

DPDK内存管理 -----(一)初始化

1 前言

DPDK通过使用hugetlbfs,减少CPU TLB表的Miss次数,提高性能。

2 初始化

DPDK的内存初始化工作,主要是将hugetlbfs的配置的大内存页,根据其映射的物理地址是否连续、属于哪个Socket等,有效的组织起来,为后续管理提供便利。

2.1 eal_hugepage_info_init()

eal_hugepage_info_init()主要是获取配置好的Hugetlbfs的相关信息,并将其保存在struct internal_config数据结构中。主要工作如下:

1、读取/sys/kernel/mm/hugepages目录下的各个子目录,通过判断目录名称中包含"hugepages-"字符串,获取hugetlbfs的相关子目录,并获取hugetlbfs配置的内存页大小。

例如:

[[email protected]_DEFAULT ~]# ls -ltr /sys/kernel/mm/hugepages/

total 0

drwxr-xr-x 2 root root 0 2014-11-04 15:54 hugepages-2048kB

2、通过读取/proc/mounts信息,找到hugetlbfs的挂载点。

例如:    

[email protected]:~# cat /proc/mounts

rootfs / rootfs rw 0 0

sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0

proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0

udev /dev devtmpfs rw,relatime,size=1016836k,nr_inodes=254209,mode=7550 0

devpts /dev/pts devptsrw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0

tmpfs /run tmpfsrw,nosuid,noexec,relatime,size=205128k,mode=755 0 0

/dev/disk/by-uuid/fd1dbca3-ac30-4bac-b93a-0d89b0fd152c/ ext4 rw,relatime,errors=remount-ro,user_xattr,barrier=1,data=ordered 0 0

none /sys/fs/fuse/connections fusectl rw,relatime 0 0

none /sys/kernel/debug debugfs rw,relatime 0 0

none /sys/kernel/security securityfs rw,relatime 0 0

none /run/lock tmpfsrw,nosuid,nodev,noexec,relatime,size=5120k 0 0

none /run/shm tmpfs rw,nosuid,nodev,relatime 0 0

none /media/sf_F_DRIVE vboxsf rw,nodev,relatime 0 0

gvfs-fuse-daemon /home/chuanxinji/.gvfsfuse.gvfs-fuse-daemon rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0

/dev/sr0 /media/VBOXADDITIONS_4.3.10_93012 iso9660ro,nosuid,nodev,relatime,uid=1000,gid=1000,iocharset=utf8,mode=0400,dmode=05000 0

none /mnt/hugehugetlbfs rw,relatime 0 0

[email protected]:~#

3、通过读取/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages,获取配置的hugepages个数。

[email protected]:~# cat/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

64

[email protected]:~#

4、以打开文件的方式,打开挂载点目录,为其FD设置互斥锁,Why??

上述所有获取的信息,都保存在internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]中,hugepage_info数据结构如下:

struct hugepage_info {

  size_t hugepage_sz;

  const char *hugedir;

  uint32_t num_pages[RTE_MAX_NUMA_NODES];

  

  int lock_descriptor;

};

赋值如下:

hpi->hugepage_sz = 2M;

hpi->hugedir = /mnt/huge;

hpi->num_pages[0] = 64; // 由于此时还不知道哪些内存页分处在哪个socket上,故,都先放在socket-0上。

hpi->lock_descriptor = open(hpi->hugedir, O_RONLY); // 在读取hugetlbfs配置的时候,需要锁住整个目录。当所有hugepage都mmap完成后,会解锁。

5、将internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]按内存页的大小排序。

2.2 rte_eal_config_create()

rte_eal_config_create()主要是初始化rte_config.mem_config。如果是以root用户运行dpdk程序的话,rte_config.mem_config指向/var/run/.rte_config文件mmap的一段 sizeof(struct rte_mem_config) 大小的内存。 rte_config.mem_config = /var/run/.rte_config文件mmap的首地址。

struct rte_config {

    uint32_tmaster_lcore;      

        ......

    structrte_mem_config *mem_config;

} __attribute__((__packed__));

struct rte_mem_config数据结构如下:

struct rte_mem_config  {

   volatile uint32_t magic;  

   uint32_t nchannel;   

   uint32_t nrank;      

   rte_rwlock_t mlock;  

   rte_rwlock_t qlock;  

   rte_rwlock_t mplock; 

   uint32_t memzone_idx;

   struct rte_memseg memseg[RTE_MAX_MEMSEG];   

   struct rte_memzone memzone[RTE_MAX_MEMZONE];

   struct rte_memseg free_memseg[RTE_MAX_MEMSEG];

   struct rte_tailq_head tailq_head[RTE_MAX_TAILQ];

   struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];

} __attribute__((__packed__));

2.3 rte_eal_hugepage_init()

rte_eal_hugepage_init()主要是在/mnt/huge目录下创建hugetlbfs配置的内存页数(在本文中就是64)的rtemap_xx文件,并为每个rtemap_xx文件做mmap映射,保证mmap后的虚拟地址与实际的物理地址是一样的。

具体如下:

1、创建nr_hugepages个structhugepage_file数组,即有多少个内存页,创建多少个struct hugepage_file数据结构。struct hugepage_file数据结构如下:

struct hugepage_file {

   void *orig_va;      

   void *final_va;    

   uint64_t physaddr; 

   size_t size;       

   int socket_id;     

   int file_id;       

   int memseg_id;     

   #ifdef RTE_EAL_SINGLE_FILE_SEGMENTS

   int repeated;      

   #endif

   char filepath[MAX_HUGEPAGE_PATH];

};

2、有多少个内存页,在挂载点目录下创建多少个rtemap_xx文件,如下所示,并为每一个文件mmap一个hugepage_sz大小的内存区域。其中

hugepage_file->orig_va = 记录每个rtemap_xx文件mmap的首地址;

hugepage_file->file_id = 创建的rtemap_xx的顺序,就是xx的值;

hugepage_file->filepath =/mnt/huge/rtemap_xx;

hugepage_file->size = hugepage_sz,也就是2M;

[email protected]:~# ls -tlr /mnt/huge/

total 131072

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_2

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_1

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_0

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_8

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_7

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_6

... ...

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_60

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_59

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_58

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_63

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_62

-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_61

3、通过读取/proc/self/pagemap页表文件,得到本进程中虚拟地址与物理地址的映射关系。使用上一步中,每个rtemap_xx文件mmap得到的虚拟地址,除以操作系统内存页的大小4k,得到一个偏移量。根据这个偏移量,在/prox/self/pagemap中,得到物理地址的页框,假设为page,那么,物理页框page乘以操作系统内存页的大小4K,再加上虚拟地址的页偏移,就是物理地址。每个rtemap_xx映射的物理地址保存在对应的hugepage_file->physaddr中。

physaddr = ((page & 0x7fffffffffffffULL) * page_size) + ((unsigned long)virtaddr % page_size); 

4、读取/proc/self/numa_maps,得到每个rtemap_xx文件mmap得到的虚拟地址在哪个Socket上,即,哪个CPU上。其socketid保存在对应的hugepage_file->socket_id中。

[email protected]:~# cat /proc/self/numa_maps 

00400000 default file=/bin/cat mapped=7 mapmax=2 N0=7

0060a000 default file=/bin/cat anon=1 dirty=1 N0=1

0060b000 default file=/bin/cat anon=1 dirty=1 N0=1

025c1000 default heap anon=3 dirty=3 active=0 N0=3

7fdf0222c000 default file=/usr/lib/locale/locale-archive mapped=10 mapmax=61 N0=10

7fdf0290f000 default file=/lib/x86_64-linux-gnu/libc-2.15.so mapped=82 mapmax=128 N0=82

7fdf02ac4000 default file=/lib/x86_64-linux-gnu/libc-2.15.so

7fdf02cc3000 default file=/lib/x86_64-linux-gnu/libc-2.15.so anon=4 dirty=4 N0=4

7fdf02cc7000 default file=/lib/x86_64-linux-gnu/libc-2.15.so anon=2 dirty=2 N0=2

7fdf02cc9000 default anon=3 dirty=3 active=1 N0=3

7fdf02cce000 default file=/lib/x86_64-linux-gnu/ld-2.15.so mapped=27 mapmax=122 N0=27

7fdf02ed7000 default anon=3 dirty=3 N0=3

7fdf02eee000 default anon=2 dirty=2 N0=2

7fdf02ef0000 default file=/lib/x86_64-linux-gnu/ld-2.15.so anon=1 dirty=1 N0=1

7fdf02ef1000 default file=/lib/x86_64-linux-gnu/ld-2.15.so anon=2 dirty=2 N0=2

7fff09be1000 default stack anon=3 dirty=3 N0=3

7fff09cc2000 default

5、在hugepage_file数组中,根据物理地址,按从小到大的顺序,将hugepage_file排序。

6、根据按物理地址排序后的结果,判断物理地址是否连续,重新mmap/mnt/huge/retmap_xx文件,使得物理地址等于第二次mmap后的虚拟地址。第二次mmap得到的虚拟地址保存在对应的hugepage_file->final_va中。

7、munmap释放第一步中各个rtemap_xx文件首次mmap得到的内存地址。

8、计算每个socket上包含多少个hugepage,信息保存在internal_config.hugepage_info[0].num_pages[socket]中。

9、calc_num_pages_per_socket(),目的是什么???

10、为/var/run/.rte_hugepage_info文件mmap一段nr_hugepages * sizeof(structhugepage_file)大小的内存块,并将第一步中创建的hugepage_file数组中的所有内容,都copy到这一块内存中。

11、rte_config.mem_config->memseg[]数组记录hugepage_file映射后物理地址连续的块数,hugepage_file->memseg_id为该huepage_file的物理地址在哪个rte_config.mem_config->memseg[]数组中。struct rte_memseg数据结构如下:

struct rte_memseg {

   phys_addr_t phys_addr;     

   union {

       void *addr;        

       uint64_t addr_64;  

   };

#ifdef RTE_LIBRTE_IVSHMEM

    phys_addr_tioremap_addr;

#endif

   size_t len;              

   size_t hugepage_sz;      

   int32_t socket_id;         

   uint32_t nchannel;         

   uint32_t nrank;            

#ifdef RTE_LIBRTE_XEN_DOM0

   uint64_t mfn[DOM0_NUM_MEMBLOCK];

#endif

} __attribute__((__packed__));

rte_config.mem_config->memseg[j].phys_addr = 各物理地址是连续的内存块的首地址。

rte_config.mem_config->memseg[j].addr = 各个物理地址是连续的内存块对应的虚拟地址的首地址。由于物理地址和虚拟地址是相同的,这个值应该等于phys_addr。

rte_config.mem_config->memseg[j].len = 各个物理地址是连续的内存块的大小。

rte_config.mem_config->memseg[j].socket_id = 内存块在哪个socket上。。

rte_config.mem_config->memseg[j].hugepage_sz = hugepage内存页的大小。本文中是2M。

2.4 rte_eal_memdevice_init()

rte_eal_memdevice_init()初始化rte_config.mem_config->nchannel和rte_config.mem_config->nrank。

rte_config.mem_config->nchannel = 启动参数中“-n”指定的值,不能为0,不能大于4。

rte_config.mem_config->nrank = 启动参数中“-r”指定的值。不能为0,不能大于16。

2.5 rte_eal_memzone_init()

rte_eal_memzone_init()主要负责初始化rte_config.mem_config->free_memseg[]及rte_config.mem_config->memzone[]。其中,rte_config.mem_config->free_memseg[]记录空闲的rte_config.mem_config->memseg[]。

DPDK内存管理 -----(一)初始化

文章来源

http://www.cnblogs.com/MerlinJ/p/4074391.html