diff --git a/include/spdk/stdinc.h b/include/spdk/stdinc.h index f200223be0..65820d58e0 100644 --- a/include/spdk/stdinc.h +++ b/include/spdk/stdinc.h @@ -57,6 +57,7 @@ extern "C" { #include #include #include +#include #include /* POSIX */ diff --git a/lib/env_dpdk/init.c b/lib/env_dpdk/init.c index 58cb363cad..9fed7c00f7 100644 --- a/lib/env_dpdk/init.c +++ b/lib/env_dpdk/init.c @@ -175,6 +175,75 @@ spdk_push_arg(char *args[], int *argcount, char *arg) return tmp; } +#if defined(__linux__) && defined(__x86_64__) + +/* TODO: Can likely get this value from rlimits in the future */ +#define SPDK_IOMMU_VA_REQUIRED_WIDTH 48 +#define VTD_CAP_MGAW_SHIFT 16 +#define VTD_CAP_MGAW_MASK (0x3F << VTD_CAP_MGAW_SHIFT) + +static int +spdk_get_iommu_width(void) +{ + DIR *dir; + FILE *file; + struct dirent *entry; + char mgaw_path[64]; + char buf[64]; + char *end; + long long int val; + int width, tmp; + + dir = opendir("/sys/devices/virtual/iommu/"); + if (dir == NULL) { + return -EINVAL; + } + + width = 0; + + while ((entry = readdir(dir)) != NULL) { + /* Find directories named "dmar0", "dmar1", etc */ + if (strncmp(entry->d_name, "dmar", sizeof("dmar") - 1) != 0) { + continue; + } + + tmp = snprintf(mgaw_path, sizeof(mgaw_path), "/sys/devices/virtual/iommu/%s/intel-iommu/cap", + entry->d_name); + if ((unsigned)tmp >= sizeof(mgaw_path)) { + continue; + } + + file = fopen(mgaw_path, "r"); + if (file == NULL) { + continue; + } + + if (fgets(buf, sizeof(buf), file) == NULL) { + fclose(file); + continue; + } + + val = strtoll(buf, &end, 16); + if (val == LLONG_MIN || val == LLONG_MAX) { + fclose(file); + continue; + } + + tmp = ((val & VTD_CAP_MGAW_MASK) >> VTD_CAP_MGAW_SHIFT) + 1; + if (width == 0 || tmp < width) { + width = tmp; + } + + fclose(file); + } + + closedir(dir); + + return width; +} + +#endif + static int spdk_build_eal_cmdline(const struct spdk_env_opts *opts) { @@ -337,7 +406,19 @@ spdk_build_eal_cmdline(const struct spdk_env_opts *opts) #ifdef __linux__ -#ifdef __PPC64__ +#if defined(__x86_64__) + /* DPDK by default guesses that it should be using iova-mode=va so that it can + * support running as an unprivileged user. However, some systems (especially + * virtual machines) don't have an IOMMU capable of handling the full virtual + * address space and DPDK doesn't currently catch that. Add a check in SPDK + * and force iova-mode=pa here. */ + if (spdk_get_iommu_width() < SPDK_IOMMU_VA_REQUIRED_WIDTH) { + args = spdk_push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa")); + if (args == NULL) { + return -1; + } + } +#elif defined(__PPC64__) /* On Linux + PowerPC, DPDK doesn't support VA mode at all. Unfortunately, it doesn't correctly * auto-detect at the moment, so we'll just force it here. */ args = spdk_push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa")); @@ -346,6 +427,7 @@ spdk_build_eal_cmdline(const struct spdk_env_opts *opts) } #endif + /* Set the base virtual address - it must be an address that is not in the * ASAN shadow region, otherwise ASAN-enabled builds will ignore the * mmap hint.