void virUSBDeviceListDel(virUSBDeviceListPtr list, virUSBDevicePtr dev) { virUSBDevicePtr ret = virUSBDeviceListSteal(list, dev); virUSBDeviceFree(ret); }
static void virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { size_t i; for (i = 0; i < nhostdevs; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) continue; if (hostdev->missing) continue; usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, NULL); if (!usb) { VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, name); continue; } /* Delete only those USB devices which belongs * to domain @name because virLXCProcessStart() might * have failed because USB device is already taken. * Therefore we want to steal only those devices from * the list which were taken by @name */ tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb); virUSBDeviceFree(usb); if (!tmp) { VIR_WARN("Unable to find device %03d.%03d " "in list of active USB devices", hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device); continue; } used_by = virUSBDeviceGetUsedBy(tmp); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, name); virUSBDeviceListDel(driver->activeUsbHostdevs, tmp); } } }
virUSBDevicePtr virUSBDeviceNew(unsigned int bus, unsigned int devno, const char *vroot) { virUSBDevicePtr dev; if (VIR_ALLOC(dev) < 0) return NULL; dev->bus = bus; dev->dev = devno; if (snprintf(dev->name, sizeof(dev->name), "%.3d:%.3d", dev->bus, dev->dev) >= sizeof(dev->name)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("dev->name buffer overflow: %.3d:%.3d"), dev->bus, dev->dev); virUSBDeviceFree(dev); return NULL; } if (virAsprintf(&dev->path, "%s" USB_DEVFS "%03d/%03d", vroot ? vroot : "", dev->bus, dev->dev) < 0) { virUSBDeviceFree(dev); return NULL; } /* XXX fixme. this should be product/vendor */ if (snprintf(dev->id, sizeof(dev->id), "%d %d", dev->bus, dev->dev) >= sizeof(dev->id)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("dev->id buffer overflow: %d %d"), dev->bus, dev->dev); virUSBDeviceFree(dev); return NULL; } VIR_DEBUG("%s %s: initialized", dev->id, dev->name); return dev; }
static void virUSBDeviceListDispose(void *obj) { virUSBDeviceListPtr list = obj; size_t i; for (i = 0; i < list->count; i++) virUSBDeviceFree(list->devs[i]); VIR_FREE(list->devs); }
int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def) { virDomainHostdevDefPtr hostdev = NULL; int i; int ret = -1; if (!def->nhostdevs) return 0; virObjectLock(driver->activeUsbHostdevs); for (i = 0; i < def->nhostdevs; i++) { virUSBDevicePtr usb = NULL; hostdev = def->hostdevs[i]; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) continue; usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, NULL); if (!usb) { VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, def->name); continue; } virUSBDeviceSetUsedBy(usb, def->name); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { virUSBDeviceFree(usb); goto cleanup; } } ret = 0; cleanup: virObjectUnlock(driver->activeUsbHostdevs); return ret; }
static int virLXCPrepareHostUSBDevices(virLXCDriverPtr driver, virDomainDefPtr def) { size_t i; int ret = -1; virUSBDeviceList *list; virUSBDevicePtr tmp; virDomainHostdevDefPtr *hostdevs = def->hostdevs; int nhostdevs = def->nhostdevs; /* To prevent situation where USB device is assigned to two domains * we need to keep a list of currently assigned USB devices. * This is done in several loops which cannot be joined into one big * loop. See virLXCPrepareHostdevPCIDevices() */ if (!(list = virUSBDeviceListNew())) goto cleanup; /* Loop 1: build temporary list */ for (i = 0; i < nhostdevs; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; bool required = true; virUSBDevicePtr usb; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) continue; if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL) required = false; if (virLXCFindHostdevUSBDevice(hostdev, required, &usb) < 0) goto cleanup; if (usb && virUSBDeviceListAdd(list, usb) < 0) { virUSBDeviceFree(usb); goto cleanup; } } /* Mark devices in temporary list as used by @name * and add them do driver list. However, if something goes * wrong, perform rollback. */ if (virLXCPrepareHostdevUSBDevices(driver, def->name, list) < 0) goto cleanup; /* Loop 2: Temporary list was successfully merged with * driver list, so steal all items to avoid freeing them * in cleanup label. */ while (virUSBDeviceListCount(list) > 0) { tmp = virUSBDeviceListGet(list, 0); virUSBDeviceListSteal(list, tmp); } ret = 0; cleanup: virObjectUnref(list); return ret; }
static virUSBDeviceListPtr virUSBDeviceSearch(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, const char *vroot, unsigned int flags) { DIR *dir = NULL; bool found = false; char *ignore = NULL; struct dirent *de; virUSBDeviceListPtr list = NULL, ret = NULL; virUSBDevicePtr usb; int direrr; if (!(list = virUSBDeviceListNew())) goto cleanup; if (virDirOpen(&dir, USB_SYSFS "/devices") < 0) goto cleanup; while ((direrr = virDirRead(dir, &de, USB_SYSFS "/devices")) > 0) { unsigned int found_prod, found_vend, found_bus, found_devno; char *tmpstr = de->d_name; if (strchr(de->d_name, ':')) continue; if (virUSBSysReadFile("idVendor", de->d_name, 16, &found_vend) < 0) goto cleanup; if (virUSBSysReadFile("idProduct", de->d_name, 16, &found_prod) < 0) goto cleanup; if (STRPREFIX(de->d_name, "usb")) tmpstr += 3; if (virStrToLong_ui(tmpstr, &ignore, 10, &found_bus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to parse dir name '%s'"), de->d_name); goto cleanup; } if (virUSBSysReadFile("devnum", de->d_name, 10, &found_devno) < 0) goto cleanup; if ((flags & USB_DEVICE_FIND_BY_VENDOR) && (found_prod != product || found_vend != vendor)) continue; if (flags & USB_DEVICE_FIND_BY_BUS) { if (found_bus != bus || found_devno != devno) continue; found = true; } usb = virUSBDeviceNew(found_bus, found_devno, vroot); if (!usb) goto cleanup; if (virUSBDeviceListAdd(list, usb) < 0) { virUSBDeviceFree(usb); goto cleanup; } if (found) break; } if (direrr < 0) goto cleanup; ret = list; cleanup: VIR_DIR_CLOSE(dir); if (!ret) virObjectUnref(list); return ret; }
int qemuSetupHostdevCGroup(virDomainObjPtr vm, virDomainHostdevDefPtr dev) { int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb; virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci; virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi; virPCIDevicePtr pci = NULL; virUSBDevicePtr usb = NULL; virSCSIDevicePtr scsi = NULL; char *path = NULL; /* currently this only does something for PCI devices using vfio * for device assignment, but it is called for *all* hostdev * devices. */ if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) return 0; if (dev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { int rv; pci = virPCIDeviceNew(pcisrc->addr.domain, pcisrc->addr.bus, pcisrc->addr.slot, pcisrc->addr.function); if (!pci) goto cleanup; if (!(path = virPCIDeviceGetIOMMUGroupDev(pci))) goto cleanup; VIR_DEBUG("Cgroup allow %s for PCI device assignment", path); rv = virCgroupAllowDevicePath(priv->cgroup, path, VIR_CGROUP_DEVICE_RW); virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path, "rw", rv == 0); if (rv < 0) goto cleanup; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: /* NB: hostdev->missing wasn't previously checked in the * case of hotplug, only when starting a domain. Now it is * always checked, and the cgroup setup skipped if true. */ if (dev->missing) break; if ((usb = virUSBDeviceNew(usbsrc->bus, usbsrc->device, NULL)) == NULL) { goto cleanup; } /* oddly, qemuSetupHostUSBDeviceCgroup doesn't ever * reference the usb object we just created */ if (virUSBDeviceFileIterate(usb, qemuSetupHostUSBDeviceCgroup, vm) < 0) { goto cleanup; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: { if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) { virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &scsisrc->u.iscsi; /* Follow qemuSetupDiskCgroup() and qemuSetImageCgroupInternal() * which does nothing for non local storage */ VIR_DEBUG("Not updating cgroups for hostdev iSCSI path '%s'", iscsisrc->path); } else { virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host; if ((scsi = virSCSIDeviceNew(NULL, scsihostsrc->adapter, scsihostsrc->bus, scsihostsrc->target, scsihostsrc->unit, dev->readonly, dev->shareable)) == NULL) goto cleanup; if (virSCSIDeviceFileIterate(scsi, qemuSetupHostSCSIDeviceCgroup, vm) < 0) goto cleanup; } break; } default: break; } } ret = 0; cleanup: virPCIDeviceFree(pci); virUSBDeviceFree(usb); virSCSIDeviceFree(scsi); VIR_FREE(path); return ret; }
int qemuSetupHostdevCGroup(virDomainObjPtr vm, virDomainHostdevDefPtr dev) { int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; virPCIDevicePtr pci = NULL; virUSBDevicePtr usb = NULL; virSCSIDevicePtr scsi = NULL; char *path = NULL; /* currently this only does something for PCI devices using vfio * for device assignment, but it is called for *all* hostdev * devices. */ if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES)) return 0; if (dev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (dev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { int rv; pci = virPCIDeviceNew(dev->source.subsys.u.pci.addr.domain, dev->source.subsys.u.pci.addr.bus, dev->source.subsys.u.pci.addr.slot, dev->source.subsys.u.pci.addr.function); if (!pci) goto cleanup; if (!(path = virPCIDeviceGetIOMMUGroupDev(pci))) goto cleanup; VIR_DEBUG("Cgroup allow %s for PCI device assignment", path); rv = virCgroupAllowDevicePath(priv->cgroup, path, VIR_CGROUP_DEVICE_RW); virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path, "rw", rv == 0); if (rv < 0) goto cleanup; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: /* NB: hostdev->missing wasn't previously checked in the * case of hotplug, only when starting a domain. Now it is * always checked, and the cgroup setup skipped if true. */ if (dev->missing) break; if ((usb = virUSBDeviceNew(dev->source.subsys.u.usb.bus, dev->source.subsys.u.usb.device, NULL)) == NULL) { goto cleanup; } /* oddly, qemuSetupHostUSBDeviceCgroup doesn't ever * reference the usb object we just created */ if (virUSBDeviceFileIterate(usb, qemuSetupHostUSBDeviceCgroup, vm) < 0) { goto cleanup; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if ((scsi = virSCSIDeviceNew(NULL, dev->source.subsys.u.scsi.adapter, dev->source.subsys.u.scsi.bus, dev->source.subsys.u.scsi.target, dev->source.subsys.u.scsi.unit, dev->readonly, dev->shareable)) == NULL) goto cleanup; if (virSCSIDeviceFileIterate(scsi, qemuSetupHostSCSIDeviceCgroup, vm) < 0) goto cleanup; default: break; } } ret = 0; cleanup: virPCIDeviceFree(pci); virUSBDeviceFree(usb); virSCSIDeviceFree(scsi); VIR_FREE(path); return ret; }