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); }
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); }
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; }
/* * 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)); } /* 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; }
unsigned get_config_by_index() { string8 name; get_config_by_name(name); return get_index_from_name(name); }