[libvirt] [PATCH v2 07/10] qemu: Basic management functions for scsi hostdev

Osier Yang jyang at redhat.com
Wed Apr 3 09:43:10 UTC 2013


On 01/04/13 20:00, Han Cheng wrote:
> Although virtio-scsi support SCSI PR, the device in host may do not
> support this. To avoid losing data, we only allow one scsi hostdev be
> passthrough to one guest.
>
> Signed-off-by: Han Cheng <hanc.fnst at cn.fujitsu.com>
> ---
>   src/qemu/qemu_conf.h    |    2 +
>   src/qemu/qemu_driver.c  |    3 +
>   src/qemu/qemu_hostdev.c |  227 +++++++++++++++++++++++++++++++++++++++++++++++
>   src/qemu/qemu_hostdev.h |   10 ++
>   src/qemu/qemu_process.c |    3 +
>   5 files changed, 245 insertions(+), 0 deletions(-)
>
> diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
> index c5ddaad..28d9685 100644
> --- a/src/qemu/qemu_conf.h
> +++ b/src/qemu/qemu_conf.h
> @@ -37,6 +37,7 @@
>   # include "vircgroup.h"
>   # include "virpci.h"
>   # include "virusb.h"
> +# include "virscsi.h"
>   # include "cpu_conf.h"
>   # include "driver.h"
>   # include "virportallocator.h"
> @@ -206,6 +207,7 @@ struct _virQEMUDriver {
>       virPCIDeviceListPtr activePciHostdevs;
>       virPCIDeviceListPtr inactivePciHostdevs;
>       virUSBDeviceListPtr activeUsbHostdevs;
> +    virSCSIDeviceListPtr activeScsiHostdevs;

We have to have a share module to manage these list, Chunyan Liu is 
doing it.
But it doesn't affect this patch, unless there is conflicts.

>   
>       /* Immutable pointer. Unsafe APIs. XXX */
>       virHashTablePtr sharedDisks;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 96bf235..408a2cb 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -682,6 +682,9 @@ qemuStartup(bool privileged,
>       if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)
>           goto error;
>   
> +    if ((qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL)
> +        goto error;
> +
>       if (!(qemu_driver->sharedDisks = virHashCreate(30, qemuSharedDiskEntryFree)))
>           goto error;
>   
> diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
> index bac38b5..96b3e8f 100644
> --- a/src/qemu/qemu_hostdev.c
> +++ b/src/qemu/qemu_hostdev.c
> @@ -29,6 +29,7 @@
>   #include "viralloc.h"
>   #include "virpci.h"
>   #include "virusb.h"
> +#include "virscsi.h"
>   #include "virnetdev.h"
>   
>   #define VIR_FROM_THIS VIR_FROM_QEMU
> @@ -214,6 +215,59 @@ cleanup:
>       return ret;
>   }
>   
> +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)
> +            continue;
> +        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
> +            continue;

Can be more compact:

if (hostdev->model != 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,
> +                     def->name);
> +            continue;
> +        }
> +
> +        virSCSIDeviceSetUsedBy(scsi, def->name);
> +
> +        if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
> +            virSCSIDeviceFree(scsi);
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("Unable to add SCSI device %d:%d:%d:%d to activeScsiHostdevs"),
> +                           virSCSIDeviceGetAdapter(scsi), virSCSIDeviceGetBus(scsi),
> +                           virSCSIDeviceGetTarget(scsi), virSCSIDeviceGetUnit(scsi));
> +            goto cleanup;
> +        }
> +    }
> +    ret = 0;
> +
> +cleanup:
> +    virObjectUnlock(driver->activeScsiHostdevs);
> +    return ret;
> +}
> +
>   static int
>   qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
>   {
> @@ -811,6 +865,103 @@ cleanup:
>       return ret;
>   }
>   
> +int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
> +                                  const char *name,
> +                                  virDomainHostdevDefPtr *hostdevs,
> +                                  int nhostdevs)
> +{
> +    int i, j, count;
> +    virSCSIDeviceListPtr list;
> +    virSCSIDevicePtr tmp;
> +
> +    /* 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 1: build temporary list
> +     */

/* Loop 1: build temporary list */

> +    for (i = 0 ; i < nhostdevs ; i++) {
> +        virDomainHostdevDefPtr hostdev = hostdevs[i];
> +        virSCSIDevicePtr scsi;
> +
> +        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> +            continue;
> +        if (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;
> +
> +        if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) {
> +            virSCSIDeviceFree(scsi);
> +            goto cleanup;
> +        }
> +    }
> +
> +    /* Loop 2: Mark devices in temporary list as used by @name
> +     * and add them do driver list. However, if something goes

s/do/to/

> +     * 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 %d:%d:%d:%d dom=%s to activeScsiHostdevs",
> +                  virSCSIDeviceGetAdapter(scsi), virSCSIDeviceGetBus(scsi),
> +                  virSCSIDeviceGetTarget(scsi), virSCSIDeviceGetUnit(scsi), name);
> +        if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0)
> +            goto error;
> +    }
> +
> +    virObjectUnlock(driver->activeScsiHostdevs);
> +
> +    /* Loop 3: Temporary list was successfully merged with
> +     * driver list, so steal all items to avoid freeing them
> +     * in cleanup label.
> +     */
> +    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 qemuPrepareHostDevices(virQEMUDriverPtr driver,
>                              virDomainDefPtr def,
>                              bool coldBoot)
> @@ -825,6 +976,10 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver,
>       if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0)
>           return -1;
>   
> +    if (qemuPrepareHostdevSCSIDevices(driver, def->name,
> +                                      def->hostdevs, def->nhostdevs) < 0)
> +        return -1;
> +
>       return 0;
>   }
>   
> @@ -1009,6 +1164,75 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver,
>       virObjectUnlock(driver->activeUsbHostdevs);
>   }
>   
> +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;
> +
> +        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> +            continue;
> +        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
> +            continue;
> +        if (hostdev->missing)
> +            continue;

These "continues" can be more compact.

> +
> +        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;
> +        }
> +
> +        /* Delete only those SCSI devices which belongs
> +         * to domain @name because qemuProcessStart() might
> +         * have failed because SCSI device is already taken.

s/because/for/

> +         * Therefore we want to steal only those devices from

s/steal/delete/

> +         * the list which were taken by @name */

s/taken/used/


How about:

/* Only delete the devices which are marked as being used by @name,
  * because qemuProcessStart could fail on the half way.
  */

How about one specify (managed='no|yes') for scsi hostdev?

Osier




More information about the libvir-list mailing list