vfio queue:

* Coverity fix
 * IGD cleanups using VFIOQuirk
 * SIGSEV fix in IOMMUFD host IOMMU device
 * Improved error reporting for MMIO region mapping failures
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmerTzQACgkQUaNDx8/7
 7KHXLw/+LaONyFor+kuorb5et6rzyrE4keIUDv8zDTM4FnwFKP31wX8feQ63o17U
 DQUYN4uM5Ah/PemF/IBCj44x1Eirzl8LW51sMtxg/weCa8xrZOsHjmoKNml4f+zs
 ERzO/KSu9PWEWEyX79XGCcu5VQKl60b8Ra5QMBNKZKjVZpfBTxCjHZFIvQxSJFvm
 gPKHFDtmtbhtBnq3U/N/PwpnUuH4+p6ofz1eKdOcin11CAks7cAt6bl1CIs7sUbC
 ttrrQg6D+UJ5b+ISZjsw5hakfRIdtlZ/lS4jk678gN06108CIMmFPLPaRu27mdX9
 4wBiMSQM8fFbbHw66FQiPgJeeGAmG/PvdLN4SbRSujkEkKyEyqtH2dRINy9PNXj4
 ZXXugx7xqfPfTEC1lwsyGDdHdHH022V0ScdDpx+K87klRvu30ZjorB7QSCI7x+ZN
 yW2ztZQ2hNH6MsgrKTQS6MLArHgU+h0ycaHy+01jj5UKSs3xAf53wNnx2uoBmKGj
 gZB/tNFg60qeSuW900ybnBTaH60qLs6xzY7evDRa5cqPYJ+z/lRUYp45fmsgQ1yR
 91PHhC/mQLFjQRc78vF6k7slMm/Fk8JOalZgYPtm+Atdw3ufjOexavoHqN3Sa1Us
 keKnR589kHikPd3zZN7sZzT8wMNTAcRbSy72360+PzEZ1Iiiu+M=
 =wpSw
 -----END PGP SIGNATURE-----

Merge tag 'pull-vfio-20250211' of https://github.com/legoater/qemu into staging

vfio queue:

* Coverity fix
* IGD cleanups using VFIOQuirk
* SIGSEV fix in IOMMUFD host IOMMU device
* Improved error reporting for MMIO region mapping failures

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmerTzQACgkQUaNDx8/7
# 7KHXLw/+LaONyFor+kuorb5et6rzyrE4keIUDv8zDTM4FnwFKP31wX8feQ63o17U
# DQUYN4uM5Ah/PemF/IBCj44x1Eirzl8LW51sMtxg/weCa8xrZOsHjmoKNml4f+zs
# ERzO/KSu9PWEWEyX79XGCcu5VQKl60b8Ra5QMBNKZKjVZpfBTxCjHZFIvQxSJFvm
# gPKHFDtmtbhtBnq3U/N/PwpnUuH4+p6ofz1eKdOcin11CAks7cAt6bl1CIs7sUbC
# ttrrQg6D+UJ5b+ISZjsw5hakfRIdtlZ/lS4jk678gN06108CIMmFPLPaRu27mdX9
# 4wBiMSQM8fFbbHw66FQiPgJeeGAmG/PvdLN4SbRSujkEkKyEyqtH2dRINy9PNXj4
# ZXXugx7xqfPfTEC1lwsyGDdHdHH022V0ScdDpx+K87klRvu30ZjorB7QSCI7x+ZN
# yW2ztZQ2hNH6MsgrKTQS6MLArHgU+h0ycaHy+01jj5UKSs3xAf53wNnx2uoBmKGj
# gZB/tNFg60qeSuW900ybnBTaH60qLs6xzY7evDRa5cqPYJ+z/lRUYp45fmsgQ1yR
# 91PHhC/mQLFjQRc78vF6k7slMm/Fk8JOalZgYPtm+Atdw3ufjOexavoHqN3Sa1Us
# keKnR589kHikPd3zZN7sZzT8wMNTAcRbSy72360+PzEZ1Iiiu+M=
# =wpSw
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 11 Feb 2025 08:23:00 EST
# gpg:                using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1
# gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full]
# gpg:                 aka "Cédric Le Goater <clg@kaod.org>" [full]
# Primary key fingerprint: A0F6 6548 F048 95EB FE6B  0B60 51A3 43C7 CFFB ECA1

* tag 'pull-vfio-20250211' of https://github.com/legoater/qemu:
  vfio: Remove superfluous error report in vfio_listener_region_add()
  vfio: Remove reports of DMA mapping errors in backends
  vfio: Improve error reporting when MMIO region mapping fails
  vfio: Introduce vfio_get_vfio_device()
  vfio: Rephrase comment in vfio_listener_region_add() error path
  vfio/pci: Replace "iommu_device" by "vIOMMU"
  util/error: Introduce warn_report_err_once()
  vfio/iommufd: Fix SIGSEV in iommufd_cdev_attach()
  vfio/igd: use VFIOConfigMirrorQuirk for mirrored registers
  vfio/pci: introduce config_offset field in VFIOConfigMirrorQuirk
  vfio/pci: declare generic quirks in a new header file
  vfio/igd: Fix potential overflow in igd_gtt_memory_size()

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-02-12 08:48:07 -05:00
commit 5f1de4d3ce
12 changed files with 177 additions and 165 deletions

View File

@ -167,8 +167,6 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
/* TODO: Not support mapping hardware PCI BAR region for now. */
if (errno == EFAULT) {
warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
} else {
error_report("IOMMU_IOAS_MAP failed: %m");
}
}
return ret;
@ -203,7 +201,6 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
if (ret) {
ret = -errno;
error_report("IOMMU_IOAS_UNMAP failed: %m");
}
return ret;
}

View File

@ -555,6 +555,18 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer,
return true;
}
static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp)
{
/*
* MMIO region mapping failures are not fatal but in this case PCI
* peer-to-peer transactions are broken.
*/
if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) {
error_append_hint(errp, "%s: PCI peer-to-peer transactions "
"on BARs are not supported.\n", vbasedev->name);
}
}
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
@ -582,8 +594,9 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
}
/* PPC64/pseries machine only */
if (!vfio_container_add_section_window(bcontainer, section, &err)) {
goto fail;
goto mmio_dma_error;
}
memory_region_ref(section->mr);
@ -668,9 +681,13 @@ static void vfio_listener_region_add(MemoryListener *listener,
"0x%"HWADDR_PRIx", %p) = %d (%s)",
bcontainer, iova, int128_get64(llsize), vaddr, ret,
strerror(-ret));
mmio_dma_error:
if (memory_region_is_ram_device(section->mr)) {
/* Allow unexpected mappings not to be fatal for RAM devices */
error_report_err(err);
VFIODevice *vbasedev =
vfio_get_vfio_device(memory_region_owner(section->mr));
vfio_device_error_append(vbasedev, &err);
warn_report_err_once(err);
return;
}
goto fail;
@ -679,16 +696,12 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
fail:
if (memory_region_is_ram_device(section->mr)) {
error_reportf_err(err, "PCI p2p may not work: ");
return;
}
/*
* On the initfn path, store the first error in the container so we
* can gracefully fail. Runtime, there's not much we can do other
* than throw a hardware error.
*/
if (!bcontainer->initialized) {
/*
* At machine init time or when the device is attached to the
* VM, store the first error in the container so we can
* gracefully fail the device realize routine.
*/
if (!bcontainer->error) {
error_propagate_prepend(&bcontainer->error, err,
"Region %s: ",
@ -697,6 +710,10 @@ fail:
error_free(err);
}
} else {
/*
* At runtime, there's not much we can do other than throw a
* hardware error.
*/
error_report_err(err);
hw_error("vfio: DMA mapping failed, unable to continue");
}
@ -786,6 +803,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
memory_region_unref(section->mr);
/* PPC64/pseries machine only */
vfio_container_del_section_window(bcontainer, section);
}

View File

@ -159,7 +159,6 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
unmap.size -= 1ULL << ctz64(bcontainer->pgsizes);
continue;
}
error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno));
return -errno;
}
@ -204,7 +203,6 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
return 0;
}
error_report("VFIO_MAP_DMA failed: %s", strerror(errno));
return -errno;
}

View File

@ -23,6 +23,7 @@
#include <sys/ioctl.h>
#include "hw/vfio/vfio-common.h"
#include "hw/vfio/pci.h"
#include "hw/hw.h"
#include "trace.h"
#include "qapi/error.h"
@ -728,3 +729,12 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp)
return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp);
}
VFIODevice *vfio_get_vfio_device(Object *obj)
{
if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) {
return &VFIO_PCI(obj)->vbasedev;
} else {
return NULL;
}
}

View File

@ -18,6 +18,7 @@
#include "hw/hw.h"
#include "hw/nvram/fw_cfg.h"
#include "pci.h"
#include "pci-quirks.h"
#include "trace.h"
/*
@ -133,7 +134,7 @@ static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch)
} else {
ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK;
if (ggms != 0) {
ggms = 1 << ggms;
ggms = 1ULL << ggms;
}
}
@ -422,83 +423,13 @@ static const MemoryRegionOps vfio_igd_index_quirk = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset,
unsigned size)
{
switch (size) {
case 1:
return pci_get_byte(vdev->pdev.config + offset);
case 2:
return pci_get_word(vdev->pdev.config + offset);
case 4:
return pci_get_long(vdev->pdev.config + offset);
case 8:
return pci_get_quad(vdev->pdev.config + offset);
default:
hw_error("igd: unsupported pci config read at %"PRIx64", size %u",
offset, size);
break;
}
return 0;
}
static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset,
uint64_t data, unsigned size)
{
switch (size) {
case 1:
pci_set_byte(vdev->pdev.config + offset, data);
break;
case 2:
pci_set_word(vdev->pdev.config + offset, data);
break;
case 4:
pci_set_long(vdev->pdev.config + offset, data);
break;
case 8:
pci_set_quad(vdev->pdev.config + offset, data);
break;
default:
hw_error("igd: unsupported pci config write at %"PRIx64", size %u",
offset, size);
break;
}
}
#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \
static uint64_t vfio_igd_quirk_read_##name(void *opaque, \
hwaddr addr, unsigned size) \
{ \
VFIOPCIDevice *vdev = opaque; \
\
return vfio_igd_pci_config_read(vdev, reg + addr, size); \
} \
\
static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \
uint64_t data, unsigned size) \
{ \
VFIOPCIDevice *vdev = opaque; \
\
vfio_igd_pci_config_write(vdev, reg + addr, data, size); \
} \
\
static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \
.read = vfio_igd_quirk_read_##name, \
.write = vfio_igd_quirk_write_##name, \
.endianness = DEVICE_LITTLE_ENDIAN, \
};
VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc)
VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm)
VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64)
#define IGD_GGC_MMIO_OFFSET 0x108040
#define IGD_BDSM_MMIO_OFFSET 0x1080C0
void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
{
VFIOQuirk *quirk;
VFIOQuirk *ggc_quirk, *bdsm_quirk;
VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror;
int gen;
/*
@ -522,33 +453,39 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
return;
}
quirk = vfio_quirk_alloc(2);
quirk->data = vdev;
ggc_quirk = vfio_quirk_alloc(1);
ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror));
ggc_mirror->mem = ggc_quirk->mem;
ggc_mirror->vdev = vdev;
ggc_mirror->bar = nr;
ggc_mirror->offset = IGD_GGC_MMIO_OFFSET;
ggc_mirror->config_offset = IGD_GMCH;
memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
&vfio_igd_quirk_mirror_ggc, vdev,
memory_region_init_io(ggc_mirror->mem, OBJECT(vdev),
&vfio_generic_mirror_quirk, ggc_mirror,
"vfio-igd-ggc-quirk", 2);
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
IGD_GGC_MMIO_OFFSET, &quirk->mem[0],
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
ggc_mirror->offset, ggc_mirror->mem,
1);
if (gen < 11) {
memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
&vfio_igd_quirk_mirror_bdsm, vdev,
"vfio-igd-bdsm-quirk", 4);
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
IGD_BDSM_MMIO_OFFSET,
&quirk->mem[1], 1);
} else {
memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
&vfio_igd_quirk_mirror_bdsm64, vdev,
"vfio-igd-bdsm-quirk", 8);
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
IGD_BDSM_MMIO_OFFSET,
&quirk->mem[1], 1);
}
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next);
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
bdsm_quirk = vfio_quirk_alloc(1);
bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror));
bdsm_mirror->mem = bdsm_quirk->mem;
bdsm_mirror->vdev = vdev;
bdsm_mirror->bar = nr;
bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET;
bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11;
memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev),
&vfio_generic_mirror_quirk, bdsm_mirror,
"vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8);
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
bdsm_mirror->offset, bdsm_mirror->mem,
1);
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next);
}
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)

View File

@ -515,8 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
} else {
ret = iommufd_cdev_ram_block_discard_disable(true);
if (ret) {
error_setg(errp,
"Cannot set discarding of RAM broken (%d)", ret);
error_setg_errno(errp, -ret,
"Cannot set discarding of RAM broken");
goto err_discard_disable;
}
goto found_container;
@ -544,6 +544,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
ret = iommufd_cdev_ram_block_discard_disable(true);
if (ret) {
error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
goto err_discard_disable;
}

View File

@ -25,6 +25,7 @@
#include "hw/nvram/fw_cfg.h"
#include "hw/qdev-properties.h"
#include "pci.h"
#include "pci-quirks.h"
#include "trace.h"
/*
@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev)
* Device specific region quirks (mostly backdoors to PCI config space)
*/
/*
* The generic window quirks operate on an address and data register,
* vfio_generic_window_address_quirk handles the address register and
* vfio_generic_window_data_quirk handles the data register. These ops
* pass reads and writes through to hardware until a value matching the
* stored address match/mask is written. When this occurs, the data
* register access emulated PCI config space for the device rather than
* passing through accesses. This enables devices where PCI config space
* is accessible behind a window register to maintain the virtualization
* provided through vfio.
*/
typedef struct VFIOConfigWindowMatch {
uint32_t match;
uint32_t mask;
} VFIOConfigWindowMatch;
typedef struct VFIOConfigWindowQuirk {
struct VFIOPCIDevice *vdev;
uint32_t address_val;
uint32_t address_offset;
uint32_t data_offset;
bool window_enabled;
uint8_t bar;
MemoryRegion *addr_mem;
MemoryRegion *data_mem;
uint32_t nr_matches;
VFIOConfigWindowMatch matches[];
} VFIOConfigWindowQuirk;
static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
hwaddr addr,
unsigned size)
@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
}
}
static const MemoryRegionOps vfio_generic_window_address_quirk = {
const MemoryRegionOps vfio_generic_window_address_quirk = {
.read = vfio_generic_window_quirk_address_read,
.write = vfio_generic_window_quirk_address_write,
.endianness = DEVICE_LITTLE_ENDIAN,
@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
addr + window->data_offset, data, size);
}
static const MemoryRegionOps vfio_generic_window_data_quirk = {
const MemoryRegionOps vfio_generic_window_data_quirk = {
.read = vfio_generic_window_quirk_data_read,
.write = vfio_generic_window_quirk_data_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
/*
* The generic mirror quirk handles devices which expose PCI config space
* through a region within a BAR. When enabled, reads and writes are
* redirected through to emulated PCI config space. XXX if PCI config space
* used memory regions, this could just be an alias.
*/
typedef struct VFIOConfigMirrorQuirk {
struct VFIOPCIDevice *vdev;
uint32_t offset;
uint8_t bar;
MemoryRegion *mem;
uint8_t data[];
} VFIOConfigMirrorQuirk;
static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
hwaddr addr, unsigned size)
{
@ -209,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
(void)vfio_region_read(&vdev->bars[mirror->bar].region,
addr + mirror->offset, size);
addr += mirror->config_offset;
data = vfio_pci_read_config(&vdev->pdev, addr, size);
trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name,
memory_region_name(mirror->mem),
@ -222,13 +176,14 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr,
VFIOConfigMirrorQuirk *mirror = opaque;
VFIOPCIDevice *vdev = mirror->vdev;
addr += mirror->config_offset;
vfio_pci_write_config(&vdev->pdev, addr, data, size);
trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name,
memory_region_name(mirror->mem),
addr, data);
}
static const MemoryRegionOps vfio_generic_mirror_quirk = {
const MemoryRegionOps vfio_generic_mirror_quirk = {
.read = vfio_generic_quirk_mirror_read,
.write = vfio_generic_quirk_mirror_write,
.endianness = DEVICE_LITTLE_ENDIAN,

72
hw/vfio/pci-quirks.h Normal file
View File

@ -0,0 +1,72 @@
/*
* vfio generic region quirks (mostly backdoors to PCI config space)
*
* Copyright Red Hat, Inc. 2012-2015
*
* Authors:
* Alex Williamson <alex.williamson@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H
#define HW_VFIO_VFIO_PCI_QUIRKS_H
#include "qemu/osdep.h"
#include "exec/memop.h"
/*
* The generic window quirks operate on an address and data register,
* vfio_generic_window_address_quirk handles the address register and
* vfio_generic_window_data_quirk handles the data register. These ops
* pass reads and writes through to hardware until a value matching the
* stored address match/mask is written. When this occurs, the data
* register access emulated PCI config space for the device rather than
* passing through accesses. This enables devices where PCI config space
* is accessible behind a window register to maintain the virtualization
* provided through vfio.
*/
typedef struct VFIOConfigWindowMatch {
uint32_t match;
uint32_t mask;
} VFIOConfigWindowMatch;
typedef struct VFIOConfigWindowQuirk {
struct VFIOPCIDevice *vdev;
uint32_t address_val;
uint32_t address_offset;
uint32_t data_offset;
bool window_enabled;
uint8_t bar;
MemoryRegion *addr_mem;
MemoryRegion *data_mem;
uint32_t nr_matches;
VFIOConfigWindowMatch matches[];
} VFIOConfigWindowQuirk;
extern const MemoryRegionOps vfio_generic_window_address_quirk;
extern const MemoryRegionOps vfio_generic_window_data_quirk;
/*
* The generic mirror quirk handles devices which expose PCI config space
* through a region within a BAR. When enabled, reads and writes are
* redirected through to emulated PCI config space. XXX if PCI config space
* used memory regions, this could just be an alias.
*/
typedef struct VFIOConfigMirrorQuirk {
struct VFIOPCIDevice *vdev;
uint32_t offset; /* Offset in BAR */
uint32_t config_offset; /* Offset in PCI config space */
uint8_t bar;
MemoryRegion *mem;
uint8_t data[];
} VFIOConfigMirrorQuirk;
extern const MemoryRegionOps vfio_generic_mirror_quirk;
#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */

View File

@ -3116,7 +3116,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
if (!vbasedev->mdev &&
!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) {
error_prepend(errp, "Failed to set iommu_device: ");
error_prepend(errp, "Failed to set vIOMMU: ");
goto out_teardown;
}

View File

@ -252,6 +252,7 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp);
bool vfio_attach_device(char *name, VFIODevice *vbasedev,
AddressSpace *as, Error **errp);
void vfio_detach_device(VFIODevice *vbasedev);
VFIODevice *vfio_get_vfio_device(Object *obj);
int vfio_kvm_device_add_fd(int fd, Error **errp);
int vfio_kvm_device_del_fd(int fd, Error **errp);

View File

@ -466,6 +466,18 @@ void warn_reportf_err(Error *err, const char *fmt, ...)
void error_reportf_err(Error *err, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
/*
* Similar to warn_report_err(), except it prints the message just once.
* Return true when it prints, false otherwise.
*/
bool warn_report_err_once_cond(bool *printed, Error *err);
#define warn_report_err_once(err) \
({ \
static bool print_once_; \
warn_report_err_once_cond(&print_once_, err); \
})
/*
* Just like error_setg(), except you get to specify the error class.
* Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is

View File

@ -247,6 +247,17 @@ void warn_report_err(Error *err)
error_free(err);
}
bool warn_report_err_once_cond(bool *printed, Error *err)
{
if (*printed) {
error_free(err);
return false;
}
*printed = true;
warn_report_err(err);
return true;
}
void error_reportf_err(Error *err, const char *fmt, ...)
{
va_list ap;