diff options
author | Eric Auger <eric.auger@redhat.com> | 2018-06-26 17:50:42 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-06-26 17:50:42 +0100 |
commit | 832e4222c82071e4399cffdecd605abed5ac0c27 (patch) | |
tree | d48f9f61abf26ee9b2bd77aa3fbfca3f3d4213b8 /hw | |
parent | cc27ed81cf11d5b7ffc7eca9f31dfcd82c983c56 (diff) | |
download | qemu-832e4222c82071e4399cffdecd605abed5ac0c27.zip qemu-832e4222c82071e4399cffdecd605abed5ac0c27.tar.gz qemu-832e4222c82071e4399cffdecd605abed5ac0c27.tar.bz2 |
hw/arm/smmuv3: Add notifications on invalidation
On TLB invalidation commands, let's call registered
IOMMU notifiers. Those can only be UNMAP notifiers.
SMMUv3 does not support notification on MAP (VFIO).
This patch allows vhost use case where IOTLB API is notified
on each guest IOTLB invalidation.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1529653501-15358-5-git-send-email-eric.auger@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/smmu-common.c | 34 | ||||
-rw-r--r-- | hw/arm/smmuv3.c | 99 | ||||
-rw-r--r-- | hw/arm/trace-events | 5 |
3 files changed, 136 insertions, 2 deletions
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f66e444..3098915 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -385,6 +385,40 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) return (k1->asid == k2->asid) && (k1->iova == k2->iova); } +/* Unmap the whole notifier's range */ +static void smmu_unmap_notifier_range(IOMMUNotifier *n) +{ + IOMMUTLBEntry entry; + + entry.target_as = &address_space_memory; + entry.iova = n->start; + entry.perm = IOMMU_NONE; + entry.addr_mask = n->end - n->start; + + memory_region_notify_one(n, &entry); +} + +/* Unmap all notifiers attached to @mr */ +inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) +{ + IOMMUNotifier *n; + + trace_smmu_inv_notifiers_mr(mr->parent_obj.name); + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmu_unmap_notifier_range(n); + } +} + +/* Unmap all notifiers of all mr's */ +void smmu_inv_notifiers_all(SMMUState *s) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + smmu_inv_notifiers_mr(&node->sdev->iommu); + } +} + static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b6dc7ed..39fbcbf 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -780,6 +780,68 @@ epilogue: return entry; } +/** + * smmuv3_notify_iova - call the notifier @n for a given + * @asid and @iova tuple. + * + * @mr: IOMMU mr region handle + * @n: notifier to be called + * @asid: address space ID or negative value if we don't care + * @iova: iova + */ +static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, + IOMMUNotifier *n, + int asid, + dma_addr_t iova) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUEventInfo event = {}; + SMMUTransTableInfo *tt; + SMMUTransCfg *cfg; + IOMMUTLBEntry entry; + + cfg = smmuv3_get_config(sdev, &event); + if (!cfg) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s error decoding the configuration for iommu mr=%s\n", + __func__, mr->parent_obj.name); + return; + } + + if (asid >= 0 && cfg->asid != asid) { + return; + } + + tt = select_tt(cfg, iova); + if (!tt) { + return; + } + + entry.target_as = &address_space_memory; + entry.iova = iova; + entry.addr_mask = (1 << tt->granule_sz) - 1; + entry.perm = IOMMU_NONE; + + memory_region_notify_one(n, &entry); +} + +/* invalidate an asid/iova tuple in all mr's */ +static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + IOMMUMemoryRegion *mr = &node->sdev->iommu; + IOMMUNotifier *n; + + trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova); + + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmuv3_notify_iova(mr, n, asid, iova); + } + } +} + static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -899,12 +961,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) uint16_t asid = CMD_ASID(&cmd); trace_smmuv3_cmdq_tlbi_nh_asid(asid); + smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_asid(bs, asid); break; } case SMMU_CMD_TLBI_NH_ALL: case SMMU_CMD_TLBI_NSNH_ALL: trace_smmuv3_cmdq_tlbi_nh(); + smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_all(bs); break; case SMMU_CMD_TLBI_NH_VAA: @@ -913,6 +977,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) uint16_t vmid = CMD_VMID(&cmd); trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr); + smmuv3_inv_notifiers_iova(bs, -1, addr); smmu_iotlb_inv_all(bs); break; } @@ -924,6 +989,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) bool leaf = CMD_LEAF(&cmd); trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf); + smmuv3_inv_notifiers_iova(bs, asid, addr); smmu_iotlb_inv_iova(bs, asid, addr); break; } @@ -1402,9 +1468,38 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { + SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu); + SMMUv3State *s3 = sdev->smmu; + SMMUState *s = &(s3->smmu_state); + SMMUNotifierNode *node = NULL; + SMMUNotifierNode *next_node = NULL; + + if (new & IOMMU_NOTIFIER_MAP) { + int bus_num = pci_bus_num(sdev->bus); + PCIDevice *pcidev = pci_find_device(sdev->bus, bus_num, sdev->devfn); + + warn_report("SMMUv3 does not support notification on MAP: " + "device %s will not function properly", pcidev->name); + } + if (old == IOMMU_NOTIFIER_NONE) { - warn_report("SMMUV3 does not support vhost/vfio integration yet: " - "devices of those types will not function properly"); + trace_smmuv3_notify_flag_add(iommu->parent_obj.name); + node = g_malloc0(sizeof(*node)); + node->sdev = sdev; + QLIST_INSERT_HEAD(&s->notifiers_list, node, next); + return; + } + + /* update notifier node with new flags */ + QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { + if (node->sdev == sdev) { + if (new == IOMMU_NOTIFIER_NONE) { + trace_smmuv3_notify_flag_del(iommu->parent_obj.name); + QLIST_REMOVE(node, next); + g_free(node); + } + return; + } } } diff --git a/hw/arm/trace-events b/hw/arm/trace-events index be69c5d..27b11d6 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -17,6 +17,7 @@ smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, smmu_iotlb_inv_all(void) "IOTLB invalidate all" smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" #hw/arm/smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" @@ -55,3 +56,7 @@ smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64 smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" +smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" +smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" +smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova) "iommu mr=%s asid=%d iova=0x%"PRIx64 + |