19db1cb5fde56a43c4f4fd8bbb9c6af228535041
[deb_dpdk.git] / lib / librte_eal / linuxapp / eal / eal_xen_memory.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <sys/mman.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/queue.h>
45 #include <sys/file.h>
46 #include <unistd.h>
47 #include <limits.h>
48 #include <sys/ioctl.h>
49 #include <sys/time.h>
50
51 #include <rte_log.h>
52 #include <rte_memory.h>
53 #include <rte_memzone.h>
54 #include <rte_launch.h>
55 #include <rte_eal.h>
56 #include <rte_eal_memconfig.h>
57 #include <rte_per_lcore.h>
58 #include <rte_lcore.h>
59 #include <rte_common.h>
60 #include <rte_string_fns.h>
61
62 #include "eal_private.h"
63 #include "eal_internal_cfg.h"
64 #include "eal_filesystem.h"
65 #include <exec-env/rte_dom0_common.h>
66
67 #define PAGE_SIZE RTE_PGSIZE_4K
68 #define DEFAUL_DOM0_NAME "dom0-mem"
69
70 static int xen_fd = -1;
71 static const char sys_dir_path[] = "/sys/kernel/mm/dom0-mm/memsize-mB";
72
73 /*
74  * Try to mmap *size bytes in /dev/zero. If it is successful, return the
75  * pointer to the mmap'd area and keep *size unmodified. Else, retry
76  * with a smaller zone: decrease *size by mem_size until it reaches
77  * 0. In this case, return NULL. Note: this function returns an address
78  * which is a multiple of mem_size size.
79  */
80 static void *
81 xen_get_virtual_area(size_t *size, size_t mem_size)
82 {
83         void *addr;
84         int fd;
85         long aligned_addr;
86
87         RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zu bytes\n", *size);
88
89         fd = open("/dev/zero", O_RDONLY);
90         if (fd < 0){
91                 RTE_LOG(ERR, EAL, "Cannot open /dev/zero\n");
92                 return NULL;
93         }
94         do {
95                 addr = mmap(NULL, (*size) + mem_size, PROT_READ,
96                         MAP_PRIVATE, fd, 0);
97                 if (addr == MAP_FAILED)
98                         *size -= mem_size;
99         } while (addr == MAP_FAILED && *size > 0);
100
101         if (addr == MAP_FAILED) {
102                 close(fd);
103                 RTE_LOG(ERR, EAL, "Cannot get a virtual area\n");
104                 return NULL;
105         }
106
107         munmap(addr, (*size) + mem_size);
108         close(fd);
109
110         /* align addr to a mem_size boundary */
111         aligned_addr = (uintptr_t)addr;
112         aligned_addr = RTE_ALIGN_CEIL(aligned_addr, mem_size);
113         addr = (void *)(aligned_addr);
114
115         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
116                 addr, *size);
117
118         return addr;
119 }
120
121 /**
122  * Get memory size configuration from /sys/devices/virtual/misc/dom0_mm
123  * /memsize-mB/memsize file, and the size unit is mB.
124  */
125 static int
126 get_xen_memory_size(void)
127 {
128         char path[PATH_MAX];
129         unsigned long mem_size = 0;
130         static const char *file_name;
131
132         file_name = "memsize";
133         snprintf(path, sizeof(path), "%s/%s",
134                         sys_dir_path, file_name);
135
136         if (eal_parse_sysfs_value(path, &mem_size) < 0)
137                 return -1;
138
139         if (mem_size == 0)
140                 rte_exit(EXIT_FAILURE,"XEN-DOM0:the %s/%s was not"
141                         " configured.\n",sys_dir_path, file_name);
142         if (mem_size % 2)
143                 rte_exit(EXIT_FAILURE,"XEN-DOM0:the %s/%s must be"
144                         " even number.\n",sys_dir_path, file_name);
145
146         if (mem_size > DOM0_CONFIG_MEMSIZE)
147                 rte_exit(EXIT_FAILURE,"XEN-DOM0:the %s/%s should not be larger"
148                         " than %d mB\n",sys_dir_path, file_name, DOM0_CONFIG_MEMSIZE);
149
150         return mem_size;
151 }
152
153 /**
154  * Based on physical address to caculate MFN in Xen Dom0.
155  */
156 phys_addr_t
157 rte_xen_mem_phy2mch(int32_t memseg_id, const phys_addr_t phy_addr)
158 {
159         int mfn_id, i;
160         uint64_t mfn, mfn_offset;
161         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
162         struct rte_memseg *memseg = mcfg->memseg;
163
164         /* find the memory segment owning the physical address */
165         if (memseg_id == -1) {
166                 for (i = 0; i < RTE_MAX_MEMSEG; i++) {
167                         if ((phy_addr >= memseg[i].phys_addr) &&
168                                         (phy_addr < memseg[i].phys_addr +
169                                                 memseg[i].len)) {
170                                 memseg_id = i;
171                                 break;
172                         }
173                 }
174                 if (memseg_id == -1)
175                         return RTE_BAD_PHYS_ADDR;
176         }
177
178         mfn_id = (phy_addr - memseg[memseg_id].phys_addr) / RTE_PGSIZE_2M;
179
180         /*the MFN is contiguous in 2M */
181         mfn_offset = (phy_addr - memseg[memseg_id].phys_addr) %
182                                         RTE_PGSIZE_2M / PAGE_SIZE;
183         mfn = mfn_offset + memseg[memseg_id].mfn[mfn_id];
184
185         /** return mechine address */
186         return mfn * PAGE_SIZE + phy_addr % PAGE_SIZE;
187 }
188
189 int
190 rte_xen_dom0_memory_init(void)
191 {
192         void *vir_addr, *vma_addr = NULL;
193         int err, ret = 0;
194         uint32_t i, requested, mem_size, memseg_idx, num_memseg = 0;
195         size_t vma_len = 0;
196         struct memory_info meminfo;
197         struct memseg_info seginfo[RTE_MAX_MEMSEG];
198         int flags, page_size = getpagesize();
199         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
200         struct rte_memseg *memseg = mcfg->memseg;
201         uint64_t total_mem = internal_config.memory;
202
203         memset(seginfo, 0, sizeof(seginfo));
204         memset(&meminfo, 0, sizeof(struct memory_info));
205
206         mem_size = get_xen_memory_size();
207         requested = (unsigned) (total_mem / 0x100000);
208         if (requested > mem_size)
209                 /* if we didn't satisfy total memory requirements */
210                 rte_exit(EXIT_FAILURE,"Not enough memory available! Requested: %uMB,"
211                                 " available: %uMB\n", requested, mem_size);
212         else if (total_mem != 0)
213                 mem_size = requested;
214
215         /* Check FD and open once */
216         if (xen_fd < 0) {
217                 xen_fd = open(DOM0_MM_DEV, O_RDWR);
218                 if (xen_fd < 0) {
219                         RTE_LOG(ERR, EAL, "Can not open %s\n",DOM0_MM_DEV);
220                         return -1;
221                 }
222         }
223
224         meminfo.size = mem_size;
225
226         /* construct memory mangement name for Dom0 */
227         snprintf(meminfo.name, DOM0_NAME_MAX, "%s-%s",
228                 internal_config.hugefile_prefix, DEFAUL_DOM0_NAME);
229
230         /* Notify kernel driver to allocate memory */
231         ret = ioctl(xen_fd, RTE_DOM0_IOCTL_PREPARE_MEMSEG, &meminfo);
232         if (ret < 0) {
233                 RTE_LOG(ERR, EAL, "XEN DOM0:failed to get memory\n");
234                 err = -EIO;
235                 goto fail;
236         }
237
238         /* Get number of memory segment from driver */
239         ret = ioctl(xen_fd, RTE_DOM0_IOCTL_GET_NUM_MEMSEG, &num_memseg);
240         if (ret < 0) {
241                 RTE_LOG(ERR, EAL, "XEN DOM0:failed to get memseg count.\n");
242                 err = -EIO;
243                 goto fail;
244         }
245
246         if(num_memseg > RTE_MAX_MEMSEG){
247                 RTE_LOG(ERR, EAL, "XEN DOM0: the memseg count %d is greater"
248                         " than max memseg %d.\n",num_memseg, RTE_MAX_MEMSEG);
249                 err = -EIO;
250                 goto fail;
251         }
252
253         /* get all memory segements information */
254         ret = ioctl(xen_fd, RTE_DOM0_IOCTL_GET_MEMSEG_INFO, seginfo);
255         if (ret < 0) {
256                 RTE_LOG(ERR, EAL, "XEN DOM0:failed to get memseg info.\n");
257                 err = -EIO;
258                 goto fail;
259         }
260
261         /* map all memory segments to contiguous user space */
262         for (memseg_idx = 0; memseg_idx < num_memseg; memseg_idx++)
263         {
264                 vma_len = seginfo[memseg_idx].size;
265
266                 /**
267                  * get the biggest virtual memory area up to vma_len. If it fails,
268                  * vma_addr is NULL, so let the kernel provide the address.
269                  */
270                 vma_addr = xen_get_virtual_area(&vma_len, RTE_PGSIZE_2M);
271                 if (vma_addr == NULL) {
272                         flags = MAP_SHARED;
273                         vma_len = RTE_PGSIZE_2M;
274                 } else
275                         flags = MAP_SHARED | MAP_FIXED;
276
277                 seginfo[memseg_idx].size = vma_len;
278                 vir_addr = mmap(vma_addr, seginfo[memseg_idx].size,
279                         PROT_READ|PROT_WRITE, flags, xen_fd,
280                         memseg_idx * page_size);
281                 if (vir_addr == MAP_FAILED) {
282                         RTE_LOG(ERR, EAL, "XEN DOM0:Could not mmap %s\n",
283                                 DOM0_MM_DEV);
284                         err = -EIO;
285                         goto fail;
286                 }
287
288                 memseg[memseg_idx].addr = vir_addr;
289                 memseg[memseg_idx].phys_addr = page_size *
290                         seginfo[memseg_idx].pfn ;
291                 memseg[memseg_idx].len = seginfo[memseg_idx].size;
292                 for ( i = 0; i < seginfo[memseg_idx].size / RTE_PGSIZE_2M; i++)
293                         memseg[memseg_idx].mfn[i] = seginfo[memseg_idx].mfn[i];
294
295                 /* MFNs are continuous in 2M, so assume that page size is 2M */
296                 memseg[memseg_idx].hugepage_sz = RTE_PGSIZE_2M;
297
298                 memseg[memseg_idx].nchannel = mcfg->nchannel;
299                 memseg[memseg_idx].nrank = mcfg->nrank;
300
301                 /* NUMA is not suppoted in Xen Dom0, so only set socket 0*/
302                 memseg[memseg_idx].socket_id = 0;
303         }
304
305         return 0;
306 fail:
307         if (xen_fd > 0) {
308                 close(xen_fd);
309                 xen_fd = -1;
310         }
311         return err;
312 }
313
314 /*
315  * This creates the memory mappings in the secondary process to match that of
316  * the server process. It goes through each memory segment in the DPDK runtime
317  * configuration, mapping them in order to form a contiguous block in the
318  * virtual memory space
319  */
320 int
321 rte_xen_dom0_memory_attach(void)
322 {
323         const struct rte_mem_config *mcfg;
324         unsigned s = 0; /* s used to track the segment number */
325         int xen_fd = -1;
326         int ret = -1;
327         void *vir_addr;
328         char name[DOM0_NAME_MAX] = {0};
329         int page_size = getpagesize();
330
331         mcfg = rte_eal_get_configuration()->mem_config;
332
333         /* Check FD and open once */
334         if (xen_fd < 0) {
335                 xen_fd = open(DOM0_MM_DEV, O_RDWR);
336                 if (xen_fd < 0) {
337                         RTE_LOG(ERR, EAL, "Can not open %s\n",DOM0_MM_DEV);
338                         goto error;
339                 }
340         }
341
342         /* construct memory mangement name for Dom0 */
343         snprintf(name, DOM0_NAME_MAX, "%s-%s",
344                 internal_config.hugefile_prefix, DEFAUL_DOM0_NAME);
345         /* attach to memory segments of primary process */
346         ret = ioctl(xen_fd, RTE_DOM0_IOCTL_ATTACH_TO_MEMSEG, name);
347         if (ret) {
348                 RTE_LOG(ERR, EAL,"attach memory segments fail.\n");
349                 goto error;
350         }
351
352         /* map all segments into memory to make sure we get the addrs */
353         for (s = 0; s < RTE_MAX_MEMSEG; ++s) {
354
355                 /*
356                  * the first memory segment with len==0 is the one that
357                  * follows the last valid segment.
358                  */
359                 if (mcfg->memseg[s].len == 0)
360                         break;
361
362                 vir_addr = mmap(mcfg->memseg[s].addr, mcfg->memseg[s].len,
363                                 PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, xen_fd,
364                                 s * page_size);
365                 if (vir_addr == MAP_FAILED) {
366                         RTE_LOG(ERR, EAL, "Could not mmap %llu bytes "
367                                 "in %s to requested address [%p]\n",
368                                 (unsigned long long)mcfg->memseg[s].len, DOM0_MM_DEV,
369                                 mcfg->memseg[s].addr);
370                         goto error;
371                 }
372         }
373         return 0;
374
375 error:
376         if (xen_fd >= 0) {
377                 close(xen_fd);
378                 xen_fd = -1;
379         }
380         return -1;
381 }