Imported Upstream version 16.07-rc1
[deb_dpdk.git] / lib / librte_ivshmem / rte_ivshmem.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 #include <fcntl.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <sys/mman.h>
37 #include <string.h>
38 #include <stdio.h>
39
40 #include <rte_eal_memconfig.h>
41 #include <rte_memory.h>
42 #include <rte_ivshmem.h>
43 #include <rte_string_fns.h>
44 #include <rte_common.h>
45 #include <rte_log.h>
46 #include <rte_debug.h>
47 #include <rte_spinlock.h>
48 #include <rte_common.h>
49 #include <rte_malloc.h>
50
51 #include "rte_ivshmem.h"
52
53 #define IVSHMEM_CONFIG_FILE_FMT "/var/run/.dpdk_ivshmem_metadata_%s"
54 #define IVSHMEM_QEMU_CMD_LINE_HEADER_FMT "-device ivshmem,size=%" PRIu64 "M,shm=fd%s"
55 #define IVSHMEM_QEMU_CMD_FD_FMT ":%s:0x%" PRIx64 ":0x%" PRIx64
56 #define IVSHMEM_QEMU_CMDLINE_BUFSIZE 1024
57 #define IVSHMEM_MAX_PAGES (1 << 12)
58 #define adjacent(x,y) (((x).phys_addr+(x).len)==(y).phys_addr)
59 #define METADATA_SIZE_ALIGNED \
60         (RTE_ALIGN_CEIL(sizeof(struct rte_ivshmem_metadata),pagesz))
61
62 #define GET_PAGEMAP_ADDR(in,addr,dlm,err)    \
63 {                                      \
64         char *end;                         \
65         errno = 0;                         \
66         addr = strtoull((in), &end, 16);   \
67         if (errno != 0 || *end != (dlm)) { \
68                 RTE_LOG(ERR, EAL, err);        \
69                 goto error;                    \
70         }                                  \
71         (in) = end + 1;                    \
72 }
73
74 static int pagesz;
75
76 struct memseg_cache_entry {
77         char filepath[PATH_MAX];
78         uint64_t offset;
79         uint64_t len;
80 };
81
82 struct ivshmem_config {
83         struct rte_ivshmem_metadata * metadata;
84         struct memseg_cache_entry memseg_cache[IVSHMEM_MAX_PAGES];
85                 /**< account for multiple files per segment case */
86         struct flock lock;
87         rte_spinlock_t sl;
88 };
89
90 static struct ivshmem_config
91 ivshmem_global_config[RTE_LIBRTE_IVSHMEM_MAX_METADATA_FILES];
92
93 static rte_spinlock_t global_cfg_sl;
94
95 static struct ivshmem_config *
96 get_config_by_name(const char * name)
97 {
98         struct rte_ivshmem_metadata * config;
99         unsigned i;
100
101         for (i = 0; i < RTE_DIM(ivshmem_global_config); i++) {
102                 config = ivshmem_global_config[i].metadata;
103                 if (config == NULL)
104                         return NULL;
105                 if (strncmp(name, config->name, IVSHMEM_NAME_LEN) == 0)
106                         return &ivshmem_global_config[i];
107         }
108
109         return NULL;
110 }
111
112 static int
113 overlap(const struct rte_memzone * s1, const struct rte_memzone * s2)
114 {
115         uint64_t start1, end1, start2, end2;
116
117         start1 = s1->addr_64;
118         end1 = s1->addr_64 + s1->len;
119         start2 = s2->addr_64;
120         end2 = s2->addr_64 + s2->len;
121
122         if (start1 >= start2 && start1 < end2)
123                 return 1;
124         if (start2 >= start1 && start2 < end1)
125                 return 1;
126
127         return 0;
128 }
129
130 static struct rte_memzone *
131 get_memzone_by_addr(const void * addr)
132 {
133         struct rte_memzone * tmp, * mz;
134         struct rte_mem_config * mcfg;
135         int i;
136
137         mcfg = rte_eal_get_configuration()->mem_config;
138         mz = NULL;
139
140         /* find memzone for the ring */
141         for (i = 0; i < RTE_MAX_MEMZONE; i++) {
142                 tmp = &mcfg->memzone[i];
143
144                 if (tmp->addr_64 == (uint64_t) addr) {
145                         mz = tmp;
146                         break;
147                 }
148         }
149
150         return mz;
151 }
152
153 static int
154 entry_compare(const void * a, const void * b)
155 {
156         const struct rte_ivshmem_metadata_entry * e1 =
157                         (const struct rte_ivshmem_metadata_entry*) a;
158         const struct rte_ivshmem_metadata_entry * e2 =
159                         (const struct rte_ivshmem_metadata_entry*) b;
160
161         /* move unallocated zones to the end */
162         if (e1->mz.addr == NULL && e2->mz.addr == NULL)
163                 return 0;
164         if (e1->mz.addr == 0)
165                 return 1;
166         if (e2->mz.addr == 0)
167                 return -1;
168
169         return e1->mz.phys_addr > e2->mz.phys_addr;
170 }
171
172 /* fills hugepage cache entry for a given start virt_addr */
173 static int
174 get_hugefile_by_virt_addr(uint64_t virt_addr, struct memseg_cache_entry * e)
175 {
176         uint64_t start_addr, end_addr;
177         char *start,*path_end;
178         char buf[PATH_MAX*2];
179         FILE *f;
180
181         start = NULL;
182         path_end = NULL;
183         start_addr = 0;
184
185         memset(e->filepath, 0, sizeof(e->filepath));
186
187         /* open /proc/self/maps */
188         f = fopen("/proc/self/maps", "r");
189         if (f == NULL) {
190                 RTE_LOG(ERR, EAL, "cannot open /proc/self/maps!\n");
191                 return -1;
192         }
193
194         /* parse maps */
195         while (fgets(buf, sizeof(buf), f) != NULL) {
196
197                 /* get endptr to end of start addr */
198                 start = buf;
199
200                 GET_PAGEMAP_ADDR(start,start_addr,'-',
201                                 "Cannot find start address in maps!\n");
202
203                 /* if start address is bigger than our address, skip */
204                 if (start_addr > virt_addr)
205                         continue;
206
207                 GET_PAGEMAP_ADDR(start,end_addr,' ',
208                                 "Cannot find end address in maps!\n");
209
210                 /* if end address is less than our address, skip */
211                 if (end_addr <= virt_addr)
212                         continue;
213
214                 /* find where the path starts */
215                 start = strstr(start, "/");
216
217                 if (start == NULL)
218                         continue;
219
220                 /* at this point, we know that this is our map.
221                  * now let's find the file */
222                 path_end = strstr(start, "\n");
223                 break;
224         }
225
226         if (path_end == NULL) {
227                 RTE_LOG(ERR, EAL, "Hugefile path not found!\n");
228                 goto error;
229         }
230
231         /* calculate offset and copy the file path */
232         snprintf(e->filepath, RTE_PTR_DIFF(path_end, start) + 1, "%s", start);
233
234         e->offset = virt_addr - start_addr;
235
236         fclose(f);
237
238         return 0;
239 error:
240         fclose(f);
241         return -1;
242 }
243
244 /*
245  * This is a complex function. What it does is the following:
246  *  1. Goes through metadata and gets list of hugepages involved
247  *  2. Sorts the hugepages by size (1G first)
248  *  3. Goes through metadata again and writes correct offsets
249  *  4. Goes through pages and finds out their filenames, offsets etc.
250  */
251 static int
252 build_config(struct rte_ivshmem_metadata * metadata)
253 {
254         struct rte_ivshmem_metadata_entry * e_local;
255         struct memseg_cache_entry * ms_local;
256         struct rte_memseg pages[IVSHMEM_MAX_PAGES];
257         struct rte_ivshmem_metadata_entry *entry;
258         struct memseg_cache_entry * c_entry, * prev_entry;
259         struct ivshmem_config * config;
260         unsigned i, j, mz_iter, ms_iter;
261         uint64_t biggest_len;
262         int biggest_idx;
263
264         /* return error if we try to use an unknown config file */
265         config = get_config_by_name(metadata->name);
266         if (config == NULL) {
267                 RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", metadata->name);
268                 goto fail_e;
269         }
270
271         memset(pages, 0, sizeof(pages));
272
273         e_local = malloc(sizeof(config->metadata->entry));
274         if (e_local == NULL)
275                 goto fail_e;
276         ms_local = malloc(sizeof(config->memseg_cache));
277         if (ms_local == NULL)
278                 goto fail_ms;
279
280
281         /* make local copies before doing anything */
282         memcpy(e_local, config->metadata->entry, sizeof(config->metadata->entry));
283         memcpy(ms_local, config->memseg_cache, sizeof(config->memseg_cache));
284
285         qsort(e_local, RTE_DIM(config->metadata->entry), sizeof(struct rte_ivshmem_metadata_entry),
286                         entry_compare);
287
288         /* first pass - collect all huge pages */
289         for (mz_iter = 0; mz_iter < RTE_DIM(config->metadata->entry); mz_iter++) {
290
291                 entry = &e_local[mz_iter];
292
293                 uint64_t start_addr = RTE_ALIGN_FLOOR(entry->mz.addr_64,
294                                 entry->mz.hugepage_sz);
295                 uint64_t offset = entry->mz.addr_64 - start_addr;
296                 uint64_t len = RTE_ALIGN_CEIL(entry->mz.len + offset,
297                                 entry->mz.hugepage_sz);
298
299                 if (entry->mz.addr_64 == 0 || start_addr == 0 || len == 0)
300                         continue;
301
302                 int start_page;
303
304                 /* find first unused page - mz are phys_addr sorted so we don't have to
305                  * look out for holes */
306                 for (i = 0; i < RTE_DIM(pages); i++) {
307
308                         /* skip if we already have this page */
309                         if (pages[i].addr_64 == start_addr) {
310                                 start_addr += entry->mz.hugepage_sz;
311                                 len -= entry->mz.hugepage_sz;
312                                 continue;
313                         }
314                         /* we found a new page */
315                         else if (pages[i].addr_64 == 0) {
316                                 start_page = i;
317                                 break;
318                         }
319                 }
320                 if (i == RTE_DIM(pages)) {
321                         RTE_LOG(ERR, EAL, "Cannot find unused page!\n");
322                         goto fail;
323                 }
324
325                 /* populate however many pages the memzone has */
326                 for (i = start_page; i < RTE_DIM(pages) && len != 0; i++) {
327
328                         pages[i].addr_64 = start_addr;
329                         pages[i].len = entry->mz.hugepage_sz;
330                         start_addr += entry->mz.hugepage_sz;
331                         len -= entry->mz.hugepage_sz;
332                 }
333                 /* if there's still length left */
334                 if (len != 0) {
335                         RTE_LOG(ERR, EAL, "Not enough space for pages!\n");
336                         goto fail;
337                 }
338         }
339
340         /* second pass - sort pages by size */
341         for (i = 0; i < RTE_DIM(pages); i++) {
342
343                 if (pages[i].addr == NULL)
344                         break;
345
346                 biggest_len = 0;
347                 biggest_idx = -1;
348
349                 /*
350                  * browse all entries starting at 'i', and find the
351                  * entry with the smallest addr
352                  */
353                 for (j=i; j< RTE_DIM(pages); j++) {
354                         if (pages[j].addr == NULL)
355                                         break;
356                         if (biggest_len == 0 ||
357                                 pages[j].len > biggest_len) {
358                                 biggest_len = pages[j].len;
359                                 biggest_idx = j;
360                         }
361                 }
362
363                 /* should not happen */
364                 if (biggest_idx == -1) {
365                         RTE_LOG(ERR, EAL, "Error sorting by size!\n");
366                         goto fail;
367                 }
368                 if (i != (unsigned) biggest_idx) {
369                         struct rte_memseg tmp;
370
371                         memcpy(&tmp, &pages[biggest_idx], sizeof(struct rte_memseg));
372
373                         /* we don't want to break contiguousness, so instead of just
374                          * swapping segments, we move all the preceding segments to the
375                          * right and then put the old segment @ biggest_idx in place of
376                          * segment @ i */
377                         for (j = biggest_idx - 1; j >= i; j--) {
378                                 memcpy(&pages[j+1], &pages[j], sizeof(struct rte_memseg));
379                                 memset(&pages[j], 0, sizeof(struct rte_memseg));
380                                 if (j == 0)
381                                         break;
382                         }
383
384                         /* put old biggest segment to its new place */
385                         memcpy(&pages[i], &tmp, sizeof(struct rte_memseg));
386                 }
387         }
388
389         /* third pass - write correct offsets */
390         for (mz_iter = 0; mz_iter < RTE_DIM(config->metadata->entry); mz_iter++) {
391
392                 uint64_t offset = 0;
393
394                 entry = &e_local[mz_iter];
395
396                 if (entry->mz.addr_64 == 0)
397                         break;
398
399                 /* find page for current memzone */
400                 for (i = 0; i < RTE_DIM(pages); i++) {
401                         /* we found our page */
402                         if (entry->mz.addr_64 >= pages[i].addr_64 &&
403                                         entry->mz.addr_64 < pages[i].addr_64 + pages[i].len) {
404                                 entry->offset = (entry->mz.addr_64 - pages[i].addr_64) +
405                                                 offset;
406                                 break;
407                         }
408                         offset += pages[i].len;
409                 }
410                 if (i == RTE_DIM(pages)) {
411                         RTE_LOG(ERR, EAL, "Page not found!\n");
412                         goto fail;
413                 }
414         }
415
416         ms_iter = 0;
417         prev_entry = NULL;
418
419         /* fourth pass - create proper memseg cache */
420         for (i = 0; i < RTE_DIM(pages) &&
421                         ms_iter <= RTE_DIM(config->memseg_cache); i++) {
422                 if (pages[i].addr_64 == 0)
423                         break;
424
425
426                 if (ms_iter == RTE_DIM(pages)) {
427                         RTE_LOG(ERR, EAL, "The universe has collapsed!\n");
428                         goto fail;
429                 }
430
431                 c_entry = &ms_local[ms_iter];
432                 c_entry->len = pages[i].len;
433
434                 if (get_hugefile_by_virt_addr(pages[i].addr_64, c_entry) < 0)
435                         goto fail;
436
437                 /* if previous entry has the same filename and is contiguous,
438                  * clear current entry and increase previous entry's length
439                  */
440                 if (prev_entry != NULL &&
441                                 strncmp(c_entry->filepath, prev_entry->filepath,
442                                 sizeof(c_entry->filepath)) == 0 &&
443                                 prev_entry->offset + prev_entry->len == c_entry->offset) {
444                         prev_entry->len += pages[i].len;
445                         memset(c_entry, 0, sizeof(struct memseg_cache_entry));
446                 }
447                 else {
448                         prev_entry = c_entry;
449                         ms_iter++;
450                 }
451         }
452
453         /* update current configuration with new valid data */
454         memcpy(config->metadata->entry, e_local, sizeof(config->metadata->entry));
455         memcpy(config->memseg_cache, ms_local, sizeof(config->memseg_cache));
456
457         free(ms_local);
458         free(e_local);
459
460         return 0;
461 fail:
462         free(ms_local);
463 fail_ms:
464         free(e_local);
465 fail_e:
466         return -1;
467 }
468
469 static int
470 add_memzone_to_metadata(const struct rte_memzone * mz,
471                 struct ivshmem_config * config)
472 {
473         struct rte_ivshmem_metadata_entry * entry;
474         unsigned i, idx;
475         struct rte_mem_config *mcfg;
476
477         if (mz->len == 0) {
478                 RTE_LOG(ERR, EAL, "Trying to add an empty memzone\n");
479                 return -1;
480         }
481
482         rte_spinlock_lock(&config->sl);
483
484         mcfg = rte_eal_get_configuration()->mem_config;
485
486         /* it prevents the memzone being freed while we add it to the metadata */
487         rte_rwlock_write_lock(&mcfg->mlock);
488
489         /* find free slot in this config */
490         for (i = 0; i < RTE_DIM(config->metadata->entry); i++) {
491                 entry = &config->metadata->entry[i];
492
493                 if (&entry->mz.addr_64 != 0 && overlap(mz, &entry->mz)) {
494                         RTE_LOG(ERR, EAL, "Overlapping memzones!\n");
495                         goto fail;
496                 }
497
498                 /* if addr is zero, the memzone is probably free */
499                 if (entry->mz.addr_64 == 0) {
500                         RTE_LOG(DEBUG, EAL, "Adding memzone '%s' at %p to metadata %s\n",
501                                         mz->name, mz->addr, config->metadata->name);
502                         memcpy(&entry->mz, mz, sizeof(struct rte_memzone));
503
504                         /* run config file parser */
505                         if (build_config(config->metadata) < 0)
506                                 goto fail;
507
508                         break;
509                 }
510         }
511
512         /* if we reached the maximum, that means we have no place in config */
513         if (i == RTE_DIM(config->metadata->entry)) {
514                 RTE_LOG(ERR, EAL, "No space left in IVSHMEM metadata %s!\n",
515                                 config->metadata->name);
516                 goto fail;
517         }
518
519         idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
520         idx = idx / sizeof(struct rte_memzone);
521
522         /* mark the memzone not freeable */
523         mcfg->memzone[idx].ioremap_addr = mz->phys_addr;
524
525         rte_rwlock_write_unlock(&mcfg->mlock);
526         rte_spinlock_unlock(&config->sl);
527         return 0;
528 fail:
529         rte_rwlock_write_unlock(&mcfg->mlock);
530         rte_spinlock_unlock(&config->sl);
531         return -1;
532 }
533
534 static int
535 add_ring_to_metadata(const struct rte_ring * r,
536                 struct ivshmem_config * config)
537 {
538         struct rte_memzone * mz;
539
540         mz = get_memzone_by_addr(r);
541
542         if (!mz) {
543                 RTE_LOG(ERR, EAL, "Cannot find memzone for ring!\n");
544                 return -1;
545         }
546
547         return add_memzone_to_metadata(mz, config);
548 }
549
550 static int
551 add_mempool_memzone_to_metadata(const void *addr,
552                 struct ivshmem_config *config)
553 {
554         struct rte_memzone *mz;
555
556         mz = get_memzone_by_addr(addr);
557
558         if (!mz) {
559                 RTE_LOG(ERR, EAL, "Cannot find memzone for mempool!\n");
560                 return -1;
561         }
562
563         return add_memzone_to_metadata(mz, config);
564 }
565
566 static int
567 add_mempool_to_metadata(const struct rte_mempool *mp,
568                 struct ivshmem_config *config)
569 {
570         struct rte_mempool_memhdr *memhdr;
571         int ret;
572
573         ret = add_mempool_memzone_to_metadata(mp, config);
574         if (ret < 0)
575                 return -1;
576
577         STAILQ_FOREACH(memhdr, &mp->mem_list, next) {
578                 ret = add_mempool_memzone_to_metadata(memhdr->addr, config);
579                 if (ret < 0)
580                         return -1;
581         }
582
583         /* mempool consists of memzone and ring */
584         return add_ring_to_metadata(mp->pool_data, config);
585 }
586
587 int
588 rte_ivshmem_metadata_add_ring(const struct rte_ring * r, const char * name)
589 {
590         struct ivshmem_config * config;
591
592         if (name == NULL || r == NULL)
593                 return -1;
594
595         config = get_config_by_name(name);
596
597         if (config == NULL) {
598                 RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name);
599                 return -1;
600         }
601
602         return add_ring_to_metadata(r, config);
603 }
604
605 int
606 rte_ivshmem_metadata_add_memzone(const struct rte_memzone * mz, const char * name)
607 {
608         struct ivshmem_config * config;
609
610         if (name == NULL || mz == NULL)
611                 return -1;
612
613         config = get_config_by_name(name);
614
615         if (config == NULL) {
616                 RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name);
617                 return -1;
618         }
619
620         return add_memzone_to_metadata(mz, config);
621 }
622
623 int
624 rte_ivshmem_metadata_add_mempool(const struct rte_mempool * mp, const char * name)
625 {
626         struct ivshmem_config * config;
627
628         if (name == NULL || mp == NULL)
629                 return -1;
630
631         config = get_config_by_name(name);
632
633         if (config == NULL) {
634                 RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name);
635                 return -1;
636         }
637
638         return add_mempool_to_metadata(mp, config);
639 }
640
641 static inline void
642 ivshmem_config_path(char *buffer, size_t bufflen, const char *name)
643 {
644         snprintf(buffer, bufflen, IVSHMEM_CONFIG_FILE_FMT, name);
645 }
646
647
648
649 static inline
650 void *ivshmem_metadata_create(const char *name, size_t size,
651                 struct flock *lock)
652 {
653         int retval, fd;
654         void *metadata_addr;
655         char pathname[PATH_MAX];
656
657         ivshmem_config_path(pathname, sizeof(pathname), name);
658
659         fd = open(pathname, O_RDWR | O_CREAT, 0660);
660         if (fd < 0) {
661                 RTE_LOG(ERR, EAL, "Cannot open '%s'\n", pathname);
662                 return NULL;
663         }
664
665         size = METADATA_SIZE_ALIGNED;
666
667         retval = fcntl(fd, F_SETLK, lock);
668         if (retval < 0){
669                 close(fd);
670                 RTE_LOG(ERR, EAL, "Cannot create lock on '%s'. Is another "
671                                 "process using it?\n", pathname);
672                 return NULL;
673         }
674
675         retval = ftruncate(fd, size);
676         if (retval < 0){
677                 close(fd);
678                 RTE_LOG(ERR, EAL, "Cannot resize '%s'\n", pathname);
679                 return NULL;
680         }
681
682         metadata_addr = mmap(NULL, size,
683                                 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
684
685         if (metadata_addr == MAP_FAILED){
686                 RTE_LOG(ERR, EAL, "Cannot mmap memory for '%s'\n", pathname);
687
688                 /* we don't care if we can't unlock */
689                 fcntl(fd, F_UNLCK, lock);
690                 close(fd);
691
692                 return NULL;
693         }
694
695         return metadata_addr;
696 }
697
698 int rte_ivshmem_metadata_create(const char *name)
699 {
700         struct ivshmem_config * ivshmem_config;
701         unsigned index;
702
703         if (pagesz == 0)
704                 pagesz = getpagesize();
705
706         if (name == NULL)
707                 return -1;
708
709         rte_spinlock_lock(&global_cfg_sl);
710
711         for (index = 0; index < RTE_DIM(ivshmem_global_config); index++) {
712                 if (ivshmem_global_config[index].metadata == NULL) {
713                         ivshmem_config = &ivshmem_global_config[index];
714                         break;
715                 }
716         }
717
718         if (index == RTE_DIM(ivshmem_global_config)) {
719                 RTE_LOG(ERR, EAL, "Cannot create more ivshmem config files. "
720                 "Maximum has been reached\n");
721                 rte_spinlock_unlock(&global_cfg_sl);
722                 return -1;
723         }
724
725         ivshmem_config->lock.l_type = F_WRLCK;
726         ivshmem_config->lock.l_whence = SEEK_SET;
727
728         ivshmem_config->lock.l_start = 0;
729         ivshmem_config->lock.l_len = METADATA_SIZE_ALIGNED;
730
731         ivshmem_global_config[index].metadata = ((struct rte_ivshmem_metadata *)
732                         ivshmem_metadata_create(
733                                         name,
734                                         sizeof(struct rte_ivshmem_metadata),
735                                         &ivshmem_config->lock));
736
737         if (ivshmem_global_config[index].metadata == NULL) {
738                 rte_spinlock_unlock(&global_cfg_sl);
739                 return -1;
740         }
741
742         /* Metadata setup */
743         memset(ivshmem_config->metadata, 0, sizeof(struct rte_ivshmem_metadata));
744         ivshmem_config->metadata->magic_number = IVSHMEM_MAGIC;
745         snprintf(ivshmem_config->metadata->name,
746                         sizeof(ivshmem_config->metadata->name), "%s", name);
747
748         rte_spinlock_unlock(&global_cfg_sl);
749
750         return 0;
751 }
752
753 int
754 rte_ivshmem_metadata_cmdline_generate(char *buffer, unsigned size, const char *name)
755 {
756         const struct memseg_cache_entry * ms_cache, *entry;
757         struct ivshmem_config * config;
758         char cmdline[IVSHMEM_QEMU_CMDLINE_BUFSIZE], *cmdline_ptr;
759         char cfg_file_path[PATH_MAX];
760         unsigned remaining_len, tmplen, iter;
761         uint64_t shared_mem_size, zero_size, total_size;
762
763         if (buffer == NULL || name == NULL)
764                 return -1;
765
766         config = get_config_by_name(name);
767
768         if (config == NULL) {
769                 RTE_LOG(ERR, EAL, "Config %s not found!\n", name);
770                 return -1;
771         }
772
773         rte_spinlock_lock(&config->sl);
774
775         /* prepare metadata file path */
776         snprintf(cfg_file_path, sizeof(cfg_file_path), IVSHMEM_CONFIG_FILE_FMT,
777                         config->metadata->name);
778
779         ms_cache = config->memseg_cache;
780
781         cmdline_ptr = cmdline;
782         remaining_len = sizeof(cmdline);
783
784         shared_mem_size = 0;
785         iter = 0;
786
787         while ((ms_cache[iter].len != 0) && (iter < RTE_DIM(config->metadata->entry))) {
788
789                 entry = &ms_cache[iter];
790
791                 /* Offset and sizes within the current pathname */
792                 tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT,
793                                 entry->filepath, entry->offset, entry->len);
794
795                 shared_mem_size += entry->len;
796
797                 cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen);
798                 remaining_len -= tmplen;
799
800                 if (remaining_len == 0) {
801                         RTE_LOG(ERR, EAL, "Command line too long!\n");
802                         rte_spinlock_unlock(&config->sl);
803                         return -1;
804                 }
805
806                 iter++;
807         }
808
809         total_size = rte_align64pow2(shared_mem_size + METADATA_SIZE_ALIGNED);
810         zero_size = total_size - shared_mem_size - METADATA_SIZE_ALIGNED;
811
812         /* add /dev/zero to command-line to fill the space */
813         tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT,
814                         "/dev/zero",
815                         (uint64_t)0x0,
816                         zero_size);
817
818         cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen);
819         remaining_len -= tmplen;
820
821         if (remaining_len == 0) {
822                 RTE_LOG(ERR, EAL, "Command line too long!\n");
823                 rte_spinlock_unlock(&config->sl);
824                 return -1;
825         }
826
827         /* add metadata file to the end of command-line */
828         tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT,
829                         cfg_file_path,
830                         (uint64_t)0x0,
831                         METADATA_SIZE_ALIGNED);
832
833         cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen);
834         remaining_len -= tmplen;
835
836         if (remaining_len == 0) {
837                 RTE_LOG(ERR, EAL, "Command line too long!\n");
838                 rte_spinlock_unlock(&config->sl);
839                 return -1;
840         }
841
842         /* if current length of the command line is bigger than the buffer supplied
843          * by the user, or if command-line is bigger than what IVSHMEM accepts */
844         if ((sizeof(cmdline) - remaining_len) > size) {
845                 RTE_LOG(ERR, EAL, "Buffer is too short!\n");
846                 rte_spinlock_unlock(&config->sl);
847                 return -1;
848         }
849         /* complete the command-line */
850         snprintf(buffer, size,
851                         IVSHMEM_QEMU_CMD_LINE_HEADER_FMT,
852                         total_size >> 20,
853                         cmdline);
854
855         rte_spinlock_unlock(&config->sl);
856
857         return 0;
858 }
859
860 void
861 rte_ivshmem_metadata_dump(FILE *f, const char *name)
862 {
863         unsigned i = 0;
864         struct ivshmem_config * config;
865         struct rte_ivshmem_metadata_entry *entry;
866 #ifdef RTE_LIBRTE_IVSHMEM_DEBUG
867         uint64_t addr;
868         uint64_t end, hugepage_sz;
869         struct memseg_cache_entry e;
870 #endif
871
872         if (name == NULL)
873                 return;
874
875         /* return error if we try to use an unknown config file */
876         config = get_config_by_name(name);
877         if (config == NULL) {
878                 RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name);
879                 return;
880         }
881
882         rte_spinlock_lock(&config->sl);
883
884         entry = &config->metadata->entry[0];
885
886         while (entry->mz.addr != NULL && i < RTE_DIM(config->metadata->entry)) {
887
888                 fprintf(f, "Entry %u: name:<%-20s>, phys:0x%-15lx, len:0x%-15lx, "
889                         "virt:%-15p, off:0x%-15lx\n",
890                         i,
891                         entry->mz.name,
892                         entry->mz.phys_addr,
893                         entry->mz.len,
894                         entry->mz.addr,
895                         entry->offset);
896                 i++;
897
898 #ifdef RTE_LIBRTE_IVSHMEM_DEBUG
899                 fprintf(f, "\tHugepage files:\n");
900
901                 hugepage_sz = entry->mz.hugepage_sz;
902                 addr = RTE_ALIGN_FLOOR(entry->mz.addr_64, hugepage_sz);
903                 end = addr + RTE_ALIGN_CEIL(entry->mz.len + (entry->mz.addr_64 - addr),
904                                 hugepage_sz);
905
906                 for (; addr < end; addr += hugepage_sz) {
907                         memset(&e, 0, sizeof(e));
908
909                         get_hugefile_by_virt_addr(addr, &e);
910
911                         fprintf(f, "\t0x%"PRIx64 "-0x%" PRIx64 " offset: 0x%" PRIx64 " %s\n",
912                                         addr, addr + hugepage_sz, e.offset, e.filepath);
913                 }
914 #endif
915                 entry++;
916         }
917
918         rte_spinlock_unlock(&config->sl);
919 }