Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1317895 > unrolled thread
| Started by | Eric Auger <eric.auger@linaro.org> |
|---|---|
| First post | 2016-01-26 14:20 +0100 |
| Last post | 2016-01-27 10:00 +0100 |
| Articles | 16 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 09/10] vfio-pci: create an iommu mapping for msi address Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 01/10] iommu: Add DOMAIN_ATTR_MSI_MAPPING attribute Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 05/10] vfio/type1: attach a reserved iova domain to vfio_domain Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
Re: [PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping kbuild test robot <lkp@intel.com> - 2016-01-26 17:50 +0100
Re: [PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping kbuild test robot <lkp@intel.com> - 2016-01-26 19:40 +0100
[PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
Re: [PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova kbuild test robot <lkp@intel.com> - 2016-01-26 17:20 +0100
Re: [PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova Eric Auger <eric.auger@linaro.org> - 2016-01-26 17:40 +0100
[PATCH 08/10] vfio: introduce vfio_group_require_msi_mapping Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 04/10] vfio: introduce VFIO_IOVA_RESERVED vfio_dma type Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 03/10] vfio_iommu_type1: add reserved binding RB tree management Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
[PATCH 02/10] vfio: expose MSI mapping requirement through VFIO_IOMMU_GET_INFO Eric Auger <eric.auger@linaro.org> - 2016-01-26 14:20 +0100
RE: [PATCH 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 Pavel Fedin <p.fedin@samsung.com> - 2016-01-26 18:30 +0100
Re: [PATCH 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 Eric Auger <eric.auger@linaro.org> - 2016-01-27 10:00 +0100
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 |
| Message-ID | <qVbHj-4Dq-7@gated-at.bofh.it> |
This series addresses KVM PCIe passthrough with MSI enabled on ARM/ARM64.
It pursues the efforts done on [1], [2], [3]. It also aims at covering the
same need on some PowerPC platforms.
On x86 all accesses to the 1MB PA region [FEE0_0000h - FEF0_000h] are directed
as interrupt messages: accesses to this special PA window directly target the
APIC configuration space and not DRAM, meaning the downstream IOMMU is bypassed.
This is not the case on above mentionned platforms where MSI messages emitted
by devices are conveyed through the IOMMU. This means an IOVA/host PA mapping
must exist for the MSI to reach the MSI controller. Normal way to create
IOVA bindings consists in using VFIO DMA MAP API. However in this case
the MSI IOVA is not mapped onto guest RAM but on host physical page (the MSI
controller frame).
Following first comments, the spirit of [2] is kept: the guest registers
an IOVA range reserved for MSI mapping. When the VFIO-PCIe driver allocates
its MSI vectors, it overwrites the MSI controller physical address with an IOVA,
allocated within the window provided by the userspace. This IOVA is mapped
onto the MSI controller frame physical page.
The series does not address yet the problematic of telling the userspace how
much IOVA he should provision.
Best Regards
Eric
Testing:
This is currently tested on ARM64 AMD Overdrive HW (single GICv2m frame)
with an e1000e PCIe card. This is not tested on PPC.
References:
[1] [RFC 0/2] VFIO: Add virtual MSI doorbell support
(https://lkml.org/lkml/2015/7/24/135)
[2] [RFC PATCH 0/6] vfio: Add interface to map MSI pages
(https://lists.cs.columbia.edu/pipermail/kvmarm/2015-September/016607.html)
[3] [PATCH v2 0/3] Introduce MSI hardware mapping for VFIO
(http://permalink.gmane.org/gmane.comp.emulators.kvm.arm.devel/3858)
Git:
https://git.linaro.org/people/eric.auger/linux.git/shortlog/refs/heads/v4.5-rc1-pcie-passthrough-v1
History:
RFC v1 [2] -> PATCH v1:
- use the existing dma map/unmap ioctl interface with a flag to register a
reserved IOVA range. Use the legacy Rb to store this special vfio_dma.
- a single reserved IOVA contiguous region now is allowed
- use of an RB tree indexed by PA to store allocated reserved slots
- use of a vfio_domain iova_domain to manage iova allocation within the
window provided by the userspace
- vfio alloc_map/unmap_free take a vfio_group handle
- vfio_group handle is cached in vfio_pci_device
- add ref counting to bindings
- user modality enabled at the end of the series
Eric Auger (10):
iommu: Add DOMAIN_ATTR_MSI_MAPPING attribute
vfio: expose MSI mapping requirement through VFIO_IOMMU_GET_INFO
vfio_iommu_type1: add reserved binding RB tree management
vfio: introduce VFIO_IOVA_RESERVED vfio_dma type
vfio/type1: attach a reserved iova domain to vfio_domain
vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova
vfio: pci: cache the vfio_group in vfio_pci_device
vfio: introduce vfio_group_require_msi_mapping
vfio-pci: create an iommu mapping for msi address
vfio: allow the user to register reserved iova range for MSI mapping
drivers/iommu/arm-smmu.c | 2 +
drivers/iommu/fsl_pamu_domain.c | 3 +
drivers/vfio/pci/vfio_pci.c | 8 +
drivers/vfio/pci/vfio_pci_intrs.c | 73 ++++++-
drivers/vfio/pci/vfio_pci_private.h | 1 +
drivers/vfio/vfio.c | 64 ++++++
drivers/vfio/vfio_iommu_type1.c | 412 +++++++++++++++++++++++++++++++++++-
include/linux/iommu.h | 1 +
include/linux/vfio.h | 39 +++-
include/uapi/linux/vfio.h | 10 +
10 files changed, 598 insertions(+), 15 deletions(-)
--
1.9.1
[toc] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 09/10] vfio-pci: create an iommu mapping for msi address |
| Message-ID | <qVbHk-4Dq-17@gated-at.bofh.it> |
| In reply to | #1317895 |
Some platforms require the MSI address programmed in the PCI
device to be an IOVA and not a host physical address.
This is typically the case for ARM and PowerPC, as opposed
to x86. This patch allocates an IOVA page and maps it onto
the physical page which contains the target MSI write transaction
address. In case an IOMMU binding already exists between those 2,
simply reprogram the device.
The binding is destroyed by the VFIO IOMMU backend.
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com>
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
---
drivers/vfio/pci/vfio_pci_intrs.c | 73 ++++++++++++++++++++++++++++++++++++---
1 file changed, 68 insertions(+), 5 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
index 3b3ba15..bac24c9 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -305,6 +305,57 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
return 0;
}
+/**
+ * vfio_set_mapped_msi_addr: overwrites the msi physical address with an iova
+ *
+ * @pdev: vfio pci device handle
+ * @irq: irq linux number
+ * returns 0 upon success, < 0 on failure
+ */
+static int vfio_set_mapped_msi_addr(struct vfio_pci_device *vdev, int irq)
+{
+ phys_addr_t msi_addr;
+ dma_addr_t msi_iova;
+ struct vfio_group *group = vdev->vfio_group;
+ struct msi_msg msg;
+ int ret;
+
+ get_cached_msi_msg(irq, &msg);
+ msi_addr = (phys_addr_t)(msg.address_hi) << 32 |
+ (phys_addr_t)(msg.address_lo);
+
+ ret = vfio_group_alloc_map_reserved_iova(group, msi_addr,
+ IOMMU_WRITE, &msi_iova);
+ if (ret)
+ goto out;
+
+ /* Re-program the msi-address with the iova */
+ msg.address_hi = (u32)(msi_iova >> 32);
+ msg.address_lo = (u32)(msi_iova & 0xffffffff);
+ pci_write_msi_msg(irq, &msg);
+
+out:
+ return ret;
+}
+
+/**
+ * vfio_unset_mapped_msi_addr: decrement the ref counter of the msi iova page
+ * associated to the linux irq (in case it is null unmaps and frees resources)
+ *
+ * @pdev: vfio pci device handle
+ * @irq: irq linux number
+ */
+static void vfio_unset_mapped_msi_addr(struct vfio_pci_device *vdev, int irq)
+{
+ dma_addr_t msi_iova;
+ struct vfio_group *group = vdev->vfio_group;
+ struct msi_msg msg;
+
+ get_cached_msi_msg(irq, &msg);
+ msi_iova = (u64)(msg.address_hi) << 32 | (u64)(msg.address_lo);
+ vfio_group_unmap_free_reserved_iova(group, msi_iova);
+}
+
static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
int vector, int fd, bool msix)
{
@@ -318,6 +369,7 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
return -EINVAL;
if (vdev->ctx[vector].trigger) {
+ vfio_unset_mapped_msi_addr(vdev, irq);
free_irq(irq, vdev->ctx[vector].trigger);
irq_bypass_unregister_producer(&vdev->ctx[vector].producer);
kfree(vdev->ctx[vector].name);
@@ -355,11 +407,8 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
ret = request_irq(irq, vfio_msihandler, 0,
vdev->ctx[vector].name, trigger);
- if (ret) {
- kfree(vdev->ctx[vector].name);
- eventfd_ctx_put(trigger);
- return ret;
- }
+ if (ret)
+ goto error_free;
vdev->ctx[vector].producer.token = trigger;
vdev->ctx[vector].producer.irq = irq;
@@ -369,9 +418,23 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
"irq bypass producer (token %p) registration fails: %d\n",
vdev->ctx[vector].producer.token, ret);
+ if (vfio_group_require_msi_mapping(vdev->vfio_group)) {
+ ret = vfio_set_mapped_msi_addr(vdev, irq);
+ if (ret)
+ goto error_free_irq;
+ }
+
vdev->ctx[vector].trigger = trigger;
return 0;
+
+error_free_irq:
+ free_irq(irq, vdev->ctx[vector].trigger);
+error_free:
+ kfree(vdev->ctx[vector].name);
+ eventfd_ctx_put(trigger);
+ return ret;
+
}
static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 01/10] iommu: Add DOMAIN_ATTR_MSI_MAPPING attribute |
| Message-ID | <qVbHk-4Dq-19@gated-at.bofh.it> |
| In reply to | #1317895 |
Introduce new DOMAIN_ATTR_MSI_MAPPING domain attribute. If supported,
this means the MSI addresses need to be mapped in the IOMMU. ARM SMMUS
and FSL PAMU, at least expose this attribute.
x86 IOMMUs typically don't expose the attribute since on x86 MSI write
transaction addresses always are within the 1MB PA region [FEE0_0000h -
FEF0_000h] window which directly targets the APIC configuration space and
hence bypass the sMMU.
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com>
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
to be honest I wonder whether this is property of the sMMU? Isn't it
a platform property?
RFC v1 -> RFC v2:
- the data field is not used
- for this attribute domain_get_attr simply returns 0 if the MSI_MAPPING
capability if needed or <0 if not.
- removed struct iommu_domain_msi_maps
---
drivers/iommu/arm-smmu.c | 2 ++
drivers/iommu/fsl_pamu_domain.c | 3 +++
include/linux/iommu.h | 1 +
3 files changed, 6 insertions(+)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 59ee4b8..c8b7e71 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1409,6 +1409,8 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
case DOMAIN_ATTR_NESTING:
*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
return 0;
+ case DOMAIN_ATTR_MSI_MAPPING:
+ return 0;
default:
return -ENODEV;
}
diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c
index da0e1e3..dd2e0d6 100644
--- a/drivers/iommu/fsl_pamu_domain.c
+++ b/drivers/iommu/fsl_pamu_domain.c
@@ -856,6 +856,9 @@ static int fsl_pamu_get_domain_attr(struct iommu_domain *domain,
case DOMAIN_ATTR_FSL_PAMUV1:
*(int *)data = DOMAIN_ATTR_FSL_PAMUV1;
break;
+ case DOMAIN_ATTR_MSI_MAPPING:
+ ret = 0;
+ break;
default:
pr_debug("Unsupported attribute type\n");
ret = -EINVAL;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f28dff3..3ae2fb6 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -112,6 +112,7 @@ enum iommu_attr {
DOMAIN_ATTR_FSL_PAMU_ENABLE,
DOMAIN_ATTR_FSL_PAMUV1,
DOMAIN_ATTR_NESTING, /* two stages of translation */
+ DOMAIN_ATTR_MSI_MAPPING, /* Require MSIs mapping in iommu */
DOMAIN_ATTR_MAX,
};
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 05/10] vfio/type1: attach a reserved iova domain to vfio_domain |
| Message-ID | <qVbHk-4Dq-31@gated-at.bofh.it> |
| In reply to | #1317895 |
This patch adds a reserved iova_domain to the vfio_domain struct.
This iova domain will enable to allocate iova within the reserved
iova region.
alloc_reserved_iova_domain makes possible to allocate and initialize
this iova domain. The user will be introduced in subsequent patches.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
drivers/vfio/vfio_iommu_type1.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 33a9ce4..33304c0 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -36,6 +36,7 @@
#include <linux/uaccess.h>
#include <linux/vfio.h>
#include <linux/workqueue.h>
+#include <linux/iova.h>
#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
@@ -77,6 +78,7 @@ struct vfio_domain {
struct list_head group_list;
/* rb tree indexed by PA, for reserved bindings only */
struct rb_root reserved_binding_list;
+ struct iova_domain *reserved_iova_domain;
int prot; /* IOMMU_CACHE */
bool fgsp; /* Fine-grained super pages */
};
@@ -178,6 +180,41 @@ static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
return NULL;
}
+/* alloc_reserved_iova_domain: allocate an iova domain used to manage
+ * reserved iova
+ * @iova: base iova of the domain
+ * @size: size of the domain
+ * @order: iommu page size order
+ */
+static int alloc_reserved_iova_domain(struct vfio_domain *domain,
+ dma_addr_t iova, size_t size,
+ unsigned long order)
+{
+ unsigned long granule, mask;
+ int ret = 0;
+
+ granule = 1UL << order;
+ mask = granule - 1;
+ if (iova & mask)
+ return -EINVAL;
+ if ((!size) || (size & mask))
+ return -EINVAL;
+
+ domain->reserved_iova_domain =
+ kzalloc(sizeof(struct iova_domain), GFP_KERNEL);
+ if (!domain->reserved_iova_domain) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ init_iova_domain(domain->reserved_iova_domain,
+ granule, iova >> order, (iova + size - 1) >> order);
+ return ret;
+
+out_free:
+ kfree(domain->reserved_iova_domain);
+ return ret;
+}
+
static void vfio_link_dma(struct vfio_iommu *iommu, struct vfio_dma *new)
{
struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL;
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping |
| Message-ID | <qVbHk-4Dq-29@gated-at.bofh.it> |
| In reply to | #1317895 |
The user is allowed to register a reserved IOVA range by using the
DMA MAP API and setting the new flag: VFIO_DMA_MAP_FLAG_MSI_RESERVED_IOVA.
It provides the base address and the size. This region is stored in the
vfio_dma rb tree. At that point the iova range is not mapped to any target
address yet. The host kernel will use those iova when needed, typically
when the VFIO-PCI device allocates its MSI's.
This patch also handles the destruction of the reserved binding RB-tree and
domain's iova_domains.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com>
---
- Currently the user is not yet informed about the number of pages to
provide
RFC v1 -> RFC v2:
- takes into account Alex comments, based on
[RFC PATCH 1/6] vfio: Add interface for add/del reserved iova region:
- use the existing dma map/unmap ioctl interface with a flag to register
a reserved IOVA range. A single reserved iova region is allowed.
---
drivers/vfio/vfio_iommu_type1.c | 98 ++++++++++++++++++++++++++++++++++++++++-
include/uapi/linux/vfio.h | 9 ++++
2 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 2f085d3..37c7d78 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -538,10 +538,40 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma)
vfio_lock_acct(-unlocked);
}
+/* vfio_unmap_reserved: unmap and free all reserved binding nodes
+ * for all domains and destroy their iova_domain
+ *
+ * @iommu: iommu handle
+ */
+static void vfio_unmap_reserved(struct vfio_iommu *iommu)
+{
+ struct vfio_domain *d;
+
+ list_for_each_entry(d, &iommu->domain_list, next) {
+ struct rb_node *node;
+
+ while ((node = rb_first(&d->reserved_binding_list))) {
+ struct vfio_reserved_binding *b =
+ rb_entry(node,
+ struct vfio_reserved_binding, node);
+
+ while (!kref_put(&b->kref,
+ vfio_reserved_binding_release)) {
+ }
+ }
+ d->reserved_binding_list = RB_ROOT;
+
+ put_iova_domain(d->reserved_iova_domain);
+ kfree(d->reserved_iova_domain);
+ }
+}
+
static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma)
{
if (likely(dma->type != VFIO_IOVA_RESERVED))
vfio_unmap_unpin(iommu, dma);
+ else
+ vfio_unmap_reserved(iommu);
vfio_unlink_dma(iommu, dma);
kfree(dma);
}
@@ -785,6 +815,68 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
return ret;
}
+static int vfio_register_reserved_iova_range(struct vfio_iommu *iommu,
+ struct vfio_iommu_type1_dma_map *map)
+{
+ dma_addr_t iova = map->iova;
+ size_t size = map->size;
+ uint64_t mask;
+ struct vfio_dma *dma;
+ int ret = 0;
+ struct vfio_domain *d;
+ unsigned long order;
+
+ /* Verify that none of our __u64 fields overflow */
+ if (map->size != size || map->iova != iova)
+ return -EINVAL;
+
+ order = __ffs(vfio_pgsize_bitmap(iommu));
+ mask = ((uint64_t)1 << order) - 1;
+
+ WARN_ON(mask & PAGE_MASK);
+
+ /* we currently only support MSI_RESERVED_IOVA */
+ if (!(map->flags & VFIO_DMA_MAP_FLAG_MSI_RESERVED_IOVA))
+ return -EINVAL;
+
+ if (!size || (size | iova) & mask)
+ return -EINVAL;
+
+ /* Don't allow IOVA address wrap */
+ if (iova + size - 1 < iova)
+ return -EINVAL;
+
+ mutex_lock(&iommu->lock);
+
+ /* check if the iova domain has not been instantiated already*/
+ d = list_first_entry(&iommu->domain_list,
+ struct vfio_domain, next);
+
+ if (d->reserved_iova_domain || vfio_find_dma(iommu, iova, size)) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dma->iova = iova;
+ dma->size = size;
+ dma->type = VFIO_IOVA_RESERVED;
+
+ vfio_link_dma(iommu, dma);
+
+ list_for_each_entry(d, &iommu->domain_list, next)
+ alloc_reserved_iova_domain(d, iova, size, order);
+
+out:
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
static int vfio_bus_type(struct device *dev, void *data)
{
struct bus_type **bus = data;
@@ -1297,7 +1389,8 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
} else if (cmd == VFIO_IOMMU_MAP_DMA) {
struct vfio_iommu_type1_dma_map map;
uint32_t mask = VFIO_DMA_MAP_FLAG_READ |
- VFIO_DMA_MAP_FLAG_WRITE;
+ VFIO_DMA_MAP_FLAG_WRITE |
+ VFIO_DMA_MAP_FLAG_MSI_RESERVED_IOVA;
minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
@@ -1307,6 +1400,9 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
if (map.argsz < minsz || map.flags & ~mask)
return -EINVAL;
+ if (map.flags & VFIO_DMA_MAP_FLAG_MSI_RESERVED_IOVA)
+ return vfio_register_reserved_iova_range(iommu, &map);
+
return vfio_dma_do_map(iommu, &map);
} else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 43e183b..982e326 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -411,12 +411,21 @@ struct vfio_iommu_type1_info {
*
* Map process virtual addresses to IO virtual addresses using the
* provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required.
+ *
+ * In case MSI_RESERVED_IOVA is set, the API only aims at registering an IOVA
+ * region which will be used on some platforms to map the host MSI frame.
+ * in that specific case, vaddr and prot are ignored. The requirement for
+ * provisioning such IOVA range can be checked by calling VFIO_IOMMU_GET_INFO
+ * with the VFIO_IOMMU_INFO_REQUIRE_MSI_MAP attribute. A single
+ * MSI_RESERVED_IOVA region can be registered
*/
struct vfio_iommu_type1_dma_map {
__u32 argsz;
__u32 flags;
#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */
#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */
+/* reserved iova for MSI vectors*/
+#define VFIO_DMA_MAP_FLAG_MSI_RESERVED_IOVA (1 << 2)
__u64 vaddr; /* Process virtual address */
__u64 iova; /* IO virtual address */
__u64 size; /* Size of mapping (bytes) */
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-26 17:50 +0100 |
| Subject | Re: [PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping |
| Message-ID | <qVeYx-6NP-1@gated-at.bofh.it> |
| In reply to | #1317899 |
[Multipart message — attachments visible in raw view] — view raw
Hi Eric,
[auto build test ERROR on v4.5-rc1]
[also build test ERROR on next-20160125]
[cannot apply to iommu/next]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/Eric-Auger/KVM-PCIe-MSI-passthrough-on-ARM-ARM64/20160126-211921
config: x86_64-randconfig-s3-01262306 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
ERROR: "alloc_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined!
>> ERROR: "init_iova_domain" [drivers/vfio/vfio_iommu_type1.ko] undefined!
>> ERROR: "put_iova_domain" [drivers/vfio/vfio_iommu_type1.ko] undefined!
ERROR: "free_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined!
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-26 19:40 +0100 |
| Subject | Re: [PATCH 10/10] vfio: allow the user to register reserved iova range for MSI mapping |
| Message-ID | <qVgH1-88L-37@gated-at.bofh.it> |
| In reply to | #1317899 |
[Multipart message — attachments visible in raw view] — view raw
Hi Eric,
[auto build test ERROR on v4.5-rc1]
[also build test ERROR on next-20160125]
[cannot apply to iommu/next]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/Eric-Auger/KVM-PCIe-MSI-passthrough-on-ARM-ARM64/20160126-211921
config: x86_64-randconfig-s3-01270003 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/built-in.o: In function `vfio_reserved_binding_release':
vfio_iommu_type1.c:(.text+0x1d537b): undefined reference to `free_iova'
drivers/built-in.o: In function `vfio_iommu_type1_alloc_map_reserved_iova':
vfio_iommu_type1.c:(.text+0x1d5b32): undefined reference to `alloc_iova'
vfio_iommu_type1.c:(.text+0x1d5d67): undefined reference to `free_iova'
drivers/built-in.o: In function `vfio_remove_dma':
>> vfio_iommu_type1.c:(.text+0x1d6c80): undefined reference to `put_iova_domain'
drivers/built-in.o: In function `vfio_iommu_type1_ioctl':
>> vfio_iommu_type1.c:(.text+0x1d81f8): undefined reference to `init_iova_domain'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova |
| Message-ID | <qVbHk-4Dq-35@gated-at.bofh.it> |
| In reply to | #1317895 |
This patch introduces vfio_group_alloc_map_/unmap_free_reserved_iova
and implements corresponding vfio_iommu_type1 operations.
alloc_map allows to allocate a new reserved iova page and map it
onto the physical page that contains a given PA. It returns the iova
that is mapped onto the provided PA. In case a mapping already exist
between both pages, the IOVA corresponding to the PA is directly returned.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
Signed-off-by: Ankit Jindal <ajindal@apm.com>
Signed-off-by: Pranavkumar Sawargaonkar <pranavkumar@linaro.org>
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com>
---
drivers/vfio/vfio.c | 39 ++++++++++
drivers/vfio/vfio_iommu_type1.c | 163 ++++++++++++++++++++++++++++++++++++++--
include/linux/vfio.h | 34 ++++++++-
3 files changed, 228 insertions(+), 8 deletions(-)
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 82f25cc..3d9de00 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -268,6 +268,45 @@ void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops)
}
EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver);
+int vfio_group_alloc_map_reserved_iova(struct vfio_group *group,
+ phys_addr_t addr, int prot,
+ dma_addr_t *iova)
+{
+ struct vfio_container *container = group->container;
+ const struct vfio_iommu_driver_ops *ops = container->iommu_driver->ops;
+ int ret;
+
+ if (!ops->alloc_map_reserved_iova)
+ return -EINVAL;
+
+ down_read(&container->group_lock);
+ ret = ops->alloc_map_reserved_iova(container->iommu_data,
+ group->iommu_group,
+ addr, prot, iova);
+ up_read(&container->group_lock);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(vfio_group_alloc_map_reserved_iova);
+
+int vfio_group_unmap_free_reserved_iova(struct vfio_group *group,
+ dma_addr_t iova)
+{
+ struct vfio_container *container = group->container;
+ const struct vfio_iommu_driver_ops *ops = container->iommu_driver->ops;
+ int ret;
+
+ if (!ops->unmap_free_reserved_iova)
+ return -EINVAL;
+
+ down_read(&container->group_lock);
+ ret = ops->unmap_free_reserved_iova(container->iommu_data,
+ group->iommu_group, iova);
+ up_read(&container->group_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_group_unmap_free_reserved_iova);
+
/**
* Group minor allocation/free - both called with vfio.group_lock held
*/
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 33304c0..a79e2a8 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -156,6 +156,19 @@ static void vfio_unlink_reserved_binding(struct vfio_domain *d,
rb_erase(&old->node, &d->reserved_binding_list);
}
+static void vfio_reserved_binding_release(struct kref *kref)
+{
+ struct vfio_reserved_binding *b =
+ container_of(kref, struct vfio_reserved_binding, kref);
+ struct vfio_domain *d = b->domain;
+ unsigned long order = __ffs(b->size);
+
+ iommu_unmap(d->domain, b->iova, b->size);
+ free_iova(d->reserved_iova_domain, b->iova >> order);
+ vfio_unlink_reserved_binding(d, b);
+ kfree(b);
+}
+
/*
* This code handles mapping and unmapping of user data buffers
* into DMA'ble space using the IOMMU
@@ -1034,6 +1047,138 @@ done:
mutex_unlock(&iommu->lock);
}
+static struct vfio_domain *vfio_find_iommu_domain(void *iommu_data,
+ struct iommu_group *group)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *g;
+ struct vfio_domain *d;
+
+ list_for_each_entry(d, &iommu->domain_list, next) {
+ list_for_each_entry(g, &d->group_list, next) {
+ if (g->iommu_group == group)
+ return d;
+ }
+ }
+ return NULL;
+}
+
+static int vfio_iommu_type1_alloc_map_reserved_iova(void *iommu_data,
+ struct iommu_group *group,
+ phys_addr_t addr, int prot,
+ dma_addr_t *iova)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_domain *d;
+ uint64_t mask, iommu_page_size;
+ struct vfio_reserved_binding *b;
+ unsigned long order;
+ struct iova *p_iova;
+ phys_addr_t aligned_addr, offset;
+ int ret = 0;
+
+ order = __ffs(vfio_pgsize_bitmap(iommu));
+ iommu_page_size = (uint64_t)1 << order;
+ mask = iommu_page_size - 1;
+ aligned_addr = addr & ~mask;
+ offset = addr - aligned_addr;
+
+ mutex_lock(&iommu->lock);
+
+ d = vfio_find_iommu_domain(iommu_data, group);
+ if (!d) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ b = vfio_find_reserved_binding(d, aligned_addr, iommu_page_size);
+ if (b) {
+ ret = 0;
+ *iova = b->iova + offset;
+ kref_get(&b->kref);
+ goto unlock;
+ }
+
+ /* allocate a new reserved IOVA page and a new binding node */
+ p_iova = alloc_iova(d->reserved_iova_domain, 1,
+ d->reserved_iova_domain->dma_32bit_pfn, true);
+ if (!p_iova) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ *iova = p_iova->pfn_lo << order;
+
+ b = kzalloc(sizeof(*b), GFP_KERNEL);
+ if (!b) {
+ ret = -ENOMEM;
+ goto free_iova_unlock;
+ }
+
+ ret = iommu_map(d->domain, *iova, aligned_addr, iommu_page_size, prot);
+ if (ret)
+ goto free_binding_iova_unlock;
+
+ kref_init(&b->kref);
+ kref_get(&b->kref);
+ b->domain = d;
+ b->addr = aligned_addr;
+ b->iova = *iova;
+ b->size = iommu_page_size;
+ vfio_link_reserved_binding(d, b);
+ *iova += offset;
+
+ goto unlock;
+
+free_binding_iova_unlock:
+ kfree(b);
+free_iova_unlock:
+ free_iova(d->reserved_iova_domain, *iova >> order);
+unlock:
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
+static int vfio_iommu_type1_unmap_free_reserved_iova(void *iommu_data,
+ struct iommu_group *group,
+ dma_addr_t iova)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_reserved_binding *b;
+ struct vfio_domain *d;
+ phys_addr_t aligned_addr;
+ dma_addr_t aligned_iova, iommu_page_size, mask, offset;
+ unsigned long order;
+ int ret = 0;
+
+ order = __ffs(vfio_pgsize_bitmap(iommu));
+ iommu_page_size = (uint64_t)1 << order;
+ mask = iommu_page_size - 1;
+ aligned_iova = iova & ~mask;
+ offset = iova - aligned_iova;
+
+ mutex_lock(&iommu->lock);
+
+ d = vfio_find_iommu_domain(iommu_data, group);
+ if (!d) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ aligned_addr = iommu_iova_to_phys(d->domain, aligned_iova);
+
+ b = vfio_find_reserved_binding(d, aligned_addr, iommu_page_size);
+ if (!b) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ kref_put(&b->kref, vfio_reserved_binding_release);
+
+unlock:
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
static void *vfio_iommu_type1_open(unsigned long arg)
{
struct vfio_iommu *iommu;
@@ -1180,13 +1325,17 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
}
static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
- .name = "vfio-iommu-type1",
- .owner = THIS_MODULE,
- .open = vfio_iommu_type1_open,
- .release = vfio_iommu_type1_release,
- .ioctl = vfio_iommu_type1_ioctl,
- .attach_group = vfio_iommu_type1_attach_group,
- .detach_group = vfio_iommu_type1_detach_group,
+ .name = "vfio-iommu-type1",
+ .owner = THIS_MODULE,
+ .open = vfio_iommu_type1_open,
+ .release = vfio_iommu_type1_release,
+ .ioctl = vfio_iommu_type1_ioctl,
+ .attach_group = vfio_iommu_type1_attach_group,
+ .detach_group = vfio_iommu_type1_detach_group,
+ .alloc_map_reserved_iova =
+ vfio_iommu_type1_alloc_map_reserved_iova,
+ .unmap_free_reserved_iova =
+ vfio_iommu_type1_unmap_free_reserved_iova,
};
static int __init vfio_iommu_type1_init(void)
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 610a86a..0020f81 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -75,7 +75,13 @@ struct vfio_iommu_driver_ops {
struct iommu_group *group);
void (*detach_group)(void *iommu_data,
struct iommu_group *group);
-
+ int (*alloc_map_reserved_iova)(void *iommu_data,
+ struct iommu_group *group,
+ phys_addr_t addr, int prot,
+ dma_addr_t *iova);
+ int (*unmap_free_reserved_iova)(void *iommu_data,
+ struct iommu_group *group,
+ dma_addr_t iova);
};
extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops);
@@ -138,4 +144,30 @@ extern int vfio_virqfd_enable(void *opaque,
void *data, struct virqfd **pvirqfd, int fd);
extern void vfio_virqfd_disable(struct virqfd **pvirqfd);
+/**
+ * vfio_group_alloc_map_reserved_iova: allocates a new iova page and map
+ * it onto the aligned physical page that contains a given physical addr.
+ * page size is the domain iommu page size.
+ *
+ * @group: vfio group handle
+ * @addr: physical address to map
+ * @prot: protection attribute
+ * @iova: returned iova that is mapped onto addr
+ *
+ * returns 0 on success, < 0 on failure
+ */
+extern int vfio_group_alloc_map_reserved_iova(struct vfio_group *group,
+ phys_addr_t addr, int prot,
+ dma_addr_t *iova);
+/**
+ * vfio_group_unmap_free_reserved_iova: unmap and free the reserved iova page
+ *
+ * @group: vfio group handle
+ * @iova: base iova, must be aligned on the IOMMU page size
+ *
+ * returns 0 on success, < 0 on failure
+ */
+extern int vfio_group_unmap_free_reserved_iova(struct vfio_group *group,
+ dma_addr_t iova);
+
#endif /* VFIO_H */
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2016-01-26 17:20 +0100 |
| Subject | Re: [PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova |
| Message-ID | <qVevx-6B0-45@gated-at.bofh.it> |
| In reply to | #1317900 |
[Multipart message — attachments visible in raw view] — view raw
Hi Eric,
[auto build test ERROR on v4.5-rc1]
[also build test ERROR on next-20160125]
[cannot apply to iommu/next]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/Eric-Auger/KVM-PCIe-MSI-passthrough-on-ARM-ARM64/20160126-211921
config: x86_64-randconfig-s3-01262306 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
>> ERROR: "alloc_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined!
>> ERROR: "free_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined!
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 17:40 +0100 |
| Subject | Re: [PATCH 06/10] vfio: introduce vfio_group_alloc_map_/unmap_free_reserved_iova |
| Message-ID | <qVeOT-6K1-21@gated-at.bofh.it> |
| In reply to | #1318111 |
Hi, On 01/26/2016 05:17 PM, kbuild test robot wrote: > Hi Eric, > > [auto build test ERROR on v4.5-rc1] > [also build test ERROR on next-20160125] > [cannot apply to iommu/next] > [if your patch is applied to the wrong git tree, please drop us a note to help improving the system] > > url: https://github.com/0day-ci/linux/commits/Eric-Auger/KVM-PCIe-MSI-passthrough-on-ARM-ARM64/20160126-211921 > config: x86_64-randconfig-s3-01262306 (attached as .config) > reproduce: > # save the attached .config to linux build tree > make ARCH=x86_64 > > All errors (new ones prefixed by >>): > >>> ERROR: "alloc_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined! >>> ERROR: "free_iova" [drivers/vfio/vfio_iommu_type1.ko] undefined! I will protect the code with CONFIG_IOMMU_IOVA if the usage of those functions is acknowledged. Thanks Eric > > --- > 0-DAY kernel test infrastructure Open Source Technology Center > https://lists.01.org/pipermail/kbuild-all Intel Corporation >
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 08/10] vfio: introduce vfio_group_require_msi_mapping |
| Message-ID | <qVbHl-4Dq-39@gated-at.bofh.it> |
| In reply to | #1317895 |
This new function enables to know whether msi write transaction
addresses must be mapped.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
drivers/vfio/vfio.c | 22 ++++++++++++++++++++++
drivers/vfio/vfio_iommu_type1.c | 8 ++++++++
include/linux/vfio.h | 2 ++
3 files changed, 32 insertions(+)
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 2760d4c..f3df5a10 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -793,6 +793,28 @@ static int vfio_iommu_group_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
+bool vfio_group_require_msi_mapping(struct vfio_group *group)
+{
+ struct vfio_container *container = group->container;
+ struct vfio_iommu_driver *driver;
+ bool ret;
+
+ down_read(&container->group_lock);
+
+ driver = container->iommu_driver;
+ if (!driver || !driver->ops || !driver->ops->require_msi_mapping) {
+ ret = -EINVAL;
+ goto up;
+ }
+
+ ret = driver->ops->require_msi_mapping(container->iommu_data);
+
+up:
+ up_read(&container->group_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_group_require_msi_mapping);
+
/**
* VFIO driver API
*/
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index a79e2a8..2f085d3 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -1179,6 +1179,13 @@ unlock:
return ret;
}
+static bool vfio_iommu_type1_require_msi_mapping(void *iommu_data)
+{
+ struct vfio_iommu *iommu = iommu_data;
+
+ return vfio_domains_require_msi_mapping(iommu);
+}
+
static void *vfio_iommu_type1_open(unsigned long arg)
{
struct vfio_iommu *iommu;
@@ -1336,6 +1343,7 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
vfio_iommu_type1_alloc_map_reserved_iova,
.unmap_free_reserved_iova =
vfio_iommu_type1_unmap_free_reserved_iova,
+ .require_msi_mapping = vfio_iommu_type1_require_msi_mapping,
};
static int __init vfio_iommu_type1_init(void)
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index f7eaf30..3e6cbeb 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -58,6 +58,7 @@ extern void *vfio_del_group_dev(struct device *dev);
extern struct vfio_device *vfio_device_get_from_dev(struct device *dev);
extern void vfio_device_put(struct vfio_device *device);
extern void *vfio_device_data(struct vfio_device *device);
+extern bool vfio_group_require_msi_mapping(struct vfio_group *group);
/**
* struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
@@ -85,6 +86,7 @@ struct vfio_iommu_driver_ops {
int (*unmap_free_reserved_iova)(void *iommu_data,
struct iommu_group *group,
dma_addr_t iova);
+ bool (*require_msi_mapping)(void *iommu_data);
};
extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops);
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 04/10] vfio: introduce VFIO_IOVA_RESERVED vfio_dma type |
| Message-ID | <qVbHl-4Dq-41@gated-at.bofh.it> |
| In reply to | #1317895 |
We introduce a vfio_dma type since we will need to discriminate
legacy vfio_dma's from new reserved ones. Since those latter are
not mapped at registration, some handlings must be unplugged:
removal, replay. They will be enhanced later on.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
drivers/vfio/vfio_iommu_type1.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 32438d9..33a9ce4 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -53,6 +53,15 @@ module_param_named(disable_hugepages,
MODULE_PARM_DESC(disable_hugepages,
"Disable VFIO IOMMU support for IOMMU hugepages.");
+enum vfio_iova_type {
+ VFIO_IOVA_USER = 0, /* standard IOVA used to map user vaddr */
+ /*
+ * IOVA reserved to map special host physical addresses,
+ * MSI frames for instance
+ */
+ VFIO_IOVA_RESERVED,
+};
+
struct vfio_iommu {
struct list_head domain_list;
struct mutex lock;
@@ -78,6 +87,7 @@ struct vfio_dma {
unsigned long vaddr; /* Process virtual addr */
size_t size; /* Map size (bytes) */
int prot; /* IOMMU_READ/WRITE */
+ enum vfio_iova_type type; /* type of IOVA */
};
struct vfio_reserved_binding {
@@ -480,7 +490,8 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma)
static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma)
{
- vfio_unmap_unpin(iommu, dma);
+ if (likely(dma->type != VFIO_IOVA_RESERVED))
+ vfio_unmap_unpin(iommu, dma);
vfio_unlink_dma(iommu, dma);
kfree(dma);
}
@@ -756,6 +767,10 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu,
dma_addr_t iova;
dma = rb_entry(n, struct vfio_dma, node);
+
+ if (unlikely(dma->type == VFIO_IOVA_RESERVED))
+ continue;
+
iova = dma->iova;
while (iova < dma->iova + dma->size) {
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 03/10] vfio_iommu_type1: add reserved binding RB tree management |
| Message-ID | <qVbHm-4Dq-49@gated-at.bofh.it> |
| In reply to | #1317895 |
Legacy dma_list is just used to insert the reserved iova region and
check mapping of reserved iova happens in this window. As opposed to
other vfio_dma slots, the reserved one is not necessarily mapped.
We will need to track which host physical addresses are mapped to
reserved IOVA. In that prospect we introduce a new RB tree indexed
by physical address. This reverse RB tree only is used for reserved
IOVA bindings. It belongs to a given iommu domain.
It is expected this RB tree will contain very few bindings. Those
generally correspond to single page mapping one MSI frame (at least
for ARM, containing the GICv2m frame or ITS GITS_TRANSLATER frame.
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
drivers/vfio/vfio_iommu_type1.c | 63 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index c5b57e1..32438d9 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -56,6 +56,7 @@ MODULE_PARM_DESC(disable_hugepages,
struct vfio_iommu {
struct list_head domain_list;
struct mutex lock;
+ /* rb tree indexed by IOVA */
struct rb_root dma_list;
bool v2;
bool nesting;
@@ -65,6 +66,8 @@ struct vfio_domain {
struct iommu_domain *domain;
struct list_head next;
struct list_head group_list;
+ /* rb tree indexed by PA, for reserved bindings only */
+ struct rb_root reserved_binding_list;
int prot; /* IOMMU_CACHE */
bool fgsp; /* Fine-grained super pages */
};
@@ -77,11 +80,70 @@ struct vfio_dma {
int prot; /* IOMMU_READ/WRITE */
};
+struct vfio_reserved_binding {
+ struct kref kref;
+ struct rb_node node;
+ struct vfio_domain *domain;
+ phys_addr_t addr;
+ dma_addr_t iova;
+ size_t size;
+};
+
struct vfio_group {
struct iommu_group *iommu_group;
struct list_head next;
};
+/* Reserved binding RB-tree manipulation */
+
+static struct vfio_reserved_binding *vfio_find_reserved_binding(
+ struct vfio_domain *d,
+ phys_addr_t start, size_t size)
+{
+ struct rb_node *node = d->reserved_binding_list.rb_node;
+
+ while (node) {
+ struct vfio_reserved_binding *binding =
+ rb_entry(node, struct vfio_reserved_binding, node);
+
+ if (start + size <= binding->addr)
+ node = node->rb_left;
+ else if (start >= binding->addr + binding->size)
+ node = node->rb_right;
+ else
+ return binding;
+ }
+
+ return NULL;
+}
+
+static void vfio_link_reserved_binding(struct vfio_domain *d,
+ struct vfio_reserved_binding *new)
+{
+ struct rb_node **link = &d->reserved_binding_list.rb_node;
+ struct rb_node *parent = NULL;
+ struct vfio_reserved_binding *binding;
+
+ while (*link) {
+ parent = *link;
+ binding = rb_entry(parent, struct vfio_reserved_binding, node);
+
+ if (new->addr + new->size <= binding->addr)
+ link = &(*link)->rb_left;
+ else
+ link = &(*link)->rb_right;
+ }
+
+ rb_link_node(&new->node, parent, link);
+ rb_insert_color(&new->node, &d->reserved_binding_list);
+}
+
+static void vfio_unlink_reserved_binding(struct vfio_domain *d,
+ struct vfio_reserved_binding *old)
+{
+ rb_erase(&old->node, &d->reserved_binding_list);
+}
+
/*
* This code handles mapping and unmapping of user data buffers
* into DMA'ble space using the IOMMU
@@ -784,6 +846,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
ret = -ENOMEM;
goto out_free;
}
+ domain->reserved_binding_list = RB_ROOT;
group->iommu_group = iommu_group;
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-26 14:20 +0100 |
| Subject | [PATCH 02/10] vfio: expose MSI mapping requirement through VFIO_IOMMU_GET_INFO |
| Message-ID | <qVbHm-4Dq-53@gated-at.bofh.it> |
| In reply to | #1317895 |
This patch allows the user-space to retrieve whether msi write
transaction addresses must be mapped. This is returned through the
VFIO_IOMMU_GET_INFO API using a new flag: VFIO_IOMMU_INFO_REQUIRE_MSI_MAP.
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com>
Signed-off-by: Eric Auger <eric.auger@linaro.org>
---
RFC v1 -> RFC v2:
- derived from
[RFC PATCH 3/6] vfio: Extend iommu-info to return MSIs automap state
- renamed allow_msi_reconfig into require_msi_mapping
- fixed VFIO_IOMMU_GET_INFO
---
drivers/vfio/vfio_iommu_type1.c | 26 ++++++++++++++++++++++++++
include/uapi/linux/vfio.h | 1 +
2 files changed, 27 insertions(+)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 6f1ea3d..c5b57e1 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -255,6 +255,29 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
}
/*
+ * vfio_domains_require_msi_mapping: indicates whether MSI write transaction
+ * addresses must be mapped
+ *
+ * returns true if it does
+ */
+static bool vfio_domains_require_msi_mapping(struct vfio_iommu *iommu)
+{
+ struct vfio_domain *d;
+ bool ret;
+
+ mutex_lock(&iommu->lock);
+ /* All domains have same require_msi_map property, pick first */
+ d = list_first_entry(&iommu->domain_list, struct vfio_domain, next);
+ if (iommu_domain_get_attr(d->domain, DOMAIN_ATTR_MSI_MAPPING, NULL) < 0)
+ ret = false;
+ else
+ ret = true;
+ mutex_unlock(&iommu->lock);
+
+ return ret;
+}
+
+/*
* Attempt to pin pages. We really don't want to track all the pfns and
* the iommu can only map chunks of consecutive pfns anyway, so get the
* first page and all consecutive pages with the same locking.
@@ -997,6 +1020,9 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
info.flags = VFIO_IOMMU_INFO_PGSIZES;
+ if (vfio_domains_require_msi_mapping(iommu))
+ info.flags |= VFIO_IOMMU_INFO_REQUIRE_MSI_MAP;
+
info.iova_pgsizes = vfio_pgsize_bitmap(iommu);
return copy_to_user((void __user *)arg, &info, minsz);
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 7d7a4c6..43e183b 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -400,6 +400,7 @@ struct vfio_iommu_type1_info {
__u32 argsz;
__u32 flags;
#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */
+#define VFIO_IOMMU_INFO_REQUIRE_MSI_MAP (1 << 1)/* MSI must be mapped */
__u64 iova_pgsizes; /* Bitmap of supported page sizes */
};
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Pavel Fedin <p.fedin@samsung.com> |
|---|---|
| Date | 2016-01-26 18:30 +0100 |
| Message-ID | <qVfBh-7mT-29@gated-at.bofh.it> |
| In reply to | #1317895 |
Hello! I'd like just to clarify some things for myself and better wrap my head around it... > On x86 all accesses to the 1MB PA region [FEE0_0000h - FEF0_000h] are directed > as interrupt messages: accesses to this special PA window directly target the > APIC configuration space and not DRAM, meaning the downstream IOMMU is bypassed. So, this is effectively the same as always having hardwired 1:1 mappings on all IOMMUs, isn't it ? If so, then we can't we just do the same, just by forcing similar 1:1 mapping? This is what i tried to do in my patchset. All of you are talking about a situation which arises when we are emulating different machine with different physical addresses layout. And e. g. if our host has MSI at 0xABADCAFE, our target could have valid RAM at the same location, and we need to handle it somehow, therefore we have to move our MSI window out of target's RAM. But how does this work on a PC then? What if our host is PC, and we want to emulate some ARM board, which has RAM at FE00 0000 ? Or does it mean that PC architecture is flawed and can reliably handle PCI passthrough only for itself ? Kind regards, Pavel Fedin Senior Engineer Samsung Electronics Research center Russia
[toc] | [prev] | [next] | [standalone]
| From | Eric Auger <eric.auger@linaro.org> |
|---|---|
| Date | 2016-01-27 10:00 +0100 |
| Message-ID | <qVu7h-TW-25@gated-at.bofh.it> |
| In reply to | #1318194 |
Hi Pavel, On 01/26/2016 06:25 PM, Pavel Fedin wrote: > Hello! > I'd like just to clarify some things for myself and better wrap my head around it... > >> On x86 all accesses to the 1MB PA region [FEE0_0000h - FEF0_000h] are directed >> as interrupt messages: accesses to this special PA window directly target the >> APIC configuration space and not DRAM, meaning the downstream IOMMU is bypassed. > > So, this is effectively the same as always having hardwired 1:1 mappings on all IOMMUs, isn't it ? > If so, then we can't we just do the same, just by forcing similar 1:1 mapping? This is what i tried to do in my patchset. All of > you are talking about a situation which arises when we are emulating different machine with different physical addresses layout. And > e. g. if our host has MSI at 0xABADCAFE, our target could have valid RAM at the same location, and we need to handle it somehow, > therefore we have to move our MSI window out of target's RAM. But how does this work on a PC then? What if our host is PC, and we > want to emulate some ARM board, which has RAM at FE00 0000 ? Or does it mean that PC architecture is flawed and can reliably handle > PCI passthrough only for itself ? Alex answered to this I think: " x86 isn't problem-free in this space. An x86 VM is going to know that the 0xfee00000 address range is special, it won't be backed by RAM and won't be a DMA target, thus we'll never attempt to map it for an iova address. However, if we run a non-x86 VM or a userspace driver, it doesn't necessarily know that there's anything special about that range of iovas. I intend to resolve this with an extension to the iommu info ioctl that describes the available iova space for the iommu. The interrupt region would simply be excluded. " I am not sure I've addressed this requirement yet but it seems more future proof to have an IOMMU mapping for those addresses. For the ARM use case I think Marc gave guidance: " We want userspace to be in control of the memory map, and it is the kernel's job to tell us whether or not this matches the HW capabilities or not. A fixed mapping may completely clash with the memory map I want (think emulating HW x on platform y), and there is no reason why we should have the restrictions x86 has. " That's the rationale behind respining that way. Waiting for other comments & discussions, I am going to address the iova and dma_addr_t kbuilt reported compilation issues. Please apologize for those. Best Regards Eric > > Kind regards, > Pavel Fedin > Senior Engineer > Samsung Electronics Research center Russia > >
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web