Skip to content

Commit

Permalink
bus/pci: forbid IOVA mode if IOMMU address width too small
Browse files Browse the repository at this point in the history
Intel VT-d supports different address widths for the IOVAs, from
39 bits to 56 bits.

While recent processors support at least 48 bits, VT-d emulation
currently only supports 39 bits. It makes DMA mapping to fail in this
case when using VA as IOVA mode, as user-space virtual addresses uses
up to 47 bits (see kernel's Documentation/x86/x86_64/mm.txt).

This patch parses VT-d CAP register value available in sysfs, and
forbid VA as IOVA mode if the GAW is 39 bits or unknown.

Fixes: f37dfab ("drivers/net: enable IOVA mode for Intel PMDs")
Cc: stable@dpdk.org

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Tested-by: Chas Williams <chas3@att.com>
  • Loading branch information
mcoquelin authored and tmonjalo committed Jan 20, 2018
1 parent de4a79c commit 54a328f
Showing 1 changed file with 81 additions and 9 deletions.
90 changes: 81 additions & 9 deletions drivers/bus/pci/linux/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,82 @@ pci_one_device_has_iova_va(void)
return 0;
}

#if defined(RTE_ARCH_X86)
static bool
pci_one_device_iommu_support_va(struct rte_pci_device *dev)
{
#define VTD_CAP_MGAW_SHIFT 16
#define VTD_CAP_MGAW_MASK (0x3fULL << VTD_CAP_MGAW_SHIFT)
#define X86_VA_WIDTH 47 /* From Documentation/x86/x86_64/mm.txt */
struct rte_pci_addr *addr = &dev->addr;
char filename[PATH_MAX];
FILE *fp;
uint64_t mgaw, vtd_cap_reg = 0;

snprintf(filename, sizeof(filename),
"%s/" PCI_PRI_FMT "/iommu/intel-iommu/cap",
rte_pci_get_sysfs_path(), addr->domain, addr->bus, addr->devid,
addr->function);
if (access(filename, F_OK) == -1) {
/* We don't have an Intel IOMMU, assume VA supported*/
return true;
}

/* We have an intel IOMMU */
fp = fopen(filename, "r");
if (fp == NULL) {
RTE_LOG(ERR, EAL, "%s(): can't open %s\n", __func__, filename);
return false;
}

if (fscanf(fp, "%" PRIx64, &vtd_cap_reg) != 1) {
RTE_LOG(ERR, EAL, "%s(): can't read %s\n", __func__, filename);
fclose(fp);
return false;
}

fclose(fp);

mgaw = ((vtd_cap_reg & VTD_CAP_MGAW_MASK) >> VTD_CAP_MGAW_SHIFT) + 1;
if (mgaw < X86_VA_WIDTH)
return false;

return true;
}
#elif defined(RTE_ARCH_PPC_64)
static bool
pci_one_device_iommu_support_va(__rte_unused struct rte_pci_device *dev)
{
return false;
}
#else
static bool
pci_one_device_iommu_support_va(__rte_unused struct rte_pci_device *dev)
{
return true;
}
#endif

/*
* All devices IOMMUs support VA as IOVA
*/
static bool
pci_devices_iommu_support_va(void)
{
struct rte_pci_device *dev = NULL;
struct rte_pci_driver *drv = NULL;

FOREACH_DRIVER_ON_PCIBUS(drv) {
FOREACH_DEVICE_ON_PCIBUS(dev) {
if (!rte_pci_match(drv, dev))
continue;
if (!pci_one_device_iommu_support_va(dev))
return false;
}
}
return true;
}

/*
* Get iommu class of PCI devices on the bus.
*/
Expand All @@ -557,26 +633,22 @@ rte_pci_get_iommu_class(void)
bool is_vfio_noiommu_enabled = true;
bool has_iova_va;
bool is_bound_uio;
bool spapr_iommu =
#if defined(RTE_ARCH_PPC_64)
true;
#else
false;
#endif
bool iommu_no_va;

is_bound = pci_one_device_is_bound();
if (!is_bound)
return RTE_IOVA_DC;

has_iova_va = pci_one_device_has_iova_va();
is_bound_uio = pci_one_device_bound_uio();
iommu_no_va = !pci_devices_iommu_support_va();
#ifdef VFIO_PRESENT
is_vfio_noiommu_enabled = rte_vfio_noiommu_is_enabled() == true ?
true : false;
#endif

if (has_iova_va && !is_bound_uio && !is_vfio_noiommu_enabled &&
!spapr_iommu)
!iommu_no_va)
return RTE_IOVA_VA;

if (has_iova_va) {
Expand All @@ -585,8 +657,8 @@ rte_pci_get_iommu_class(void)
RTE_LOG(WARNING, EAL, "vfio-noiommu mode configured\n");
if (is_bound_uio)
RTE_LOG(WARNING, EAL, "few device bound to UIO\n");
if (spapr_iommu)
RTE_LOG(WARNING, EAL, "sPAPR IOMMU does not support IOVA as VA\n");
if (iommu_no_va)
RTE_LOG(WARNING, EAL, "IOMMU does not support IOVA as VA\n");
}

return RTE_IOVA_PA;
Expand Down

0 comments on commit 54a328f

Please sign in to comment.