DPDK 內存管理(一)(內存初始化)

轉自:http://blog.csdn.net/xy010902100449/article/details/47282995

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配置的內存頁大小。例如:  

  [root@YMOS_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的掛載點。例如:    

  root@Ubuntu:~# 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=755 0 0
  devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
  tmpfs /run tmpfs rw,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 tmpfs rw,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/.gvfs fuse.gvfs-fuse-daemon rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
  /dev/sr0 /media/VBOXADDITIONS_4.3.10_93012 iso9660 ro,nosuid,nodev,relatime,uid=1000,gid=1000,iocharset=utf8,mode=0400,dmode=0500 0 0
  none /mnt/huge hugetlbfs rw,relatime 0 0
  root@Ubuntu:~#

  3、通過讀取/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages,獲取配置的hugepages個數。

  root@Ubuntu:~# cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
  64
  root@Ubuntu:~#

  4、以打開文件的方式,打開掛載點目錄,爲其FD設置互斥鎖,Why??

上述所有獲取的信息,都保存在internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]中,hugepage_info數據結構如下:

複製代碼
1 struct hugepage_info {
2   size_t hugepage_sz; /**< size of a huge page */
3   const char *hugedir; /**< dir where hugetlbfs is mounted */
4   uint32_t num_pages[RTE_MAX_NUMA_NODES];
5   /**< number of hugepages of that size on each socket */
6   int lock_descriptor; /**< file descriptor for hugepage dir */
7 };
複製代碼

  具體賦值如下,

  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的首地址;

複製代碼
1 struct rte_config {
2     uint32_t master_lcore;       /**< Id of the master lcore */
3 
4         ... ...
5     
6     struct rte_mem_config *mem_config;
7 } __attribute__((__packed__));
複製代碼

struct rte_mem_config數據結構如下:

複製代碼
 1 struct rte_mem_config {
 2     volatile uint32_t magic;   /**< Magic number - Sanity check. */
 3 
 4     /* memory topology */
 5     uint32_t nchannel;    /**< Number of channels (0 if unknown). */
 6     uint32_t nrank;       /**< Number of ranks (0 if unknown). */
 7 
 8     /**
 9      * current lock nest order
10      *  - qlock->mlock (ring/hash/lpm)
11      *  - mplock->qlock->mlock (mempool)
12      * Notice:
13      *  *ALWAYS* obtain qlock first if having to obtain both qlock and mlock
14      */
15     rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */
16     rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */
17     rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */
18 
19     uint32_t memzone_idx; /**< Index of memzone */
20 
21     /* memory segments and zones */
22     struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */
23     struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
24 
25     /* Runtime Physmem descriptors. */
26     struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
27 
28     struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
29 
30     /* Heaps of Malloc per socket */
31     struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];
32 } __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個struct hugepage_file數組,即有多少個內存頁,創建多少個struct hugepage_file數據結構。struct hugepage_file數據結構如下:

複製代碼
 1 struct hugepage_file {
 2     void *orig_va;      /**< virtual addr of first mmap() */
 3     void *final_va;     /**< virtual addr of 2nd mmap() */
 4     uint64_t physaddr;  /**< physical addr */
 5     size_t size;        /**< the page size */
 6     int socket_id;      /**< NUMA socket ID */
 7     int file_id;        /**< the '%d' in HUGEFILE_FMT */
 8     int memseg_id;      /**< the memory segment to which page belongs */
 9 #ifdef RTE_EAL_SINGLE_FILE_SEGMENTS
10     int repeated;       /**< number of times the page size is repeated */
11 #endif
12     char filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */
13 };
複製代碼

 

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;

     root@Ubuntu:~# 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
	root@Ubuntu:~# 

3、通過讀取/proc/self/pagemap頁表文件,得到本進程中虛擬地址與物理地址的映射關係。使用上一步中,每個rtemap_xx文件mmap得到的虛擬地址,除以操作系統內存頁的大小4k,得到一個偏移量。根據這個偏移量,在/prox/self/pagemap中,得到物理地址的頁框,假設爲page,那麼,物理頁框page乘以操作系統內存頁的大小4K,再加上虛擬地址的頁偏移,就是物理地址。每個rtemap_xx映射的物理地址保存在對應的hugepage_file->physaddr中。

1 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中。

root@Ubuntu:~# 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
root@Ubuntu:~#

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(struct hugepage_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數據結構如下:

複製代碼
 1 struct rte_memseg {
 2     phys_addr_t phys_addr;      /**< Start physical address. */
 3     union {
 4         void *addr;         /**< Start virtual address. */
 5         uint64_t addr_64;   /**< Makes sure addr is always 64 bits */
 6     };
 7 #ifdef RTE_LIBRTE_IVSHMEM
 8     phys_addr_t ioremap_addr; /**< Real physical address inside the VM */
 9 #endif
10     size_t len;               /**< Length of the segment. */
11     size_t hugepage_sz;       /**< The pagesize of underlying memory */
12     int32_t socket_id;          /**< NUMA socket ID. */
13     uint32_t nchannel;          /**< Number of channels. */
14     uint32_t nrank;             /**< Number of ranks. */
15 #ifdef RTE_LIBRTE_XEN_DOM0
16      /**< store segment MFNs */
17     uint64_t mfn[DOM0_NUM_MEMBLOCK];
18 #endif
19 } __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[]。

3、總結

如下圖:

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