Exemple #1
0
static int
qemuPrepareHostUSBDevices(struct qemud_driver *driver,
                          virDomainDefPtr def)
{
    int i, ret = -1;
    usbDeviceList *list;
    usbDevice *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 qemuPrepareHostdevPCIDevices()
     */
    if (!(list = usbDeviceListNew()))
        goto cleanup;

    /* Loop 1: build temporary list
     */
    for (i = 0 ; i < nhostdevs ; i++) {
        virDomainHostdevDefPtr hostdev = hostdevs[i];
        usbDevice *usb = NULL;

        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
            continue;
        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
            continue;

        unsigned vendor = hostdev->source.subsys.u.usb.vendor;
        unsigned product = hostdev->source.subsys.u.usb.product;
        unsigned bus = hostdev->source.subsys.u.usb.bus;
        unsigned device = hostdev->source.subsys.u.usb.device;

        if (vendor && bus) {
            usb = usbFindDevice(vendor, product, bus, device);

        } else if (vendor && !bus) {
            usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
            if (!devs)
                goto cleanup;

            if (usbDeviceListCount(devs) > 1) {
                virReportError(VIR_ERR_OPERATION_FAILED,
                               _("multiple USB devices for %x:%x, "
                                 "use <address> to specify one"), vendor, product);
                usbDeviceListFree(devs);
                goto cleanup;
            }
            usb = usbDeviceListGet(devs, 0);
            usbDeviceListSteal(devs, usb);
            usbDeviceListFree(devs);

            hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
            hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);

        } else if (!vendor && bus) {
            usb = usbFindDeviceByBus(bus, device);
        }

        if (!usb)
            goto cleanup;

        if (usbDeviceListAdd(list, usb) < 0) {
            usbFreeDevice(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 (qemuPrepareHostdevUSBDevices(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 (usbDeviceListCount(list) > 0) {
        tmp = usbDeviceListGet(list, 0);
        usbDeviceListSteal(list, tmp);
    }

    ret = 0;

cleanup:
    usbDeviceListFree(list);
    return ret;
}
int
qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
                         bool mandatory,
                         usbDevice **usb)
{
    unsigned vendor = hostdev->source.subsys.u.usb.vendor;
    unsigned product = hostdev->source.subsys.u.usb.product;
    unsigned bus = hostdev->source.subsys.u.usb.bus;
    unsigned device = hostdev->source.subsys.u.usb.device;
    bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
    int rc;

    *usb = NULL;

    if (vendor && bus) {
        rc = usbFindDevice(vendor, product, bus, device,
                           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);
        }
    }

    /* When vendor is specified, its USB address is either unspecified or the
     * device could not be found at the USB device where it had been
     * automatically found before.
     */
    if (vendor) {
        usbDeviceList *devs;

        rc = usbFindDeviceByVendor(vendor, product, mandatory, &devs);
        if (rc < 0)
            return -1;

        if (rc == 1) {
            *usb = usbDeviceListGet(devs, 0);
            usbDeviceListSteal(devs, *usb);
        }
        usbDeviceListFree(devs);

        if (rc == 0) {
            goto out;
        } else if (rc > 1) {
            if (autoAddress) {
                virReportError(VIR_ERR_OPERATION_FAILED,
                               _("Multiple USB devices for %x:%x were found,"
                                 " but none of them is at bus:%u device:%u"),
                               vendor, product, bus, device);
            } else {
                virReportError(VIR_ERR_OPERATION_FAILED,
                               _("Multiple USB devices for %x:%x, "
                                 "use <address> to specify one"),
                               vendor, product);
            }
            return -1;
        }

        hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(*usb);
        hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(*usb);
        hostdev->source.subsys.u.usb.autoAddress = true;

        if (autoAddress) {
            VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved"
                     " from bus:%u device:%u)",
                     vendor, product,
                     hostdev->source.subsys.u.usb.bus,
                     hostdev->source.subsys.u.usb.device,
                     bus, device);
        }
    } else if (!vendor && bus) {
        if (usbFindDeviceByBus(bus, device, mandatory, usb) < 0)
            return -1;
    }

out:
    if (!*usb)
        hostdev->missing = 1;
    return 0;
}
int
qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
                             const char *name,
                             virDomainHostdevDefPtr *hostdevs,
                             int nhostdevs)
{
    int ret = -1;
    int i;
    usbDeviceList *list;
    usbDevice *tmp;

    /* 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 qemuPrepareHostdevPCIDevices()
     */
    if (!(list = usbDeviceListNew()))
        goto cleanup;

    /* Loop 1: build temporary list and validate no usb device
     * is already taken
     */
    for (i = 0 ; i < nhostdevs ; i++) {
        virDomainHostdevDefPtr hostdev = hostdevs[i];

        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
            continue;
        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
            continue;

        /* Resolve a vendor/product to bus/device */
        if (hostdev->source.subsys.u.usb.vendor) {
            usbDevice *usb
                = usbFindDevice(hostdev->source.subsys.u.usb.vendor,
                                hostdev->source.subsys.u.usb.product);

            if (!usb)
                return -1;

            hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
            hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);

            if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) {
                const char *other_name = usbDeviceGetUsedBy(tmp);

                if (other_name)
                    qemuReportError(VIR_ERR_OPERATION_INVALID,
                                    _("USB device %s is in use by domain %s"),
                                    usbDeviceGetName(tmp), other_name);
                else
                    qemuReportError(VIR_ERR_OPERATION_INVALID,
                                    _("USB device %s is already in use"),
                                    usbDeviceGetName(tmp));
                usbFreeDevice(usb);
                goto cleanup;
            }

            if (usbDeviceListAdd(list, usb) < 0) {
                usbFreeDevice(usb);
                goto cleanup;
            }

        }
    }

    /* Loop 2: Mark devices in temporary list as used by @name
     * and add them do driver list. However, if something goes
     * wrong, perform rollback.
     */
    for (i = 0; i < usbDeviceListCount(list); i++) {
        tmp = usbDeviceListGet(list, i);
        usbDeviceSetUsedBy(tmp, name);
        if (usbDeviceListAdd(driver->activeUsbHostdevs, tmp) < 0) {
            usbFreeDevice(tmp);
            goto inactivedevs;
        }
    }

    /* Loop 3: Temporary list was successfully merged with
     * driver list, so steal all items to avoid freeing them
     * in cleanup label.
     */
    while (usbDeviceListCount(list) > 0) {
        tmp = usbDeviceListGet(list, 0);
        usbDeviceListSteal(list, tmp);
    }

    ret = 0;
    goto cleanup;

inactivedevs:
    /* Steal devices from driver->activeUsbHostdevs.
     * We will free them later.
     */
    for (i = 0; i < usbDeviceListCount(list); i++) {
        tmp = usbDeviceListGet(list, i);
        usbDeviceListSteal(driver->activeUsbHostdevs, tmp);
    }

cleanup:
    usbDeviceListFree(list);
    return ret;
}