[libvirt] [PATCH 9/9] Implement disk- and controller hotremove

Daniel P. Berrange berrange at redhat.com
Thu Sep 24 11:05:06 UTC 2009


On Fri, Sep 18, 2009 at 05:26:16PM +0200, Wolfgang Mauerer wrote:
> Since both disks and disk controllers can be dynamically
> added to the system, it makes sense to be also able to remove
> them.

This is the main patch which requires additional support in QEMu
if I'm understanding your patcheset to qemu-devel correctly. 

> diff --git a/src/domain_conf.h b/src/domain_conf.h
> index 17f8b14..c7d49cf 100644
> --- a/src/domain_conf.h
> +++ b/src/domain_conf.h
> @@ -149,6 +149,14 @@ virDiskHasValidPciAddr(virDomainDiskDefPtr def)
>      return def->pci_addr.domain || def->pci_addr.domain || def->pci_addr.slot;
>  }
>  
> +static inline int
> +virDiskHasValidController(virDomainDiskDefPtr def)
> +{
> +    return def->controller_id != NULL ||
> +        def->controller_pci_addr.domain || def->controller_pci_addr.domain 
> +        || def->controller_pci_addr.slot;
> +}
> +
>  
>  /* Two types of disk backends */
>  enum virDomainFSType {
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index ddc46f6..5ac89a1 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -6204,32 +6204,31 @@ cleanup:
>      return ret;
>  }
>  
> -static int qemudDomainDetachPciDiskDevice(virConnectPtr conn,
> -                                          virDomainObjPtr vm, virDomainDeviceDefPtr dev)
> +static int qemudDomainDetachDiskController(virConnectPtr conn,
> +                                           virDomainObjPtr vm, virDomainDeviceDefPtr dev)
>  {
>      int i, ret = -1;
>      char *cmd = NULL;
>      char *reply = NULL;
> -    virDomainDiskDefPtr detach = NULL;
> +    virDomainControllerDefPtr detach = NULL;
>      int tryOldSyntax = 0;
>  
> -    for (i = 0 ; i < vm->def->ndisks ; i++) {
> -        if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) {
> -            detach = vm->def->disks[i];
> +//    virDomainControllerDefPtr is dev->data.controller
> +    for (i = 0 ; i < vm->def->ncontrollers ; i++) {
> +        if (vm->def->controllers[i]->pci_addr.domain == 
> +            dev->data.controller->pci_addr.domain &&
> +            vm->def->controllers[i]->pci_addr.bus == 
> +            dev->data.controller->pci_addr.bus &&
> +            vm->def->controllers[i]->pci_addr.slot == 
> +            dev->data.controller->pci_addr.slot) {
> +            detach = vm->def->controllers[i];
>              break;
>          }
>      }
>  
>      if (!detach) {
>          qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> -                         _("disk %s not found"), dev->data.disk->dst);
> -        goto cleanup;
> -    }
> -
> -    if (!virDiskHasValidPciAddr(detach)) {
> -        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> -                         _("disk %s cannot be detached - no PCI address for device"),
> -                           detach->dst);
> +                         _("Controller %s not found"), dev->data.disk->dst);
>          goto cleanup;
>      }
>  
> @@ -6251,7 +6250,9 @@ try_command:
>  
>      if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
>          qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> -                          _("failed to execute detach disk %s command"), detach->dst);
> +                         _("failed to execute detach controller %d:%d:%d " \
> +                           "command"), detach->pci_addr.domain,
> +                         detach->pci_addr.bus, detach->pci_addr.slot);
>          goto cleanup;
>      }
>  
> @@ -6267,6 +6268,124 @@ try_command:
>      if (strstr(reply, "invalid slot") ||
>          strstr(reply, "Invalid pci address")) {
>          qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> +                          _("failed to detach controller: invalid PCI address %.4x:%.2x:%.2x: %s"),
> +                          detach->pci_addr.domain,
> +                          detach->pci_addr.bus,
> +                          detach->pci_addr.slot,
> +                          reply);
> +        goto cleanup;
> +    }
> +
> +    if (vm->def->ncontrollers > 1) {
> +        vm->def->controllers[i] = vm->def->controllers[--vm->def->ncontrollers];
> +        if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) < 0) {
> +            virReportOOMError(conn);
> +            goto cleanup;
> +        }
> +        qsort(vm->def->disks, vm->def->ncontrollers, 
> +              sizeof(*vm->def->controllers),
> +              virDomainControllerQSort);
> +    } else {
> +        VIR_FREE(vm->def->controllers[0]);
> +        vm->def->controllers = 0;
> +    }
> +    ret = 0;
> +
> +cleanup:
> +    VIR_FREE(reply);
> +    VIR_FREE(cmd);
> +    return ret;
> +}
> +
> +static int qemudDomainDetachDiskDevice(virConnectPtr conn,
> +                                       virDomainObjPtr vm, virDomainDeviceDefPtr dev)
> +{
> +    int i, ret = -1;
> +    char *cmd = NULL;
> +    char *reply = NULL;
> +    const char* type;
> +    virDomainDiskDefPtr detach = NULL;
> +    int tryOldSyntax = 0;
> +
> +    /* If bus and unit are specified, use these first to identify
> +       the disk */
> +    if (dev->data.disk->controller_pci_addr.domain != -1) {
> +        for (i = 0 ; i < vm->def->ndisks ; i++) {
> +            if (dev->data.disk->bus_id != -1 &&
> +                dev->data.disk->bus_id == vm->def->disks[i]->bus_id &&
> +                dev->data.disk->unit_id != -1 &&
> +                dev->data.disk->unit_id == vm->def->disks[i]->unit_id) {
> +                detach = vm->def->disks[i];
> +                break;
> +            }
> +        }
> +    }
> +
> +    /* If the device did not specify a controller explicitely (and therefore
> +       lacks a bus and unit specification), revert to the explicit target
> +       device specification to identify a device for removal. */
> +    if (!detach) {
> +        for (i = 0 ; i < vm->def->ndisks ; i++) {
> +            if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) {
> +                detach = vm->def->disks[i];
> +                break;
> +            }
> +        }
> +    }
> +
> +    if (!detach) {
> +        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> +                         _("disk %s not found"), dev->data.disk->dst);
> +        goto cleanup;
> +    }
> +
> +    if (!virDiskHasValidPciAddr(detach) && !virDiskHasValidController(detach)) {
> +        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> +                         _("disk %s cannot be detached - no PCI address for device"),
> +                           detach->dst);
> +        goto cleanup;
> +    }
> +
> +    type = virDomainDiskBusTypeToString(detach->bus);
> +
> +try_command:
> +    if (tryOldSyntax) {
> +        if (virAsprintf(&cmd, "drive_del 0 %d:%d:%d bus=%d,unit=%d,if=%s", 
> +                        detach->pci_addr.domain, detach->pci_addr.bus, 
> +                        detach->pci_addr.slot, detach->bus_id, 
> +                        detach->unit_id, type) < 0) {
> +            virReportOOMError(conn);
> +            goto cleanup;
> +        }
> +    } else {
> +        if (virAsprintf(&cmd, "drive_del pci_addr=%d:%d:%d bus=%d,unit=%d,if=%s",
> +                        detach->pci_addr.domain,
> +                        detach->pci_addr.bus,
> +                        detach->pci_addr.slot, detach->bus_id, 
> +                        detach->unit_id, type) < 0) {
> +            virReportOOMError(conn);
> +            goto cleanup;
> +        }
> +    }
> +
> +    if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
> +        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> +                          _("failed to execute detach disk %s command"), detach->dst);
> +        goto cleanup;
> +    }
> +
> +    DEBUG ("%s: drive_del reply: %s",vm->def->name,  reply);
> +
> +    if (!tryOldSyntax &&
> +        strstr(reply, "extraneous characters")) {
> +        tryOldSyntax = 1;
> +        goto try_command;
> +    }
> +    /* OK bux x, unit x is printed on success, but this does not provide
> +       any new information to us.*/
> +    if (strstr(reply, "Invalid pci address") ||
> +        strstr(reply, "No pci device with address")) {
> +        qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
>                            _("failed to detach disk %s: invalid PCI address %.4x:%.2x:%.2x: %s"),
>                            detach->dst,
>                            detach->pci_addr.domain,
> @@ -6274,6 +6393,11 @@ try_command:
>                            detach->pci_addr.slot,
>                            reply);
>          goto cleanup;
> +    } else if (strstr(reply, "Need to specify bus") ||
> +               strstr(reply, "Need to specify unit")) {
> +        qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> +                          _("failed to detach disk %s: bus and unit not (internal error)"),
> +                          detach->dst);
>      }
>  
>      if (vm->def->ndisks > 1) {
> @@ -6575,7 +6699,7 @@ static int qemudDomainDetachDevice(virDomainPtr dom,
>          dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
>          (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI ||
>           dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)) {
> -        ret = qemudDomainDetachPciDiskDevice(dom->conn, vm, dev);
> +        ret = qemudDomainDetachDiskDevice(dom->conn, vm, dev);
>          if (driver->securityDriver)
>              driver->securityDriver->domainRestoreSecurityImageLabel(dom->conn, dev->data.disk);
>          if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0)
> @@ -6584,6 +6708,11 @@ static int qemudDomainDetachDevice(virDomainPtr dom,
>          ret = qemudDomainDetachNetDevice(dom->conn, vm, dev);
>      } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
>          ret = qemudDomainDetachHostDevice(dom->conn, driver, vm, dev);
> +    } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) {
> +        /* NOTE: We can unplug the controller without having to
> +           check if all disks are unplugged; it is at the user's 
> +           responsibility to use the required OS services first. */
> +        ret = qemudDomainDetachDiskController(dom->conn, vm, dev);

I wonder if we'd be better off raising an error if the app tries to
remove a controller which still has disks attached. ie, force the
app to hotunplug each disk first.  These days I tend towards avoiding
side-effects in operations, and reporting errors whereever there's a
situation that the app could have avoided the problem. Any other
opinions people... ?

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list