int qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def) { virDomainHostdevDefPtr hostdev = NULL; int i; int ret = -1; if (!def->nhostdevs) return 0; virObjectLock(driver->activeScsiHostdevs); for (i = 0; i < def->nhostdevs; i++) { virSCSIDevicePtr scsi = NULL; hostdev = def->hostdevs[i]; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) continue; if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit, hostdev->readonly))) goto cleanup; virSCSIDeviceSetUsedBy(scsi, def->name); if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { virSCSIDeviceFree(scsi); goto cleanup; } } ret = 0; cleanup: virObjectUnlock(driver->activeScsiHostdevs); return ret; }
int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { int i, j, count; virSCSIDeviceListPtr list; virSCSIDevicePtr tmp; /* Loop 1: Add the shared scsi host device to shared device * table. */ for (i = 0; i < nhostdevs; i++) { virDomainDeviceDef dev; dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; dev.data.hostdev = hostdevs[i]; if (qemuAddSharedDevice(driver, &dev, name) < 0) return -1; if (qemuSetUnprivSGIO(&dev) < 0) return -1; } /* To prevent situation where SCSI device is assigned to two domains * we need to keep a list of currently assigned SCSI devices. * This is done in several loops which cannot be joined into one big * loop. See qemuPrepareHostdevPCIDevices() */ if (!(list = virSCSIDeviceListNew())) goto cleanup; /* Loop 2: build temporary list */ for (i = 0; i < nhostdevs; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; virSCSIDevicePtr scsi; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) continue; if (hostdev->managed) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI host device doesn't support managed mode")); goto cleanup; } if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit, hostdev->readonly))) goto cleanup; if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) { virSCSIDeviceFree(scsi); goto cleanup; } } /* Loop 3: Mark devices in temporary list as used by @name * and add them to driver list. However, if something goes * wrong, perform rollback. */ virObjectLock(driver->activeScsiHostdevs); count = virSCSIDeviceListCount(list); for (i = 0; i < count; i++) { virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { const char *other_name = virSCSIDeviceGetUsedBy(tmp); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, _("SCSI device %s is in use by domain %s"), virSCSIDeviceGetName(tmp), other_name); else virReportError(VIR_ERR_OPERATION_INVALID, _("SCSI device %s is already in use"), virSCSIDeviceGetName(tmp)); goto error; } virSCSIDeviceSetUsedBy(scsi, name); VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) goto error; } virObjectUnlock(driver->activeScsiHostdevs); /* Loop 4: Temporary list was successfully merged with * driver list, so steal all items to avoid freeing them * when freeing temporary list. */ while (virSCSIDeviceListCount(list) > 0) { tmp = virSCSIDeviceListGet(list, 0); virSCSIDeviceListSteal(list, tmp); } virObjectUnref(list); return 0; error: for (j = 0; j < i; j++) { tmp = virSCSIDeviceListGet(list, i); virSCSIDeviceListSteal(driver->activeScsiHostdevs, tmp); } virObjectUnlock(driver->activeScsiHostdevs); cleanup: virObjectUnref(list); return -1; }
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; }
void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { int i; virObjectLock(driver->activeScsiHostdevs); for (i = 0; i < nhostdevs; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; virSCSIDevicePtr scsi, tmp; const char *used_by = NULL; virDomainDeviceDef dev; dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; dev.data.hostdev = hostdev; ignore_value(qemuRemoveSharedDevice(driver, &dev, name)); if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) continue; if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit, hostdev->readonly))) { VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on domain %s", hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit, name); continue; } /* Only delete the devices which are marked as being used by @name, * because qemuProcessStart could fail on the half way. */ tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi); virSCSIDeviceFree(scsi); if (!tmp) { VIR_WARN("Unable to find device %s:%d:%d:%d " "in list of active SCSI devices", hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit); continue; } used_by = virSCSIDeviceGetUsedBy(tmp); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from activeScsiHostdevs", hostdev->source.subsys.u.scsi.adapter, hostdev->source.subsys.u.scsi.bus, hostdev->source.subsys.u.scsi.target, hostdev->source.subsys.u.scsi.unit, name); virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp); } } virObjectUnlock(driver->activeScsiHostdevs); }
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; }