ae23c9
From 22bf46f7576cc12a301d34ca743a6661afcb61ac Mon Sep 17 00:00:00 2001
ae23c9
From: Peter Xu <peterx@redhat.com>
ae23c9
Date: Fri, 12 Oct 2018 07:58:42 +0100
ae23c9
Subject: [PATCH 12/17] intel-iommu: introduce vtd_page_walk_info
ae23c9
ae23c9
RH-Author: Peter Xu <peterx@redhat.com>
ae23c9
Message-id: <20181012075846.25449-6-peterx@redhat.com>
ae23c9
Patchwork-id: 82678
ae23c9
O-Subject: [RHEL-8 qemu-kvm PATCH 5/9] intel-iommu: introduce vtd_page_walk_info
ae23c9
Bugzilla: 1450712
ae23c9
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
ae23c9
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
ae23c9
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
ae23c9
ae23c9
During the recursive page walking of IOVA page tables, some stack
ae23c9
variables are constant variables and never changed during the whole page
ae23c9
walking procedure.  Isolate them into a struct so that we don't need to
ae23c9
pass those contants down the stack every time and multiple times.
ae23c9
ae23c9
CC: QEMU Stable <qemu-stable@nongnu.org>
ae23c9
Signed-off-by: Peter Xu <peterx@redhat.com>
ae23c9
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
ae23c9
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
ae23c9
(cherry picked from commit fe215b0cbb8c1f4b4af0a64aa5c02042080dd537)
ae23c9
Signed-off-by: Peter Xu <peterx@redhat.com>
ae23c9
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ae23c9
---
ae23c9
 hw/i386/intel_iommu.c | 84 +++++++++++++++++++++++++++++++--------------------
ae23c9
 1 file changed, 52 insertions(+), 32 deletions(-)
ae23c9
ae23c9
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
ae23c9
index 38ccc74..e247269 100644
ae23c9
--- a/hw/i386/intel_iommu.c
ae23c9
+++ b/hw/i386/intel_iommu.c
ae23c9
@@ -748,9 +748,27 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write,
ae23c9
 
ae23c9
 typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private);
ae23c9
 
ae23c9
+/**
ae23c9
+ * Constant information used during page walking
ae23c9
+ *
ae23c9
+ * @hook_fn: hook func to be called when detected page
ae23c9
+ * @private: private data to be passed into hook func
ae23c9
+ * @notify_unmap: whether we should notify invalid entries
ae23c9
+ * @aw: maximum address width
ae23c9
+ */
ae23c9
+typedef struct {
ae23c9
+    vtd_page_walk_hook hook_fn;
ae23c9
+    void *private;
ae23c9
+    bool notify_unmap;
ae23c9
+    uint8_t aw;
ae23c9
+} vtd_page_walk_info;
ae23c9
+
ae23c9
 static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level,
ae23c9
-                             vtd_page_walk_hook hook_fn, void *private)
ae23c9
+                             vtd_page_walk_info *info)
ae23c9
 {
ae23c9
+    vtd_page_walk_hook hook_fn = info->hook_fn;
ae23c9
+    void *private = info->private;
ae23c9
+
ae23c9
     assert(hook_fn);
ae23c9
     trace_vtd_page_walk_one(level, entry->iova, entry->translated_addr,
ae23c9
                             entry->addr_mask, entry->perm);
ae23c9
@@ -763,17 +781,13 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, int level,
ae23c9
  * @addr: base GPA addr to start the walk
ae23c9
  * @start: IOVA range start address
ae23c9
  * @end: IOVA range end address (start <= addr < end)
ae23c9
- * @hook_fn: hook func to be called when detected page
ae23c9
- * @private: private data to be passed into hook func
ae23c9
  * @read: whether parent level has read permission
ae23c9
  * @write: whether parent level has write permission
ae23c9
- * @notify_unmap: whether we should notify invalid entries
ae23c9
- * @aw: maximum address width
ae23c9
+ * @info: constant information for the page walk
ae23c9
  */
ae23c9
 static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
ae23c9
-                               uint64_t end, vtd_page_walk_hook hook_fn,
ae23c9
-                               void *private, uint32_t level, bool read,
ae23c9
-                               bool write, bool notify_unmap, uint8_t aw)
ae23c9
+                               uint64_t end, uint32_t level, bool read,
ae23c9
+                               bool write, vtd_page_walk_info *info)
ae23c9
 {
ae23c9
     bool read_cur, write_cur, entry_valid;
ae23c9
     uint32_t offset;
ae23c9
@@ -823,24 +837,24 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
ae23c9
 
ae23c9
         if (vtd_is_last_slpte(slpte, level)) {
ae23c9
             /* NOTE: this is only meaningful if entry_valid == true */
ae23c9
-            entry.translated_addr = vtd_get_slpte_addr(slpte, aw);
ae23c9
-            if (!entry_valid && !notify_unmap) {
ae23c9
+            entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw);
ae23c9
+            if (!entry_valid && !info->notify_unmap) {
ae23c9
                 trace_vtd_page_walk_skip_perm(iova, iova_next);
ae23c9
                 goto next;
ae23c9
             }
ae23c9
-            ret = vtd_page_walk_one(&entry, level, hook_fn, private);
ae23c9
+            ret = vtd_page_walk_one(&entry, level, info);
ae23c9
             if (ret < 0) {
ae23c9
                 return ret;
ae23c9
             }
ae23c9
         } else {
ae23c9
             if (!entry_valid) {
ae23c9
-                if (notify_unmap) {
ae23c9
+                if (info->notify_unmap) {
ae23c9
                     /*
ae23c9
                      * The whole entry is invalid; unmap it all.
ae23c9
                      * Translated address is meaningless, zero it.
ae23c9
                      */
ae23c9
                     entry.translated_addr = 0x0;
ae23c9
-                    ret = vtd_page_walk_one(&entry, level, hook_fn, private);
ae23c9
+                    ret = vtd_page_walk_one(&entry, level, info);
ae23c9
                     if (ret < 0) {
ae23c9
                         return ret;
ae23c9
                     }
ae23c9
@@ -849,10 +863,9 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
ae23c9
                 }
ae23c9
                 goto next;
ae23c9
             }
ae23c9
-            ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova,
ae23c9
-                                      MIN(iova_next, end), hook_fn, private,
ae23c9
-                                      level - 1, read_cur, write_cur,
ae23c9
-                                      notify_unmap, aw);
ae23c9
+            ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw),
ae23c9
+                                      iova, MIN(iova_next, end), level - 1,
ae23c9
+                                      read_cur, write_cur, info);
ae23c9
             if (ret < 0) {
ae23c9
                 return ret;
ae23c9
             }
ae23c9
@@ -871,28 +884,24 @@ next:
ae23c9
  * @ce: context entry to walk upon
ae23c9
  * @start: IOVA address to start the walk
ae23c9
  * @end: IOVA range end address (start <= addr < end)
ae23c9
- * @hook_fn: the hook that to be called for each detected area
ae23c9
- * @private: private data for the hook function
ae23c9
- * @aw: maximum address width
ae23c9
+ * @info: page walking information struct
ae23c9
  */
ae23c9
 static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end,
ae23c9
-                         vtd_page_walk_hook hook_fn, void *private,
ae23c9
-                         bool notify_unmap, uint8_t aw)
ae23c9
+                         vtd_page_walk_info *info)
ae23c9
 {
ae23c9
     dma_addr_t addr = vtd_ce_get_slpt_base(ce);
ae23c9
     uint32_t level = vtd_ce_get_level(ce);
ae23c9
 
ae23c9
-    if (!vtd_iova_range_check(start, ce, aw)) {
ae23c9
+    if (!vtd_iova_range_check(start, ce, info->aw)) {
ae23c9
         return -VTD_FR_ADDR_BEYOND_MGAW;
ae23c9
     }
ae23c9
 
ae23c9
-    if (!vtd_iova_range_check(end, ce, aw)) {
ae23c9
+    if (!vtd_iova_range_check(end, ce, info->aw)) {
ae23c9
         /* Fix end so that it reaches the maximum */
ae23c9
-        end = vtd_iova_limit(ce, aw);
ae23c9
+        end = vtd_iova_limit(ce, info->aw);
ae23c9
     }
ae23c9
 
ae23c9
-    return vtd_page_walk_level(addr, start, end, hook_fn, private,
ae23c9
-                               level, true, true, notify_unmap, aw);
ae23c9
+    return vtd_page_walk_level(addr, start, end, level, true, true, info);
ae23c9
 }
ae23c9
 
ae23c9
 /* Map a device to its corresponding domain (context-entry) */
ae23c9
@@ -1449,14 +1458,19 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
ae23c9
                                        vtd_as->devfn, &ce);
ae23c9
         if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) {
ae23c9
             if (vtd_as_has_map_notifier(vtd_as)) {
ae23c9
+                vtd_page_walk_info info = {
ae23c9
+                    .hook_fn = vtd_page_invalidate_notify_hook,
ae23c9
+                    .private = (void *)&vtd_as->iommu,
ae23c9
+                    .notify_unmap = true,
ae23c9
+                    .aw = s->aw_bits,
ae23c9
+                };
ae23c9
+
ae23c9
                 /*
ae23c9
                  * As long as we have MAP notifications registered in
ae23c9
                  * any of our IOMMU notifiers, we need to sync the
ae23c9
                  * shadow page table.
ae23c9
                  */
ae23c9
-                vtd_page_walk(&ce, addr, addr + size,
ae23c9
-                              vtd_page_invalidate_notify_hook,
ae23c9
-                              (void *)&vtd_as->iommu, true, s->aw_bits);
ae23c9
+                vtd_page_walk(&ce, addr, addr + size, &info;;
ae23c9
             } else {
ae23c9
                 /*
ae23c9
                  * For UNMAP-only notifiers, we don't need to walk the
ae23c9
@@ -2924,8 +2938,14 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
ae23c9
                                   ce.hi, ce.lo);
ae23c9
         if (vtd_as_has_map_notifier(vtd_as)) {
ae23c9
             /* This is required only for MAP typed notifiers */
ae23c9
-            vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false,
ae23c9
-                          s->aw_bits);
ae23c9
+            vtd_page_walk_info info = {
ae23c9
+                .hook_fn = vtd_replay_hook,
ae23c9
+                .private = (void *)n,
ae23c9
+                .notify_unmap = false,
ae23c9
+                .aw = s->aw_bits,
ae23c9
+            };
ae23c9
+
ae23c9
+            vtd_page_walk(&ce, 0, ~0ULL, &info;;
ae23c9
         }
ae23c9
     } else {
ae23c9
         trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn),
ae23c9
-- 
ae23c9
1.8.3.1
ae23c9