Commit 549d4005 authored by Eric Auger's avatar Eric Auger Committed by Paolo Bonzini
Browse files

memory: allow memory_region_register_iommu_notifier() to fail



Currently, when a notifier is attempted to be registered and its
flags are not supported (especially the MAP one) by the IOMMU MR,
we generally abruptly exit in the IOMMU code. The failure could be
handled more nicely in the caller and especially in the VFIO code.

So let's allow memory_region_register_iommu_notifier() to fail as
well as notify_flag_changed() callback.

All sites implementing the callback are updated. This patch does
not yet remove the exit(1) in the amd_iommu code.

in SMMUv3 we turn the warning message into an error message saying
that the assigned device would not work properly.

Signed-off-by: default avatarEric Auger <eric.auger@redhat.com>
Reviewed-by: default avatarPeter Xu <peterx@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent d7d87836
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -660,7 +660,8 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
     */
    MemoryRegion *mr = MEMORY_REGION(iommu_mr);
    TCGIOMMUNotifier *notifier;
    int i;
    Error *err = NULL;
    int i, ret;

    for (i = 0; i < cpu->iommu_notifiers->len; i++) {
        notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i);
@@ -689,7 +690,12 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
                            0,
                            HWADDR_MAX,
                            iommu_idx);
        memory_region_register_iommu_notifier(notifier->mr, &notifier->n);
        ret = memory_region_register_iommu_notifier(notifier->mr, &notifier->n,
                                                    &err);
        if (ret) {
            error_report_err(err);
            exit(1);
        }
    }

    if (!notifier->active) {
+10 −8
Original line number Diff line number Diff line
@@ -1469,20 +1469,21 @@ static void smmuv3_class_init(ObjectClass *klass, void *data)
    dc->realize = smmu_realize;
}

static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
                                      IOMMUNotifierFlag old,
                                       IOMMUNotifierFlag new)
                                      IOMMUNotifierFlag new,
                                      Error **errp)
{
    SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu);
    SMMUv3State *s3 = sdev->smmu;
    SMMUState *s = &(s3->smmu_state);

    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);
        error_setg(errp,
                   "device %02x.%02x.%x requires iommu MAP notifier which is "
                   "not currently supported", pci_bus_num(sdev->bus),
                   PCI_SLOT(sdev->devfn), PCI_FUNC(sdev->devfn));
        return -EINVAL;
    }

    if (old == IOMMU_NOTIFIER_NONE) {
@@ -1492,6 +1493,7 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
        trace_smmuv3_notify_flag_del(iommu->parent_obj.name);
        QLIST_REMOVE(sdev, next);
    }
    return 0;
}

static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass,
+10 −7
Original line number Diff line number Diff line
@@ -1466,18 +1466,21 @@ static const MemoryRegionOps mmio_mem_ops = {
    }
};

static void amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
static int amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
                                           IOMMUNotifierFlag old,
                                            IOMMUNotifierFlag new)
                                           IOMMUNotifierFlag new,
                                           Error **errp)
{
    AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu);

    if (new & IOMMU_NOTIFIER_MAP) {
        error_report("device %02x.%02x.%x requires iommu notifier which is not "
        error_setg(errp,
                   "device %02x.%02x.%x requires iommu notifier which is not "
                   "currently supported", as->bus_num, PCI_SLOT(as->devfn),
                   PCI_FUNC(as->devfn));
        exit(1);
        return -EINVAL;
    }
    return 0;
}

static void amdvi_init(AMDVIState *s)
+5 −3
Original line number Diff line number Diff line
@@ -2929,9 +2929,10 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
    return iotlb;
}

static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
static int vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
                                         IOMMUNotifierFlag old,
                                          IOMMUNotifierFlag new)
                                         IOMMUNotifierFlag new,
                                         Error **errp)
{
    VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
    IntelIOMMUState *s = vtd_as->iommu_state;
@@ -2944,6 +2945,7 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
    } else if (new == IOMMU_NOTIFIER_NONE) {
        QLIST_REMOVE(vtd_as, next);
    }
    return 0;
}

static int vtd_post_load(void *opaque, int version_id)
+5 −3
Original line number Diff line number Diff line
@@ -205,9 +205,10 @@ static int spapr_tce_get_attr(IOMMUMemoryRegion *iommu,
    return -EINVAL;
}

static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
static int spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
                                         IOMMUNotifierFlag old,
                                          IOMMUNotifierFlag new)
                                         IOMMUNotifierFlag new,
                                         Error **errp)
{
    struct SpaprTceTable *tbl = container_of(iommu, SpaprTceTable, iommu);

@@ -216,6 +217,7 @@ static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu,
    } else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) {
        spapr_tce_set_need_vfio(tbl, false);
    }
    return 0;
}

static int spapr_tce_table_post_load(void *opaque, int version_id)
Loading