[libvirt] [PATCH v2 3/9] conf: Add new domain XML element 'iothreadids'

Peter Krempa pkrempa at redhat.com
Mon Apr 13 12:13:17 UTC 2015


On Fri, Apr 10, 2015 at 17:36:21 -0400, John Ferlan wrote:
> Adding a new XML element 'iothreadids' in order to allow defining
> specific IOThread ID's rather than relying on the algorithm to assign
> IOThread ID's starting at 1 and incrementing to iothreads count.
> 
> This will allow future patches to be able to add new IOThreads by
> a specific iothread_id and of course delete any exisiting IOThread.
> 
> Each iothreads element will have 'n' <iothread> children elements
> which will have attributes "id" and "name".  The "id" will allow for
> definition of any "valid" (eg > 0) iothread_id value.  The "name"
> attribute will allow for adding a name to the alias generated for
> the IOThread. The name cannot contain "iothread" since that's part
> of the default IOThread naming scheme already in use.
> 
> On input, if any <iothreadids> <iothread>'s are provided, they will
> be marked so that we only print out what we read in.
> 
> On input, if no <iothreadids> are provided, the PostParse code will
> self generate a list of ID's starting at 1 and going to the number
> of iothreads defined for the domain (just like the current algorithm
> numbering scheme).  A future patch will rework the existing algorithm
> to make use of the iothreadids list.
> the input XML
> 
> On output, only print out the <iothreadids> if they were read in.
> 
> Signed-off-by: John Ferlan <jferlan at redhat.com>
> ---
>  docs/formatdomain.html.in     |  28 +++++
>  docs/schemas/domaincommon.rng |  17 +++
>  src/conf/domain_conf.c        | 245 +++++++++++++++++++++++++++++++++++++++++-
>  src/conf/domain_conf.h        |  23 ++++
>  src/libvirt_private.syms      |   6 ++
>  5 files changed, 317 insertions(+), 2 deletions(-)
> 
> diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
> index 7ceb1fa..3224c20 100644
> --- a/docs/formatdomain.html.in
> +++ b/docs/formatdomain.html.in
> @@ -521,6 +521,18 @@
>    ...
>  </domain>
>  </pre>
> +<pre>
> +<domain>
> +  ...
> +  <iothreadids>
> +    <iothread id="2" name="sysdisk"/>
> +    <iothread id="4" name="userdisk"/>
> +    <iothread id="6" name="datadisk"/>
> +    <iothread id="8" name="datadisk"/>
> +  </iothreadids>
> +  ...
> +</domain>
> +</pre>
>  
>      <dl>
>        <dt><code>iothreads</code></dt>
> @@ -530,7 +542,23 @@
>          virtio-blk-pci and virtio-blk-ccw target storage devices. There
>          should be only 1 or 2 IOThreads per host CPU. There may be more
>          than one supported device assigned to each IOThread.
> +        <span class="since">Since 1.2.8</span>
>        </dd>
> +      <dt><code>iothreadids</code></dt>
> +      <dd>
> +        The optional <code>iothreadids</code> element provides the capability
> +        to specifically define the IOThread ID's for the domain.  By default,
> +        IOThread ID's are sequentially numbered starting from 1 through the
> +        number of <code>iothreads</code> defined for the domain. The
> +        <code>id</code> attribute is used to define the IOThread ID and
> +        the optional <code>name</code> attribute is a user defined name that
> +        may be used to name the IOThread for the hypervisor. The id attribute
> +        must be a positive integer greater than 0. If there are less
> +        <code>iothreadids</code> defined than <code>iothreads</code>
> +        defined for the domain, then libvirt will sequentially fill
> +        <code>iothreadids</code> starting at 1 avoiding any predefined id.
> +        <span class="since">Since 1.2.15</span>
> +       </dd>
>      </dl>
>  
>      <h3><a name="elementsCPUTuning">CPU Tuning</a></h3>

> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index 1f5bf62..844caf6 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c

...

> @@ -3304,6 +3332,18 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
>          return -1;
>      }
>  
> +    /* Fully populate the IOThread ID list */
> +    if (def->iothreads && def->iothreads != def->niothreadids) {
> +        unsigned int iothread_id = 1;
> +        while (def->niothreadids != def->iothreads) {
> +            if (!virDomainIOThreadIDIsDuplicate(def, iothread_id)) {
> +                if (virDomainIOThreadIDAdd(def, iothread_id, NULL) < 0)
> +                    return -1;
> +            }
> +            iothread_id++;
> +        }
> +    }
> +
>      if (virDomainDefGetMemoryInitial(def) == 0) {
>          virReportError(VIR_ERR_XML_ERROR, "%s",
>                         _("Memory size must be specified via <memory> or in the "
> @@ -13192,6 +13232,65 @@ virDomainIdmapDefParseXML(xmlXPathContextPtr ctxt,
>      return idmap;
>  }
>  
> +/* Parse the XML definition for an IOThread ID
> + *
> + * Format is :
> + *
> + *     <iothreads>4</iothreads>
> + *     <iothreadids>
> + *       <iothread id='1' name='string'/>
> + *       <iothread id='3' name='string'/>
> + *       <iothread id='5' name='string'/>
> + *       <iothread id='7' name='string'/>
> + *     </iothreadids>
> + */
> +static virDomainIOThreadIDDefPtr
> +virDomainIOThreadIDDefParseXML(xmlNodePtr node,
> +                               xmlXPathContextPtr ctxt)
> +{
> +    virDomainIOThreadIDDefPtr def;
> +    xmlNodePtr oldnode = ctxt->node;
> +    char *tmp = NULL;
> +
> +    if (VIR_ALLOC(def) < 0)
> +        return NULL;
> +
> +    ctxt->node = node;
> +
> +    if (!(tmp = virXPathString("string(./@id)", ctxt))) {
> +        virReportError(VIR_ERR_XML_ERROR, "%s",
> +                       _("Missing <id> element in IOThread ID"));

'id' is an attribute, not an element.

> +        goto error;
> +    }
> +    if (virStrToLong_uip(tmp, NULL, 10, &def->iothread_id) < 0 ||
> +        def->iothread_id == 0) {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                        _("invalid iothread 'id' value '%s'"), tmp);
> +        goto error;
> +    }
> +
> +    def->name = virXMLPropString(node, "name");
> +    if (def->name && strstr(def->name, "iothread")) {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                       _("invalid iothread 'name' value '%s' - cannot "
> +                         "contain 'iothread'"),
> +                       def->name);
> +        VIR_FREE(def->name);

This line can be dropped if ...

> +        goto error;
> +    }
> +    def->defined = true;
> +
> + cleanup:
> +    VIR_FREE(tmp);
> +    ctxt->node = oldnode;
> +    return def;
> +
> + error:
> +    VIR_FREE(def);

... you use the freeing function you've added.

> +    goto cleanup;
> +}
> +
> +
>  /* Parse the XML definition for a vcpupin or emulatorpin.
>   *
>   * vcpupin has the form of
> @@ -13899,6 +13998,37 @@ virDomainDefParseXML(xmlDocPtr xml,
>      }
>      VIR_FREE(tmp);
>  
> +    /* Extract any iothread id's defined */
> +    if ((n = virXPathNodeSet("./iothreadids/iothread", ctxt, &nodes)) < 0)
> +        goto error;
> +
> +    if (n > def->iothreads) {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                       _("number of iothreadid iothread elements '%u' is "
> +                         "greater than the number of iothreads '%u'"),
> +                       n, def->iothreads);

Or you can perhaps silently fix def->iothreads so that the user doesn't
need to keep them in sync at least when increasing number of threads.

> +        goto error;
> +    }
> +
> +    if (n && VIR_ALLOC_N(def->iothreadids, n) < 0)
> +        goto error;

Here you'll need to allocate the array to MAX(n, def->iothreads) to
unify the structures as I'll suggest below.

> +
> +    for (i = 0; i < n; i++) {
> +        virDomainIOThreadIDDefPtr iothreadid = NULL;
> +        if (!(iothreadid = virDomainIOThreadIDDefParseXML(nodes[i], ctxt)))
> +            goto error;
> +
> +        if (virDomainIOThreadIDIsDuplicate(def, iothreadid->iothread_id)) {
> +            virReportError(VIR_ERR_XML_ERROR,
> +                           _("duplicate iothread id '%u' found"),
> +                           iothreadid->iothread_id);
> +            virDomainIOThreadIDDefFree(iothreadid);
> +            goto error;
> +        }
> +        def->iothreadids[def->niothreadids++] = iothreadid;
> +    }
> +    VIR_FREE(nodes);
> +
>      /* Extract cpu tunables. */
>      if ((n = virXPathULong("string(./cputune/shares[1])", ctxt,
>                             &def->cputune.shares)) < -1) {
> @@ -17275,6 +17405,94 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def)
>      return 0;
>  }
>  
> +bool
> +virDomainIOThreadIDIsDuplicate(virDomainDefPtr def,
> +                               unsigned int iothread_id)
> +{
> +    size_t i;
> +
> +    if (!def->iothreadids || !def->niothreadids)
> +        return false;
> +
> +    for (i = 0; i < def->niothreadids; i++) {
> +        if (iothread_id == def->iothreadids[i]->iothread_id)
> +            return true;
> +    }
> +
> +    return false;

How about "return !!virDomainIOThreadIDFind(def, iothread_id) rather
than open coding it?


> +}
> +
> +virDomainIOThreadIDDefPtr
> +virDomainIOThreadIDFind(virDomainDefPtr def,
> +                        unsigned int iothread_id)
> +{
> +    size_t i;
> +
> +    if (!def->iothreadids || !def->niothreadids)
> +        return NULL;
> +
> +    for (i = 0; i < def->niothreadids; i++) {
> +        if (iothread_id == def->iothreadids[i]->iothread_id)
> +            return def->iothreadids[i];
> +    }
> +
> +    return NULL;
> +}
> +
> +int
> +virDomainIOThreadIDAdd(virDomainDefPtr def,
> +                       unsigned int iothread_id,
> +                       const char *name)
> +{
> +    virDomainIOThreadIDDefPtr iddef = NULL;
> +
> +    if (virDomainIOThreadIDIsDuplicate(def, iothread_id)) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("cannot duplicate iothread_id '%u' in iothreadids"),
> +                       iothread_id);
> +        return -1;
> +    }
> +
> +    if (VIR_ALLOC(iddef) < 0)
> +        goto error;
> +
> +    iddef->iothread_id = iothread_id;
> +  if (name && VIR_STRDUP(iddef->name, name) < 0)

VIR_STRDUP handles NULL just fine, no need to check @name.

> +        goto error;
> +
> +    if (!def->iothreadids) {
> +        if (VIR_ALLOC_N(def->iothreadids, 1) < 0)
> +            goto error;
> +        def->niothreadids = 1;
> +        def->iothreadids[0] = iddef;

The clause above is not necessary, code below handles NULL arrays just
fine.

> +    } else {
> +        if (VIR_APPEND_ELEMENT(def->iothreadids, def->niothreadids, iddef) < 0)
> +            goto error;
> +    }
> +
> +    return 0;
> +
> + error:
> +    virDomainIOThreadIDDefFree(iddef);
> +    return -1;
> +}
> +
> +void
> +virDomainIOThreadIDDel(virDomainDefPtr def,
> +                       unsigned int iothread_id)
> +{
> +    int n;
> +
> +    for (n = 0; n < def->niothreadids; n++) {
> +        if (def->iothreadids[n]->iothread_id == iothread_id) {
> +            VIR_FREE(def->iothreadids[n]->name);
> +            VIR_FREE(def->iothreadids[n]);

You've introduced a helper that frees the entries. You should use it
here.

> +            VIR_DELETE_ELEMENT(def->iothreadids, n, def->niothreadids);
> +            return;
> +        }
> +    }
> +}
> +
>  /* Check if vcpupin with same id already exists.
>   * Return 1 if exists, 0 if not. */
>  bool
> @@ -20618,8 +20836,31 @@ virDomainDefFormatInternal(virDomainDefPtr def,
>          virBufferAsprintf(buf, " current='%u'", def->vcpus);
>      virBufferAsprintf(buf, ">%u</vcpu>\n", def->maxvcpus);
>  
> -    if (def->iothreads > 0)
> -        virBufferAsprintf(buf, "<iothreads>%u</iothreads>\n", def->iothreads);
> +    if (def->iothreads > 0) {
> +        virBufferAsprintf(buf, "<iothreads>%u</iothreads>\n",
> +                          def->iothreads);
> +        /* If we parsed the iothreadids, then write out those that we parsed */
> +        for (i = 0; i < def->niothreadids; i++) {
> +            if (def->iothreadids[i]->defined)
> +                break;
> +        }

Dead code.

> +        if (i < def->niothreadids) {
> +            virBufferAddLit(buf, "<iothreadids>\n");
> +            virBufferAdjustIndent(buf, 2);
> +            for (i = 0; i < def->niothreadids; i++) {
> +                if (!def->iothreadids[i]->defined)
> +                    continue;

If all iothreadids are implicit from the post parse callback this will
output an empty '<iothreadis>' element.

> +                virBufferAsprintf(buf, "<iothread id='%u'",
> +                                  def->iothreadids[i]->iothread_id);
> +                if (def->iothreadids[i]->name)
> +                    virBufferAsprintf(buf, " name='%s'",
> +                                      def->iothreadids[i]->name);
> +                virBufferAddLit(buf, "/>\n");
> +            }
> +            virBufferAdjustIndent(buf, -2);
> +            virBufferAddLit(buf, "</iothreadids>\n");
> +        }
> +    }
>  
>      if (def->cputune.sharesSpecified ||
>          (def->cputune.nvcpupin && !virDomainIsAllVcpupinInherited(def)) ||
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index 95cbb9c..03a0ecd 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -2041,6 +2041,19 @@ struct _virDomainHugePage {
>      unsigned long long size;    /* hugepage size in KiB */
>  };
>  
> +typedef struct _virDomainIOThreadIDDef virDomainIOThreadIDDef;
> +typedef virDomainIOThreadIDDef *virDomainIOThreadIDDefPtr;
> +
> +struct _virDomainIOThreadIDDef {
> +    bool defined;
> +    unsigned int iothread_id;
> +    char *name;

This structure along with the one you add in the next patch and the
pinning structures that already exist make now three places where we
store data regarding IO threads. I don't think we should do that.
Keeping track of which data introduces messy code.

I think it will be desirable that you move the data regarding pinning of
iothreads into this structure along with thread ids from the monitor so
that we can keep everything in one place.


> +};
> +
> +void virDomainIOThreadIDDefFree(virDomainIOThreadIDDefPtr def);
> +void virDomainIOThreadIDDefArrayFree(virDomainIOThreadIDDefPtr *def,
> +                                     int nids);
> +
>  typedef struct _virDomainCputune virDomainCputune;
>  typedef virDomainCputune *virDomainCputunePtr;
>  

I think the direction this series is taking is okay.

Peter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20150413/5d61c092/attachment-0001.sig>


More information about the libvir-list mailing list