On Mon, Jun 23, 2025 at 04:11:30PM +0200, Michal Prívozník via Devel wrote: > On 4/21/25 21:38, Maximilian Martin via Devel wrote: > > From: Maximilian Martin <maximilian_mar...@gmx.de> > > > > This patch implements USB bus/port matching. In addition > > to vendor/product and bus/device matching, bus/port > > matching is implemented. This involves additions and > > refactorings in the domain configuration, host device > > handling, and USB helpers. Also, test methods are > > refactored and extended. > > > > Signed-off-by: Maximilian Martin <maximilian_mar...@gmx.de> > > --- > > src/conf/domain_conf.c | 69 ++++++++++++++-- > > src/conf/domain_conf.h | 1 + > > src/hypervisor/virhostdev.c | 131 +++++++++++++++++------------- > > src/libvirt_private.syms | 2 - > > src/util/virusb.c | 156 ++++++++++++------------------------ > > src/util/virusb.h | 32 ++++---- > > tests/virusbtest.c | 149 ++++++++++++++++++++++++---------- > > 7 files changed, 312 insertions(+), 228 deletions(-) > > There's still a lot happening here, but see my comments below. > > > > > diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c > > index 542d6ade91..423cad99e5 100644 > > --- a/src/conf/domain_conf.c > > +++ b/src/conf/domain_conf.c > > @@ -2670,6 +2670,15 @@ > > virDomainHostdevSubsysSCSIClear(virDomainHostdevSubsysSCSI *scsisrc) > > } > > } > > > > +static void > > +virDomainHostdevSubsysUSBClear(virDomainHostdevSubsysUSB *usbsrc) > > +{ > > + if (!usbsrc) > > + return; > > + > > + VIR_FREE(usbsrc->port); > > +} > > + > > > > static void > > virDomainHostdevDefClear(virDomainHostdevDef *def) > > @@ -2713,6 +2722,8 @@ virDomainHostdevDefClear(virDomainHostdevDef *def) > > g_clear_pointer(&def->source.subsys.u.pci.origstates, > > virBitmapFree); > > break; > > case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: > > + virDomainHostdevSubsysUSBClear(&def->source.subsys.u.usb); > > + break; > > case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: > > case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: > > break; > > @@ -5945,13 +5956,47 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr > > node, > > } > > > > if ((addressNode = virXPathNode("./address", ctxt))) { > > - if (virXMLPropUInt(addressNode, "bus", 0, > > - VIR_XML_PROP_REQUIRED, &usbsrc->bus) < 0) > > + bool found_device = false; > > + bool found_port = false; > > Nitpick, we prefer cammelCase. > > > + char *port = NULL; > > + int ret = -1; > > The 'ret' name is reserved for actual return value of functions. For > storing of retvals of other functions you can use 'rc', 'rv' or anything > else. > > > + > > + ret = virXMLPropUInt(addressNode, "bus", 0, > > + VIR_XML_PROP_REQUIRED, &usbsrc->bus); > > + if (ret < 0) { > > + return -1; > > + } else if (ret == 0) { > > + virReportError(VIR_ERR_INTERNAL_ERROR, > > + "%s", _("missing bus")); > > return -1; > > This case can never happen. When VIR_XML_PROP_REQUIRED flag is specified > then virXMLProp*() functions return either -1 or 1. > > > + } > > > > - if (virXMLPropUInt(addressNode, "device", 0, > > - VIR_XML_PROP_REQUIRED, &usbsrc->device) < 0) > > + ret = virXMLPropUInt(addressNode, "device", 0, > > + VIR_XML_PROP_NONE, &usbsrc->device); > > + if (ret < 0) > > return -1; > > + else if (ret > 0) > > + found_device = true; > > + > > + port = virXMLPropString(addressNode, "port"); > > + if (port) { > > + if (*port) { > > + usbsrc->port = port; > > + found_port = true; > > + } else { > > + VIR_FREE(port); > > If 'port' is declared as g_autofree then this is needless. > > > + } > > + } > > + > > + if (!found_device && !found_port) { > > + virReportError(VIR_ERR_INTERNAL_ERROR, > > + "%s", _("usb address needs either device id or port")); > > + return -1; > > + } else if (found_device && found_port) { > > + virReportError(VIR_ERR_INTERNAL_ERROR, > > + "%s", _("found both device id and port in usb address > > (ambiguous setting)")); > > + return -1; > > + } > > } > > > > return 0; > > @@ -14774,8 +14819,13 @@ virDomainHostdevMatchSubsysUSB(virDomainHostdevDef > > *first, > > virDomainHostdevSubsysUSB *first_usbsrc = &first->source.subsys.u.usb; > > virDomainHostdevSubsysUSB *second_usbsrc = > > &second->source.subsys.u.usb; > > > > - if (first_usbsrc->bus && first_usbsrc->device) { > > - /* specified by bus location on host */ > > + if (first_usbsrc->bus && first_usbsrc->port) { > > + /* specified by bus and port on host */ > > + if (first_usbsrc->bus == second_usbsrc->bus && > > + STREQ_NULLABLE(first_usbsrc->port, second_usbsrc->port)) > > + return 1; > > + } else if (first_usbsrc->bus && first_usbsrc->device) { > > + /* specified by bus and device id on host */ > > if (first_usbsrc->bus == second_usbsrc->bus && > > first_usbsrc->device == second_usbsrc->device) > > return 1; > > @@ -24345,10 +24395,15 @@ virDomainHostdevDefFormatSubsysUSB(virBuffer *buf, > > virBufferAsprintf(&sourceChildBuf, "<product id='0x%.4x'/>\n", > > usbsrc->product); > > } > > > > - if (usbsrc->bus || usbsrc->device) > > + if (usbsrc->bus && usbsrc->port) { > > + virBufferAsprintf(&sourceChildBuf, "<address %sbus='%d' > > port='%s'/>\n", > > + includeTypeInAddr ? "type='usb' " : "", > > + usbsrc->bus, usbsrc->port); > > + } else if (usbsrc->bus || usbsrc->device) { > > virBufferAsprintf(&sourceChildBuf, "<address %sbus='%d' > > device='%d'/>\n", > > includeTypeInAddr ? "type='usb' " : "", > > usbsrc->bus, usbsrc->device); > > + } > > > > virXMLFormatElement(buf, "source", &sourceAttrBuf, &sourceChildBuf); > > } > > diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h > > index 58b97a2b54..aaa8b8573a 100644 > > --- a/src/conf/domain_conf.h > > +++ b/src/conf/domain_conf.h > > @@ -228,6 +228,7 @@ struct _virDomainHostdevSubsysUSB { > > on vendor/product */ > > unsigned bus; > > unsigned device; > > + char *port; > > > > unsigned vendor; > > unsigned product; > > diff --git a/src/hypervisor/virhostdev.c b/src/hypervisor/virhostdev.c > > index 0a1d8500d4..9e77477a9b 100644 > > --- a/src/hypervisor/virhostdev.c > > +++ b/src/hypervisor/virhostdev.c > > @@ -1358,6 +1358,42 @@ virHostdevMarkUSBDevices(virHostdevManager *mgr, > > return -1; > > } > > > > +static int > > +virHostdevFindUSBDeviceWithFlags(virDomainHostdevDef *hostdev, > > + bool mandatory, > > + unsigned int flags, > > + virUSBDevice **usb) > > +{ > > + virDomainHostdevSubsysUSB *usbsrc = &hostdev->source.subsys.u.usb; > > + unsigned vendor = usbsrc->vendor; > > + unsigned product = usbsrc->product; > > + unsigned bus = usbsrc->bus; > > + char *port = usbsrc->port; > > + unsigned device = usbsrc->device; > > + virUSBDeviceList *devs; > > g_autoptr(virUSBDeviceList) devs = NULL; > > > + int rc; > > + > > + rc = virUSBDeviceFind(vendor, product, bus, device, port, NULL, > > + mandatory, flags, &devs); > > + if (rc < 0) > > + return -1; > > + > > + if (rc == 1) { > > + *usb = virUSBDeviceListGet(devs, 0); > > + virUSBDeviceListSteal(devs, *usb); > > + } > > + virObjectUnref(devs); > > + > > + if (rc > 1) { > > + virReportError(VIR_ERR_OPERATION_FAILED, > > + _("Multiple USB devices for %1$x:%2$x, use > > <address> to specify one"), > > + vendor, product); > > + return -1; > > + } > > + > > + return rc; > > +} > > + > > > > int > > virHostdevFindUSBDevice(virDomainHostdevDef *hostdev, > > @@ -1366,77 +1402,64 @@ virHostdevFindUSBDevice(virDomainHostdevDef > > *hostdev, > > { > > virDomainHostdevSubsysUSB *usbsrc = &hostdev->source.subsys.u.usb; > > unsigned vendor = usbsrc->vendor; > > - unsigned product = usbsrc->product; > > unsigned bus = usbsrc->bus; > > unsigned device = usbsrc->device; > > + char *port = usbsrc->port; > > bool autoAddress = usbsrc->autoAddress; > > + unsigned int flags = 0; > > int rc; > > > > *usb = NULL; > > > > - if (vendor && bus) { > > - rc = virUSBDeviceFind(vendor, product, bus, device, > > - NULL, > > - autoAddress ? false : mandatory, > > - usb); > > - if (rc < 0) { > > - return -1; > > - } else if (!autoAddress) { > > - goto out; > > - } else { > > - VIR_INFO("USB device %x:%x could not be found at previous" > > - " address (bus:%u device:%u)", > > - vendor, product, bus, device); > > - } > > + if (vendor) > > + flags |= USB_DEVICE_FIND_BY_VENDOR; > > + if (device) > > + flags |= USB_DEVICE_FIND_BY_DEVICE; > > + if (port) > > + flags |= USB_DEVICE_FIND_BY_PORT; > > + > > + /* Rule out invalid cases. */ > > + if (vendor && device && port) { > > + VIR_WARN("Cannot match USB device on vendor/product, bus/device, > > and bus/port at once. Ignoring bus/device."); > > + flags &= ~((unsigned int) USB_DEVICE_FIND_BY_DEVICE); > > This typecasting is unnecessary. These values are already declared as > non-negative values. And in C99 enums must fit into int. Everywhere else > in our code we clear flags without typecasting. > > > + } else if (device && port) { > > + VIR_WARN("Cannot match USB device on bus/device and bus/port at > > once. Ignoring bus/device."); > > + flags &= ~((unsigned int) USB_DEVICE_FIND_BY_DEVICE); > > + } else if (!vendor && !device && !port) { > > + VIR_WARN("No matching fields for USB device found. Vendor/product, > > bus/device, or bus/port required."); > > + return -1; > > Since this returns an error the VIR_WARN() should be promoted to > virReportError().
I'm wondering why we're ignoring invalid input for the previous two cases too ? Quietly ignoring incorrect user specified data is generally an anti-pattern we aim to avoid. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|