Some USB devices have a buggy firmware that either crashes on device reset, or make the device unusable in some other way. Fortunately, QEMU offers a way to skip device reset either completely, or if device is not initialized yet. Expose this ability to users under: <hostdev mode='subsystem' type='usb'> <source guestReset='off'/> </hostdev> Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> Reviewed-by: Ján Tomko <jtomko@xxxxxxxxxx> --- docs/formatdomain.rst | 15 ++++++++++++++- src/conf/domain_conf.c | 18 ++++++++++++++++++ src/conf/domain_conf.h | 13 +++++++++++++ src/conf/schemas/domaincommon.rng | 9 +++++++++ tests/qemuxml2argvdata/hostdev-usb-address.xml | 2 +- .../hostdev-usb-address.x86_64-latest.xml | 2 +- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 89b627f4bd..3ea094e64c 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -4051,7 +4051,7 @@ for PCI (KVM only) and 1.0.6 for SCSI (KVM only)` : ... <devices> <hostdev mode='subsystem' type='usb'> - <source startupPolicy='optional'> + <source startupPolicy='optional' guestReset='off'> <vendor id='0x1234'/> <product id='0xbeef'/> </source> @@ -4231,6 +4231,19 @@ or: optional drop if missing at any start attempt ========= ===================================================================== + :since:`Since 8.6.0`, the ``source`` element can contain ``guestReset`` + attribute with the following value: + + ============= ===================================================== + off all guest initiated device reset requests are ignored + uninitialized device request is ignored if device is initialized, + otherwise reset is performed + on device is reset on every guest initiated request + ============= ===================================================== + + This attribute can be helpful when assigning an USB device with a + firmware that crashes on reset. + ``pci`` PCI devices can only be described by their ``address``. :since:`Since 6.8.0 (Xen only)` , the ``source`` element of a PCI device diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e80616fe7b..4c7a5a044c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1042,6 +1042,14 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIProtocol, "iscsi", ); +VIR_ENUM_IMPL(virDomainHostdevSubsysUSBGuestReset, + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_LAST, + "default", + "off", + "uninitialized", + "on", +); + VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIHostProtocol, VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_LAST, "none", @@ -5489,6 +5497,11 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node, return -1; virTristateBoolToBool(autoAddress, &usbsrc->autoAddress); + if (virXMLPropEnum(node, "guestReset", + virDomainHostdevSubsysUSBGuestResetTypeFromString, + VIR_XML_PROP_NONZERO, &usbsrc->guestReset) < 0) + return -1; + /* Product can validly be 0, so we need some extra help to determine * if it is uninitialized */ vendorNode = virXPathNode("./vendor", ctxt); @@ -22989,6 +23002,11 @@ virDomainHostdevDefFormatSubsysUSB(virBuffer *buf, if (def->missing && !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) virBufferAddLit(&sourceAttrBuf, " missing='yes'"); + if (usbsrc->guestReset) { + virBufferAsprintf(&sourceAttrBuf, " guestReset='%s'", + virDomainHostdevSubsysUSBGuestResetTypeToString(usbsrc->guestReset)); + } + if (usbsrc->vendor) { virBufferAsprintf(&sourceChildBuf, "<vendor id='0x%.4x'/>\n", usbsrc->vendor); virBufferAsprintf(&sourceChildBuf, "<product id='0x%.4x'/>\n", usbsrc->product); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index c56b84683c..90de50c12f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -233,6 +233,17 @@ typedef enum { VIR_ENUM_DECL(virDomainHostdevSubsysSCSIProtocol); +typedef enum { + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_DEFAULT = 0, + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_OFF, /* reset forbidden */ + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_UNINITIALIZED, /* reset iff uninitialized */ + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_ON, /* reset allowed */ + + VIR_DOMAIN_HOSTDEV_USB_GUEST_RESET_LAST +} virDomainHostdevSubsysUSBGuestReset; + +VIR_ENUM_DECL(virDomainHostdevSubsysUSBGuestReset); + struct _virDomainHostdevSubsysUSB { bool autoAddress; /* bus/device were filled automatically based on vendor/product */ @@ -241,6 +252,8 @@ struct _virDomainHostdevSubsysUSB { unsigned vendor; unsigned product; + + virDomainHostdevSubsysUSBGuestReset guestReset; }; struct _virDomainHostdevSubsysPCI { diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index da2fb0d5cb..faa2561665 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5936,6 +5936,15 @@ <optional> <ref name="startupPolicy"/> </optional> + <optional> + <attribute name="guestReset"> + <choice> + <value>off</value> + <value>uninitialized</value> + <value>on</value> + </choice> + </attribute> + </optional> <choice> <group> <ref name="usbproduct"/> diff --git a/tests/qemuxml2argvdata/hostdev-usb-address.xml b/tests/qemuxml2argvdata/hostdev-usb-address.xml index 03c802a532..50c02f9b0e 100644 --- a/tests/qemuxml2argvdata/hostdev-usb-address.xml +++ b/tests/qemuxml2argvdata/hostdev-usb-address.xml @@ -26,7 +26,7 @@ <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <hostdev mode='subsystem' type='usb' managed='no'> - <source> + <source guestReset='uninitialized'> <address bus='14' device='6'/> </source> </hostdev> diff --git a/tests/qemuxml2xmloutdata/hostdev-usb-address.x86_64-latest.xml b/tests/qemuxml2xmloutdata/hostdev-usb-address.x86_64-latest.xml index e5e3620971..2e927252a8 100644 --- a/tests/qemuxml2xmloutdata/hostdev-usb-address.x86_64-latest.xml +++ b/tests/qemuxml2xmloutdata/hostdev-usb-address.x86_64-latest.xml @@ -34,7 +34,7 @@ <input type='keyboard' bus='ps2'/> <audio id='1' type='none'/> <hostdev mode='subsystem' type='usb' managed='no'> - <source> + <source guestReset='uninitialized'> <address bus='14' device='6'/> </source> </hostdev> -- 2.35.1