diff options
author | C.J. Collier <cjcollier@linuxfoundation.org> | 2016-06-14 07:54:47 -0700 |
---|---|---|
committer | C.J. Collier <cjcollier@linuxfoundation.org> | 2016-06-14 07:55:43 -0700 |
commit | 5129044dce1f85ce4950f31bcf90f3886466f06a (patch) | |
tree | 1c6269614c0c15ffef8451c58ae8f8b30a1bc804 /lib/librte_ivshmem/rte_ivshmem.c | |
parent | e04be89c2409570e0055b2cda60bd11395bb93b0 (diff) |
Imported upstream release 16.04
* gbp import-orig ../dpdk-16.04.tar.xz
Change-Id: Iac2196db782ba322f6974d8a752acc34ce5024c3
Signed-off-by: C.J. Collier <cjcollier@linuxfoundation.org>
Diffstat (limited to 'lib/librte_ivshmem/rte_ivshmem.c')
-rw-r--r-- | lib/librte_ivshmem/rte_ivshmem.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/lib/librte_ivshmem/rte_ivshmem.c b/lib/librte_ivshmem/rte_ivshmem.c new file mode 100644 index 00000000..c8b332ce --- /dev/null +++ b/lib/librte_ivshmem/rte_ivshmem.c @@ -0,0 +1,905 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <stdio.h> + +#include <rte_eal_memconfig.h> +#include <rte_memory.h> +#include <rte_ivshmem.h> +#include <rte_string_fns.h> +#include <rte_common.h> +#include <rte_log.h> +#include <rte_debug.h> +#include <rte_spinlock.h> +#include <rte_common.h> +#include <rte_malloc.h> + +#include "rte_ivshmem.h" + +#define IVSHMEM_CONFIG_FILE_FMT "/var/run/.dpdk_ivshmem_metadata_%s" +#define IVSHMEM_QEMU_CMD_LINE_HEADER_FMT "-device ivshmem,size=%" PRIu64 "M,shm=fd%s" +#define IVSHMEM_QEMU_CMD_FD_FMT ":%s:0x%" PRIx64 ":0x%" PRIx64 +#define IVSHMEM_QEMU_CMDLINE_BUFSIZE 1024 +#define IVSHMEM_MAX_PAGES (1 << 12) +#define adjacent(x,y) (((x).phys_addr+(x).len)==(y).phys_addr) +#define METADATA_SIZE_ALIGNED \ + (RTE_ALIGN_CEIL(sizeof(struct rte_ivshmem_metadata),pagesz)) + +#define GET_PAGEMAP_ADDR(in,addr,dlm,err) \ +{ \ + char *end; \ + errno = 0; \ + addr = strtoull((in), &end, 16); \ + if (errno != 0 || *end != (dlm)) { \ + RTE_LOG(ERR, EAL, err); \ + goto error; \ + } \ + (in) = end + 1; \ +} + +static int pagesz; + +struct memseg_cache_entry { + char filepath[PATH_MAX]; + uint64_t offset; + uint64_t len; +}; + +struct ivshmem_config { + struct rte_ivshmem_metadata * metadata; + struct memseg_cache_entry memseg_cache[IVSHMEM_MAX_PAGES]; + /**< account for multiple files per segment case */ + struct flock lock; + rte_spinlock_t sl; +}; + +static struct ivshmem_config +ivshmem_global_config[RTE_LIBRTE_IVSHMEM_MAX_METADATA_FILES]; + +static rte_spinlock_t global_cfg_sl; + +static struct ivshmem_config * +get_config_by_name(const char * name) +{ + struct rte_ivshmem_metadata * config; + unsigned i; + + for (i = 0; i < RTE_DIM(ivshmem_global_config); i++) { + config = ivshmem_global_config[i].metadata; + if (config == NULL) + return NULL; + if (strncmp(name, config->name, IVSHMEM_NAME_LEN) == 0) + return &ivshmem_global_config[i]; + } + + return NULL; +} + +static int +overlap(const struct rte_memzone * s1, const struct rte_memzone * s2) +{ + uint64_t start1, end1, start2, end2; + + start1 = s1->addr_64; + end1 = s1->addr_64 + s1->len; + start2 = s2->addr_64; + end2 = s2->addr_64 + s2->len; + + if (start1 >= start2 && start1 < end2) + return 1; + if (start2 >= start1 && start2 < end1) + return 1; + + return 0; +} + +static struct rte_memzone * +get_memzone_by_addr(const void * addr) +{ + struct rte_memzone * tmp, * mz; + struct rte_mem_config * mcfg; + int i; + + mcfg = rte_eal_get_configuration()->mem_config; + mz = NULL; + + /* find memzone for the ring */ + for (i = 0; i < RTE_MAX_MEMZONE; i++) { + tmp = &mcfg->memzone[i]; + + if (tmp->addr_64 == (uint64_t) addr) { + mz = tmp; + break; + } + } + + return mz; +} + +static int +entry_compare(const void * a, const void * b) +{ + const struct rte_ivshmem_metadata_entry * e1 = + (const struct rte_ivshmem_metadata_entry*) a; + const struct rte_ivshmem_metadata_entry * e2 = + (const struct rte_ivshmem_metadata_entry*) b; + + /* move unallocated zones to the end */ + if (e1->mz.addr == NULL && e2->mz.addr == NULL) + return 0; + if (e1->mz.addr == 0) + return 1; + if (e2->mz.addr == 0) + return -1; + + return e1->mz.phys_addr > e2->mz.phys_addr; +} + +/* fills hugepage cache entry for a given start virt_addr */ +static int +get_hugefile_by_virt_addr(uint64_t virt_addr, struct memseg_cache_entry * e) +{ + uint64_t start_addr, end_addr; + char *start,*path_end; + char buf[PATH_MAX*2]; + FILE *f; + + start = NULL; + path_end = NULL; + start_addr = 0; + + memset(e->filepath, 0, sizeof(e->filepath)); + + /* open /proc/self/maps */ + f = fopen("/proc/self/maps", "r"); + if (f == NULL) { + RTE_LOG(ERR, EAL, "cannot open /proc/self/maps!\n"); + return -1; + } + + /* parse maps */ + while (fgets(buf, sizeof(buf), f) != NULL) { + + /* get endptr to end of start addr */ + start = buf; + + GET_PAGEMAP_ADDR(start,start_addr,'-', + "Cannot find start address in maps!\n"); + + /* if start address is bigger than our address, skip */ + if (start_addr > virt_addr) + continue; + + GET_PAGEMAP_ADDR(start,end_addr,' ', + "Cannot find end address in maps!\n"); + + /* if end address is less than our address, skip */ + if (end_addr <= virt_addr) + continue; + + /* find where the path starts */ + start = strstr(start, "/"); + + if (start == NULL) + continue; + + /* at this point, we know that this is our map. + * now let's find the file */ + path_end = strstr(start, "\n"); + break; + } + + if (path_end == NULL) { + RTE_LOG(ERR, EAL, "Hugefile path not found!\n"); + goto error; + } + + /* calculate offset and copy the file path */ + snprintf(e->filepath, RTE_PTR_DIFF(path_end, start) + 1, "%s", start); + + e->offset = virt_addr - start_addr; + + fclose(f); + + return 0; +error: + fclose(f); + return -1; +} + +/* + * This is a complex function. What it does is the following: + * 1. Goes through metadata and gets list of hugepages involved + * 2. Sorts the hugepages by size (1G first) + * 3. Goes through metadata again and writes correct offsets + * 4. Goes through pages and finds out their filenames, offsets etc. + */ +static int +build_config(struct rte_ivshmem_metadata * metadata) +{ + struct rte_ivshmem_metadata_entry * e_local; + struct memseg_cache_entry * ms_local; + struct rte_memseg pages[IVSHMEM_MAX_PAGES]; + struct rte_ivshmem_metadata_entry *entry; + struct memseg_cache_entry * c_entry, * prev_entry; + struct ivshmem_config * config; + unsigned i, j, mz_iter, ms_iter; + uint64_t biggest_len; + int biggest_idx; + + /* return error if we try to use an unknown config file */ + config = get_config_by_name(metadata->name); + if (config == NULL) { + RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", metadata->name); + goto fail_e; + } + + memset(pages, 0, sizeof(pages)); + + e_local = malloc(sizeof(config->metadata->entry)); + if (e_local == NULL) + goto fail_e; + ms_local = malloc(sizeof(config->memseg_cache)); + if (ms_local == NULL) + goto fail_ms; + + + /* make local copies before doing anything */ + memcpy(e_local, config->metadata->entry, sizeof(config->metadata->entry)); + memcpy(ms_local, config->memseg_cache, sizeof(config->memseg_cache)); + + qsort(e_local, RTE_DIM(config->metadata->entry), sizeof(struct rte_ivshmem_metadata_entry), + entry_compare); + + /* first pass - collect all huge pages */ + for (mz_iter = 0; mz_iter < RTE_DIM(config->metadata->entry); mz_iter++) { + + entry = &e_local[mz_iter]; + + uint64_t start_addr = RTE_ALIGN_FLOOR(entry->mz.addr_64, + entry->mz.hugepage_sz); + uint64_t offset = entry->mz.addr_64 - start_addr; + uint64_t len = RTE_ALIGN_CEIL(entry->mz.len + offset, + entry->mz.hugepage_sz); + + if (entry->mz.addr_64 == 0 || start_addr == 0 || len == 0) + continue; + + int start_page; + + /* find first unused page - mz are phys_addr sorted so we don't have to + * look out for holes */ + for (i = 0; i < RTE_DIM(pages); i++) { + + /* skip if we already have this page */ + if (pages[i].addr_64 == start_addr) { + start_addr += entry->mz.hugepage_sz; + len -= entry->mz.hugepage_sz; + continue; + } + /* we found a new page */ + else if (pages[i].addr_64 == 0) { + start_page = i; + break; + } + } + if (i == RTE_DIM(pages)) { + RTE_LOG(ERR, EAL, "Cannot find unused page!\n"); + goto fail; + } + + /* populate however many pages the memzone has */ + for (i = start_page; i < RTE_DIM(pages) && len != 0; i++) { + + pages[i].addr_64 = start_addr; + pages[i].len = entry->mz.hugepage_sz; + start_addr += entry->mz.hugepage_sz; + len -= entry->mz.hugepage_sz; + } + /* if there's still length left */ + if (len != 0) { + RTE_LOG(ERR, EAL, "Not enough space for pages!\n"); + goto fail; + } + } + + /* second pass - sort pages by size */ + for (i = 0; i < RTE_DIM(pages); i++) { + + if (pages[i].addr == NULL) + break; + + biggest_len = 0; + biggest_idx = -1; + + /* + * browse all entries starting at 'i', and find the + * entry with the smallest addr + */ + for (j=i; j< RTE_DIM(pages); j++) { + if (pages[j].addr == NULL) + break; + if (biggest_len == 0 || + pages[j].len > biggest_len) { + biggest_len = pages[j].len; + biggest_idx = j; + } + } + + /* should not happen */ + if (biggest_idx == -1) { + RTE_LOG(ERR, EAL, "Error sorting by size!\n"); + goto fail; + } + if (i != (unsigned) biggest_idx) { + struct rte_memseg tmp; + + memcpy(&tmp, &pages[biggest_idx], sizeof(struct rte_memseg)); + + /* we don't want to break contiguousness, so instead of just + * swapping segments, we move all the preceding segments to the + * right and then put the old segment @ biggest_idx in place of + * segment @ i */ + for (j = biggest_idx - 1; j >= i; j--) { + memcpy(&pages[j+1], &pages[j], sizeof(struct rte_memseg)); + memset(&pages[j], 0, sizeof(struct rte_memseg)); + if (j == 0) + break; + } + + /* put old biggest segment to its new place */ + memcpy(&pages[i], &tmp, sizeof(struct rte_memseg)); + } + } + + /* third pass - write correct offsets */ + for (mz_iter = 0; mz_iter < RTE_DIM(config->metadata->entry); mz_iter++) { + + uint64_t offset = 0; + + entry = &e_local[mz_iter]; + + if (entry->mz.addr_64 == 0) + break; + + /* find page for current memzone */ + for (i = 0; i < RTE_DIM(pages); i++) { + /* we found our page */ + if (entry->mz.addr_64 >= pages[i].addr_64 && + entry->mz.addr_64 < pages[i].addr_64 + pages[i].len) { + entry->offset = (entry->mz.addr_64 - pages[i].addr_64) + + offset; + break; + } + offset += pages[i].len; + } + if (i == RTE_DIM(pages)) { + RTE_LOG(ERR, EAL, "Page not found!\n"); + goto fail; + } + } + + ms_iter = 0; + prev_entry = NULL; + + /* fourth pass - create proper memseg cache */ + for (i = 0; i < RTE_DIM(pages) && + ms_iter <= RTE_DIM(config->memseg_cache); i++) { + if (pages[i].addr_64 == 0) + break; + + + if (ms_iter == RTE_DIM(pages)) { + RTE_LOG(ERR, EAL, "The universe has collapsed!\n"); + goto fail; + } + + c_entry = &ms_local[ms_iter]; + c_entry->len = pages[i].len; + + if (get_hugefile_by_virt_addr(pages[i].addr_64, c_entry) < 0) + goto fail; + + /* if previous entry has the same filename and is contiguous, + * clear current entry and increase previous entry's length + */ + if (prev_entry != NULL && + strncmp(c_entry->filepath, prev_entry->filepath, + sizeof(c_entry->filepath)) == 0 && + prev_entry->offset + prev_entry->len == c_entry->offset) { + prev_entry->len += pages[i].len; + memset(c_entry, 0, sizeof(struct memseg_cache_entry)); + } + else { + prev_entry = c_entry; + ms_iter++; + } + } + + /* update current configuration with new valid data */ + memcpy(config->metadata->entry, e_local, sizeof(config->metadata->entry)); + memcpy(config->memseg_cache, ms_local, sizeof(config->memseg_cache)); + + free(ms_local); + free(e_local); + + return 0; +fail: + free(ms_local); +fail_ms: + free(e_local); +fail_e: + return -1; +} + +static int +add_memzone_to_metadata(const struct rte_memzone * mz, + struct ivshmem_config * config) +{ + struct rte_ivshmem_metadata_entry * entry; + unsigned i, idx; + struct rte_mem_config *mcfg; + + if (mz->len == 0) { + RTE_LOG(ERR, EAL, "Trying to add an empty memzone\n"); + return -1; + } + + rte_spinlock_lock(&config->sl); + + mcfg = rte_eal_get_configuration()->mem_config; + + /* it prevents the memzone being freed while we add it to the metadata */ + rte_rwlock_write_lock(&mcfg->mlock); + + /* find free slot in this config */ + for (i = 0; i < RTE_DIM(config->metadata->entry); i++) { + entry = &config->metadata->entry[i]; + + if (&entry->mz.addr_64 != 0 && overlap(mz, &entry->mz)) { + RTE_LOG(ERR, EAL, "Overlapping memzones!\n"); + goto fail; + } + + /* if addr is zero, the memzone is probably free */ + if (entry->mz.addr_64 == 0) { + RTE_LOG(DEBUG, EAL, "Adding memzone '%s' at %p to metadata %s\n", + mz->name, mz->addr, config->metadata->name); + memcpy(&entry->mz, mz, sizeof(struct rte_memzone)); + + /* run config file parser */ + if (build_config(config->metadata) < 0) + goto fail; + + break; + } + } + + /* if we reached the maximum, that means we have no place in config */ + if (i == RTE_DIM(config->metadata->entry)) { + RTE_LOG(ERR, EAL, "No space left in IVSHMEM metadata %s!\n", + config->metadata->name); + goto fail; + } + + idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone); + idx = idx / sizeof(struct rte_memzone); + + /* mark the memzone not freeable */ + mcfg->memzone[idx].ioremap_addr = mz->phys_addr; + + rte_rwlock_write_unlock(&mcfg->mlock); + rte_spinlock_unlock(&config->sl); + return 0; +fail: + rte_rwlock_write_unlock(&mcfg->mlock); + rte_spinlock_unlock(&config->sl); + return -1; +} + +static int +add_ring_to_metadata(const struct rte_ring * r, + struct ivshmem_config * config) +{ + struct rte_memzone * mz; + + mz = get_memzone_by_addr(r); + + if (!mz) { + RTE_LOG(ERR, EAL, "Cannot find memzone for ring!\n"); + return -1; + } + + return add_memzone_to_metadata(mz, config); +} + +static int +add_mempool_to_metadata(const struct rte_mempool * mp, + struct ivshmem_config * config) +{ + struct rte_memzone * mz; + int ret; + + mz = get_memzone_by_addr(mp); + ret = 0; + + if (!mz) { + RTE_LOG(ERR, EAL, "Cannot find memzone for mempool!\n"); + return -1; + } + + /* mempool consists of memzone and ring */ + ret = add_memzone_to_metadata(mz, config); + if (ret < 0) + return -1; + + return add_ring_to_metadata(mp->ring, config); +} + +int +rte_ivshmem_metadata_add_ring(const struct rte_ring * r, const char * name) +{ + struct ivshmem_config * config; + + if (name == NULL || r == NULL) + return -1; + + config = get_config_by_name(name); + + if (config == NULL) { + RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name); + return -1; + } + + return add_ring_to_metadata(r, config); +} + +int +rte_ivshmem_metadata_add_memzone(const struct rte_memzone * mz, const char * name) +{ + struct ivshmem_config * config; + + if (name == NULL || mz == NULL) + return -1; + + config = get_config_by_name(name); + + if (config == NULL) { + RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name); + return -1; + } + + return add_memzone_to_metadata(mz, config); +} + +int +rte_ivshmem_metadata_add_mempool(const struct rte_mempool * mp, const char * name) +{ + struct ivshmem_config * config; + + if (name == NULL || mp == NULL) + return -1; + + config = get_config_by_name(name); + + if (config == NULL) { + RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name); + return -1; + } + + return add_mempool_to_metadata(mp, config); +} + +static inline void +ivshmem_config_path(char *buffer, size_t bufflen, const char *name) +{ + snprintf(buffer, bufflen, IVSHMEM_CONFIG_FILE_FMT, name); +} + + + +static inline +void *ivshmem_metadata_create(const char *name, size_t size, + struct flock *lock) +{ + int retval, fd; + void *metadata_addr; + char pathname[PATH_MAX]; + + ivshmem_config_path(pathname, sizeof(pathname), name); + + fd = open(pathname, O_RDWR | O_CREAT, 0660); + if (fd < 0) { + RTE_LOG(ERR, EAL, "Cannot open '%s'\n", pathname); + return NULL; + } + + size = METADATA_SIZE_ALIGNED; + + retval = fcntl(fd, F_SETLK, lock); + if (retval < 0){ + close(fd); + RTE_LOG(ERR, EAL, "Cannot create lock on '%s'. Is another " + "process using it?\n", pathname); + return NULL; + } + + retval = ftruncate(fd, size); + if (retval < 0){ + close(fd); + RTE_LOG(ERR, EAL, "Cannot resize '%s'\n", pathname); + return NULL; + } + + metadata_addr = mmap(NULL, size, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (metadata_addr == MAP_FAILED){ + RTE_LOG(ERR, EAL, "Cannot mmap memory for '%s'\n", pathname); + + /* we don't care if we can't unlock */ + fcntl(fd, F_UNLCK, lock); + close(fd); + + return NULL; + } + + return metadata_addr; +} + +int rte_ivshmem_metadata_create(const char *name) +{ + struct ivshmem_config * ivshmem_config; + unsigned index; + + if (pagesz == 0) + pagesz = getpagesize(); + + if (name == NULL) + return -1; + + rte_spinlock_lock(&global_cfg_sl); + + for (index = 0; index < RTE_DIM(ivshmem_global_config); index++) { + if (ivshmem_global_config[index].metadata == NULL) { + ivshmem_config = &ivshmem_global_config[index]; + break; + } + } + + if (index == RTE_DIM(ivshmem_global_config)) { + RTE_LOG(ERR, EAL, "Cannot create more ivshmem config files. " + "Maximum has been reached\n"); + rte_spinlock_unlock(&global_cfg_sl); + return -1; + } + + ivshmem_config->lock.l_type = F_WRLCK; + ivshmem_config->lock.l_whence = SEEK_SET; + + ivshmem_config->lock.l_start = 0; + ivshmem_config->lock.l_len = METADATA_SIZE_ALIGNED; + + ivshmem_global_config[index].metadata = ((struct rte_ivshmem_metadata *) + ivshmem_metadata_create( + name, + sizeof(struct rte_ivshmem_metadata), + &ivshmem_config->lock)); + + if (ivshmem_global_config[index].metadata == NULL) { + rte_spinlock_unlock(&global_cfg_sl); + return -1; + } + + /* Metadata setup */ + memset(ivshmem_config->metadata, 0, sizeof(struct rte_ivshmem_metadata)); + ivshmem_config->metadata->magic_number = IVSHMEM_MAGIC; + snprintf(ivshmem_config->metadata->name, + sizeof(ivshmem_config->metadata->name), "%s", name); + + rte_spinlock_unlock(&global_cfg_sl); + + return 0; +} + +int +rte_ivshmem_metadata_cmdline_generate(char *buffer, unsigned size, const char *name) +{ + const struct memseg_cache_entry * ms_cache, *entry; + struct ivshmem_config * config; + char cmdline[IVSHMEM_QEMU_CMDLINE_BUFSIZE], *cmdline_ptr; + char cfg_file_path[PATH_MAX]; + unsigned remaining_len, tmplen, iter; + uint64_t shared_mem_size, zero_size, total_size; + + if (buffer == NULL || name == NULL) + return -1; + + config = get_config_by_name(name); + + if (config == NULL) { + RTE_LOG(ERR, EAL, "Config %s not found!\n", name); + return -1; + } + + rte_spinlock_lock(&config->sl); + + /* prepare metadata file path */ + snprintf(cfg_file_path, sizeof(cfg_file_path), IVSHMEM_CONFIG_FILE_FMT, + config->metadata->name); + + ms_cache = config->memseg_cache; + + cmdline_ptr = cmdline; + remaining_len = sizeof(cmdline); + + shared_mem_size = 0; + iter = 0; + + while ((ms_cache[iter].len != 0) && (iter < RTE_DIM(config->metadata->entry))) { + + entry = &ms_cache[iter]; + + /* Offset and sizes within the current pathname */ + tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT, + entry->filepath, entry->offset, entry->len); + + shared_mem_size += entry->len; + + cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen); + remaining_len -= tmplen; + + if (remaining_len == 0) { + RTE_LOG(ERR, EAL, "Command line too long!\n"); + rte_spinlock_unlock(&config->sl); + return -1; + } + + iter++; + } + + total_size = rte_align64pow2(shared_mem_size + METADATA_SIZE_ALIGNED); + zero_size = total_size - shared_mem_size - METADATA_SIZE_ALIGNED; + + /* add /dev/zero to command-line to fill the space */ + tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT, + "/dev/zero", + (uint64_t)0x0, + zero_size); + + cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen); + remaining_len -= tmplen; + + if (remaining_len == 0) { + RTE_LOG(ERR, EAL, "Command line too long!\n"); + rte_spinlock_unlock(&config->sl); + return -1; + } + + /* add metadata file to the end of command-line */ + tmplen = snprintf(cmdline_ptr, remaining_len, IVSHMEM_QEMU_CMD_FD_FMT, + cfg_file_path, + (uint64_t)0x0, + METADATA_SIZE_ALIGNED); + + cmdline_ptr = RTE_PTR_ADD(cmdline_ptr, tmplen); + remaining_len -= tmplen; + + if (remaining_len == 0) { + RTE_LOG(ERR, EAL, "Command line too long!\n"); + rte_spinlock_unlock(&config->sl); + return -1; + } + + /* if current length of the command line is bigger than the buffer supplied + * by the user, or if command-line is bigger than what IVSHMEM accepts */ + if ((sizeof(cmdline) - remaining_len) > size) { + RTE_LOG(ERR, EAL, "Buffer is too short!\n"); + rte_spinlock_unlock(&config->sl); + return -1; + } + /* complete the command-line */ + snprintf(buffer, size, + IVSHMEM_QEMU_CMD_LINE_HEADER_FMT, + total_size >> 20, + cmdline); + + rte_spinlock_unlock(&config->sl); + + return 0; +} + +void +rte_ivshmem_metadata_dump(FILE *f, const char *name) +{ + unsigned i = 0; + struct ivshmem_config * config; + struct rte_ivshmem_metadata_entry *entry; +#ifdef RTE_LIBRTE_IVSHMEM_DEBUG + uint64_t addr; + uint64_t end, hugepage_sz; + struct memseg_cache_entry e; +#endif + + if (name == NULL) + return; + + /* return error if we try to use an unknown config file */ + config = get_config_by_name(name); + if (config == NULL) { + RTE_LOG(ERR, EAL, "Cannot find IVSHMEM config %s!\n", name); + return; + } + + rte_spinlock_lock(&config->sl); + + entry = &config->metadata->entry[0]; + + while (entry->mz.addr != NULL && i < RTE_DIM(config->metadata->entry)) { + + fprintf(f, "Entry %u: name:<%-20s>, phys:0x%-15lx, len:0x%-15lx, " + "virt:%-15p, off:0x%-15lx\n", + i, + entry->mz.name, + entry->mz.phys_addr, + entry->mz.len, + entry->mz.addr, + entry->offset); + i++; + +#ifdef RTE_LIBRTE_IVSHMEM_DEBUG + fprintf(f, "\tHugepage files:\n"); + + hugepage_sz = entry->mz.hugepage_sz; + addr = RTE_ALIGN_FLOOR(entry->mz.addr_64, hugepage_sz); + end = addr + RTE_ALIGN_CEIL(entry->mz.len + (entry->mz.addr_64 - addr), + hugepage_sz); + + for (; addr < end; addr += hugepage_sz) { + memset(&e, 0, sizeof(e)); + + get_hugefile_by_virt_addr(addr, &e); + + fprintf(f, "\t0x%"PRIx64 "-0x%" PRIx64 " offset: 0x%" PRIx64 " %s\n", + addr, addr + hugepage_sz, e.offset, e.filepath); + } +#endif + entry++; + } + + rte_spinlock_unlock(&config->sl); +} |