:p
atchew
Login
rom_bar is tristate but was defined as uint32_t so convert it into OnOffAuto to clarify that. For compatibility, a uint32 value set via QOM will be converted into OnOffAuto. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- Changes in v2: - Documented in docs/about/deprecated.rst (Daniel P. Berrangé) - Link to v1: https://lore.kernel.org/r/20240706-rombar-v1-0-802daef2aec1@daynix.com --- Akihiko Odaki (4): qapi: Add visit_type_str_preserving() qapi: Do not consume a value when visit_type_enum() fails hw/pci: Convert rom_bar into OnOffAuto hw/qdev: Remove opts member docs/about/deprecated.rst | 7 +++++ docs/igd-assign.txt | 2 +- include/hw/pci/pci_device.h | 2 +- include/hw/qdev-core.h | 4 --- include/qapi/visitor-impl.h | 3 ++- include/qapi/visitor.h | 25 +++++++++++++---- hw/core/qdev.c | 1 - hw/pci/pci.c | 57 +++++++++++++++++++++++++++++++++++++-- hw/vfio/pci-quirks.c | 2 +- hw/vfio/pci.c | 11 ++++---- hw/xen/xen_pt_load_rom.c | 4 +-- qapi/opts-visitor.c | 12 ++++----- qapi/qapi-clone-visitor.c | 2 +- qapi/qapi-dealloc-visitor.c | 4 +-- qapi/qapi-forward-visitor.c | 4 ++- qapi/qapi-visit-core.c | 21 ++++++++++++--- qapi/qobject-input-visitor.c | 23 ++++++++-------- qapi/qobject-output-visitor.c | 2 +- qapi/string-input-visitor.c | 2 +- qapi/string-output-visitor.c | 2 +- system/qdev-monitor.c | 12 +++++---- tests/qtest/virtio-net-failover.c | 32 +++++++++++----------- 22 files changed, 161 insertions(+), 73 deletions(-) --- base-commit: f2cb4026fccfe073f84a4b440e41d3ed0c3134f6 change-id: 20240704-rombar-1a4ba2890d6c Best regards, -- Akihiko Odaki <akihiko.odaki@daynix.com>
visit_type_str_preserving() is mostly indentical with visit_type_str() but leaves the value intact. This is useful when the caller may interpret the value with a different type. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/qapi/visitor-impl.h | 3 ++- include/qapi/visitor.h | 20 ++++++++++++++++++++ qapi/opts-visitor.c | 7 +++++-- qapi/qapi-clone-visitor.c | 2 +- qapi/qapi-dealloc-visitor.c | 4 ++-- qapi/qapi-forward-visitor.c | 4 +++- qapi/qapi-visit-core.c | 17 +++++++++++++++-- qapi/qobject-input-visitor.c | 23 ++++++++++++----------- qapi/qobject-output-visitor.c | 2 +- qapi/string-input-visitor.c | 2 +- qapi/string-output-visitor.c | 2 +- 11 files changed, 63 insertions(+), 23 deletions(-) diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index XXXXXXX..XXXXXXX 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -XXX,XX +XXX,XX @@ struct Visitor bool (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp); /* Must be set */ - bool (*type_str)(Visitor *v, const char *name, char **obj, Error **errp); + bool (*type_str)(Visitor *v, const char *name, char **obj, bool consume, + Error **errp); /* Must be set to visit numbers */ bool (*type_number)(Visitor *v, const char *name, double *obj, diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index XXXXXXX..XXXXXXX 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -XXX,XX +XXX,XX @@ bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); */ bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp); +/* + * Visit a string value but do not consume it. + * + * @name expresses the relationship of this string to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input and clone visitors set *@obj to the + * value (always using "" rather than NULL for an empty string). + * Other visitors leave *@obj unchanged, and commonly treat NULL like + * "". + * + * This function must be called only with an input visitor. + * + * This is mostly identical with visit_type_str() but leaves the value intact. + * This is useful when the caller may interpret the value with a different + * type. + */ +bool visit_type_str_preserving(Visitor *v, const char *name, char **obj, + Error **errp); + /* * Visit a number (i.e. double) value. * diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -XXX,XX +XXX,XX @@ processed(OptsVisitor *ov, const char *name) static bool -opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) +opts_type_str(Visitor *v, const char *name, char **obj, bool consume, + Error **errp) { OptsVisitor *ov = to_ov(v); const QemuOpt *opt; @@ -XXX,XX +XXX,XX @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) * valid enum value; this is harmless because tracking what gets * consumed only matters to visit_end_struct() as the final error * check if there were no other failures during the visit. */ - processed(ov, name); + if (consume) { + processed(ov, name); + } return true; } diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -XXX,XX +XXX,XX @@ static bool qapi_clone_type_bool(Visitor *v, const char *name, bool *obj, } static bool qapi_clone_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { QapiCloneVisitor *qcv = to_qcv(v); diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -XXX,XX +XXX,XX @@ static void qapi_dealloc_end_list(Visitor *v, void **obj) } static bool qapi_dealloc_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { - if (obj) { + if (obj && consume) { g_free(*obj); } return true; diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -XXX,XX +XXX,XX @@ static bool forward_field_type_bool(Visitor *v, const char *name, bool *obj, } static bool forward_field_type_str(Visitor *v, const char *name, char **obj, + bool consume, Error **errp) { ForwardFieldVisitor *ffv = to_ffv(v); @@ -XXX,XX +XXX,XX @@ static bool forward_field_type_str(Visitor *v, const char *name, char **obj, if (!forward_field_translate_name(ffv, &name, errp)) { return false; } - return visit_type_str(ffv->target, name, obj, errp); + return (consume ? visit_type_str : visit_type_str_preserving)( + ffv->target, name, obj, errp); } static bool forward_field_type_size(Visitor *v, const char *name, uint64_t *obj, diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -XXX,XX +XXX,XX @@ bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) return v->type_bool(v, name, obj, errp); } -bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) +static bool visit_type_str_common(Visitor *v, const char *name, char **obj, + bool consume, Error **errp) { bool ok; @@ -XXX,XX +XXX,XX @@ bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) assert(!(v->type & VISITOR_OUTPUT) || *obj); */ trace_visit_type_str(v, name, obj); - ok = v->type_str(v, name, obj, errp); + ok = v->type_str(v, name, obj, consume, errp); if (v->type & VISITOR_INPUT) { assert(ok != !*obj); } return ok; } +bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) +{ + return visit_type_str_common(v, name, obj, true, errp); +} + +bool visit_type_str_preserving(Visitor *v, const char *name, char **obj, + Error **errp) +{ + assert(v->type & VISITOR_INPUT); + return visit_type_str_common(v, name, obj, false, errp); +} + bool visit_type_number(Visitor *v, const char *name, double *obj, Error **errp) { diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -XXX,XX +XXX,XX @@ static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, } static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, - const char *name, + const char *name, bool consume, Error **errp) { QObject *qobj; QString *qstr; - qobj = qobject_input_get_object(qiv, name, true, errp); + qobj = qobject_input_get_object(qiv, name, consume, errp); if (!qobj) { return NULL; } @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, int64_t *obj, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, true, errp); if (!str) { return false; @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, uint64_t *obj, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, true, errp); if (!str) { return false; @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, bool *obj, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, true, errp); if (!str) { return false; @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, } static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - QObject *qobj = qobject_input_get_object(qiv, name, true, errp); + QObject *qobj = qobject_input_get_object(qiv, name, consume, errp); QString *qstr; *obj = NULL; @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, } static bool qobject_input_type_str_keyval(Visitor *v, const char *name, - char **obj, Error **errp) + char **obj, bool consume, + Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, consume, errp); *obj = g_strdup(str); return !!str; @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_number_keyval(Visitor *v, const char *name, double *obj, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, true, errp); double val; if (!str) { @@ -XXX,XX +XXX,XX @@ static bool qobject_input_type_size_keyval(Visitor *v, const char *name, uint64_t *obj, Error **errp) { QObjectInputVisitor *qiv = to_qiv(v); - const char *str = qobject_input_get_keyval(qiv, name, errp); + const char *str = qobject_input_get_keyval(qiv, name, true, errp); if (!str) { return false; diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -XXX,XX +XXX,XX @@ static bool qobject_output_type_bool(Visitor *v, const char *name, bool *obj, } static bool qobject_output_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { QObjectOutputVisitor *qov = to_qov(v); if (*obj) { diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -XXX,XX +XXX,XX @@ static bool parse_type_bool(Visitor *v, const char *name, bool *obj, } static bool parse_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { StringInputVisitor *siv = to_siv(v); diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -XXX,XX +XXX,XX @@ static bool print_type_bool(Visitor *v, const char *name, bool *obj, } static bool print_type_str(Visitor *v, const char *name, char **obj, - Error **errp) + bool consume, Error **errp) { StringOutputVisitor *sov = to_sov(v); char *out; -- 2.45.2
Consuming a value when visit_type_enum() fails makes it impossible to reinterpret the value with a different type. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/qapi/visitor.h | 5 ----- qapi/opts-visitor.c | 5 ----- qapi/qapi-visit-core.c | 4 +++- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index XXXXXXX..XXXXXXX 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -XXX,XX +XXX,XX @@ void visit_set_policy(Visitor *v, CompatPolicy *policy); * is an input visitor. * * Return true on success, false on failure. - * - * May call visit_type_str() under the hood, and the enum visit may - * fail even if the corresponding string visit succeeded; this implies - * that an input visitor's visit_type_str() must have no unwelcome - * side effects. */ bool visit_type_enum(Visitor *v, const char *name, int *obj, const QEnumLookup *lookup, Error **errp); diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -XXX,XX +XXX,XX @@ opts_type_str(Visitor *v, const char *name, char **obj, bool consume, return false; } *obj = g_strdup(opt->str ? opt->str : ""); - /* Note that we consume a string even if this is called as part of - * an enum visit that later fails because the string is not a - * valid enum value; this is harmless because tracking what gets - * consumed only matters to visit_end_struct() as the final error - * check if there were no other failures during the visit. */ if (consume) { processed(ov, name); } diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index XXXXXXX..XXXXXXX 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -XXX,XX +XXX,XX @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, int64_t value; g_autofree char *enum_str = NULL; - if (!visit_type_str(v, name, &enum_str, errp)) { + if (!visit_type_str_preserving(v, name, &enum_str, errp)) { return false; } @@ -XXX,XX +XXX,XX @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, return false; } + enum_str = NULL; + visit_type_str(v, name, &enum_str, &error_abort); *obj = value; return true; } -- 2.45.2
rom_bar is tristate but was defined as uint32_t so convert it into OnOffAuto to clarify that. For compatibility, a uint32 value set via QOM will be converted into OnOffAuto. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- docs/about/deprecated.rst | 7 +++++ docs/igd-assign.txt | 2 +- include/hw/pci/pci_device.h | 2 +- hw/pci/pci.c | 57 +++++++++++++++++++++++++++++++++++++-- hw/vfio/pci-quirks.c | 2 +- hw/vfio/pci.c | 11 ++++---- hw/xen/xen_pt_load_rom.c | 4 +-- tests/qtest/virtio-net-failover.c | 32 +++++++++++----------- 8 files changed, 88 insertions(+), 29 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -XXX,XX +XXX,XX @@ recommending to switch to their stable counterparts: SD physical layer specification v2.00 supersedes the v1.10 one. v2.00 is the default since QEMU 3.0.0. +Integer specification of PCI device's ``rombar`` property (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specifying an integer for the ``rombar`` property of a PCI device is being +deprecated starting in 9.1 due to obscurity of such an specification. Replace +zero with ``off`` and a non-zero value with ``on``, respectively. + Block device options '''''''''''''''''''' diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt index XXXXXXX..XXXXXXX 100644 --- a/docs/igd-assign.txt +++ b/docs/igd-assign.txt @@ -XXX,XX +XXX,XX @@ IGD has two different modes for assignment using vfio-pci: ISA/LPC bridge device (vfio-pci-igd-lpc-bridge) on the root bus at PCI address 1f.0. * The IGD device must have a VGA ROM, either provided via the romfile - option or loaded automatically through vfio (standard). rombar=0 + option or loaded automatically through vfio (standard). rombar=off will disable legacy mode support. * Hotplug of the IGD device is not supported. * The IGD device must be a SandyBridge or newer model device. diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -XXX,XX +XXX,XX @@ struct PCIDevice { uint32_t romsize; bool has_rom; MemoryRegion rom; - uint32_t rom_bar; + OnOffAuto rom_bar; /* INTx routing notifier */ PCIINTxRoutingNotifier intx_routing_notifier; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ static char *pcibus_get_fw_dev_path(DeviceState *dev); static void pcibus_reset_hold(Object *obj, ResetType type); static bool pcie_has_upstream_port(PCIDevice *dev); +static void rom_bar_get(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + int *ptr = object_field_prop_ptr(obj, prop); + + visit_type_enum(v, name, ptr, prop->info->enum_table, errp); +} + +static void rom_bar_set(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + Error *local_err = NULL; + int *ptr = object_field_prop_ptr(obj, prop); + uint32_t value; + + visit_type_enum(v, name, ptr, prop->info->enum_table, &local_err); + if (!local_err) { + return; + } + + if (visit_type_uint32(v, name, &value, NULL)) { + if (value) { + *ptr = ON_OFF_AUTO_ON; + warn_report("Specifying a number for rombar is deprecated; replace a non-zero value with 'on'"); + } else { + *ptr = ON_OFF_AUTO_OFF; + warn_report("Specifying a number for rombar is deprecated; replace 0 with 'off'"); + } + + return; + } + + error_propagate(errp, local_err); +} + +static void rom_bar_set_default_value(ObjectProperty *op, const Property *prop) +{ + object_property_set_default_str(op, + qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); +} + +static const PropertyInfo qdev_prop_rom_bar = { + .name = "OnOffAuto", + .description = "on/off/auto", + .enum_table = &OnOffAuto_lookup, + .get = rom_bar_get, + .set = rom_bar_set, + .set_default_value = rom_bar_set_default_value, +}; + static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), - DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_SIGNED("rombar", PCIDevice, rom_bar, ON_OFF_AUTO_AUTO, + qdev_prop_rom_bar, OnOffAuto), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, @@ -XXX,XX +XXX,XX @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, return; } - if (!pdev->rom_bar) { + if (pdev->rom_bar == ON_OFF_AUTO_OFF) { /* * Load rom via fw_cfg instead of creating a rom bar, * for 0.11 compatibility. diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -XXX,XX +XXX,XX @@ * execution as noticed with the BCM 57810 card for lack of a * more better way to handle such issues. * The user can still override by specifying a romfile or - * rombar=1. + * rombar=on. * Please see https://bugs.launchpad.net/qemu/+bug/1284874 * for an analysis of the 57810 card hang. When adding * a new vendor id/device id combination below, please also add diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -XXX,XX +XXX,XX @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) error_report("vfio-pci: Cannot read device rom at " "%s", vdev->vbasedev.name); error_printf("Device option ROM contents are probably invalid " - "(check dmesg).\nSkip option ROM probe with rombar=0, " + "(check dmesg).\nSkip option ROM probe with rombar=off, " "or load from file with romfile=\n"); return; } @@ -XXX,XX +XXX,XX @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; - DeviceState *dev = DEVICE(vdev); char *name; int fd = vdev->vbasedev.fd; - if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { + if (vdev->pdev.romfile || vdev->pdev.rom_bar == ON_OFF_AUTO_OFF) { /* Since pci handles romfile, just print a message and return */ if (vfio_opt_rom_in_denylist(vdev) && vdev->pdev.romfile) { warn_report("Device at %s is known to cause system instability" @@ -XXX,XX +XXX,XX @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (dev->opts && qdict_haskey(dev->opts, "rombar")) { + if (vdev->pdev.rom_bar == ON_OFF_AUTO_ON) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); error_printf("Proceeding anyway since user specified" - " non zero value for rombar\n"); + " on for rombar\n"); } else { warn_report("Rom loading for device at %s has been disabled" " due to system instability issues", vdev->vbasedev.name); - error_printf("Specify rombar=1 or romfile to force\n"); + error_printf("Specify rombar=on or romfile to force\n"); return; } } diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -XXX,XX +XXX,XX @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, Object *owner = OBJECT(dev); /* If loading ROM from file, pci handles it */ - if (dev->romfile || !dev->rom_bar) { + if (dev->romfile || dev->rom_bar == ON_OFF_AUTO_OFF) { return NULL; } @@ -XXX,XX +XXX,XX @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, if (!fread(ptr, 1, st.st_size, fp)) { error_report("pci-assign: Cannot read from host %s", rom_file); error_printf("Device option ROM contents are probably invalid " - "(check dmesg).\nSkip option ROM probe with rombar=0, " + "(check dmesg).\nSkip option ROM probe with rombar=off, " "or load from file with romfile=\n"); goto close_rom; } diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -XXX,XX +XXX,XX @@ static void test_hotplug_2_reverse(void) "{'bus': 'root0'," "'failover': true," "'netdev': 'hs0'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_STANDBY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_out(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_in(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_off_migrate_out(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_off_migrate_in(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_guest_off_migrate_out(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_guest_off_migrate_in(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_guest_off_abort(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_abort_wait_unplug(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_abort_active(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_off_abort(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_migrate_abort_timeout(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_multi_out(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_multi_out(gconstpointer opaque) "{'bus': 'root3'," "'failover_pair_id': 'standby1'," "'netdev': 'hs3'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY1"'}"); @@ -XXX,XX +XXX,XX @@ static void test_multi_in(gconstpointer opaque) "{'bus': 'root1'," "'failover_pair_id': 'standby0'," "'netdev': 'hs1'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY0"'}"); @@ -XXX,XX +XXX,XX @@ static void test_multi_in(gconstpointer opaque) "{'bus': 'root3'," "'failover_pair_id': 'standby1'," "'netdev': 'hs3'," - "'rombar': 0," + "'rombar': 'off'," "'romfile': ''," "'mac': '"MAC_PRIMARY1"'}"); -- 2.45.2
It is no longer used. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> --- include/hw/qdev-core.h | 4 ---- hw/core/qdev.c | 1 - system/qdev-monitor.c | 12 +++++++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -XXX,XX +XXX,XX @@ struct DeviceState { * @pending_deleted_expires_ms: optional timeout for deletion events */ int64_t pending_deleted_expires_ms; - /** - * @opts: QDict of options for the device - */ - QDict *opts; /** * @hotplugged: was device added after PHASE_MACHINE_READY? */ diff --git a/hw/core/qdev.c b/hw/core/qdev.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -XXX,XX +XXX,XX @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qobject_unref(dev->opts); g_free(dev->id); } diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index XXXXXXX..XXXXXXX 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -XXX,XX +XXX,XX @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, char *id; DeviceState *dev = NULL; BusState *bus = NULL; + QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { @@ -XXX,XX +XXX,XX @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } /* set properties */ - dev->opts = qdict_clone_shallow(opts); - qdict_del(dev->opts, "driver"); - qdict_del(dev->opts, "bus"); - qdict_del(dev->opts, "id"); + properties = qdict_clone_shallow(opts); + qdict_del(properties, "driver"); + qdict_del(properties, "bus"); + qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); + qobject_unref(properties); if (*errp) { goto err_del_dev; } -- 2.45.2
Supersedes: <20240714-rombar-v2-0-af1504ef55de@daynix.com> ("[PATCH v2 0/4] hw/pci: Convert rom_bar into OnOffAuto") I submitted a RFC series[1] to add support for SR-IOV emulation to virtio-net-pci. During the development of the series, I fixed some trivial bugs and made improvements that I think are independently useful. This series extracts those fixes and improvements from the RFC series. [1]: https://patchew.org/QEMU/20231210-sriov-v2-0-b959e8a6dfaf@daynix.com/ Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- Changes in v13: - Added patch "s390x/pci: Check for multifunction after device realization". I found SR-IOV devices, which are multifunction devices, are not supposed to work at all with s390x on QEMU. - Link to v12: https://lore.kernel.org/r/20240804-reuse-v12-0-d3930c4111b2@daynix.com Changes in v12: - Changed to ignore invalid PCI_SRIOV_NUM_VF writes as done for PCI_SRIOV_CTRL_VFE. - Updated the message for patch "hw/pci: Use -1 as the default value for rombar". (Markus Armbruster) - Link to v11: https://lore.kernel.org/r/20240802-reuse-v11-0-fb83bb8c19fb@daynix.com Changes in v11: - Rebased. - Dropped patch "hw/pci: Convert rom_bar into OnOffAuto". - Added patch "hw/pci: Use -1 as the default value for rombar". - Added for-9.2 to give a testing period for possible breakage with libvirt/s390x. - Link to v10: https://lore.kernel.org/r/20240627-reuse-v10-0-7ca0b8ed3d9f@daynix.com Changes in v10: - Added patch "hw/ppc/spapr_pci: Do not reject VFs created after a PF". - Added patch "hw/ppc/spapr_pci: Do not create DT for disabled PCI device". - Added patch "hw/pci: Convert rom_bar into OnOffAuto". - Dropped patch "hw/pci: Determine if rombar is explicitly enabled". - Dropped patch "hw/qdev: Remove opts member". - Link to v9: https://lore.kernel.org/r/20240315-reuse-v9-0-67aa69af4d53@daynix.com Changes in v9: - Rebased. - Restored '#include "qapi/error.h"' (Michael S. Tsirkin) - Added patch "pcie_sriov: Ensure VF function number does not overflow" to fix abortion with wrong PF addr. - Link to v8: https://lore.kernel.org/r/20240228-reuse-v8-0-282660281e60@daynix.com Changes in v8: - Clarified that "hw/pci: Replace -1 with UINT32_MAX for romsize" is not a bug fix. (Markus Armbruster) - Squashed patch "vfio: Avoid inspecting option QDict for rombar" into "hw/pci: Determine if rombar is explicitly enabled". (Markus Armbruster) - Noted the minor semantics change for patch "hw/pci: Determine if rombar is explicitly enabled". (Markus Armbruster) - Link to v7: https://lore.kernel.org/r/20240224-reuse-v7-0-29c14bcb952e@daynix.com Changes in v7: - Replaced -1 with UINT32_MAX when expressing uint32_t. (Markus Armbruster) - Added patch "hw/pci: Replace -1 with UINT32_MAX for romsize". - Link to v6: https://lore.kernel.org/r/20240220-reuse-v6-0-2e42a28b0cf2@daynix.com Changes in v6: - Fixed migration. - Added patch "pcie_sriov: Do not manually unrealize". - Restored patch "pcie_sriov: Release VFs failed to realize" that was missed in v5. - Link to v5: https://lore.kernel.org/r/20240218-reuse-v5-0-e4fc1c19b5a9@daynix.com Changes in v5: - Added patch "hw/pci: Always call pcie_sriov_pf_reset()". - Added patch "pcie_sriov: Reset SR-IOV extended capability". - Removed a reference to PCI_SRIOV_CTRL_VFE in hw/nvme. (Michael S. Tsirkin) - Noted the impact on the guest of patch "pcie_sriov: Do not reset NumVFs after unregistering VFs". (Michael S. Tsirkin) - Changed to use pcie_sriov_num_vfs(). - Restored pci_set_power() and changed it to call pci_set_enabled() only for PFs with an expalanation. (Michael S. Tsirkin) - Reordered patches. - Link to v4: https://lore.kernel.org/r/20240214-reuse-v4-0-89ad093a07f4@daynix.com Changes in v4: - Reverted the change to pci_rom_bar_explicitly_enabled(). (Michael S. Tsirkin) - Added patch "pcie_sriov: Do not reset NumVFs after unregistering VFs". - Added patch "hw/nvme: Refer to dev->exp.sriov_pf.num_vfs". - Link to v3: https://lore.kernel.org/r/20240212-reuse-v3-0-8017b689ce7f@daynix.com Changes in v3: - Extracted patch "hw/pci: Use -1 as a default value for rombar" from patch "hw/pci: Determine if rombar is explicitly enabled" (Philippe Mathieu-Daudé) - Added an audit result of PCIDevice::rom_bar to the message of patch "hw/pci: Use -1 as a default value for rombar" (Philippe Mathieu-Daudé) - Link to v2: https://lore.kernel.org/r/20240210-reuse-v2-0-24ba2a502692@daynix.com Changes in v2: - Reset after enabling a function so that NVMe VF state gets updated. - Link to v1: https://lore.kernel.org/r/20240203-reuse-v1-0-5be8c5ce6338@daynix.com --- Akihiko Odaki (12): hw/pci: Rename has_power to enabled hw/ppc/spapr_pci: Do not create DT for disabled PCI device hw/ppc/spapr_pci: Do not reject VFs created after a PF s390x/pci: Check for multifunction after device realization pcie_sriov: Do not manually unrealize pcie_sriov: Ensure VF function number does not overflow pcie_sriov: Reuse SR-IOV VF device instances pcie_sriov: Release VFs failed to realize pcie_sriov: Remove num_vfs from PCIESriovPF pcie_sriov: Register VFs after migration hw/pci: Use -1 as the default value for rombar hw/qdev: Remove opts member docs/pcie_sriov.txt | 8 ++- include/hw/pci/pci.h | 2 +- include/hw/pci/pci_device.h | 19 ++++- include/hw/pci/pcie_sriov.h | 9 +-- include/hw/qdev-core.h | 4 -- hw/core/qdev.c | 1 - hw/net/igb.c | 13 +++- hw/nvme/ctrl.c | 24 ++++--- hw/pci/pci.c | 23 +++--- hw/pci/pci_host.c | 4 +- hw/pci/pcie_sriov.c | 165 +++++++++++++++++++++++++------------------- hw/ppc/spapr_pci.c | 8 ++- hw/s390x/s390-pci-bus.c | 14 ++-- hw/vfio/pci.c | 5 +- system/qdev-monitor.c | 12 ++-- hw/pci/trace-events | 2 +- 16 files changed, 187 insertions(+), 126 deletions(-) --- base-commit: 31669121a01a14732f57c49400bc239cf9fd505f change-id: 20240129-reuse-faae22b11934 Best regards, -- Akihiko Odaki <akihiko.odaki@daynix.com>
The renamed state will not only represent powering state of PFs, but also represent SR-IOV VF enablement in the future. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/hw/pci/pci.h | 7 ++++++- include/hw/pci/pci_device.h | 2 +- hw/pci/pci.c | 14 +++++++------- hw/pci/pci_host.c | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -XXX,XX +XXX,XX @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) } MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); -void pci_set_power(PCIDevice *pci_dev, bool state); +void pci_set_enabled(PCIDevice *pci_dev, bool state); + +static inline void pci_set_power(PCIDevice *pci_dev, bool state) +{ + pci_set_enabled(pci_dev, state); +} #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -XXX,XX +XXX,XX @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; - bool has_power; + bool enabled; /* PCI config space */ uint8_t *config; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->has_power) { + if (!d->enabled) { new_addr = PCI_BAR_UNMAPPED; } @@ -XXX,XX +XXX,XX @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int pci_update_irq_disabled(d, was_irq_disabled); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); + & PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -XXX,XX +XXX,XX @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) return msg; } -void pci_set_power(PCIDevice *d, bool state) +void pci_set_enabled(PCIDevice *d, bool state) { - if (d->has_power == state) { + if (d->enabled == state) { return; } - d->has_power = state; + d->enabled = state; pci_update_mappings(d); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); - if (!d->has_power) { + & PCI_COMMAND_MASTER) && d->enabled); + if (!d->enabled) { pci_device_reset(d); } } diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -XXX,XX +XXX,XX @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return; } @@ -XXX,XX +XXX,XX @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return ~0x0; } -- 2.45.2
Disabled means it is a disabled SR-IOV VF or it is powered off, and hidden from the guest. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- hw/ppc/spapr_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -XXX,XX +XXX,XX @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev, return; } + if (!pdev->enabled) { + return; + } + err = spapr_dt_pci_device(p->sphb, pdev, p->fdt, p->offset); if (err < 0) { p->err = err; -- 2.45.2
A PF may automatically create VFs and the PF may be function 0. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- hw/ppc/spapr_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -XXX,XX +XXX,XX @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * hotplug, we do not allow functions to be hotplugged to a * slot that already has function 0 present */ - if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + if (plugged_dev->hotplugged && + !pci_is_vf(pdev) && + bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " additional functions can no longer be exposed to guest.", -- 2.45.2
The SR-IOV PFs set the multifunction bits during device realization so check them after that. This forbids adding SR-IOV devices to s390x. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- hw/s390x/s390-pci-bus.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index XXXXXXX..XXXXXXX 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -XXX,XX +XXX,XX @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, "this device"); } - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pdev = PCI_DEVICE(dev); - - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - error_setg(errp, "multifunction not supported in s390"); - return; - } - } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); if (!s390_pci_alloc_idx(s, pbdev)) { @@ -XXX,XX +XXX,XX @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pdev = PCI_DEVICE(dev); + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + error_setg(errp, "multifunction not supported in s390"); + return; + } + if (!dev->id) { /* In the case the PCI device does not define an id */ /* we generate one based on the PCI address */ -- 2.45.2
A device gets automatically unrealized when being unparented. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- hw/pci/pcie_sriov.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - Error *err = NULL; PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) { - error_reportf_err(err, "Failed to unplug: "); - } object_unparent(OBJECT(vf)); object_unref(OBJECT(vf)); } -- 2.45.2
pci_new() aborts when creating a VF with a function number equals to or is greater than PCI_DEVFN_MAX. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- docs/pcie_sriov.txt | 8 +++++--- include/hw/pci/pcie_sriov.h | 5 +++-- hw/net/igb.c | 13 ++++++++++--- hw/nvme/ctrl.c | 24 ++++++++++++++++-------- hw/pci/pcie_sriov.c | 19 +++++++++++++++++-- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index XXXXXXX..XXXXXXX 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -XXX,XX +XXX,XX @@ setting up a BAR for a VF. ... /* Add and initialize the SR/IOV capability */ - pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", - vf_devid, initial_vfs, total_vfs, - fun_offset, stride); + if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride, errp)) { + return; + } /* Set up individual VF BARs (parameters as for normal BARs) */ pcie_sriov_pf_init_vf_bar( ... ) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -XXX,XX +XXX,XX @@ typedef struct PCIESriovVF { uint16_t vf_number; /* Logical VF number of this function */ } PCIESriovVF; -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride); + uint16_t vf_offset, uint16_t vf_stride, + Error **errp); void pcie_sriov_pf_exit(PCIDevice *dev); /* Set up a VF bar in the SR/IOV bar area */ diff --git a/hw/net/igb.c b/hw/net/igb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -XXX,XX +XXX,XX @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_ari_init(pci_dev, 0x150); - pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, - IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, - IGB_VF_OFFSET, IGB_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, + TYPE_IGBVF, IGB_82576_VF_DEV_ID, + IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, + IGB_VF_OFFSET, IGB_VF_STRIDE, + errp)) { + pcie_cap_exit(pci_dev); + igb_cleanup_msix(s); + msi_uninit(pci_dev); + return; + } pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index XXXXXXX..XXXXXXX 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -XXX,XX +XXX,XX @@ out: return pow2ceil(bar_size); } -static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) +static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, + Error **errp) { uint16_t vf_dev_id = n->params.use_intel_id ? PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; @@ -XXX,XX +XXX,XX @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) le16_to_cpu(cap->vifrsm), NULL, NULL); - pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, - n->params.sriov_max_vfs, n->params.sriov_max_vfs, - NVME_VF_OFFSET, NVME_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE, + errp)) { + return false; + } pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); + + return true; } static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) @@ -XXX,XX +XXX,XX @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return false; } + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs && + !nvme_init_sriov(n, pci_dev, 0x120, errp)) { + msix_uninit(pci_dev, &n->bar0, &n->bar0); + return false; + } + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); pcie_cap_deverr_init(pci_dev); @@ -XXX,XX +XXX,XX @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } - if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { - nvme_init_sriov(n, pci_dev, 0x120); - } - return true; } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, uint16_t vf_num); static void unregister_vfs(PCIDevice *dev); -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride) + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) { uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (total_vfs) { + uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI); + uint16_t first_vf_devfn = dev->devfn + vf_offset; + uint16_t last_vf_devfn = first_vf_devfn + vf_stride * (total_vfs - 1); + + if ((!ari_cap && PCI_SLOT(dev->devfn) != PCI_SLOT(last_vf_devfn)) || + last_vf_devfn >= PCI_DEVFN_MAX) { + error_setg(errp, "VF function number overflows"); + return false; + } + } + pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; @@ -XXX,XX +XXX,XX @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); qdev_prop_set_bit(&dev->qdev, "multifunction", true); + + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) -- 2.45.2
Disable SR-IOV VF devices by reusing code to power down PCI devices instead of removing them when the guest requests to disable VFs. This allows to realize devices and report VF realization errors at PF realization time. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/hw/pci/pci.h | 5 --- include/hw/pci/pci_device.h | 15 +++++++ include/hw/pci/pcie_sriov.h | 1 - hw/pci/pci.c | 2 +- hw/pci/pcie_sriov.c | 95 +++++++++++++++++++-------------------------- 5 files changed, 56 insertions(+), 62 deletions(-) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -XXX,XX +XXX,XX @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_enabled(PCIDevice *pci_dev, bool state); -static inline void pci_set_power(PCIDevice *pci_dev, bool state) -{ - pci_set_enabled(pci_dev, state); -} - #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -XXX,XX +XXX,XX @@ static inline uint16_t pci_get_bdf(PCIDevice *dev) return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn); } +static inline void pci_set_power(PCIDevice *pci_dev, bool state) +{ + /* + * Don't change the enabled state of VFs when powering on/off the device. + * + * When powering on, VFs must not be enabled immediately but they must + * wait until the guest configures SR-IOV. + * When powering off, their corresponding PFs will be reset and disable + * VFs. + */ + if (!pci_is_vf(pci_dev)) { + pci_set_enabled(pci_dev, state); + } +} + uint16_t pci_requester_id(PCIDevice *dev); /* DMA access functions */ diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -XXX,XX +XXX,XX @@ typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ - const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ void pci_set_enabled(PCIDevice *d, bool state) memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_MASTER) && d->enabled); - if (!d->enabled) { + if (d->qdev.realized) { pci_device_reset(d); } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ #include "qapi/error.h" #include "trace.h" -static PCIDevice *register_vf(PCIDevice *pf, int devfn, - const char *name, uint16_t vf_num); -static void unregister_vfs(PCIDevice *dev); +static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) +{ + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); + } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; +} bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, @@ -XXX,XX +XXX,XX @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint16_t vf_offset, uint16_t vf_stride, Error **errp) { + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -XXX,XX +XXX,XX @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; dev->exp.sriov_pf.num_vfs = 0; - dev->exp.sriov_pf.vfname = g_strdup(vfname); dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -XXX,XX +XXX,XX @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); + + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = pci_new(devfn, vfname); + vf->exp.sriov_vf.pf = dev; + vf->exp.sriov_vf.vf_number = i; + + if (!qdev_realize(&vf->qdev, bus, errp)) { + unparent_vfs(dev, i); + return false; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vf->config, 0xffff); + pci_config_set_device_id(vf->config, 0xffff); + + dev->exp.sriov_pf.vf[i] = vf; + devfn += vf_stride; + } + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) { - unregister_vfs(dev); - g_free((char *)dev->exp.sriov_pf.vfname); - dev->exp.sriov_pf.vfname = NULL; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -XXX,XX +XXX,XX @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } -static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, - uint16_t vf_num) -{ - PCIDevice *dev = pci_new(devfn, name); - dev->exp.sriov_vf.pf = pf; - dev->exp.sriov_vf.vf_number = vf_num; - PCIBus *bus = pci_get_bus(pf); - Error *local_err = NULL; - - qdev_realize(&dev->qdev, &bus->qbus, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; - } - - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(dev->config, 0xffff); - pci_config_set_device_id(dev->config, 0xffff); - - return dev; -} - static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; uint16_t i; uint16_t sriov_cap = dev->exp.sriov_cap; - uint16_t vf_offset = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); - uint16_t vf_stride = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - int32_t devfn = dev->devfn + vf_offset; assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); @@ -XXX,XX +XXX,XX @@ static void register_vfs(PCIDevice *dev) return; } - dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, - dev->exp.sriov_pf.vfname, i); - if (!dev->exp.sriov_pf.vf[i]) { - num_vfs = i; - break; - } - devfn += vf_stride; + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } dev->exp.sriov_pf.num_vfs = num_vfs; } @@ -XXX,XX +XXX,XX @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - object_unparent(OBJECT(vf)); - object_unref(OBJECT(vf)); + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - g_free(dev->exp.sriov_pf.vf); - dev->exp.sriov_pf.vf = NULL; dev->exp.sriov_pf.num_vfs = 0; } @@ -XXX,XX +XXX,XX @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, PCI_FUNC(dev->devfn), off, val, len); if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (dev->exp.sriov_pf.num_vfs) { - if (!(val & PCI_SRIOV_CTRL_VFE)) { - unregister_vfs(dev); - } + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); } else { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } + unregister_vfs(dev); } } } -- 2.45.2
Release VFs failed to realize just as we do in unregister_vfs(). Fixes: 7c0fa8dff811 ("pcie: Add support for Single Root I/O Virtualization (SR/IOV)") Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- hw/pci/pcie_sriov.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, vf->exp.sriov_vf.vf_number = i; if (!qdev_realize(&vf->qdev, bus, errp)) { + object_unparent(OBJECT(vf)); + object_unref(vf); unparent_vfs(dev, i); return false; } -- 2.45.2
num_vfs is not migrated so use PCI_SRIOV_CTRL_VFE and PCI_SRIOV_NUM_VF instead. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/hw/pci/pcie_sriov.h | 1 - hw/pci/pcie_sriov.c | 38 +++++++++++++++++++++++++++----------- hw/pci/trace-events | 2 +- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -XXX,XX +XXX,XX @@ #include "hw/pci/pci.h" typedef struct PCIESriovPF { - uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; - dev->exp.sriov_pf.num_vfs = 0; dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -XXX,XX +XXX,XX @@ static void register_vfs(PCIDevice *dev) assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); - if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { - return; - } trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } - dev->exp.sriov_pf.num_vfs = num_vfs; + + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); } static void unregister_vfs(PCIDevice *dev) { - uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; uint16_t i; trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - dev->exp.sriov_pf.num_vfs = 0; + + pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -XXX,XX +XXX,XX @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } else { unregister_vfs(dev); } + } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { + uint8_t *cfg = dev->config + sriov_cap; + uint8_t *wmask = dev->wmask + sriov_cap; + uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); + uint16_t val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; + + if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { + val |= PCI_SRIOV_CTRL_VFE; + } + + pci_set_word(wmask + PCI_SRIOV_CTRL, val); } } @@ -XXX,XX +XXX,XX @@ void pcie_sriov_pf_reset(PCIDevice *dev) unregister_vfs(dev); pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0); + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_CTRL, + PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); /* * Default is to use 4K pages, software can modify it @@ -XXX,XX +XXX,XX @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) { assert(!pci_is_vf(dev)); - if (n < dev->exp.sriov_pf.num_vfs) { + if (n < pcie_sriov_num_vfs(dev)) { return dev->exp.sriov_pf.vf[n]; } return NULL; @@ -XXX,XX +XXX,XX @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) uint16_t pcie_sriov_num_vfs(PCIDevice *dev) { - return dev->exp.sriov_pf.num_vfs; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint8_t *cfg = dev->config + sriov_cap; + + return sriov_cap && + (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? + pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; } diff --git a/hw/pci/trace-events b/hw/pci/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -XXX,XX +XXX,XX @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask # hw/pci/pcie_sriov.c sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" -sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" +sriov_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering vf devs" sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" # pcie.c -- 2.45.2
pcie_sriov doesn't have code to restore its state after migration, but igb, which uses pcie_sriov, naively claimed its migration capability. Add code to register VFs after migration and fix igb migration. Fixes: 3a977deebe6b ("Intrdocue igb device emulation") Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/hw/pci/pcie_sriov.h | 2 ++ hw/pci/pci.c | 7 +++++++ hw/pci/pcie_sriov.c | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -XXX,XX +XXX,XX @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, uint32_t val, int len); +void pcie_sriov_pf_post_load(PCIDevice *dev); + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ static bool migrate_is_not_pcie(void *opaque, int version_id) return !pci_is_express((PCIDevice *)opaque); } +static int pci_post_load(void *opaque, int version_id) +{ + pcie_sriov_pf_post_load(opaque); + return 0; +} + const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, + .post_load = pci_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -XXX,XX +XXX,XX @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } } +void pcie_sriov_pf_post_load(PCIDevice *dev) +{ + if (dev->exp.sriov_cap) { + register_vfs(dev); + } +} + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev) -- 2.45.2
vfio_pci_size_rom() distinguishes whether rombar is explicitly set to 1 by checking dev->opts, bypassing the QOM property infrastructure. Use -1 as the default value for rombar to tell if the user explicitly set it to 1. The property is also converted from unsigned to signed. -1 is signed so it is safe to give it a new meaning. The values in [2 ^ 31, 2 ^ 32) become invalid, but nobody should have typed these values by chance. Suggested-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> --- include/hw/pci/pci_device.h | 2 +- hw/pci/pci.c | 2 +- hw/vfio/pci.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -XXX,XX +XXX,XX @@ struct PCIDevice { uint32_t romsize; bool has_rom; MemoryRegion rom; - uint32_t rom_bar; + int32_t rom_bar; /* INTx routing notifier */ PCIINTxRoutingNotifier intx_routing_notifier; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -XXX,XX +XXX,XX @@ static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), - DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_INT32("rombar", PCIDevice, rom_bar, -1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -XXX,XX +XXX,XX @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; - DeviceState *dev = DEVICE(vdev); char *name; int fd = vdev->vbasedev.fd; @@ -XXX,XX +XXX,XX @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (dev->opts && qdict_haskey(dev->opts, "rombar")) { + if (vdev->pdev.rom_bar > 0) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); error_printf("Proceeding anyway since user specified" - " non zero value for rombar\n"); + " positive value for rombar\n"); } else { warn_report("Rom loading for device at %s has been disabled" " due to system instability issues", -- 2.45.2
It is no longer used. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> --- include/hw/qdev-core.h | 4 ---- hw/core/qdev.c | 1 - system/qdev-monitor.c | 12 +++++++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -XXX,XX +XXX,XX @@ struct DeviceState { * @pending_deleted_expires_ms: optional timeout for deletion events */ int64_t pending_deleted_expires_ms; - /** - * @opts: QDict of options for the device - */ - QDict *opts; /** * @hotplugged: was device added after PHASE_MACHINE_READY? */ diff --git a/hw/core/qdev.c b/hw/core/qdev.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -XXX,XX +XXX,XX @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qobject_unref(dev->opts); g_free(dev->id); } diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index XXXXXXX..XXXXXXX 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -XXX,XX +XXX,XX @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, char *id; DeviceState *dev = NULL; BusState *bus = NULL; + QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { @@ -XXX,XX +XXX,XX @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } /* set properties */ - dev->opts = qdict_clone_shallow(opts); - qdict_del(dev->opts, "driver"); - qdict_del(dev->opts, "bus"); - qdict_del(dev->opts, "id"); + properties = qdict_clone_shallow(opts); + qdict_del(properties, "driver"); + qdict_del(properties, "bus"); + qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); + qobject_unref(properties); if (*errp) { goto err_del_dev; } -- 2.45.2