[libvirt] patch: allow disk cache mode to be specified in a domain's xml definition.

Daniel P. Berrange berrange at redhat.com
Mon Jan 19 12:58:22 UTC 2009


On Fri, Jan 16, 2009 at 03:46:01PM -0500, john cooper wrote:
> We have found certain application scenarios where
> overriding of the default qemu host cache mode
> provides a substantial improvement in guest
> performance.  In particular, disabling host caching
> of the file/dev backing a guest drive.  A summary
> of performance metrics may be found below.
> 
> Attached is a simple patch which adds general
> support for a "cache=*" attribute to be passed
> to qemu from libvirt as part of a xml <driver>
> element.  It is parsed in a domain's xml definition
> as well  as being generated during output of the
> same.  For example:
> 
>    <disk type='file' device='disk'>
>      <source file='/guest_roots/fc8.root'/>
>      <target dev='hda'/>
>      <driver name='qemu' type='qwerty' cache='none'/>
>    </disk>
> 
> where both the existing "type=*" and added "cache=*"
> attributes are optional and independent.

That scheme looks good.  FYI, I have a patch ready which supports 
the existing driver name & type attributes for QEMU/KVM, as per 
existing usage in Xen driver

>  domain_conf.c |   32 +++++++++++++++++++++++++-------
>  domain_conf.h |   15 +++++++++++++++
>  qemu_conf.c   |   32 ++++++++++++++++++++++++++++----

A couple of extra things needed 

 - Addition to tests/qemuxml2argvtest.c to validate the XML to
   struct to QEMU ARGV conversion.
 - Addition to tests/qemuxml2xmltest.c to  validate XML to
   struct to XML round-trip conversions.
 - Addition to the docs/libvirt.rng XML schema.

>  3 files changed, 68 insertions(+), 11 deletions(-)
> =================================================================
> --- a/src/domain_conf.h
> +++ b/src/domain_conf.h
> @@ -80,6 +80,20 @@ enum virDomainDiskBus {
>      VIR_DOMAIN_DISK_BUS_LAST
>  };
>  
> +/* summary of all possible host open file cache modes
> + */
> +typedef enum {
> +    VIR_DOMAIN_DISK_CACHE_DEFAULT = 0,
> +    VIR_DOMAIN_DISK_CACHE_DISABLE,
> +    VIR_DOMAIN_DISK_CACHE_WRITEBACK,
> +    VIR_DOMAIN_DISK_CACHE_WRITETHRU,
> +    } host_cachemode;

Can you rename this typedef to virDomainDiskCache, and
add a sentinal value  VIR_DOMAIN_DISK_CACHE_LAST

> +
> +typedef struct {
> +    char *tag;
> +    host_cachemode idx;
> +    } cache_map_t;

This 2nd struct is not required.

>  /* Stores the virtual disk configuration */
>  typedef struct _virDomainDiskDef virDomainDiskDef;
>  typedef virDomainDiskDef *virDomainDiskDefPtr;
> @@ -91,6 +105,7 @@ struct _virDomainDiskDef {
>      char *dst;
>      char *driverName;
>      char *driverType;
> +    host_cachemode cachemode;

Our code policy is to use 'int' when enums are used, since
enums have no fixed size.

> --- a/src/qemu_conf.c
> +++ b/src/qemu_conf.c
> @@ -616,6 +616,26 @@ qemudNetworkIfaceConnect(virConnectPtr c
>      return NULL;
>  }
>  
> +/* map from internal cache mode to qemu cache arg text
> + */ 
> +static cache_map_t qemu_cache_map[] = {
> +    {"none", VIR_DOMAIN_DISK_CACHE_DISABLE},
> +    {"writeback", VIR_DOMAIN_DISK_CACHE_WRITEBACK},
> +    {"writethrough", VIR_DOMAIN_DISK_CACHE_WRITETHRU},
> +    {NULL}};
> +
> +/* return qemu arg text * corresponding to idx if found, NULL otherwise
> + */
> +static inline char *qemu_cache_mode(host_cachemode idx)
> +{
> +    int i;
> +
> +    for (i = 0; qemu_cache_map[i].tag; ++i)
> +	if (qemu_cache_map[i].idx == idx)
> +            return (qemu_cache_map[i].tag);
> +    return (NULL);
> +}

This static map & function are not required. Instead the int
to char * conversion (and its reverse) are automatically generated
through use of our built-in enum support macros

In the domain_conf.h header file add

  VIR_ENUM_DECL(virDomainDiskCache)

And then in the  domain_conf.c  file the impl is provided by

   VIR_ENUM_IMPL(virDomainDiskCache, VIR_DOMAIN_DISK_CACHE_LAST,
                 "default", "none", "writeback", "writethrough");

This guarentees (with a compile check) that there is the correct
number of constant strings, to match the enum values.

You have two generated functions that can be used in the XML
parsing/formatting calls

  int virDomainDiskCacheTypeFromString(const char *type);
  const char *virDomainDiskCacheTypeFromString(int type);

>  static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
>                                            char *buf,
>                                            int buflen)
> @@ -946,6 +966,8 @@ int qemudBuildCommandLine(virConnectPtr 
>              virDomainDiskDefPtr disk = vm->def->disks[i];
>              int idx = virDiskNameToIndex(disk->dst);
>              const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
> +            int nc;
> +            char *cachemode;
>  
>              if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
>                  if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
> @@ -980,15 +1002,17 @@ int qemudBuildCommandLine(virConnectPtr 
>                  break;
>              }
>  
> -            snprintf(opt, PATH_MAX, "file=%s,if=%s,%sindex=%d%s%s",
> +            nc = snprintf(opt, PATH_MAX, "file=%s,if=%s,%sindex=%d%s",
>                       disk->src ? disk->src : "", bus,
>                       media ? media : "",
>                       idx,
>                       bootable &&
>                       disk->device == VIR_DOMAIN_DISK_DEVICE_DISK
> -                     ? ",boot=on" : "",
> -                     disk->shared && ! disk->readonly
> -                     ? ",cache=off" : "");
> +                     ? ",boot=on" : "");
> +            cachemode = qemu_cache_mode(disk->cachemode);
> +            if (cachemode || disk->shared && !disk->readonly)
> +                snprintf(opt + nc, PATH_MAX - nc, ",cache=%s",
> +                    cachemode ? cachemode : "off");

There have been two differnet syntaxes supported in QEMU for caching,
so we need to detect this and switch between them. Originally there
was just

  cache=on & cache=off (writethrough and no caching)

Now it supports

  cache=writeback, cache=writethrough and cache=none|off

So, if we detect the earlier syntax we need to raise an error if
the user requested writeback (or translate it to 'cache=off' for
safety sake).

>              ADD_ARG_LIT("-drive");
>              ADD_ARG_LIT(opt);
> =================================================================
> --- a/src/domain_conf.c
> +++ b/src/domain_conf.c
> @@ -528,6 +528,16 @@ int virDomainDiskCompare(virDomainDiskDe
>  
>  
>  #ifndef PROXY
> +
> +/* map from xml cache tag to internal cache mode
> + */
> +static cache_map_t cache_map[] = {
> +    {"none", VIR_DOMAIN_DISK_CACHE_DISABLE},
> +    {"off", VIR_DOMAIN_DISK_CACHE_DISABLE},
> +    {"writeback", VIR_DOMAIN_DISK_CACHE_WRITEBACK},
> +    {"writethrough", VIR_DOMAIN_DISK_CACHE_WRITETHRU},
> +    {NULL}};
> +

This chunk is not required, because the VIR_ENUM code takes
care of this.

>  /* Parse the XML definition for a disk
>   * @param node XML nodeset to parse for disk definition
>   */
> @@ -543,6 +553,8 @@ virDomainDiskDefParseXML(virConnectPtr c
>      char *source = NULL;
>      char *target = NULL;
>      char *bus = NULL;
> +    char *cachetag = NULL;
> +    int i;
>  
>      if (VIR_ALLOC(def) < 0) {
>          virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
> @@ -592,6 +604,14 @@ virDomainDiskDefParseXML(virConnectPtr c
>                         (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
>                  driverName = virXMLPropString(cur, "name");
>                  driverType = virXMLPropString(cur, "type");
> +                if (cachetag = virXMLPropString(cur, "cache")) {
> +                    for (i = 0; cache_map[i].tag; ++i)
> +                        if (!strcmp(cache_map[i].tag, cachetag)) {
> +                            def->cachemode = cache_map[i].idx;
> +                            break;
> +                        }
> +                VIR_FREE(cachetag);
> +                }

Indentation got a little mixed up here . The for() loop is can be replaced
by a call to virDomainDiskCacheTypeFromString.

>              } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
>                  def->readonly = 1;
>              } else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) {
> @@ -2620,14 +2640,12 @@ virDomainDiskDefFormat(virConnectPtr con
>                        type, device);
>  
>      if (def->driverName) {
> +        virBufferVSprintf(buf, "      <driver name='%s'", def->driverName);
>          if (def->driverType)
> -            virBufferVSprintf(buf,
> -                              "      <driver name='%s' type='%s'/>\n",
> -                              def->driverName, def->driverType);
> -        else
> -            virBufferVSprintf(buf,
> -                              "      <driver name='%s'/>\n",
> -                              def->driverName);
> +            virBufferVSprintf(buf, " type='%s'", def->driverType);
> +        if (def->cachemode)
> +            virBufferVSprintf(buf, " cache='%s'", cache_map[def->cachemode].tag);

The virDomainDiskCacheTypeToString() method should be used for this 
instead


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