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(a)redhat.com>
Reviewed-by: Ján Tomko <jtomko(a)redhat.com>
---
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