[libvirt] [PATCHv8 2/4] libvirt/qemu - synchronous update of device definition

Wen Congyang wency at cn.fujitsu.com
Fri Apr 1 06:10:18 UTC 2011


At 04/01/2011 12:19 PM, KAMEZAWA Hiroyuki Write:
>>From 229cfc90781bfd7024f79db1aed8bea5963757e3 Mon Sep 17 00:00:00 2001
> From: KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>
> Date: Thu, 31 Mar 2011 18:52:24 +0900
> Subject: [PATCHv8 2/4] libvirt/qemu - synchronous update of device definition.
> 
> At persistent modification of inactive domains, we go several steps as
>   - insert disk to vmdef
>   - assign pci address if necessary
>   - assign controller if necessary
>   - save it to file

Step 2 should be after step 3.

> 
> If failure happens in above sequence, that implies there is inconsistency
> between XML definition in file and vmdef cached in memory. So, it's better
> to have some rollback method. Implementing undo in each stage doesn't seem
> very neat, this patch implements a generic one.
> 
> This patch adds 3 calls.
>  virDomainTemporalCopyAlloc(vmdef)
>  virDomainTemporalCopySync(orig, copy)
>  virDomainTemporalCopyUndo(orig, copy)
> 
> CopyAlloc() will create a copy of vmdef, CopySync() is for synchronizing
> copy and orginal, updating origina., CopyUndo() is for discarding for
> discarding unnecessary things in copy at failure. With these, vmdef
> modification can be done in following manner.
> 
>   copy = virDomainTemporalCopyAlloc(orig)
>   ....do update on copy
>   ....save updated data to its file
>   if (error)
>          virDomainTemporalCopyUndo(orig, copy);
>   else
>          virDomainTemporalCopySync(orig, copy) # this never fails.

You only copy arrays in orig. How about copy all from orig to copy?
And we can introduce a new API virDomainObjAssignPersistentDef() that is
like the API virDomainObjAssignDef().

So vmdef modification can be done like this:
  copy = virDomainTemporalCopyAlloc(orig)
  ....do update on copy
  if (error) {
         virDomainDefFree(copy);
  } else {
         virDomainObjAssignPersistentDef(vm, copy) # this never fails.
         ....save updated data to its file
  }

We can copy vmdef easily:
1. xml = virDomainDefFormat(def, VIR_DOMAIN_XML_WRITE_FLAGS)
2. newdef = virDomainDefParseString(caps, xml, VIR_DOMAIN_XML_READ_FLAGS)

> 
> At failure of attach or success of detach, all stale devices in
> copy or orig will be freed automatically.
> 
> Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>
> ---
>  src/conf/domain_conf.c   |  208 ++++++++++++++++++++++++++++++++++++++++++++++
>  src/conf/domain_conf.h   |    5 +
>  src/libvirt_private.syms |    3 +
>  src/qemu/qemu_driver.c   |   18 +++-
>  4 files changed, 230 insertions(+), 4 deletions(-)
> 
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index b6aaf33..098face 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -9368,3 +9368,211 @@ cleanup:
>  
>      return ret;
>  }
> +
> +/*
> + * This function creates a copy of vmdef. The copied vmdef and
> + * original vmdef share their pointers other than arrays.
> + * The caller can make any changes to Copy, and later
> + * sync copy and original one, later.
> + *
> + * The caller must guarantee
> + *  - vmdef is under a lock and no other one touches it.
> + *  - never free pointers pointed by vmdef or copy before calling
> + *    virDomainTemporalCopySync() or virDomainTemporalCopyUndo().
> + *
> + * This function just handles 'generic' structure of virDomainDef.
> + * So, if some driver specific handling is required, you need another
> + * calls. See qemuDomainModifyDevicePersistent() for example.
> + */
> +
> +static void virDomainTemporalCopyFree(virDomainDefPtr copy)
> +{
> +    VIR_FREE(copy->graphics);
> +    VIR_FREE(copy->inputs);
> +    VIR_FREE(copy->disks);
> +    VIR_FREE(copy->controllers);
> +    VIR_FREE(copy->fss);
> +    VIR_FREE(copy->nets);
> +    VIR_FREE(copy->smartcards);
> +    VIR_FREE(copy->serials);
> +    VIR_FREE(copy->parallels);
> +    VIR_FREE(copy->channels);
> +    VIR_FREE(copy->sounds);
> +    VIR_FREE(copy->videos);
> +    VIR_FREE(copy->hostdevs);
> +    VIR_FREE(copy);
> +}
> +
> +#define DupArray(ptr, count, orig) do {\
> +    if (VIR_ALLOC_N((ptr), (count)) < 0) {\
> +        virReportOOMError();\
> +        goto oom_out;\
> +    }\
> +    memcpy((ptr), (orig), sizeof(*ptr) * (count));\
> +} while (0)
> +
> +virDomainDefPtr virDomainTemporalCopyAlloc(virDomainDefPtr vmdef)
> +{
> +    virDomainDefPtr copy;
> +
> +    if (VIR_ALLOC(copy) < 0) {
> +        virReportOOMError();
> +        return NULL;
> +    }
> +
> +    memcpy(copy, vmdef, sizeof(*copy));
> +
> +    /*
> +     * For now, this is used for modify devices. Further updates
> +     * may be necessary for handle other components.
> +     * At handling copy, array of disks,networks....can be
> +     * replaced by realloc(). We need to allocate another array
> +     * to avoid confusions.
> +     */
> +    copy->graphics = NULL;
> +    copy->inputs = NULL;
> +    copy->disks = NULL;
> +    copy->controllers = NULL;
> +    copy->fss = NULL;
> +    copy->nets = NULL;
> +    copy->smartcards = NULL;
> +    copy->serials = NULL;
> +    copy->parallels = NULL;
> +    copy->channels = NULL;
> +    copy->sounds = NULL;
> +    copy->videos = NULL;
> +    copy->hostdevs = NULL;
> +
> +    DupArray(copy->graphics, vmdef->ngraphics, vmdef->graphics);
> +    DupArray(copy->inputs, vmdef->ninputs, vmdef->inputs);
> +    DupArray(copy->disks, vmdef->ndisks, vmdef->disks);
> +    DupArray(copy->controllers, vmdef->ncontrollers, vmdef->controllers);
> +    DupArray(copy->fss, vmdef->nfss, vmdef->fss);
> +    DupArray(copy->nets, vmdef->nnets, vmdef->nets);
> +    DupArray(copy->smartcards, vmdef->nsmartcards, vmdef->smartcards);
> +    DupArray(copy->serials, vmdef->nserials, vmdef->serials);
> +    DupArray(copy->parallels, vmdef->nparallels, vmdef->parallels);
> +    DupArray(copy->channels, vmdef->nchannels, vmdef->channels);
> +    DupArray(copy->sounds, vmdef->nsounds, vmdef->sounds);
> +    DupArray(copy->videos, vmdef->nvideos, vmdef->videos);
> +    DupArray(copy->hostdevs, vmdef->nhostdevs, vmdef->hostdevs);
> +
> +    /*
> +     * After this, we can modify copy with usual routines.
> +     */
> +    return copy;
> +oom_out:
> +    virDomainTemporalCopyFree(copy);
> +    return NULL;
> +}
> +#undef DupArray
> +
> +
> +/*
> + * Original anc copy has different arrays, but objects potinted by
> + * arrays are shared. At syncing, if an object is in orig but not in copy,
> + * free it. If an object is not in orig but in copy, copy it.
> + * At rollback, if an object is in copy but not in orig, free it.
> + */
> +
> +/* use macro for avoiding warnings */
> +#define SyncArray(orig, copy, member, num, func) do {\
> +    int i, j;\
> +    for (i = 0; i < orig->num; i++) {\
> +        for (j = 0; j < copy->num; j++) \
> +            if (orig->member[i] == copy->member[j])\
> +                break;\
> +        if (j == copy->num)\
> +            func(orig->member[i]);\
> +    }\
> +} while (0);
> +
> +#define ReplaceArray(orig, copy, member, num) do {\
> +    VIR_FREE(orig->member);\
> +    orig->member = copy->member;\
> +    copy->member = NULL;\
> +    orig->num = copy->num;\
> +} while (0);
> +
> +void virDomainTemporalCopySync(virDomainDefPtr orig,
> +                               virDomainDefPtr copy)
> +{
> +    SyncArray(orig, copy, graphics, ngraphics, virDomainGraphicsDefFree);
> +    ReplaceArray(orig, copy, graphics, ngraphics);
> +
> +    SyncArray(orig, copy, inputs, ninputs, virDomainInputDefFree);
> +    ReplaceArray(orig, copy, inputs, ninputs);
> +
> +    SyncArray(orig, copy, disks, ndisks, virDomainDiskDefFree);
> +    ReplaceArray(orig, copy, disks, ndisks);
> +
> +    SyncArray(orig, copy, controllers,
> +              ncontrollers, virDomainControllerDefFree);
> +    ReplaceArray(orig, copy, controllers, ncontrollers);
> +
> +    SyncArray(orig, copy, fss, nfss, virDomainFSDefFree);
> +    ReplaceArray(orig, copy, fss, nfss);
> +
> +    SyncArray(orig, copy, nets, nnets, virDomainNetDefFree);
> +    ReplaceArray(orig, copy, nets, nnets);
> +
> +    SyncArray(orig, copy, smartcards, nsmartcards, virDomainSmartcardDefFree);
> +    ReplaceArray(orig, copy, smartcards, nsmartcards);
> +
> +    SyncArray(orig, copy, serials, nserials, virDomainChrDefFree);
> +    ReplaceArray(orig, copy, serials, nserials);
> +
> +    SyncArray(orig, copy, parallels, nparallels, virDomainChrDefFree);
> +    ReplaceArray(orig, copy, parallels, nparallels);
> +
> +    SyncArray(orig, copy, channels, nchannels, virDomainChrDefFree);
> +    ReplaceArray(orig, copy, channels, nchannels);
> +
> +    SyncArray(orig, copy, sounds, nsounds, virDomainSoundDefFree);
> +    ReplaceArray(orig, copy, sounds, nsounds);
> +
> +    SyncArray(orig, copy, videos, nvideos, virDomainVideoDefFree);
> +    ReplaceArray(orig, copy, videos, nvideos);
> +
> +    SyncArray(orig, copy, hostdevs, nhostdevs, virDomainHostdevDefFree);
> +    ReplaceArray(orig, copy, hostdevs, nhostdevs);
> +
> +    VIR_FREE(copy);
> +}
> +/*
> + * Free all devices exists only in the copy.
> + */
> +void virDomainTemporalCopyUndo(virDomainDefPtr orig,
> +                               virDomainDefPtr copy)
> +{
> +    /* Free redundunt objects in the copied array */
> +    SyncArray(copy, orig, graphics, ngraphics, virDomainGraphicsDefFree);
> +
> +    SyncArray(copy, orig, inputs, ninputs, virDomainInputDefFree);
> +
> +    SyncArray(copy, orig, disks, ndisks, virDomainDiskDefFree);
> +
> +    SyncArray(copy, orig,
> +              controllers, ncontrollers, virDomainControllerDefFree);
> +    SyncArray(copy, orig, fss, nfss, virDomainFSDefFree);
> +
> +    SyncArray(copy, orig, nets, nnets, virDomainNetDefFree);
> +
> +    SyncArray(copy, orig, smartcards, nsmartcards, virDomainSmartcardDefFree);
> +
> +    SyncArray(copy, orig, serials, nserials, virDomainChrDefFree);
> +
> +    SyncArray(copy, orig, parallels, nparallels, virDomainChrDefFree);
> +
> +    SyncArray(copy, orig, channels, nchannels, virDomainChrDefFree);
> +
> +    SyncArray(copy, orig, sounds, nsounds, virDomainSoundDefFree);
> +
> +    SyncArray(copy, orig, videos, nvideos, virDomainVideoDefFree);
> +
> +    SyncArray(copy, orig, hostdevs, nhostdevs, virDomainHostdevDefFree);
> +
> +    virDomainTemporalCopyFree(copy);
> +}
> +#undef SyncArray
> +#undef ReplaceArray
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index 10e73cb..28b3c0e 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -1377,6 +1377,11 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
>                                  virDomainDiskDefPathIterator iter,
>                                  void *opaque);
>  
> +virDomainDefPtr virDomainTemporalCopyAlloc(virDomainDefPtr vmdef);
> +void virDomainTemporalCopySync(virDomainDefPtr orig,
> +                               virDomainDefPtr copy);
> +void virDomainTemporalCopyUndo(virDomainDefPtr orig, virDomainDefPtr copy);
> +
>  typedef const char* (*virLifecycleToStringFunc)(int type);
>  typedef int (*virLifecycleFromStringFunc)(const char *type);
>  
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 65a86d3..62643d4 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -312,6 +312,9 @@ virDomainSoundModelTypeFromString;
>  virDomainSoundModelTypeToString;
>  virDomainStateTypeFromString;
>  virDomainStateTypeToString;
> +virDomainTemporalCopyAlloc;
> +virDomainTemporalCopySync;
> +virDomainTemporalCopyUndo;
>  virDomainTimerModeTypeFromString;
>  virDomainTimerModeTypeToString;
>  virDomainTimerNameTypeFromString;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index b89bc8f..08055ef 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -3914,7 +3914,7 @@ static int qemuDomainModifyDevicePersistent(virDomainPtr dom,
>  {
>      struct qemud_driver *driver;
>      virDomainDeviceDefPtr device;
> -    virDomainDefPtr vmdef;
> +    virDomainDefPtr vmdef, copied;
>      virDomainObjPtr vm;
>      int ret = -1;
>  
> @@ -3962,16 +3962,26 @@ static int qemuDomainModifyDevicePersistent(virDomainPtr dom,
>      if (!device)
>          goto endjob;
>  
> +    copied = virDomainTemporalCopyAlloc(vmdef);
> +    if (!copied)
> +        goto endjob;
> +
>      if (attach)
> -        ret = qemuDomainAttachDevicePersistent(vmdef, device);
> +        ret = qemuDomainAttachDevicePersistent(copied, device);
>      else
> -        ret = qemuDomainDetachDevicePersistent(vmdef, device);
> +        ret = qemuDomainDetachDevicePersistent(copied, device);
>      /*
>       * At(De)tachDevicePersistent() must guarantee that vmdef is consistent
>       * with XML definition when they returns a failure, ret != 0.
>       */
>      if (!ret)
> -        ret = virDomainSaveConfig(driver->configDir, vmdef);
> +        ret = virDomainSaveConfig(driver->configDir, copied);
> +
> +    /* If successfully saved the new vmdef, sync it. this never fails */
> +    if (!ret)
> +        virDomainTemporalCopySync(vmdef, copied);
> +    else /* discard */
> +        virDomainTemporalCopyUndo(vmdef, copied);
>  
>      virDomainDeviceDefFree(device);
>  




More information about the libvir-list mailing list