diff mbox

[v8,12/17] pci: Convert msi_init() to Error and fix callers to check it

Message ID 1465552478-5540-13-git-send-email-caoj.fnst@cn.fujitsu.com
State New
Headers show

Commit Message

Cao jin June 10, 2016, 9:54 a.m. UTC
msi_init() reports errors with error_report(), which is wrong
when it's used in realize().

Fix by converting it to Error.

Fix its callers to handle failure instead of ignoring it.

For those callers who don't handle the failure, it might happen:
when user want msi on, but he doesn't get what he want because of
msi_init fails silently.

cc: Gerd Hoffmann <kraxel@redhat.com>
cc: John Snow <jsnow@redhat.com>
cc: Dmitry Fleytman <dmitry@daynix.com>
cc: Jason Wang <jasowang@redhat.com>
cc: Michael S. Tsirkin <mst@redhat.com>
cc: Hannes Reinecke <hare@suse.de>
cc: Paolo Bonzini <pbonzini@redhat.com>
cc: Alex Williamson <alex.williamson@redhat.com>
cc: Markus Armbruster <armbru@redhat.com>
cc: Marcel Apfelbaum <marcel@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Cao jin <caoj.fnst@cn.fujitsu.com>
---
 hw/audio/intel-hda.c               | 24 ++++++++++++++++++++----
 hw/ide/ich.c                       | 15 +++++++++------
 hw/net/e1000e.c                    |  8 ++------
 hw/net/vmxnet3.c                   | 37 ++++++++++++-------------------------
 hw/pci-bridge/ioh3420.c            |  6 +++++-
 hw/pci-bridge/pci_bridge_dev.c     | 20 ++++++++++++++++----
 hw/pci-bridge/xio3130_downstream.c |  6 +++++-
 hw/pci-bridge/xio3130_upstream.c   |  6 +++++-
 hw/pci/msi.c                       | 11 ++++++++---
 hw/scsi/megasas.c                  | 26 +++++++++++++++++++++-----
 hw/scsi/mptsas.c                   | 31 ++++++++++++++++++++++++-------
 hw/scsi/vmw_pvscsi.c               |  2 +-
 hw/usb/hcd-xhci.c                  | 23 +++++++++++++++++++----
 hw/vfio/pci.c                      |  7 +++++--
 include/hw/pci/msi.h               |  3 ++-
 15 files changed, 154 insertions(+), 71 deletions(-)

Comments

Marcel Apfelbaum June 13, 2016, 10:16 a.m. UTC | #1
On 06/10/2016 12:54 PM, Cao jin wrote:
> msi_init() reports errors with error_report(), which is wrong
> when it's used in realize().
>
> Fix by converting it to Error.
>
> Fix its callers to handle failure instead of ignoring it.
>
> For those callers who don't handle the failure, it might happen:
> when user want msi on, but he doesn't get what he want because of
> msi_init fails silently.
>
> cc: Gerd Hoffmann <kraxel@redhat.com>
> cc: John Snow <jsnow@redhat.com>
> cc: Dmitry Fleytman <dmitry@daynix.com>
> cc: Jason Wang <jasowang@redhat.com>
> cc: Michael S. Tsirkin <mst@redhat.com>
> cc: Hannes Reinecke <hare@suse.de>
> cc: Paolo Bonzini <pbonzini@redhat.com>
> cc: Alex Williamson <alex.williamson@redhat.com>
> cc: Markus Armbruster <armbru@redhat.com>
> cc: Marcel Apfelbaum <marcel@redhat.com>
>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Cao jin <caoj.fnst@cn.fujitsu.com>
> ---
>   hw/audio/intel-hda.c               | 24 ++++++++++++++++++++----
>   hw/ide/ich.c                       | 15 +++++++++------
>   hw/net/e1000e.c                    |  8 ++------
>   hw/net/vmxnet3.c                   | 37 ++++++++++++-------------------------
>   hw/pci-bridge/ioh3420.c            |  6 +++++-
>   hw/pci-bridge/pci_bridge_dev.c     | 20 ++++++++++++++++----
>   hw/pci-bridge/xio3130_downstream.c |  6 +++++-
>   hw/pci-bridge/xio3130_upstream.c   |  6 +++++-
>   hw/pci/msi.c                       | 11 ++++++++---
>   hw/scsi/megasas.c                  | 26 +++++++++++++++++++++-----
>   hw/scsi/mptsas.c                   | 31 ++++++++++++++++++++++++-------
>   hw/scsi/vmw_pvscsi.c               |  2 +-
>   hw/usb/hcd-xhci.c                  | 23 +++++++++++++++++++----
>   hw/vfio/pci.c                      |  7 +++++--
>   include/hw/pci/msi.h               |  3 ++-
>   15 files changed, 154 insertions(+), 71 deletions(-)
>
> diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
> index 6b4dda0..82101f8 100644
> --- a/hw/audio/intel-hda.c
> +++ b/hw/audio/intel-hda.c
> @@ -1135,6 +1135,8 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>   {
>       IntelHDAState *d = INTEL_HDA(pci);
>       uint8_t *conf = d->pci.config;
> +    Error *err = NULL;
> +    int ret;
>
>       d->name = object_get_typename(OBJECT(d));
>
> @@ -1143,13 +1145,27 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>       /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
>       conf[0x40] = 0x01;
>
> +    if (d->msi != ON_OFF_AUTO_OFF) {
> +        ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60,
> +                       1, true, false, &err);
> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
> +         * is a programming error */
> +        assert(!ret || ret == -ENOTSUP);
> +        if (ret && d->msi == ON_OFF_AUTO_ON) {
> +            /* Can't satisfy user's explicit msi=on request, fail */
> +            error_append_hint(&err, "You have to use msi=auto (default) or "
> +                    "msi=off with this machine type.\n");
> +            error_propagate(errp, err);
> +            return;
> +        }
> +        assert(!err || d->msi == ON_OFF_AUTO_AUTO);
> +        /* With msi=auto, we fall back to MSI off silently */
> +        error_free(err);
> +    }
> +
>       memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d,
>                             "intel-hda", 0x4000);
>       pci_register_bar(&d->pci, 0, 0, &d->mmio);
> -    if (d->msi != ON_OFF_AUTO_OFF) {
> -         /* TODO check for errors */
> -        msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false);
> -    }
>
>       hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs),
>                          intel_hda_response, intel_hda_xfer);
> diff --git a/hw/ide/ich.c b/hw/ide/ich.c
> index 0a13334..084bef8 100644
> --- a/hw/ide/ich.c
> +++ b/hw/ide/ich.c
> @@ -68,7 +68,6 @@
>   #include <hw/isa/isa.h>
>   #include "sysemu/block-backend.h"
>   #include "sysemu/dma.h"
> -
>   #include <hw/ide/pci.h>
>   #include <hw/ide/ahci.h>
>
> @@ -111,6 +110,15 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>       int sata_cap_offset;
>       uint8_t *sata_cap;
>       d = ICH_AHCI(dev);
> +    int ret;
> +
> +    /* Although the AHCI 1.3 specification states that the first capability
> +     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
> +     * AHCI device puts the MSI capability first, pointing to 0x80. */
> +    ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL);
> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
> +     * is a programming error.  Fall back to INTx silently on -ENOTSUP */
> +    assert(!ret || ret == -ENOTSUP);
>
>       ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
>
> @@ -142,11 +150,6 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>       pci_set_long(sata_cap + SATA_CAP_BAR,
>                    (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
>       d->ahci.idp_offset = ICH9_IDP_INDEX;
> -
> -    /* Although the AHCI 1.3 specification states that the first capability
> -     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
> -     * AHCI device puts the MSI capability first, pointing to 0x80. */
> -    msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false);
>   }
>
>   static void pci_ich9_uninit(PCIDevice *dev)
> diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
> index 692283f..a06d184 100644
> --- a/hw/net/e1000e.c
> +++ b/hw/net/e1000e.c
> @@ -268,13 +268,9 @@ e1000e_init_msi(E1000EState *s)
>   {
>       int res;
>
> -    res = msi_init(PCI_DEVICE(s),
> -                   0xD0,   /* MSI capability offset              */
> -                   1,      /* MAC MSI interrupts                 */
> -                   true,   /* 64-bit message addresses supported */
> -                   false); /* Per vector mask supported          */
> +    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
>

Why did you chose to remove author's comments?


> -    if (res > 0) {
> +    if (!res) {
>           s->intr_state |= E1000E_USE_MSI;
>       } else {
>           trace_e1000e_msi_init_fail(res);
> diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
> index d978976..63f8904 100644
> --- a/hw/net/vmxnet3.c
> +++ b/hw/net/vmxnet3.c
> @@ -2197,27 +2197,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
>       }
>   }
>
> -#define VMXNET3_USE_64BIT         (true)
> -#define VMXNET3_PER_VECTOR_MASK   (false)
> -
> -static bool
> -vmxnet3_init_msi(VMXNET3State *s)
> -{
> -    PCIDevice *d = PCI_DEVICE(s);
> -    int res;
> -
> -    res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
> -                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
> -    if (0 > res) {
> -        VMW_WRPRN("Failed to initialize MSI, error %d", res);
> -        s->msi_used = false;
> -    } else {
> -        s->msi_used = true;
> -    }
> -
> -    return s->msi_used;
> -}
> -
>   static void
>   vmxnet3_cleanup_msi(VMXNET3State *s)
>   {
> @@ -2279,10 +2258,15 @@ static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
>       return dsn_payload;
>   }
>
> +
> +#define VMXNET3_USE_64BIT         (true)
> +#define VMXNET3_PER_VECTOR_MASK   (false)
> +
>   static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>   {
>       DeviceState *dev = DEVICE(pci_dev);
>       VMXNET3State *s = VMXNET3(pci_dev);
> +    int ret;
>
>       VMW_CBPRN("Starting init...");
>
> @@ -2306,14 +2290,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>       /* Interrupt pin A */
>       pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
>
> +    ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
> +                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL);
> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
> +     * is a programming error. Fall back to INTx silently on -ENOTSUP */
> +    assert(!ret || ret == -ENOTSUP);
> +    s->msi_used = !ret;
> +
>       if (!vmxnet3_init_msix(s)) {
>           VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
>       }
>
> -    if (!vmxnet3_init_msi(s)) {
> -        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
> -    }
> -
>       vmxnet3_net_init(s);
>
>       if (pci_is_express(pci_dev)) {
> diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
> index b4a7806..93c6f0b 100644
> --- a/hw/pci-bridge/ioh3420.c
> +++ b/hw/pci-bridge/ioh3420.c
> @@ -25,6 +25,7 @@
>   #include "hw/pci/msi.h"
>   #include "hw/pci/pcie.h"
>   #include "ioh3420.h"
> +#include "qapi/error.h"
>
>   #define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
>   #define PCI_DEVICE_ID_IOH_REV           0x2
> @@ -97,6 +98,7 @@ static int ioh3420_initfn(PCIDevice *d)
>       PCIEPort *p = PCIE_PORT(d);
>       PCIESlot *s = PCIE_SLOT(d);
>       int rc;
> +    Error *err = NULL;
>
>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>       pcie_port_init_reg(d);
> @@ -109,8 +111,10 @@ static int ioh3420_initfn(PCIDevice *d)
>
>       rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
>                     IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
> -                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
> +                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>       if (rc < 0) {
> +        assert(rc == -ENOTSUP);
> +        error_report_err(err);
>           goto err_bridge;
>       }
>
> diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
> index 0fbecc4..c4d2c0b 100644
> --- a/hw/pci-bridge/pci_bridge_dev.c
> +++ b/hw/pci-bridge/pci_bridge_dev.c
> @@ -54,6 +54,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>       PCIBridge *br = PCI_BRIDGE(dev);
>       PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
>       int err;
> +    Error *local_err = NULL;
>
>       pci_bridge_initfn(dev, TYPE_PCI_BUS);
>
> @@ -75,12 +76,23 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>           goto slotid_error;
>       }
>
> -    if (bridge_dev->msi != ON_OFF_AUTO_OFF &&
> -        msi_nonbroken) {
> -        err = msi_init(dev, 0, 1, true, true);
> -        if (err < 0) {
> +    if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
> +        /* it means SHPC exists, because SHPC is required for MSI */

Is the other way around, MSI is needed by SHPC (but not compulsory)

> +
> +        err = msi_init(dev, 0, 1, true, true, &local_err);
> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
> +         * is a programming error */
> +        assert(!err || err == -ENOTSUP);
> +        if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
> +            /* Can't satisfy user's explicit msi=on request, fail */
> +            error_append_hint(&local_err, "You have to use msi=auto (default) "
> +                    "or msi=off with this machine type.\n");
> +            error_report_err(local_err);
>               goto msi_error;
>           }
> +        assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
> +        /* With msi=auto, we fall back to MSI off silently */
> +        error_free(local_err);
>       }
>
>       if (shpc_present(dev)) {
> diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
> index e6d653d..f6149a3 100644
> --- a/hw/pci-bridge/xio3130_downstream.c
> +++ b/hw/pci-bridge/xio3130_downstream.c
> @@ -24,6 +24,7 @@
>   #include "hw/pci/msi.h"
>   #include "hw/pci/pcie.h"
>   #include "xio3130_downstream.h"
> +#include "qapi/error.h"
>
>   #define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
>   #define XIO3130_REVISION                0x1
> @@ -60,14 +61,17 @@ static int xio3130_downstream_initfn(PCIDevice *d)
>       PCIEPort *p = PCIE_PORT(d);
>       PCIESlot *s = PCIE_SLOT(d);
>       int rc;
> +    Error *err = NULL;
>
>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>       pcie_port_init_reg(d);
>
>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>       if (rc < 0) {
> +        assert(rc == -ENOTSUP);
> +        error_report_err(err);
>           goto err_bridge;
>       }
>
> diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
> index d976844..487edac 100644
> --- a/hw/pci-bridge/xio3130_upstream.c
> +++ b/hw/pci-bridge/xio3130_upstream.c
> @@ -24,6 +24,7 @@
>   #include "hw/pci/msi.h"
>   #include "hw/pci/pcie.h"
>   #include "xio3130_upstream.h"
> +#include "qapi/error.h"
>
>   #define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
>   #define XIO3130_REVISION                0x2
> @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d)
>   {
>       PCIEPort *p = PCIE_PORT(d);
>       int rc;
> +    Error *err = NULL;
>
>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>       pcie_port_init_reg(d);
>
>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>       if (rc < 0) {
> +        assert(rc == -ENOTSUP);
> +        error_report_err(err);

Hi Jin, Markus

It looks a little weird to me to check for negative error code
and then assert for a specific error as the only "valid error".
Maybe we should assert inside msi_init to leave the callers "clean"?

I am well aware a lot of time was spent for this series, but I just
spotted it and I want to be sure we are doing it right.

Thanks,
Marcel

>           goto err_bridge;
>       }
>
> diff --git a/hw/pci/msi.c b/hw/pci/msi.c
> index ed79225..a87b227 100644
> --- a/hw/pci/msi.c
> +++ b/hw/pci/msi.c
> @@ -22,6 +22,7 @@
>   #include "hw/pci/msi.h"
>   #include "hw/xen/xen.h"
>   #include "qemu/range.h"
> +#include "qapi/error.h"
>
>   /* PCI_MSI_ADDRESS_LO */
>   #define PCI_MSI_ADDRESS_LO_MASK         (~0x3)
> @@ -173,7 +174,8 @@ bool msi_enabled(const PCIDevice *dev)
>    * If @msi64bit, make the device capable of sending a 64-bit message
>    * address.
>    * If @msi_per_vector_mask, make the device support per-vector masking.
> - * Return 0 on success, return -errno on error.
> + * @errp is for returning errors.
> + * Return 0 on success; set @errp and return -errno on error.
>    *
>    * -ENOTSUP means lacking msi support for a msi-capable platform.
>    * -EINVAL means capability overlap, happens when @offset is non-zero,
> @@ -181,7 +183,8 @@ bool msi_enabled(const PCIDevice *dev)
>    *  if a real HW is broken.
>    */
>   int msi_init(struct PCIDevice *dev, uint8_t offset,
> -             unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
> +             unsigned int nr_vectors, bool msi64bit,
> +             bool msi_per_vector_mask, Error **errp)
>   {
>       unsigned int vectors_order;
>       uint16_t flags;
> @@ -189,6 +192,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
>       int config_offset;
>
>       if (!msi_nonbroken) {
> +        error_setg(errp, "MSI is not supported by interrupt controller");
>           return -ENOTSUP;
>       }
>
> @@ -212,7 +216,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
>       }
>
>       cap_size = msi_cap_sizeof(flags);
> -    config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
> +    config_offset = pci_add_capability2(dev, PCI_CAP_ID_MSI, offset,
> +                                        cap_size, errp);
>       if (config_offset < 0) {
>           return config_offset;
>       }
> diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
> index 2ede192..345783d 100644
> --- a/hw/scsi/megasas.c
> +++ b/hw/scsi/megasas.c
> @@ -29,7 +29,7 @@
>   #include "hw/scsi/scsi.h"
>   #include "block/scsi.h"
>   #include "trace.h"
> -
> +#include "qapi/error.h"
>   #include "mfi.h"
>
>   #define MEGASAS_VERSION_GEN1 "1.70"
> @@ -2333,6 +2333,8 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
>       MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s);
>       uint8_t *pci_conf;
>       int i, bar_type;
> +    Error *err = NULL;
> +    int ret;
>
>       pci_conf = dev->config;
>
> @@ -2341,6 +2343,24 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
>       /* Interrupt pin 1 */
>       pci_conf[PCI_INTERRUPT_PIN] = 0x01;
>
> +    if (megasas_use_msi(s)) {
> +        ret = msi_init(dev, 0x50, 1, true, false, &err);
> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
> +         * is a programming error */
> +        assert(!ret || ret == -ENOTSUP);
> +        if (ret && s->msi == ON_OFF_AUTO_ON) {
> +            /* Can't satisfy user's explicit msi=on request, fail */
> +            error_append_hint(&err, "You have to use msi=auto (default) or "
> +                    "msi=off with this machine type.\n");
> +            error_propagate(errp, err);
> +            return;
> +        } else if (ret) {
> +            /* With msi=auto, we fall back to MSI off silently */
> +            s->msi = ON_OFF_AUTO_OFF;
> +            error_free(err);
> +        }
> +    }
> +
>       memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s,
>                             "megasas-mmio", 0x4000);
>       memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s,
> @@ -2348,10 +2368,6 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
>       memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s,
>                             "megasas-queue", 0x40000);
>
> -    if (megasas_use_msi(s) &&
> -        msi_init(dev, 0x50, 1, true, false) < 0) {
> -        s->msi = ON_OFF_AUTO_OFF;
> -    }
>       if (megasas_use_msix(s) &&
>           msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000,
>                     &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) {
> diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
> index dfbc0c4..698be42 100644
> --- a/hw/scsi/mptsas.c
> +++ b/hw/scsi/mptsas.c
> @@ -32,7 +32,7 @@
>   #include "hw/scsi/scsi.h"
>   #include "block/scsi.h"
>   #include "trace.h"
> -
> +#include "qapi/error.h"
>   #include "mptsas.h"
>   #include "mpi.h"
>
> @@ -1273,10 +1273,33 @@ static void mptsas_scsi_realize(PCIDevice *dev, Error **errp)
>   {
>       DeviceState *d = DEVICE(dev);
>       MPTSASState *s = MPT_SAS(dev);
> +    Error *err = NULL;
> +    int ret;
>
>       dev->config[PCI_LATENCY_TIMER] = 0;
>       dev->config[PCI_INTERRUPT_PIN] = 0x01;
>
> +    if (s->msi != ON_OFF_AUTO_OFF) {
> +        ret = msi_init(dev, 0, 1, true, false, &err);
> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
> +         * is a programming error */
> +        assert(!ret || ret == -ENOTSUP);
> +        if (ret && s->msi == ON_OFF_AUTO_ON) {
> +            /* Can't satisfy user's explicit msi=on request, fail */
> +            error_append_hint(&err, "You have to use msi=auto (default) or "
> +                    "msi=off with this machine type.\n");
> +            error_propagate(errp, err);
> +            s->msi_in_use = false;
> +            return;
> +        } else if (ret) {
> +            /* With msi=auto, we fall back to MSI off silently */
> +            error_free(err);
> +            s->msi_in_use = false;
> +        } else {
> +            s->msi_in_use = true;
> +        }
> +    }
> +
>       memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s,
>                             "mptsas-mmio", 0x4000);
>       memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s,
> @@ -1284,12 +1307,6 @@ static void mptsas_scsi_realize(PCIDevice *dev, Error **errp)
>       memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s,
>                             "mptsas-diag", 0x10000);
>
> -    if (s->msi != ON_OFF_AUTO_OFF &&
> -        msi_init(dev, 0, 1, true, false) >= 0) {
> -        /* TODO check for errors */
> -        s->msi_in_use = true;
> -    }
> -
>       pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
>       pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY |
>                                    PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io);
> diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
> index e035fce..ecd6077 100644
> --- a/hw/scsi/vmw_pvscsi.c
> +++ b/hw/scsi/vmw_pvscsi.c
> @@ -1063,7 +1063,7 @@ pvscsi_init_msi(PVSCSIState *s)
>       PCIDevice *d = PCI_DEVICE(s);
>
>       res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
> -                   PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
> +                   PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL);
>       if (res < 0) {
>           trace_pvscsi_init_msi_fail(res);
>           s->msi_used = false;
> diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
> index 0a5510f..1a3377f 100644
> --- a/hw/usb/hcd-xhci.c
> +++ b/hw/usb/hcd-xhci.c
> @@ -26,6 +26,7 @@
>   #include "hw/pci/msi.h"
>   #include "hw/pci/msix.h"
>   #include "trace.h"
> +#include "qapi/error.h"
>
>   //#define DEBUG_XHCI
>   //#define DEBUG_DATA
> @@ -3581,6 +3582,7 @@ static void usb_xhci_init(XHCIState *xhci)
>   static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
>   {
>       int i, ret;
> +    Error *err = NULL;
>
>       XHCIState *xhci = XHCI(dev);
>
> @@ -3591,6 +3593,23 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
>
>       usb_xhci_init(xhci);
>
> +    if (xhci->msi != ON_OFF_AUTO_OFF) {
> +        ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
> +         * is a programming error */
> +        assert(!ret || ret == -ENOTSUP);
> +        if (ret && xhci->msi == ON_OFF_AUTO_ON) {
> +            /* Can't satisfy user's explicit msi=on request, fail */
> +            error_append_hint(&err, "You have to use msi=auto (default) or "
> +                    "msi=off with this machine type.\n");
> +            error_propagate(errp, err);
> +            return;
> +        }
> +        assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
> +        /* With msi=auto, we fall back to MSI off silently */
> +        error_free(err);
> +    }
> +
>       if (xhci->numintrs > MAXINTRS) {
>           xhci->numintrs = MAXINTRS;
>       }
> @@ -3648,10 +3667,6 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
>           assert(ret >= 0);
>       }
>
> -    if (xhci->msi != ON_OFF_AUTO_OFF) {
> -        /* TODO check for errors */
> -        msi_init(dev, 0x70, xhci->numintrs, true, false);
> -    }
>       if (xhci->msix != ON_OFF_AUTO_OFF) {
>           /* TODO check for errors */
>           msix_init(dev, xhci->numintrs,
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index deab0c6..dfbf8ba 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -32,6 +32,7 @@
>   #include "sysemu/sysemu.h"
>   #include "pci.h"
>   #include "trace.h"
> +#include "qapi/error.h"
>
>   #define MSIX_CAP_LENGTH 12
>
> @@ -1171,6 +1172,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
>       uint16_t ctrl;
>       bool msi_64bit, msi_maskbit;
>       int ret, entries;
> +    Error *err = NULL;
>
>       if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl),
>                 vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
> @@ -1184,12 +1186,13 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
>
>       trace_vfio_msi_setup(vdev->vbasedev.name, pos);
>
> -    ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
> +    ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err);
>       if (ret < 0) {
>           if (ret == -ENOTSUP) {
>               return 0;
>           }
> -        error_report("vfio: msi_init failed");
> +        error_prepend(&err, "vfio: msi_init failed: ");
> +        error_report_err(err);
>           return ret;
>       }
>       vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
> diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h
> index 8124908..4837bcf 100644
> --- a/include/hw/pci/msi.h
> +++ b/include/hw/pci/msi.h
> @@ -35,7 +35,8 @@ void msi_set_message(PCIDevice *dev, MSIMessage msg);
>   MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector);
>   bool msi_enabled(const PCIDevice *dev);
>   int msi_init(struct PCIDevice *dev, uint8_t offset,
> -             unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
> +             unsigned int nr_vectors, bool msi64bit,
> +             bool msi_per_vector_mask, Error **errp);
>   void msi_uninit(struct PCIDevice *dev);
>   void msi_reset(PCIDevice *dev);
>   void msi_notify(PCIDevice *dev, unsigned int vector);
>
Markus Armbruster June 13, 2016, 11:07 a.m. UTC | #2
Marcel Apfelbaum <marcel@redhat.com> writes:

> On 06/10/2016 12:54 PM, Cao jin wrote:
>> msi_init() reports errors with error_report(), which is wrong
>> when it's used in realize().
>>
>> Fix by converting it to Error.
>>
>> Fix its callers to handle failure instead of ignoring it.
>>
>> For those callers who don't handle the failure, it might happen:
>> when user want msi on, but he doesn't get what he want because of
>> msi_init fails silently.
>>
>> cc: Gerd Hoffmann <kraxel@redhat.com>
>> cc: John Snow <jsnow@redhat.com>
>> cc: Dmitry Fleytman <dmitry@daynix.com>
>> cc: Jason Wang <jasowang@redhat.com>
>> cc: Michael S. Tsirkin <mst@redhat.com>
>> cc: Hannes Reinecke <hare@suse.de>
>> cc: Paolo Bonzini <pbonzini@redhat.com>
>> cc: Alex Williamson <alex.williamson@redhat.com>
>> cc: Markus Armbruster <armbru@redhat.com>
>> cc: Marcel Apfelbaum <marcel@redhat.com>
>>
>> Reviewed-by: Markus Armbruster <armbru@redhat.com>
>> Signed-off-by: Cao jin <caoj.fnst@cn.fujitsu.com>
>> ---
>>   hw/audio/intel-hda.c               | 24 ++++++++++++++++++++----
>>   hw/ide/ich.c                       | 15 +++++++++------
>>   hw/net/e1000e.c                    |  8 ++------
>>   hw/net/vmxnet3.c                   | 37 ++++++++++++-------------------------
>>   hw/pci-bridge/ioh3420.c            |  6 +++++-
>>   hw/pci-bridge/pci_bridge_dev.c     | 20 ++++++++++++++++----
>>   hw/pci-bridge/xio3130_downstream.c |  6 +++++-
>>   hw/pci-bridge/xio3130_upstream.c   |  6 +++++-
>>   hw/pci/msi.c                       | 11 ++++++++---
>>   hw/scsi/megasas.c                  | 26 +++++++++++++++++++++-----
>>   hw/scsi/mptsas.c                   | 31 ++++++++++++++++++++++++-------
>>   hw/scsi/vmw_pvscsi.c               |  2 +-
>>   hw/usb/hcd-xhci.c                  | 23 +++++++++++++++++++----
>>   hw/vfio/pci.c                      |  7 +++++--
>>   include/hw/pci/msi.h               |  3 ++-
>>   15 files changed, 154 insertions(+), 71 deletions(-)
>>
>> diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
>> index 6b4dda0..82101f8 100644
>> --- a/hw/audio/intel-hda.c
>> +++ b/hw/audio/intel-hda.c
>> @@ -1135,6 +1135,8 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>>   {
>>       IntelHDAState *d = INTEL_HDA(pci);
>>       uint8_t *conf = d->pci.config;
>> +    Error *err = NULL;
>> +    int ret;
>>
>>       d->name = object_get_typename(OBJECT(d));
>>
>> @@ -1143,13 +1145,27 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>>       /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
>>       conf[0x40] = 0x01;
>>
>> +    if (d->msi != ON_OFF_AUTO_OFF) {
>> +        ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60,
>> +                       1, true, false, &err);
>> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
>> +         * is a programming error */
>> +        assert(!ret || ret == -ENOTSUP);
>> +        if (ret && d->msi == ON_OFF_AUTO_ON) {
>> +            /* Can't satisfy user's explicit msi=on request, fail */
>> +            error_append_hint(&err, "You have to use msi=auto (default) or "
>> +                    "msi=off with this machine type.\n");
>> +            error_propagate(errp, err);
>> +            return;
>> +        }
>> +        assert(!err || d->msi == ON_OFF_AUTO_AUTO);
>> +        /* With msi=auto, we fall back to MSI off silently */
>> +        error_free(err);
>> +    }
>> +
>>       memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d,
>>                             "intel-hda", 0x4000);
>>       pci_register_bar(&d->pci, 0, 0, &d->mmio);
>> -    if (d->msi != ON_OFF_AUTO_OFF) {
>> -         /* TODO check for errors */
>> -        msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false);
>> -    }
>>
>>       hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs),
>>                          intel_hda_response, intel_hda_xfer);
>> diff --git a/hw/ide/ich.c b/hw/ide/ich.c
>> index 0a13334..084bef8 100644
>> --- a/hw/ide/ich.c
>> +++ b/hw/ide/ich.c
>> @@ -68,7 +68,6 @@
>>   #include <hw/isa/isa.h>
>>   #include "sysemu/block-backend.h"
>>   #include "sysemu/dma.h"
>> -
>>   #include <hw/ide/pci.h>
>>   #include <hw/ide/ahci.h>
>>
>> @@ -111,6 +110,15 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>>       int sata_cap_offset;
>>       uint8_t *sata_cap;
>>       d = ICH_AHCI(dev);
>> +    int ret;
>> +
>> +    /* Although the AHCI 1.3 specification states that the first capability
>> +     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
>> +     * AHCI device puts the MSI capability first, pointing to 0x80. */
>> +    ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL);
>> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
>> +     * is a programming error.  Fall back to INTx silently on -ENOTSUP */
>> +    assert(!ret || ret == -ENOTSUP);
>>
>>       ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
>>
>> @@ -142,11 +150,6 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>>       pci_set_long(sata_cap + SATA_CAP_BAR,
>>                    (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
>>       d->ahci.idp_offset = ICH9_IDP_INDEX;
>> -
>> -    /* Although the AHCI 1.3 specification states that the first capability
>> -     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
>> -     * AHCI device puts the MSI capability first, pointing to 0x80. */
>> -    msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false);
>>   }
>>
>>   static void pci_ich9_uninit(PCIDevice *dev)
>> diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
>> index 692283f..a06d184 100644
>> --- a/hw/net/e1000e.c
>> +++ b/hw/net/e1000e.c
>> @@ -268,13 +268,9 @@ e1000e_init_msi(E1000EState *s)
>>   {
>>       int res;
>>
>> -    res = msi_init(PCI_DEVICE(s),
>> -                   0xD0,   /* MSI capability offset              */
>> -                   1,      /* MAC MSI interrupts                 */
>> -                   true,   /* 64-bit message addresses supported */
>> -                   false); /* Per vector mask supported          */
>> +    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
>>
>
> Why did you chose to remove author's comments?
>
>
>> -    if (res > 0) {
>> +    if (!res) {
>>           s->intr_state |= E1000E_USE_MSI;
>>       } else {
>>           trace_e1000e_msi_init_fail(res);
>> diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
>> index d978976..63f8904 100644
>> --- a/hw/net/vmxnet3.c
>> +++ b/hw/net/vmxnet3.c
>> @@ -2197,27 +2197,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
>>       }
>>   }
>>
>> -#define VMXNET3_USE_64BIT         (true)
>> -#define VMXNET3_PER_VECTOR_MASK   (false)
>> -
>> -static bool
>> -vmxnet3_init_msi(VMXNET3State *s)
>> -{
>> -    PCIDevice *d = PCI_DEVICE(s);
>> -    int res;
>> -
>> -    res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
>> -                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
>> -    if (0 > res) {
>> -        VMW_WRPRN("Failed to initialize MSI, error %d", res);
>> -        s->msi_used = false;
>> -    } else {
>> -        s->msi_used = true;
>> -    }
>> -
>> -    return s->msi_used;
>> -}
>> -
>>   static void
>>   vmxnet3_cleanup_msi(VMXNET3State *s)
>>   {
>> @@ -2279,10 +2258,15 @@ static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
>>       return dsn_payload;
>>   }
>>
>> +
>> +#define VMXNET3_USE_64BIT         (true)
>> +#define VMXNET3_PER_VECTOR_MASK   (false)
>> +
>>   static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>>   {
>>       DeviceState *dev = DEVICE(pci_dev);
>>       VMXNET3State *s = VMXNET3(pci_dev);
>> +    int ret;
>>
>>       VMW_CBPRN("Starting init...");
>>
>> @@ -2306,14 +2290,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>>       /* Interrupt pin A */
>>       pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
>>
>> +    ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
>> +                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL);
>> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
>> +     * is a programming error. Fall back to INTx silently on -ENOTSUP */
>> +    assert(!ret || ret == -ENOTSUP);
>> +    s->msi_used = !ret;
>> +
>>       if (!vmxnet3_init_msix(s)) {
>>           VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
>>       }
>>
>> -    if (!vmxnet3_init_msi(s)) {
>> -        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
>> -    }
>> -
>>       vmxnet3_net_init(s);
>>
>>       if (pci_is_express(pci_dev)) {
>> diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
>> index b4a7806..93c6f0b 100644
>> --- a/hw/pci-bridge/ioh3420.c
>> +++ b/hw/pci-bridge/ioh3420.c
>> @@ -25,6 +25,7 @@
>>   #include "hw/pci/msi.h"
>>   #include "hw/pci/pcie.h"
>>   #include "ioh3420.h"
>> +#include "qapi/error.h"
>>
>>   #define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
>>   #define PCI_DEVICE_ID_IOH_REV           0x2
>> @@ -97,6 +98,7 @@ static int ioh3420_initfn(PCIDevice *d)
>>       PCIEPort *p = PCIE_PORT(d);
>>       PCIESlot *s = PCIE_SLOT(d);
>>       int rc;
>> +    Error *err = NULL;
>>
>>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>       pcie_port_init_reg(d);
>> @@ -109,8 +111,10 @@ static int ioh3420_initfn(PCIDevice *d)
>>
>>       rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
>>                     IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>> -                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>> +                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>       if (rc < 0) {
>> +        assert(rc == -ENOTSUP);
>> +        error_report_err(err);
>>           goto err_bridge;
>>       }
>>
>> diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
>> index 0fbecc4..c4d2c0b 100644
>> --- a/hw/pci-bridge/pci_bridge_dev.c
>> +++ b/hw/pci-bridge/pci_bridge_dev.c
>> @@ -54,6 +54,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>>       PCIBridge *br = PCI_BRIDGE(dev);
>>       PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
>>       int err;
>> +    Error *local_err = NULL;
>>
>>       pci_bridge_initfn(dev, TYPE_PCI_BUS);
>>
>> @@ -75,12 +76,23 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>>           goto slotid_error;
>>       }
>>
>> -    if (bridge_dev->msi != ON_OFF_AUTO_OFF &&
>> -        msi_nonbroken) {
>> -        err = msi_init(dev, 0, 1, true, true);
>> -        if (err < 0) {
>> +    if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
>> +        /* it means SHPC exists, because SHPC is required for MSI */
>
> Is the other way around, MSI is needed by SHPC (but not compulsory)
>
>> +
>> +        err = msi_init(dev, 0, 1, true, true, &local_err);
>> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
>> +         * is a programming error */
>> +        assert(!err || err == -ENOTSUP);
>> +        if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
>> +            /* Can't satisfy user's explicit msi=on request, fail */
>> +            error_append_hint(&local_err, "You have to use msi=auto (default) "
>> +                    "or msi=off with this machine type.\n");
>> +            error_report_err(local_err);
>>               goto msi_error;
>>           }
>> +        assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
>> +        /* With msi=auto, we fall back to MSI off silently */
>> +        error_free(local_err);
>>       }
>>
>>       if (shpc_present(dev)) {
>> diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
>> index e6d653d..f6149a3 100644
>> --- a/hw/pci-bridge/xio3130_downstream.c
>> +++ b/hw/pci-bridge/xio3130_downstream.c
>> @@ -24,6 +24,7 @@
>>   #include "hw/pci/msi.h"
>>   #include "hw/pci/pcie.h"
>>   #include "xio3130_downstream.h"
>> +#include "qapi/error.h"
>>
>>   #define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
>>   #define XIO3130_REVISION                0x1
>> @@ -60,14 +61,17 @@ static int xio3130_downstream_initfn(PCIDevice *d)
>>       PCIEPort *p = PCIE_PORT(d);
>>       PCIESlot *s = PCIE_SLOT(d);
>>       int rc;
>> +    Error *err = NULL;
>>
>>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>       pcie_port_init_reg(d);
>>
>>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>       if (rc < 0) {
>> +        assert(rc == -ENOTSUP);
>> +        error_report_err(err);
>>           goto err_bridge;
>>       }
>>
>> diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
>> index d976844..487edac 100644
>> --- a/hw/pci-bridge/xio3130_upstream.c
>> +++ b/hw/pci-bridge/xio3130_upstream.c
>> @@ -24,6 +24,7 @@
>>   #include "hw/pci/msi.h"
>>   #include "hw/pci/pcie.h"
>>   #include "xio3130_upstream.h"
>> +#include "qapi/error.h"
>>
>>   #define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
>>   #define XIO3130_REVISION                0x2
>> @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d)
>>   {
>>       PCIEPort *p = PCIE_PORT(d);
>>       int rc;
>> +    Error *err = NULL;
>>
>>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>       pcie_port_init_reg(d);
>>
>>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>       if (rc < 0) {
>> +        assert(rc == -ENOTSUP);
>> +        error_report_err(err);
>
> Hi Jin, Markus
>
> It looks a little weird to me to check for negative error code
> and then assert for a specific error as the only "valid error".
> Maybe we should assert inside msi_init to leave the callers "clean"?
>
> I am well aware a lot of time was spent for this series, but I just
> spotted it and I want to be sure we are doing it right.

Only the callers know how to handle these errors.  Let me explain using
the function comment:

 * -ENOTSUP means lacking msi support for a msi-capable platform.

Our board emulation is broken.  The device decides whether this is an
error (say because the user requested msi=on), or not.  If it isn't,
devices generally fall back to a non-MSI variant.

 * -EINVAL means capability overlap, happens when @offset is non-zero,
 *  also means a programming error, except device assignment, which can check
 *  if a real HW is broken.

For virtual devices, this is a programming error.  Such callers should
therefore abort.  But for device assignment, it's a physical device with
messed up capabilities --- not a programming error, aborting would be
inappropriate.  See vfio_msi_setup() for an example.
Cao jin June 13, 2016, 11:09 a.m. UTC | #3
On 06/13/2016 06:16 PM, Marcel Apfelbaum wrote:
> On 06/10/2016 12:54 PM, Cao jin wrote:

>> diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
>> index 692283f..a06d184 100644
>> --- a/hw/net/e1000e.c
>> +++ b/hw/net/e1000e.c
>> @@ -268,13 +268,9 @@ e1000e_init_msi(E1000EState *s)
>>   {
>>       int res;
>>
>> -    res = msi_init(PCI_DEVICE(s),
>> -                   0xD0,   /* MSI capability offset              */
>> -                   1,      /* MAC MSI interrupts                 */
>> -                   true,   /* 64-bit message addresses supported */
>> -                   false); /* Per vector mask supported          */
>> +    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
>>
>
> Why did you chose to remove author's comments?
>

Because msi_init() has its function comments now, which is the same is 
the author`s comments, I guess author add these comments because 
function has no comment before, remove comments also is to save screen 
space:)

some macros of some devices is also unnecessary I think, because we have 
comments of msi_init().

>> +
>> +#define VMXNET3_USE_64BIT         (true)
>> +#define VMXNET3_PER_VECTOR_MASK   (false)
>> +

like these macros, but it does't take too much space as above one I 
feel, so I didn't touch them.

>> @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d)
>>   {
>>       PCIEPort *p = PCIE_PORT(d);
>>       int rc;
>> +    Error *err = NULL;
>>
>>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>       pcie_port_init_reg(d);
>>
>>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>> +                  XIO3130_MSI_SUPPORTED_FLAGS &
>> PCI_MSI_FLAGS_MASKBIT, &err);
>>       if (rc < 0) {
>> +        assert(rc == -ENOTSUP);
>> +        error_report_err(err);
>
> Hi Jin, Markus
>
> It looks a little weird to me to check for negative error code
> and then assert for a specific error as the only "valid error".
> Maybe we should assert inside msi_init to leave the callers "clean"?
>
Hi Marcel,

I think it is because: except assigned device(vfio), the emulated pci 
devices won`t have config space overlapped(-EINVAL), unless programming 
error.

If implemented as you said, I guess there need a judgement (if..else..) 
to check if current device is assigned in msi_init(), or else assert the 
error

> I am well aware a lot of time was spent for this series, but I just
> spotted it and I want to be sure we are doing it right.
>
> Thanks,
> Marcel
>
Marcel Apfelbaum June 13, 2016, 11:53 a.m. UTC | #4
On 06/13/2016 02:07 PM, Markus Armbruster wrote:
> Marcel Apfelbaum <marcel@redhat.com> writes:
>
>> On 06/10/2016 12:54 PM, Cao jin wrote:
>>> msi_init() reports errors with error_report(), which is wrong
>>> when it's used in realize().
>>>
>>> Fix by converting it to Error.
>>>
>>> Fix its callers to handle failure instead of ignoring it.
>>>
>>> For those callers who don't handle the failure, it might happen:
>>> when user want msi on, but he doesn't get what he want because of
>>> msi_init fails silently.
>>>
>>> cc: Gerd Hoffmann <kraxel@redhat.com>
>>> cc: John Snow <jsnow@redhat.com>
>>> cc: Dmitry Fleytman <dmitry@daynix.com>
>>> cc: Jason Wang <jasowang@redhat.com>
>>> cc: Michael S. Tsirkin <mst@redhat.com>
>>> cc: Hannes Reinecke <hare@suse.de>
>>> cc: Paolo Bonzini <pbonzini@redhat.com>
>>> cc: Alex Williamson <alex.williamson@redhat.com>
>>> cc: Markus Armbruster <armbru@redhat.com>
>>> cc: Marcel Apfelbaum <marcel@redhat.com>
>>>
>>> Reviewed-by: Markus Armbruster <armbru@redhat.com>
>>> Signed-off-by: Cao jin <caoj.fnst@cn.fujitsu.com>
>>> ---
>>>    hw/audio/intel-hda.c               | 24 ++++++++++++++++++++----
>>>    hw/ide/ich.c                       | 15 +++++++++------
>>>    hw/net/e1000e.c                    |  8 ++------
>>>    hw/net/vmxnet3.c                   | 37 ++++++++++++-------------------------
>>>    hw/pci-bridge/ioh3420.c            |  6 +++++-
>>>    hw/pci-bridge/pci_bridge_dev.c     | 20 ++++++++++++++++----
>>>    hw/pci-bridge/xio3130_downstream.c |  6 +++++-
>>>    hw/pci-bridge/xio3130_upstream.c   |  6 +++++-
>>>    hw/pci/msi.c                       | 11 ++++++++---
>>>    hw/scsi/megasas.c                  | 26 +++++++++++++++++++++-----
>>>    hw/scsi/mptsas.c                   | 31 ++++++++++++++++++++++++-------
>>>    hw/scsi/vmw_pvscsi.c               |  2 +-
>>>    hw/usb/hcd-xhci.c                  | 23 +++++++++++++++++++----
>>>    hw/vfio/pci.c                      |  7 +++++--
>>>    include/hw/pci/msi.h               |  3 ++-
>>>    15 files changed, 154 insertions(+), 71 deletions(-)
>>>
>>> diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
>>> index 6b4dda0..82101f8 100644
>>> --- a/hw/audio/intel-hda.c
>>> +++ b/hw/audio/intel-hda.c
>>> @@ -1135,6 +1135,8 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>>>    {
>>>        IntelHDAState *d = INTEL_HDA(pci);
>>>        uint8_t *conf = d->pci.config;
>>> +    Error *err = NULL;
>>> +    int ret;
>>>
>>>        d->name = object_get_typename(OBJECT(d));
>>>
>>> @@ -1143,13 +1145,27 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp)
>>>        /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
>>>        conf[0x40] = 0x01;
>>>
>>> +    if (d->msi != ON_OFF_AUTO_OFF) {
>>> +        ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60,
>>> +                       1, true, false, &err);
>>> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
>>> +         * is a programming error */
>>> +        assert(!ret || ret == -ENOTSUP);
>>> +        if (ret && d->msi == ON_OFF_AUTO_ON) {
>>> +            /* Can't satisfy user's explicit msi=on request, fail */
>>> +            error_append_hint(&err, "You have to use msi=auto (default) or "
>>> +                    "msi=off with this machine type.\n");
>>> +            error_propagate(errp, err);
>>> +            return;
>>> +        }
>>> +        assert(!err || d->msi == ON_OFF_AUTO_AUTO);
>>> +        /* With msi=auto, we fall back to MSI off silently */
>>> +        error_free(err);
>>> +    }
>>> +
>>>        memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d,
>>>                              "intel-hda", 0x4000);
>>>        pci_register_bar(&d->pci, 0, 0, &d->mmio);
>>> -    if (d->msi != ON_OFF_AUTO_OFF) {
>>> -         /* TODO check for errors */
>>> -        msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false);
>>> -    }
>>>
>>>        hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs),
>>>                           intel_hda_response, intel_hda_xfer);
>>> diff --git a/hw/ide/ich.c b/hw/ide/ich.c
>>> index 0a13334..084bef8 100644
>>> --- a/hw/ide/ich.c
>>> +++ b/hw/ide/ich.c
>>> @@ -68,7 +68,6 @@
>>>    #include <hw/isa/isa.h>
>>>    #include "sysemu/block-backend.h"
>>>    #include "sysemu/dma.h"
>>> -
>>>    #include <hw/ide/pci.h>
>>>    #include <hw/ide/ahci.h>
>>>
>>> @@ -111,6 +110,15 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>>>        int sata_cap_offset;
>>>        uint8_t *sata_cap;
>>>        d = ICH_AHCI(dev);
>>> +    int ret;
>>> +
>>> +    /* Although the AHCI 1.3 specification states that the first capability
>>> +     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
>>> +     * AHCI device puts the MSI capability first, pointing to 0x80. */
>>> +    ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL);
>>> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
>>> +     * is a programming error.  Fall back to INTx silently on -ENOTSUP */
>>> +    assert(!ret || ret == -ENOTSUP);
>>>
>>>        ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
>>>
>>> @@ -142,11 +150,6 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
>>>        pci_set_long(sata_cap + SATA_CAP_BAR,
>>>                     (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
>>>        d->ahci.idp_offset = ICH9_IDP_INDEX;
>>> -
>>> -    /* Although the AHCI 1.3 specification states that the first capability
>>> -     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
>>> -     * AHCI device puts the MSI capability first, pointing to 0x80. */
>>> -    msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false);
>>>    }
>>>
>>>    static void pci_ich9_uninit(PCIDevice *dev)
>>> diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
>>> index 692283f..a06d184 100644
>>> --- a/hw/net/e1000e.c
>>> +++ b/hw/net/e1000e.c
>>> @@ -268,13 +268,9 @@ e1000e_init_msi(E1000EState *s)
>>>    {
>>>        int res;
>>>
>>> -    res = msi_init(PCI_DEVICE(s),
>>> -                   0xD0,   /* MSI capability offset              */
>>> -                   1,      /* MAC MSI interrupts                 */
>>> -                   true,   /* 64-bit message addresses supported */
>>> -                   false); /* Per vector mask supported          */
>>> +    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
>>>
>>
>> Why did you chose to remove author's comments?
>>
>>
>>> -    if (res > 0) {
>>> +    if (!res) {
>>>            s->intr_state |= E1000E_USE_MSI;
>>>        } else {
>>>            trace_e1000e_msi_init_fail(res);
>>> diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
>>> index d978976..63f8904 100644
>>> --- a/hw/net/vmxnet3.c
>>> +++ b/hw/net/vmxnet3.c
>>> @@ -2197,27 +2197,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
>>>        }
>>>    }
>>>
>>> -#define VMXNET3_USE_64BIT         (true)
>>> -#define VMXNET3_PER_VECTOR_MASK   (false)
>>> -
>>> -static bool
>>> -vmxnet3_init_msi(VMXNET3State *s)
>>> -{
>>> -    PCIDevice *d = PCI_DEVICE(s);
>>> -    int res;
>>> -
>>> -    res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
>>> -                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
>>> -    if (0 > res) {
>>> -        VMW_WRPRN("Failed to initialize MSI, error %d", res);
>>> -        s->msi_used = false;
>>> -    } else {
>>> -        s->msi_used = true;
>>> -    }
>>> -
>>> -    return s->msi_used;
>>> -}
>>> -
>>>    static void
>>>    vmxnet3_cleanup_msi(VMXNET3State *s)
>>>    {
>>> @@ -2279,10 +2258,15 @@ static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
>>>        return dsn_payload;
>>>    }
>>>
>>> +
>>> +#define VMXNET3_USE_64BIT         (true)
>>> +#define VMXNET3_PER_VECTOR_MASK   (false)
>>> +
>>>    static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>>>    {
>>>        DeviceState *dev = DEVICE(pci_dev);
>>>        VMXNET3State *s = VMXNET3(pci_dev);
>>> +    int ret;
>>>
>>>        VMW_CBPRN("Starting init...");
>>>
>>> @@ -2306,14 +2290,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
>>>        /* Interrupt pin A */
>>>        pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
>>>
>>> +    ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
>>> +                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL);
>>> +    /* Any error other than -ENOTSUP(board's MSI support is broken)
>>> +     * is a programming error. Fall back to INTx silently on -ENOTSUP */
>>> +    assert(!ret || ret == -ENOTSUP);
>>> +    s->msi_used = !ret;
>>> +
>>>        if (!vmxnet3_init_msix(s)) {
>>>            VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
>>>        }
>>>
>>> -    if (!vmxnet3_init_msi(s)) {
>>> -        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
>>> -    }
>>> -
>>>        vmxnet3_net_init(s);
>>>
>>>        if (pci_is_express(pci_dev)) {
>>> diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
>>> index b4a7806..93c6f0b 100644
>>> --- a/hw/pci-bridge/ioh3420.c
>>> +++ b/hw/pci-bridge/ioh3420.c
>>> @@ -25,6 +25,7 @@
>>>    #include "hw/pci/msi.h"
>>>    #include "hw/pci/pcie.h"
>>>    #include "ioh3420.h"
>>> +#include "qapi/error.h"
>>>
>>>    #define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
>>>    #define PCI_DEVICE_ID_IOH_REV           0x2
>>> @@ -97,6 +98,7 @@ static int ioh3420_initfn(PCIDevice *d)
>>>        PCIEPort *p = PCIE_PORT(d);
>>>        PCIESlot *s = PCIE_SLOT(d);
>>>        int rc;
>>> +    Error *err = NULL;
>>>
>>>        pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>>        pcie_port_init_reg(d);
>>> @@ -109,8 +111,10 @@ static int ioh3420_initfn(PCIDevice *d)
>>>
>>>        rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
>>>                      IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>>> -                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>>> +                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>>        if (rc < 0) {
>>> +        assert(rc == -ENOTSUP);
>>> +        error_report_err(err);
>>>            goto err_bridge;
>>>        }
>>>
>>> diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
>>> index 0fbecc4..c4d2c0b 100644
>>> --- a/hw/pci-bridge/pci_bridge_dev.c
>>> +++ b/hw/pci-bridge/pci_bridge_dev.c
>>> @@ -54,6 +54,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>>>        PCIBridge *br = PCI_BRIDGE(dev);
>>>        PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
>>>        int err;
>>> +    Error *local_err = NULL;
>>>
>>>        pci_bridge_initfn(dev, TYPE_PCI_BUS);
>>>
>>> @@ -75,12 +76,23 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
>>>            goto slotid_error;
>>>        }
>>>
>>> -    if (bridge_dev->msi != ON_OFF_AUTO_OFF &&
>>> -        msi_nonbroken) {
>>> -        err = msi_init(dev, 0, 1, true, true);
>>> -        if (err < 0) {
>>> +    if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
>>> +        /* it means SHPC exists, because SHPC is required for MSI */
>>
>> Is the other way around, MSI is needed by SHPC (but not compulsory)
>>
>>> +
>>> +        err = msi_init(dev, 0, 1, true, true, &local_err);
>>> +        /* Any error other than -ENOTSUP(board's MSI support is broken)
>>> +         * is a programming error */
>>> +        assert(!err || err == -ENOTSUP);
>>> +        if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
>>> +            /* Can't satisfy user's explicit msi=on request, fail */
>>> +            error_append_hint(&local_err, "You have to use msi=auto (default) "
>>> +                    "or msi=off with this machine type.\n");
>>> +            error_report_err(local_err);
>>>                goto msi_error;
>>>            }
>>> +        assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
>>> +        /* With msi=auto, we fall back to MSI off silently */
>>> +        error_free(local_err);
>>>        }
>>>
>>>        if (shpc_present(dev)) {
>>> diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
>>> index e6d653d..f6149a3 100644
>>> --- a/hw/pci-bridge/xio3130_downstream.c
>>> +++ b/hw/pci-bridge/xio3130_downstream.c
>>> @@ -24,6 +24,7 @@
>>>    #include "hw/pci/msi.h"
>>>    #include "hw/pci/pcie.h"
>>>    #include "xio3130_downstream.h"
>>> +#include "qapi/error.h"
>>>
>>>    #define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
>>>    #define XIO3130_REVISION                0x1
>>> @@ -60,14 +61,17 @@ static int xio3130_downstream_initfn(PCIDevice *d)
>>>        PCIEPort *p = PCIE_PORT(d);
>>>        PCIESlot *s = PCIE_SLOT(d);
>>>        int rc;
>>> +    Error *err = NULL;
>>>
>>>        pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>>        pcie_port_init_reg(d);
>>>
>>>        rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>>                      XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>>> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>>        if (rc < 0) {
>>> +        assert(rc == -ENOTSUP);
>>> +        error_report_err(err);
>>>            goto err_bridge;
>>>        }
>>>
>>> diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
>>> index d976844..487edac 100644
>>> --- a/hw/pci-bridge/xio3130_upstream.c
>>> +++ b/hw/pci-bridge/xio3130_upstream.c
>>> @@ -24,6 +24,7 @@
>>>    #include "hw/pci/msi.h"
>>>    #include "hw/pci/pcie.h"
>>>    #include "xio3130_upstream.h"
>>> +#include "qapi/error.h"
>>>
>>>    #define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
>>>    #define XIO3130_REVISION                0x2
>>> @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d)
>>>    {
>>>        PCIEPort *p = PCIE_PORT(d);
>>>        int rc;
>>> +    Error *err = NULL;
>>>
>>>        pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>>        pcie_port_init_reg(d);
>>>
>>>        rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>>                      XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>>> +                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
>>>        if (rc < 0) {
>>> +        assert(rc == -ENOTSUP);
>>> +        error_report_err(err);
>>
>> Hi Jin, Markus
>>
>> It looks a little weird to me to check for negative error code
>> and then assert for a specific error as the only "valid error".
>> Maybe we should assert inside msi_init to leave the callers "clean"?
>>
>> I am well aware a lot of time was spent for this series, but I just
>> spotted it and I want to be sure we are doing it right.
>
> Only the callers know how to handle these errors.  Let me explain using
> the function comment:
>
>   * -ENOTSUP means lacking msi support for a msi-capable platform.
>
> Our board emulation is broken.  The device decides whether this is an
> error (say because the user requested msi=on), or not.  If it isn't,
> devices generally fall back to a non-MSI variant.
>
>   * -EINVAL means capability overlap, happens when @offset is non-zero,
>   *  also means a programming error, except device assignment, which can check
>   *  if a real HW is broken.
>
> For virtual devices, this is a programming error.  Such callers should
> therefore abort.  But for device assignment, it's a physical device with
> messed up capabilities --- not a programming error, aborting would be
> inappropriate.  See vfio_msi_setup() for an example.
>

I missed the vfio scenario.
Now I got it.

Thanks!
Marcel
Marcel Apfelbaum June 13, 2016, 11:55 a.m. UTC | #5
On 06/13/2016 02:09 PM, Cao jin wrote:
>
>
> On 06/13/2016 06:16 PM, Marcel Apfelbaum wrote:
>> On 06/10/2016 12:54 PM, Cao jin wrote:
>
>>> diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
>>> index 692283f..a06d184 100644
>>> --- a/hw/net/e1000e.c
>>> +++ b/hw/net/e1000e.c
>>> @@ -268,13 +268,9 @@ e1000e_init_msi(E1000EState *s)
>>>   {
>>>       int res;
>>>
>>> -    res = msi_init(PCI_DEVICE(s),
>>> -                   0xD0,   /* MSI capability offset              */
>>> -                   1,      /* MAC MSI interrupts                 */
>>> -                   true,   /* 64-bit message addresses supported */
>>> -                   false); /* Per vector mask supported          */
>>> +    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
>>>
>>
>> Why did you chose to remove author's comments?
>>
>
> Because msi_init() has its function comments now, which is the same is the author`s comments, I guess author add these comments because function has no comment before, remove comments also is to save
> screen space:)
>
> some macros of some devices is also unnecessary I think, because we have comments of msi_init().
>

Right.

>>> +
>>> +#define VMXNET3_USE_64BIT         (true)
>>> +#define VMXNET3_PER_VECTOR_MASK   (false)
>>> +
>
> like these macros, but it does't take too much space as above one I feel, so I didn't touch them.
>
>>> @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d)
>>>   {
>>>       PCIEPort *p = PCIE_PORT(d);
>>>       int rc;
>>> +    Error *err = NULL;
>>>
>>>       pci_bridge_initfn(d, TYPE_PCIE_BUS);
>>>       pcie_port_init_reg(d);
>>>
>>>       rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
>>>                     XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
>>> -                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
>>> +                  XIO3130_MSI_SUPPORTED_FLAGS &
>>> PCI_MSI_FLAGS_MASKBIT, &err);
>>>       if (rc < 0) {
>>> +        assert(rc == -ENOTSUP);
>>> +        error_report_err(err);
>>
>> Hi Jin, Markus
>>
>> It looks a little weird to me to check for negative error code
>> and then assert for a specific error as the only "valid error".
>> Maybe we should assert inside msi_init to leave the callers "clean"?
>>
> Hi Marcel,
>
> I think it is because: except assigned device(vfio), the emulated pci devices won`t have config space overlapped(-EINVAL), unless programming error.
>

Understood, thanks for the explanation.

For the PCI part:
Reviewed-by: Marcel Apfelbaum <marcel@redhat.com>

Thanks,
Marcel

> If implemented as you said, I guess there need a judgement (if..else..) to check if current device is assigned in msi_init(), or else assert the error
>



>> I am well aware a lot of time was spent for this series, but I just
>> spotted it and I want to be sure we are doing it right.
>>
>> Thanks,
>> Marcel
>>
>
diff mbox

Patch

diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 6b4dda0..82101f8 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -1135,6 +1135,8 @@  static void intel_hda_realize(PCIDevice *pci, Error **errp)
 {
     IntelHDAState *d = INTEL_HDA(pci);
     uint8_t *conf = d->pci.config;
+    Error *err = NULL;
+    int ret;
 
     d->name = object_get_typename(OBJECT(d));
 
@@ -1143,13 +1145,27 @@  static void intel_hda_realize(PCIDevice *pci, Error **errp)
     /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
     conf[0x40] = 0x01;
 
+    if (d->msi != ON_OFF_AUTO_OFF) {
+        ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60,
+                       1, true, false, &err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!ret || ret == -ENOTSUP);
+        if (ret && d->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&err, "You have to use msi=auto (default) or "
+                    "msi=off with this machine type.\n");
+            error_propagate(errp, err);
+            return;
+        }
+        assert(!err || d->msi == ON_OFF_AUTO_AUTO);
+        /* With msi=auto, we fall back to MSI off silently */
+        error_free(err);
+    }
+
     memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d,
                           "intel-hda", 0x4000);
     pci_register_bar(&d->pci, 0, 0, &d->mmio);
-    if (d->msi != ON_OFF_AUTO_OFF) {
-         /* TODO check for errors */
-        msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false);
-    }
 
     hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs),
                        intel_hda_response, intel_hda_xfer);
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 0a13334..084bef8 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -68,7 +68,6 @@ 
 #include <hw/isa/isa.h>
 #include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
-
 #include <hw/ide/pci.h>
 #include <hw/ide/ahci.h>
 
@@ -111,6 +110,15 @@  static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
     int sata_cap_offset;
     uint8_t *sata_cap;
     d = ICH_AHCI(dev);
+    int ret;
+
+    /* Although the AHCI 1.3 specification states that the first capability
+     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
+     * AHCI device puts the MSI capability first, pointing to 0x80. */
+    ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL);
+    /* Any error other than -ENOTSUP(board's MSI support is broken)
+     * is a programming error.  Fall back to INTx silently on -ENOTSUP */
+    assert(!ret || ret == -ENOTSUP);
 
     ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
 
@@ -142,11 +150,6 @@  static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
     pci_set_long(sata_cap + SATA_CAP_BAR,
                  (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
     d->ahci.idp_offset = ICH9_IDP_INDEX;
-
-    /* Although the AHCI 1.3 specification states that the first capability
-     * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
-     * AHCI device puts the MSI capability first, pointing to 0x80. */
-    msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false);
 }
 
 static void pci_ich9_uninit(PCIDevice *dev)
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index 692283f..a06d184 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -268,13 +268,9 @@  e1000e_init_msi(E1000EState *s)
 {
     int res;
 
-    res = msi_init(PCI_DEVICE(s),
-                   0xD0,   /* MSI capability offset              */
-                   1,      /* MAC MSI interrupts                 */
-                   true,   /* 64-bit message addresses supported */
-                   false); /* Per vector mask supported          */
+    res = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL);
 
-    if (res > 0) {
+    if (!res) {
         s->intr_state |= E1000E_USE_MSI;
     } else {
         trace_e1000e_msi_init_fail(res);
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index d978976..63f8904 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2197,27 +2197,6 @@  vmxnet3_cleanup_msix(VMXNET3State *s)
     }
 }
 
-#define VMXNET3_USE_64BIT         (true)
-#define VMXNET3_PER_VECTOR_MASK   (false)
-
-static bool
-vmxnet3_init_msi(VMXNET3State *s)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    int res;
-
-    res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
-                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
-    if (0 > res) {
-        VMW_WRPRN("Failed to initialize MSI, error %d", res);
-        s->msi_used = false;
-    } else {
-        s->msi_used = true;
-    }
-
-    return s->msi_used;
-}
-
 static void
 vmxnet3_cleanup_msi(VMXNET3State *s)
 {
@@ -2279,10 +2258,15 @@  static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
     return dsn_payload;
 }
 
+
+#define VMXNET3_USE_64BIT         (true)
+#define VMXNET3_PER_VECTOR_MASK   (false)
+
 static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
 {
     DeviceState *dev = DEVICE(pci_dev);
     VMXNET3State *s = VMXNET3(pci_dev);
+    int ret;
 
     VMW_CBPRN("Starting init...");
 
@@ -2306,14 +2290,17 @@  static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
     /* Interrupt pin A */
     pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
 
+    ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
+                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL);
+    /* Any error other than -ENOTSUP(board's MSI support is broken)
+     * is a programming error. Fall back to INTx silently on -ENOTSUP */
+    assert(!ret || ret == -ENOTSUP);
+    s->msi_used = !ret;
+
     if (!vmxnet3_init_msix(s)) {
         VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
     }
 
-    if (!vmxnet3_init_msi(s)) {
-        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
-    }
-
     vmxnet3_net_init(s);
 
     if (pci_is_express(pci_dev)) {
diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
index b4a7806..93c6f0b 100644
--- a/hw/pci-bridge/ioh3420.c
+++ b/hw/pci-bridge/ioh3420.c
@@ -25,6 +25,7 @@ 
 #include "hw/pci/msi.h"
 #include "hw/pci/pcie.h"
 #include "ioh3420.h"
+#include "qapi/error.h"
 
 #define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
 #define PCI_DEVICE_ID_IOH_REV           0x2
@@ -97,6 +98,7 @@  static int ioh3420_initfn(PCIDevice *d)
     PCIEPort *p = PCIE_PORT(d);
     PCIESlot *s = PCIE_SLOT(d);
     int rc;
+    Error *err = NULL;
 
     pci_bridge_initfn(d, TYPE_PCIE_BUS);
     pcie_port_init_reg(d);
@@ -109,8 +111,10 @@  static int ioh3420_initfn(PCIDevice *d)
 
     rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
                   IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
     if (rc < 0) {
+        assert(rc == -ENOTSUP);
+        error_report_err(err);
         goto err_bridge;
     }
 
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index 0fbecc4..c4d2c0b 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -54,6 +54,7 @@  static int pci_bridge_dev_initfn(PCIDevice *dev)
     PCIBridge *br = PCI_BRIDGE(dev);
     PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
     int err;
+    Error *local_err = NULL;
 
     pci_bridge_initfn(dev, TYPE_PCI_BUS);
 
@@ -75,12 +76,23 @@  static int pci_bridge_dev_initfn(PCIDevice *dev)
         goto slotid_error;
     }
 
-    if (bridge_dev->msi != ON_OFF_AUTO_OFF &&
-        msi_nonbroken) {
-        err = msi_init(dev, 0, 1, true, true);
-        if (err < 0) {
+    if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
+        /* it means SHPC exists, because SHPC is required for MSI */
+
+        err = msi_init(dev, 0, 1, true, true, &local_err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!err || err == -ENOTSUP);
+        if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&local_err, "You have to use msi=auto (default) "
+                    "or msi=off with this machine type.\n");
+            error_report_err(local_err);
             goto msi_error;
         }
+        assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
+        /* With msi=auto, we fall back to MSI off silently */
+        error_free(local_err);
     }
 
     if (shpc_present(dev)) {
diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
index e6d653d..f6149a3 100644
--- a/hw/pci-bridge/xio3130_downstream.c
+++ b/hw/pci-bridge/xio3130_downstream.c
@@ -24,6 +24,7 @@ 
 #include "hw/pci/msi.h"
 #include "hw/pci/pcie.h"
 #include "xio3130_downstream.h"
+#include "qapi/error.h"
 
 #define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
 #define XIO3130_REVISION                0x1
@@ -60,14 +61,17 @@  static int xio3130_downstream_initfn(PCIDevice *d)
     PCIEPort *p = PCIE_PORT(d);
     PCIESlot *s = PCIE_SLOT(d);
     int rc;
+    Error *err = NULL;
 
     pci_bridge_initfn(d, TYPE_PCIE_BUS);
     pcie_port_init_reg(d);
 
     rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
                   XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
     if (rc < 0) {
+        assert(rc == -ENOTSUP);
+        error_report_err(err);
         goto err_bridge;
     }
 
diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
index d976844..487edac 100644
--- a/hw/pci-bridge/xio3130_upstream.c
+++ b/hw/pci-bridge/xio3130_upstream.c
@@ -24,6 +24,7 @@ 
 #include "hw/pci/msi.h"
 #include "hw/pci/pcie.h"
 #include "xio3130_upstream.h"
+#include "qapi/error.h"
 
 #define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
 #define XIO3130_REVISION                0x2
@@ -56,14 +57,17 @@  static int xio3130_upstream_initfn(PCIDevice *d)
 {
     PCIEPort *p = PCIE_PORT(d);
     int rc;
+    Error *err = NULL;
 
     pci_bridge_initfn(d, TYPE_PCIE_BUS);
     pcie_port_init_reg(d);
 
     rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
                   XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err);
     if (rc < 0) {
+        assert(rc == -ENOTSUP);
+        error_report_err(err);
         goto err_bridge;
     }
 
diff --git a/hw/pci/msi.c b/hw/pci/msi.c
index ed79225..a87b227 100644
--- a/hw/pci/msi.c
+++ b/hw/pci/msi.c
@@ -22,6 +22,7 @@ 
 #include "hw/pci/msi.h"
 #include "hw/xen/xen.h"
 #include "qemu/range.h"
+#include "qapi/error.h"
 
 /* PCI_MSI_ADDRESS_LO */
 #define PCI_MSI_ADDRESS_LO_MASK         (~0x3)
@@ -173,7 +174,8 @@  bool msi_enabled(const PCIDevice *dev)
  * If @msi64bit, make the device capable of sending a 64-bit message
  * address.
  * If @msi_per_vector_mask, make the device support per-vector masking.
- * Return 0 on success, return -errno on error.
+ * @errp is for returning errors.
+ * Return 0 on success; set @errp and return -errno on error.
  *
  * -ENOTSUP means lacking msi support for a msi-capable platform.
  * -EINVAL means capability overlap, happens when @offset is non-zero,
@@ -181,7 +183,8 @@  bool msi_enabled(const PCIDevice *dev)
  *  if a real HW is broken.
  */
 int msi_init(struct PCIDevice *dev, uint8_t offset,
-             unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
+             unsigned int nr_vectors, bool msi64bit,
+             bool msi_per_vector_mask, Error **errp)
 {
     unsigned int vectors_order;
     uint16_t flags;
@@ -189,6 +192,7 @@  int msi_init(struct PCIDevice *dev, uint8_t offset,
     int config_offset;
 
     if (!msi_nonbroken) {
+        error_setg(errp, "MSI is not supported by interrupt controller");
         return -ENOTSUP;
     }
 
@@ -212,7 +216,8 @@  int msi_init(struct PCIDevice *dev, uint8_t offset,
     }
 
     cap_size = msi_cap_sizeof(flags);
-    config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
+    config_offset = pci_add_capability2(dev, PCI_CAP_ID_MSI, offset,
+                                        cap_size, errp);
     if (config_offset < 0) {
         return config_offset;
     }
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index 2ede192..345783d 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -29,7 +29,7 @@ 
 #include "hw/scsi/scsi.h"
 #include "block/scsi.h"
 #include "trace.h"
-
+#include "qapi/error.h"
 #include "mfi.h"
 
 #define MEGASAS_VERSION_GEN1 "1.70"
@@ -2333,6 +2333,8 @@  static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
     MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s);
     uint8_t *pci_conf;
     int i, bar_type;
+    Error *err = NULL;
+    int ret;
 
     pci_conf = dev->config;
 
@@ -2341,6 +2343,24 @@  static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
     /* Interrupt pin 1 */
     pci_conf[PCI_INTERRUPT_PIN] = 0x01;
 
+    if (megasas_use_msi(s)) {
+        ret = msi_init(dev, 0x50, 1, true, false, &err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!ret || ret == -ENOTSUP);
+        if (ret && s->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&err, "You have to use msi=auto (default) or "
+                    "msi=off with this machine type.\n");
+            error_propagate(errp, err);
+            return;
+        } else if (ret) {
+            /* With msi=auto, we fall back to MSI off silently */
+            s->msi = ON_OFF_AUTO_OFF;
+            error_free(err);
+        }
+    }
+
     memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s,
                           "megasas-mmio", 0x4000);
     memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s,
@@ -2348,10 +2368,6 @@  static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
     memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s,
                           "megasas-queue", 0x40000);
 
-    if (megasas_use_msi(s) &&
-        msi_init(dev, 0x50, 1, true, false) < 0) {
-        s->msi = ON_OFF_AUTO_OFF;
-    }
     if (megasas_use_msix(s) &&
         msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000,
                   &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) {
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index dfbc0c4..698be42 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -32,7 +32,7 @@ 
 #include "hw/scsi/scsi.h"
 #include "block/scsi.h"
 #include "trace.h"
-
+#include "qapi/error.h"
 #include "mptsas.h"
 #include "mpi.h"
 
@@ -1273,10 +1273,33 @@  static void mptsas_scsi_realize(PCIDevice *dev, Error **errp)
 {
     DeviceState *d = DEVICE(dev);
     MPTSASState *s = MPT_SAS(dev);
+    Error *err = NULL;
+    int ret;
 
     dev->config[PCI_LATENCY_TIMER] = 0;
     dev->config[PCI_INTERRUPT_PIN] = 0x01;
 
+    if (s->msi != ON_OFF_AUTO_OFF) {
+        ret = msi_init(dev, 0, 1, true, false, &err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!ret || ret == -ENOTSUP);
+        if (ret && s->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&err, "You have to use msi=auto (default) or "
+                    "msi=off with this machine type.\n");
+            error_propagate(errp, err);
+            s->msi_in_use = false;
+            return;
+        } else if (ret) {
+            /* With msi=auto, we fall back to MSI off silently */
+            error_free(err);
+            s->msi_in_use = false;
+        } else {
+            s->msi_in_use = true;
+        }
+    }
+
     memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s,
                           "mptsas-mmio", 0x4000);
     memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s,
@@ -1284,12 +1307,6 @@  static void mptsas_scsi_realize(PCIDevice *dev, Error **errp)
     memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s,
                           "mptsas-diag", 0x10000);
 
-    if (s->msi != ON_OFF_AUTO_OFF &&
-        msi_init(dev, 0, 1, true, false) >= 0) {
-        /* TODO check for errors */
-        s->msi_in_use = true;
-    }
-
     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
     pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY |
                                  PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io);
diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
index e035fce..ecd6077 100644
--- a/hw/scsi/vmw_pvscsi.c
+++ b/hw/scsi/vmw_pvscsi.c
@@ -1063,7 +1063,7 @@  pvscsi_init_msi(PVSCSIState *s)
     PCIDevice *d = PCI_DEVICE(s);
 
     res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
-                   PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
+                   PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL);
     if (res < 0) {
         trace_pvscsi_init_msi_fail(res);
         s->msi_used = false;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 0a5510f..1a3377f 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -26,6 +26,7 @@ 
 #include "hw/pci/msi.h"
 #include "hw/pci/msix.h"
 #include "trace.h"
+#include "qapi/error.h"
 
 //#define DEBUG_XHCI
 //#define DEBUG_DATA
@@ -3581,6 +3582,7 @@  static void usb_xhci_init(XHCIState *xhci)
 static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
 {
     int i, ret;
+    Error *err = NULL;
 
     XHCIState *xhci = XHCI(dev);
 
@@ -3591,6 +3593,23 @@  static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
 
     usb_xhci_init(xhci);
 
+    if (xhci->msi != ON_OFF_AUTO_OFF) {
+        ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
+        /* Any error other than -ENOTSUP(board's MSI support is broken)
+         * is a programming error */
+        assert(!ret || ret == -ENOTSUP);
+        if (ret && xhci->msi == ON_OFF_AUTO_ON) {
+            /* Can't satisfy user's explicit msi=on request, fail */
+            error_append_hint(&err, "You have to use msi=auto (default) or "
+                    "msi=off with this machine type.\n");
+            error_propagate(errp, err);
+            return;
+        }
+        assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
+        /* With msi=auto, we fall back to MSI off silently */
+        error_free(err);
+    }
+
     if (xhci->numintrs > MAXINTRS) {
         xhci->numintrs = MAXINTRS;
     }
@@ -3648,10 +3667,6 @@  static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
         assert(ret >= 0);
     }
 
-    if (xhci->msi != ON_OFF_AUTO_OFF) {
-        /* TODO check for errors */
-        msi_init(dev, 0x70, xhci->numintrs, true, false);
-    }
     if (xhci->msix != ON_OFF_AUTO_OFF) {
         /* TODO check for errors */
         msix_init(dev, xhci->numintrs,
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index deab0c6..dfbf8ba 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -32,6 +32,7 @@ 
 #include "sysemu/sysemu.h"
 #include "pci.h"
 #include "trace.h"
+#include "qapi/error.h"
 
 #define MSIX_CAP_LENGTH 12
 
@@ -1171,6 +1172,7 @@  static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
     uint16_t ctrl;
     bool msi_64bit, msi_maskbit;
     int ret, entries;
+    Error *err = NULL;
 
     if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl),
               vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
@@ -1184,12 +1186,13 @@  static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
 
     trace_vfio_msi_setup(vdev->vbasedev.name, pos);
 
-    ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
+    ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err);
     if (ret < 0) {
         if (ret == -ENOTSUP) {
             return 0;
         }
-        error_report("vfio: msi_init failed");
+        error_prepend(&err, "vfio: msi_init failed: ");
+        error_report_err(err);
         return ret;
     }
     vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h
index 8124908..4837bcf 100644
--- a/include/hw/pci/msi.h
+++ b/include/hw/pci/msi.h
@@ -35,7 +35,8 @@  void msi_set_message(PCIDevice *dev, MSIMessage msg);
 MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector);
 bool msi_enabled(const PCIDevice *dev);
 int msi_init(struct PCIDevice *dev, uint8_t offset,
-             unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
+             unsigned int nr_vectors, bool msi64bit,
+             bool msi_per_vector_mask, Error **errp);
 void msi_uninit(struct PCIDevice *dev);
 void msi_reset(PCIDevice *dev);
 void msi_notify(PCIDevice *dev, unsigned int vector);