[libvirt] [PATCH] Added QEMU support for IVSHMEM devices

Eric Blake eblake at redhat.com
Thu Oct 4 15:53:49 UTC 2012


On 09/24/2012 09:44 AM, Shawn Furrow wrote:

[sorry for my delay in replying]

> *Created at Virginia Tech's Systems Software Research Group
> 
> This patch adds the XML schema and implementation for IVSHMEM device driver     
> support. Currently it defaults to using interrupts. A sample IVSHMEM entry     
> in the VM's XML file is:

Thanks for starting to tackle this.

> 
> <ivshmem id='nahanni' size='16834' path='/tmp/'/>

Elsewhere, we have represented memory sized in KiB (1024 byte units);
what scale are you using here?  A default unit of byte might be nicer,
on the other hand, does shared memory have to be page aligned, at which
point it will always be a multiple of 4k (and thus listing in KiB still
makes sense)?  At any rate, I think that this argues you need to support
units on output to show the preferred scale, as well as parse it on
input to allow users to specify units='M' size='1' to reserve 2**20
bytes with ease.

> 
> ---
>  docs/schemas/domaincommon.rng |   17 +++++

Incomplete without also patching docs/formatdomain.html.in to describe
the new element.  I'll go ahead and review the code, but cannot apply
this without documentation.

>  src/conf/domain_conf.c        |  152 ++++++++++++++++++++++++++++++++++++++++-
>  src/conf/domain_conf.h        |   16 ++++-
>  src/qemu/qemu_command.c       |   75 ++++++++++++++++++++
>  src/qemu/qemu_command.h       |    6 ++
>  5 files changed, 264 insertions(+), 2 deletions(-)
> 
> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
> index f47fdad..ddf8eb1 100644
> --- a/docs/schemas/domaincommon.rng
> +++ b/docs/schemas/domaincommon.rng
> @@ -2576,6 +2576,23 @@
>        </optional>
>      </element>
>    </define>
> +  <define name="ivshmem">
> +    <element name="ivshmem">
> +      <attribute name="id">
> +        <ref name="deviceName">
> +      </attribute>
> +      <optional>
> +        <attribute name="size">
> +          <ref name="memoryKB">
> +        </attribute>
> +      </optional>

For ease of parsing (that is, for purposes of code reuse), I would
suggest listing size as a sub-element rather than an attribute:

<ivshmem name='...' path='...'>
  <size units='KiB'>nnn</size>
</ivshmem>

> +      <optional>
> +        <attribute name="path">
> +          <ref name="filePath">
> +        </attribute>
> +      </optional>
> +    </element>
> +  </define>
>    <define name="parallel">
>      <element name="parallel">
>        <ref name="qemucdev"/>


> +void virDomainIvshmemDefFree(virDomainIvshmemDefPtr def)

Style - newline after the return type so that the function name begins
in column 1.  Also, can this be static?

> +{
> +    if (!def)
> +        return;
> +
> +    virDomainDeviceInfoClear(&def->info);
> +
> +    VIR_FREE(def);

Memory leak of def->id and def->path.

> +static virDomainIvshmemDefPtr
> +virDomainIvshmemDefParseXML(const xmlNodePtr node,
> +                               unsigned int flags)
> +{
> +    char *id = NULL;
> +    char *size = NULL;
> +    char *path = NULL;
> +    virDomainIvshmemDefPtr def;
> +
> +    if (VIR_ALLOC(def) < 0) {
> +        virReportOOMError();
> +        return NULL;
> +    }
> +
> +    id = virXMLPropString(node, "id");
> +    VIR_DEBUG("ivshmem: id = '%s'", id);
> +    virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("ivshmem id=' %s'"), id);

Alignment is off.

> +    if (id == NULL) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("ERROR: ivshmem, id not defined' %s'"), id);
> +        goto error;
> +    }
> +
> +    def->id = id;
> +    size = virXMLPropString(node, "size");
> +    virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("ivshmem size=' %s'"), size);

And again.

> +
> +    VIR_DEBUG("ivshmem: size = '%s'", size);
> +    if (size) {
> +        if (virStrToLong_i(size, NULL, 10, &def->size) < 0) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("cannot parse ivshmem size %s"), size);
> +            VIR_FREE(size);
> +            goto error;
> +        }

What happens if size is not a multiple of page size?  Is that an error,
or does it get rounded up?

> +    } else {
> +        def->size = 16834;

Why 16k?  Where is this default documented?

>  static virSysinfoDefPtr
>  virSysinfoParseXML(const xmlNodePtr node,
>                    xmlXPathContextPtr ctxt)
> @@ -9742,6 +9824,28 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
>          }
>      }
>  
> +    /* analysis of the ivshmem devices */
> +    def->ivshmem = NULL;
> +    if ((n = virXPathNodeSet("./devices/ivshmem", ctxt, &nodes)) < 0) {
> +        goto error;
> +    }
> +
> +    if (n > 0) {
> +        virDomainIvshmemDefPtr ivshmem =
> +            virDomainIvshmemDefParseXML(nodes[0], flags);
> +        if (!ivshmem)
> +            goto error;
> +
> +        def->ivshmem = ivshmem;
> +        VIR_FREE(nodes);
> +    } else if (def->virtType != VIR_DOMAIN_VIRT_QEMU) {
> +        /* TODO: currently ivshmem only on QEMU */

Don't do this.  The code in domain_conf.c is supposed to be
driver-agnostic.  If anything, the right way to reject <ivshmem> would
be to add a new capability in virCapsPtr, and set that capability only
on qemu, and reject the device if the capability is not present.  Then,
when other drivers add support for ivshmem (I imagine LXC might be a
good candidate for this, by using a POSIX shared memory region), then
that driver sets the capability bit, and you don't have to revisit this
code.

> +
> +static int
> +virDomainIvshmemDefFormat(virBufferPtr buf,
> +                            virDomainIvshmemDefPtr def,
> +                             unsigned int flags)

Alignment.

> +{
> +    const char *id = def->id;
> +    const char *path = def->path;
> +
> +    if (!id) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("unexpected ivshmem id =  %s"), def->id);
> +        return -1;
> +    }
> +    if (!def->size) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("unexpected ivshmem size =  %d"), def->size);
> +        return -1;
> +    }

In our formatters, we can generally assume that the struct is properly
populated, and therefore these paranoia checks do not need to be preformed.

> +    virBufferAsprintf(buf, "    <ivshmem id='%s'", id);
> +    virBufferAsprintf(buf, " size='%d'", def->size);

If you don't take my advice of making size a subelement, then these two
lines can be merged.

> +    virBufferAsprintf(buf, " path='%s'", path);

path is arbitrary user text, and therefore needs xml escaping; you must
use virBufferEscapeString here.

> +
> +    if (virDomainDeviceInfoIsSet(&def->info, flags)) {
> +        virBufferAddLit(buf, ">\n");
> +        if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0)
> +            return -1;

How is the shared memory presented to the guest (that is, what will
virDomainDeviceInfoFormat output here, if it is set)?  Does a qemu guest
see it as a PCI device, or what?

>  
> +struct _virDomainIvshmemDef {
> +    char *id;
> +    int size;

int is probably the wrong type, since it is feasible to have a shared
memory region larger than 4GB.  You also ought to document what units
this is stored in (I recommend bytes internally, even if you decide to
default the XML to KiB for consistency with other memory descriptions
already present).

> @@ -2194,6 +2207,7 @@ VIR_ENUM_DECL(virDomainSoundCodec)
>  VIR_ENUM_DECL(virDomainSoundModel)
>  VIR_ENUM_DECL(virDomainMemDump)
>  VIR_ENUM_DECL(virDomainMemballoonModel)
> +VIR_ENUM_DECL(virDomainIvshmemModel)

Where do you define this enum?  I don't think you need this line.

> +++ b/src/qemu/qemu_command.c

>  
> +//adds the options for the "-device" portion of QEMU command line for ivshmem

C89 comments, please (/* */, not //)

> +char *
> +qemuBuildIvshmemDevStr(virDomainIvshmemDefPtr dev,
> +                       qemuCapsPtr caps)
> +{
> +virBuffer buf = VIR_BUFFER_INITIALIZER;
> +
> +    virBufferAddLit(&buf, "ivshmem");
> +    virBufferAsprintf(&buf, ",chardev=%s", dev->id);
> +    virBufferAsprintf(&buf, ",size=%dm", dev->size/1024);

Yuck, this rounds.  I'd rather pass this information in bytes than in
MiB, to ensure that we are as faithful to the user's request as possible.

> +    virBufferAsprintf(&buf, ",ioeventfd=on");
> +    virBufferAsprintf(&buf, ",vectors=8");

You can cram these into a single virBufferAsprintf call:

virBufferAsprintf(&buf,
  "ivshmem,chardev=%s,size=%dm,ioeventfd=on,vectors=8",
  dev->id, dev->size/1024);

Also, does ioeventfd or vectors need to be configurable in the XML?

> +    //virBufferAsprintf(&buf, ",shm=%s", dev->id);

Leftover debugging?


> +
> +//adds the options for the "-chardev" portion of QEMU command line for ivshmem

Again, C89 comment.

> +char *
> +qemuBuildIvshmemCharDevStr(virDomainIvshmemDefPtr dev,
> +                           qemuCapsPtr caps)
> +{
> +virBuffer buf = VIR_BUFFER_INITIALIZER;
> +
> +    virBufferAddLit(&buf, "socket");
> +    virBufferAsprintf(&buf, ",id=%s", dev->id);
> +    virBufferAsprintf(&buf, ",path=%s%s", dev->path,dev->id);

Again, these can be combined.

> @@ -6582,6 +6638,25 @@ qemuBuildCommandLine(virConnectPtr conn,
>          }
>      }
>  
> +    // adds ivshmem QEMU command line entries

C89 comment.

Also, it would help to add tests to tests/qemuxml2argvdata to prove that
you map the new XML to the proper command line (and that the new XML is
valid per the RNG).

> +    if ((def->ivshmem) && (def->ivshmem->id != NULL)) {
> +        char *optstr;
> +        virCommandAddArg(cmd, "-chardev");

-chardev was not always available in older qemu.  You probably also need
a patch to qemu_capabilities.[hc] to detect whether qemu is new enough
to support a shared memory device, so that you can give a graceful error
message here when it is not supported (rather than a cryptic message
from qemu saying that the command line could not be parsed).

-- 
Eric Blake   eblake at redhat.com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 617 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20121004/c0cd4335/attachment-0001.sig>


More information about the libvir-list mailing list