Re: [PATCH V1 07/26] vfio/container: recover from unmap-all-vaddr failure

2025-02-04 Thread Steven Sistare

On 2/4/2025 9:10 AM, Cédric Le Goater wrote:

On 1/29/25 15:43, Steve Sistare wrote:

If there are multiple containers and unmap-all fails for some container, we
need to remap vaddr for the other containers for which unmap-all succeeded.
Recover by walking all address ranges of all containers to restore the vaddr
for each.  Do so by invoking the vfio listener callback, and passing a new
"remap" flag that tells it to restore a mapping without re-allocating new
userland data structures.

Signed-off-by: Steve Sistare 
---
  hw/vfio/common.c  | 47 ++-
  hw/vfio/cpr-legacy.c  | 44 
  include/hw/vfio/vfio-common.h |  6 +-
  3 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 7370332..c8ee71a 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -580,6 +580,13 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
  {
  VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
   listener);
+    vfio_container_region_add(bcontainer, section, false);
+}
+
+void vfio_container_region_add(VFIOContainerBase *bcontainer,
+   MemoryRegionSection *section,
+   bool remap)
+{


vfio_container_region_add() is already complex enough. Please consider
doing an initial refactoring before adding a new code path. It would be
welcome !


I'll take a look after factoring out the cpr code into helpers as you
request below.


  hwaddr iova, end;
  Int128 llend, llsize;
  void *vaddr;
@@ -614,6 +621,30 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
  int iommu_idx;
  trace_vfio_listener_region_add_iommu(section->mr->name, iova, end);
+
+    /*
+ * If remap, then VFIO_DMA_UNMAP_FLAG_VADDR has been called, and we
+ * want to remap the vaddr.  vfio_container_region_add was already
+ * called in the past, so the giommu already exists.  Find it and
+ * replay it, which calls vfio_dma_map further down the stack.
+ */
+
+    if (remap) {
+    hwaddr as_offset = section->offset_within_address_space;
+    hwaddr iommu_offset = as_offset - section->offset_within_region;
+
+    QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
+    if (giommu->iommu_mr == iommu_mr &&
+    giommu->iommu_offset == iommu_offset) {
+    memory_region_iommu_replay(giommu->iommu_mr, &giommu->n);
+    return;
+    }
+    }
+    error_report("Container cannot find iommu region %s offset %lx",
+    memory_region_name(section->mr), iommu_offset);


error_report() are not welcomed. We need to find a way to propagate
this error.


I follow the existing practice in this function, which already reports other
errors.  This is called in the context of a memory listener region_add method,
so returning an error affects the signature of all listeners and should be a
seperate RFE.


+    goto fail;
+    }


Please introduce a vfio_cpr helper for the section above and move it
under the hw/vfio/cpr* files.


OK.


  /*
   * FIXME: For VFIO iommu types which have KVM acceleration to
   * avoid bouncing all map/unmaps through qemu this way, this
@@ -656,7 +687,21 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
   * about changes.
   */
  if (memory_region_has_ram_discard_manager(section->mr)) {
-    vfio_register_ram_discard_listener(bcontainer, section);
+    /*
+ * If remap, then VFIO_DMA_UNMAP_FLAG_VADDR has been called, and we
+ * want to remap the vaddr.  vfio_container_region_add was already
+ * called in the past, so the ram discard listener already exists.
+ * Call its populate function directly, which calls vfio_dma_map.
+ */
+    if (remap)  {
+    VFIORamDiscardListener *vrdl =
+    vfio_find_ram_discard_listener(bcontainer, section);
+    if (vrdl->listener.notify_populate(&vrdl->listener, section)) {
+    error_report("listener.notify_populate failed");
+    }
+    } else {
+    vfio_register_ram_discard_listener(bcontainer, section);
+    }


idem.


OK.


  return;
  }
diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c
index f3a31d1..3139de1 100644
--- a/hw/vfio/cpr-legacy.c
+++ b/hw/vfio/cpr-legacy.c
@@ -26,9 +26,18 @@ static bool vfio_dma_unmap_vaddr_all(VFIOContainer 
*container, Error **errp)
  error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all");
  return false;
  }
+    container->vaddr_unmapped = true;
  return true;>   }
+static void vfio_region_remap(MemoryListener *listener,
+  MemoryRe

Re: [PATCH V1 07/26] vfio/container: recover from unmap-all-vaddr failure

2025-02-04 Thread Cédric Le Goater

On 1/29/25 15:43, Steve Sistare wrote:

If there are multiple containers and unmap-all fails for some container, we
need to remap vaddr for the other containers for which unmap-all succeeded.
Recover by walking all address ranges of all containers to restore the vaddr
for each.  Do so by invoking the vfio listener callback, and passing a new
"remap" flag that tells it to restore a mapping without re-allocating new
userland data structures.

Signed-off-by: Steve Sistare 
---
  hw/vfio/common.c  | 47 ++-
  hw/vfio/cpr-legacy.c  | 44 
  include/hw/vfio/vfio-common.h |  6 +-
  3 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 7370332..c8ee71a 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -580,6 +580,13 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
  {
  VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
   listener);
+vfio_container_region_add(bcontainer, section, false);
+}
+
+void vfio_container_region_add(VFIOContainerBase *bcontainer,
+   MemoryRegionSection *section,
+   bool remap)
+{


vfio_container_region_add() is already complex enough. Please consider
doing an initial refactoring before adding a new code path. It would be
welcome !


  hwaddr iova, end;
  Int128 llend, llsize;
  void *vaddr;
@@ -614,6 +621,30 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
  int iommu_idx;
  
  trace_vfio_listener_region_add_iommu(section->mr->name, iova, end);

+
+/*
+ * If remap, then VFIO_DMA_UNMAP_FLAG_VADDR has been called, and we
+ * want to remap the vaddr.  vfio_container_region_add was already
+ * called in the past, so the giommu already exists.  Find it and
+ * replay it, which calls vfio_dma_map further down the stack.
+ */
+
+if (remap) {
+hwaddr as_offset = section->offset_within_address_space;
+hwaddr iommu_offset = as_offset - section->offset_within_region;
+
+QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
+if (giommu->iommu_mr == iommu_mr &&
+giommu->iommu_offset == iommu_offset) {
+memory_region_iommu_replay(giommu->iommu_mr, &giommu->n);
+return;
+}
+}
+error_report("Container cannot find iommu region %s offset %lx",
+memory_region_name(section->mr), iommu_offset);


error_report() are not welcomed. We need to find a way to propagate
this error.


+goto fail;
+}


Please introduce a vfio_cpr helper for the section above and move it
under the hw/vfio/cpr* files.



  /*
   * FIXME: For VFIO iommu types which have KVM acceleration to
   * avoid bouncing all map/unmaps through qemu this way, this
@@ -656,7 +687,21 @@ static void vfio_listener_region_add(MemoryListener 
*listener,
   * about changes.
   */
  if (memory_region_has_ram_discard_manager(section->mr)) {
-vfio_register_ram_discard_listener(bcontainer, section);
+/*
+ * If remap, then VFIO_DMA_UNMAP_FLAG_VADDR has been called, and we
+ * want to remap the vaddr.  vfio_container_region_add was already
+ * called in the past, so the ram discard listener already exists.
+ * Call its populate function directly, which calls vfio_dma_map.
+ */
+if (remap)  {
+VFIORamDiscardListener *vrdl =
+vfio_find_ram_discard_listener(bcontainer, section);
+if (vrdl->listener.notify_populate(&vrdl->listener, section)) {
+error_report("listener.notify_populate failed");
+}
+} else {
+vfio_register_ram_discard_listener(bcontainer, section);
+}


idem.


  return;
  }
  
diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c

index f3a31d1..3139de1 100644
--- a/hw/vfio/cpr-legacy.c
+++ b/hw/vfio/cpr-legacy.c
@@ -26,9 +26,18 @@ static bool vfio_dma_unmap_vaddr_all(VFIOContainer 
*container, Error **errp)
  error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all");
  return false;
  }
+container->vaddr_unmapped = true;
  return true;>   }
  
+static void vfio_region_remap(MemoryListener *listener,

+  MemoryRegionSection *section)
+{
+VFIOContainer *container = container_of(listener, VFIOContainer,
+remap_listener);
+vfio_container_region_add(&container->bcontainer, section, true);
+}
+
  static bool vfio_cpr_supported(VFIOContainer *container, Error **errp)
  {
  if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR