7f1c5b
From 55aad90e347599e88747888ddbefcba33427f386 Mon Sep 17 00:00:00 2001
7f1c5b
From: Jason Wang <jasowang@redhat.com>
7f1c5b
Date: Fri, 16 Dec 2022 11:35:52 +0800
7f1c5b
Subject: [PATCH 12/31] vhost: fix vq dirty bitmap syncing when vIOMMU is
7f1c5b
 enabled
7f1c5b
7f1c5b
RH-Author: Eric Auger <eric.auger@redhat.com>
7f1c5b
RH-MergeRequest: 134: vhost: fix vq dirty bitmap syncing when vIOMMU is enabled
7f1c5b
RH-Bugzilla: 2124856
7f1c5b
RH-Acked-by: Peter Xu <peterx@redhat.com>
7f1c5b
RH-Acked-by: Jason Wang <jasowang@redhat.com>
7f1c5b
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
7f1c5b
RH-Commit: [1/1] 57ef499b63dc2cca6e64ee84d1dc127635868ca2 (eauger1/centos-qemu-kvm)
7f1c5b
7f1c5b
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2124856
7f1c5b
Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=49989924
7f1c5b
Upstream: yes
7f1c5b
7f1c5b
When vIOMMU is enabled, the vq->used_phys is actually the IOVA not
7f1c5b
GPA. So we need to translate it to GPA before the syncing otherwise we
7f1c5b
may hit the following crash since IOVA could be out of the scope of
7f1c5b
the GPA log size. This could be noted when using virtio-IOMMU with
7f1c5b
vhost using 1G memory.
7f1c5b
7f1c5b
Fixes: c471ad0e9bd46 ("vhost_net: device IOTLB support")
7f1c5b
Cc: qemu-stable@nongnu.org
7f1c5b
Tested-by: Lei Yang <leiyang@redhat.com>
7f1c5b
Reported-by: Yalan Zhang <yalzhang@redhat.com>
7f1c5b
Signed-off-by: Jason Wang <jasowang@redhat.com>
7f1c5b
Message-Id: <20221216033552.77087-1-jasowang@redhat.com>
7f1c5b
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
7f1c5b
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
7f1c5b
(cherry picked from commit 345cc1cbcbce2bab00abc2b88338d7d89c702d6b)
7f1c5b
Signed-off-by: Eric Auger <eric.auger@redhat.com>
7f1c5b
---
7f1c5b
 hw/virtio/vhost.c | 84 ++++++++++++++++++++++++++++++++++++-----------
7f1c5b
 1 file changed, 64 insertions(+), 20 deletions(-)
7f1c5b
7f1c5b
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
7f1c5b
index 84dbb39e07..2c566dc539 100644
7f1c5b
--- a/hw/virtio/vhost.c
7f1c5b
+++ b/hw/virtio/vhost.c
7f1c5b
@@ -20,6 +20,7 @@
7f1c5b
 #include "qemu/range.h"
7f1c5b
 #include "qemu/error-report.h"
7f1c5b
 #include "qemu/memfd.h"
7f1c5b
+#include "qemu/log.h"
7f1c5b
 #include "standard-headers/linux/vhost_types.h"
7f1c5b
 #include "hw/virtio/virtio-bus.h"
7f1c5b
 #include "hw/virtio/virtio-access.h"
7f1c5b
@@ -106,6 +107,24 @@ static void vhost_dev_sync_region(struct vhost_dev *dev,
7f1c5b
     }
7f1c5b
 }
7f1c5b
 
7f1c5b
+static bool vhost_dev_has_iommu(struct vhost_dev *dev)
7f1c5b
+{
7f1c5b
+    VirtIODevice *vdev = dev->vdev;
7f1c5b
+
7f1c5b
+    /*
7f1c5b
+     * For vhost, VIRTIO_F_IOMMU_PLATFORM means the backend support
7f1c5b
+     * incremental memory mapping API via IOTLB API. For platform that
7f1c5b
+     * does not have IOMMU, there's no need to enable this feature
7f1c5b
+     * which may cause unnecessary IOTLB miss/update transactions.
7f1c5b
+     */
7f1c5b
+    if (vdev) {
7f1c5b
+        return virtio_bus_device_iommu_enabled(vdev) &&
7f1c5b
+            virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
7f1c5b
+    } else {
7f1c5b
+        return false;
7f1c5b
+    }
7f1c5b
+}
7f1c5b
+
7f1c5b
 static int vhost_sync_dirty_bitmap(struct vhost_dev *dev,
7f1c5b
                                    MemoryRegionSection *section,
7f1c5b
                                    hwaddr first,
7f1c5b
@@ -137,8 +156,51 @@ static int vhost_sync_dirty_bitmap(struct vhost_dev *dev,
7f1c5b
             continue;
7f1c5b
         }
7f1c5b
 
7f1c5b
-        vhost_dev_sync_region(dev, section, start_addr, end_addr, vq->used_phys,
7f1c5b
-                              range_get_last(vq->used_phys, vq->used_size));
7f1c5b
+        if (vhost_dev_has_iommu(dev)) {
7f1c5b
+            IOMMUTLBEntry iotlb;
7f1c5b
+            hwaddr used_phys = vq->used_phys, used_size = vq->used_size;
7f1c5b
+            hwaddr phys, s, offset;
7f1c5b
+
7f1c5b
+            while (used_size) {
7f1c5b
+                rcu_read_lock();
7f1c5b
+                iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as,
7f1c5b
+                                                      used_phys,
7f1c5b
+                                                      true,
7f1c5b
+                                                      MEMTXATTRS_UNSPECIFIED);
7f1c5b
+                rcu_read_unlock();
7f1c5b
+
7f1c5b
+                if (!iotlb.target_as) {
7f1c5b
+                    qemu_log_mask(LOG_GUEST_ERROR, "translation "
7f1c5b
+                                  "failure for used_iova %"PRIx64"\n",
7f1c5b
+                                  used_phys);
7f1c5b
+                    return -EINVAL;
7f1c5b
+                }
7f1c5b
+
7f1c5b
+                offset = used_phys & iotlb.addr_mask;
7f1c5b
+                phys = iotlb.translated_addr + offset;
7f1c5b
+
7f1c5b
+                /*
7f1c5b
+                 * Distance from start of used ring until last byte of
7f1c5b
+                 * IOMMU page.
7f1c5b
+                 */
7f1c5b
+                s = iotlb.addr_mask - offset;
7f1c5b
+                /*
7f1c5b
+                 * Size of used ring, or of the part of it until end
7f1c5b
+                 * of IOMMU page. To avoid zero result, do the adding
7f1c5b
+                 * outside of MIN().
7f1c5b
+                 */
7f1c5b
+                s = MIN(s, used_size - 1) + 1;
7f1c5b
+
7f1c5b
+                vhost_dev_sync_region(dev, section, start_addr, end_addr, phys,
7f1c5b
+                                      range_get_last(phys, s));
7f1c5b
+                used_size -= s;
7f1c5b
+                used_phys += s;
7f1c5b
+            }
7f1c5b
+        } else {
7f1c5b
+            vhost_dev_sync_region(dev, section, start_addr,
7f1c5b
+                                  end_addr, vq->used_phys,
7f1c5b
+                                  range_get_last(vq->used_phys, vq->used_size));
7f1c5b
+        }
7f1c5b
     }
7f1c5b
     return 0;
7f1c5b
 }
7f1c5b
@@ -306,24 +368,6 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size)
7f1c5b
     dev->log_size = size;
7f1c5b
 }
7f1c5b
 
7f1c5b
-static bool vhost_dev_has_iommu(struct vhost_dev *dev)
7f1c5b
-{
7f1c5b
-    VirtIODevice *vdev = dev->vdev;
7f1c5b
-
7f1c5b
-    /*
7f1c5b
-     * For vhost, VIRTIO_F_IOMMU_PLATFORM means the backend support
7f1c5b
-     * incremental memory mapping API via IOTLB API. For platform that
7f1c5b
-     * does not have IOMMU, there's no need to enable this feature
7f1c5b
-     * which may cause unnecessary IOTLB miss/update transactions.
7f1c5b
-     */
7f1c5b
-    if (vdev) {
7f1c5b
-        return virtio_bus_device_iommu_enabled(vdev) &&
7f1c5b
-            virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
7f1c5b
-    } else {
7f1c5b
-        return false;
7f1c5b
-    }
7f1c5b
-}
7f1c5b
-
7f1c5b
 static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr,
7f1c5b
                               hwaddr *plen, bool is_write)
7f1c5b
 {
7f1c5b
-- 
7f1c5b
2.31.1
7f1c5b