[libvirt] [PATCH V4 2/5] remote handler for virDomainGetCPUStats()

KAMEZAWA Hiroyuki kamezawa.hiroyu at jp.fujitsu.com
Mon Jan 30 02:07:22 UTC 2012


On Sat, 28 Jan 2012 11:16:03 -0700
Eric Blake <eblake at redhat.com> wrote:

> On 01/27/2012 11:21 PM, KAMEZAWA Hiroyuki wrote:
> > remote protocol driver for virDomainGetCPUStats()
> > 
> > Note:
> > Unlike other users of virTypedParameter with RPC, this interface
> > can return zero-filled entries because the interface assumes
> > 2 dimentional array. Then, the function has its own serialize/deserialize
> 
> s/dimentional/dimensional/
> 
> > routine.
> > 
> >  daemon/remote.c              |  146 +++++++++++++++++++++++++++++++++++++++++++
> >  src/remote/remote_driver.c   |  117 ++++++++++++++++++++++++++++++++++
> >  src/remote/remote_protocol.x |   22 ++++++
> >  src/remote_protocol-structs  |   15 ++++
> > ---
> >  daemon/remote.c              |  147 ++++++++++++++++++++++++++++++++++++++++++
> >  src/remote/remote_driver.c   |  117 +++++++++++++++++++++++++++++++++
> >  src/remote/remote_protocol.x |   22 ++++++-
> >  src/remote_protocol-structs  |   15 +++
> 
> Odd that the diffstat listed twice.
> 
> I'm going to reorder my review, to start with the .x file.
> 
> > diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
> > index 0f354bb..205aeeb 100644
> > --- a/src/remote/remote_protocol.x
> > +++ b/src/remote/remote_protocol.x
> > @@ -208,6 +208,12 @@ const REMOTE_DOMAIN_SEND_KEY_MAX = 16;
> >   */
> >  const REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX = 16;
> >
> > +/*
> > + * Upper limit on list of (real) cpu parameters
> > + * nparams(<=16) * ncpus(<=128) <= 2048.
> > + */
> > +const REMOTE_DOMAIN_GET_CPU_STATS_MAX = 2048;
> 
> We should enforce the two limits independently, which means we also need
> two smaller constants (it's a shame that rpcgen doesn't allow products
> when computing a constant, but we can document how we arrive at various
> numbers).  We can reuse VIR_NODE_CPU_STATS_MAX for one of the two limits.
> 

Ok.

> > +
> >  /* UUID.  VIR_UUID_BUFLEN definition comes from libvirt.h */
> >  typedef opaque remote_uuid[VIR_UUID_BUFLEN];
> >
> > @@ -1149,6 +1155,19 @@ struct remote_domain_get_block_io_tune_ret {
> >      int nparams;
> >  };
> >
> > +struct remote_domain_get_cpu_stats_args {
> > +    remote_nonnull_domain dom;
> > +    unsigned int nparams;
> > +    int          start_cpu;
> > +    unsigned int ncpus;
> > +    unsigned int flags;
> > +};
> > +
> > +struct remote_domain_get_cpu_stats_ret {
> > +    remote_typed_param params<REMOTE_DOMAIN_GET_CPU_STATS_MAX>;
> > +    int nparams;
> > +};
> 
> Looks good.
> 
> > +
> >  /* Network calls: */
> >
> >  struct remote_num_of_networks_ret {
> > @@ -2667,7 +2686,8 @@ enum remote_procedure {
> >      REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, /* autogen
> autogen */
> >      REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, /* skipgen
> skipgen */
> >      REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, /* autogen autogen */
> > -    REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259 /* autogen autogen */
> > +    REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259, /* autogen autogen */
> > +    REMOTE_PROC_DOMAIN_GET_CPU_STATS = 260 /* skipgen skipgen */
> 
> Trivial conflict resolution.
> 
> 
> > +static int
> > +remoteDispatchDomainGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
> > +                                virNetServerClientPtr client ATTRIBUTE_UNUSED,
> > +                                virNetMessagePtr hdr ATTRIBUTE_UNUSED,
> > +                                virNetMessageErrorPtr rerr,
> > +                                remote_domain_get_cpu_stats_args *args,
> > +                                remote_domain_get_cpu_stats_ret *ret)
> > +{
> > +    virDomainPtr dom = NULL;
> > +    struct daemonClientPrivate *priv;
> > +    virTypedParameterPtr params = NULL;
> > +    remote_typed_param *info = NULL;
> > +    int cpu, idx, src, dest;
> > +    int rv = -1;
> > +    unsigned int return_size = 0;
> > +    int percpu_len = 0;
> > +
> > +    priv = virNetServerClientGetPrivateData(client);
> > +    if (!priv->conn) {
> > +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> > +        goto cleanup;
> > +    }
> > +
> > +    /*
> > +     * nparams * ncpus should be smaller than
> > +     * REMOTE_DOMAIN_GET_CPU_STATS_MAX but nparams
> > +     * and ncpus are limited by API. check it.
> > +     */
> > +    if (args->nparams > 16) {
> > +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large"));
> > +        goto cleanup;
> > +    }
> > +    if (args->ncpus > 128) {
> 
> Use the constants from the .x file, rather than open-coding things.

Sorry.


> Oops, I just realized that I mentioned that libvirt.c should not have a
> length check, but then I forgot to delete it.  Meanwhile, even if
> libvirt.c does not have a length check, it still needs to have a
> multiplication overflow check.
> 
> > +    /*
> > +     * Here, we return values based on the real param size returned by
> > +     * a driver rather than the passed one. RPC Client stub should decode
> > +     * it to fit callar's expectation.
> 
> s/callar/caller/
> 
> > +     *
> > +     * If cpu ID is sparse, The whole array for some cpu IDs will be
> > +     * zero-cleared. This doesn't meet to remoteSerializeTypedParameters()
> > +     * and we do serialization by ourselves.
> 
> We can still use remoteSerializeTypedParameters; we just have to teach
> it to skip zero entries like it already can skip string entries.
> 
Yes. you're right.

> Oh, I just realized that to support string entries, we have to support a
> flags of VIR_TYPED_PARAM_STRING_OKAY.
> 

Ah, I missed that.

> > +success:
> > +    rv = 0;
> > +    ret->params.params_len = return_size;
> > +    ret->params.params_val = info;
> > +    ret->nparams = percpu_len;
> > +cleanup:
> > +    if (rv < 0) {
> > +         virNetMessageSaveError(rerr);
> > +         if (info) {
> 
> Cleanup is a lot simpler if we let the existing serialization skip zero
> entries.

Ah, skip is not 




> 
> > +++ b/src/remote/remote_driver.c
> > @@ -2305,6 +2305,122 @@ done:
> >      return rv;
> >  }
> >  
> > +static int remoteDomainGetCPUStats(virDomainPtr domain,
> > +                                   virTypedParameterPtr params,
> > +                                   unsigned int nparams,
> > +                                   int start_cpu,
> > +                                   unsigned int ncpus,
> > +                                   unsigned int flags)
> > +{
> > +    struct private_data *priv = domain->conn->privateData;
> > +    remote_domain_get_cpu_stats_args args;
> > +    remote_domain_get_cpu_stats_ret ret;
> > +    remote_typed_param *info;
> > +    int rv = -1;
> > +    int cpu, idx, src, dest;
> > +
> > +    remoteDriverLock(priv);
> > +
> > +    make_nonnull_domain(&args.dom, domain);
> > +    args.nparams = nparams;
> > +    args.start_cpu = start_cpu;
> > +    args.ncpus = ncpus;
> > +    args.flags = flags;
> > +
> > +    memset(&ret, 0, sizeof(ret));
> 
> This side needs to validate incoming parameters before sending over the
> wire.  Hmm, remoteNodeGetCPUStats needs to do likewise.
> 
> > +
> > +    if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_CPU_STATS,
> > +             (xdrproc_t) xdr_remote_domain_get_cpu_stats_args,
> > +             (char *) &args,
> > +             (xdrproc_t) xdr_remote_domain_get_cpu_stats_ret,
> > +             (char *) &ret) == -1)
> > +        goto done;
> > +
> > +    if (nparams == 0) {
> > +        rv = ret.nparams;
> > +        goto cleanup;
> > +    }
> > +    /*
> > +     * the returned arrray's size is not same to nparams * ncpus. And
> > +     * if cpu ID is not contiguous, all-zero entries can be found.
> > +     */
> > +    memset(params, 0, sizeof(virTypedParameter) * nparams * ncpus);
> 
> I prefer sizeof(expr) over sizeof(type), in case expr ever changes types.
> 
> > +
> > +    /* Here, ret.nparams is always smaller than nparams */
> > +    info = ret.params.params_val;
> > +
> > +    for (cpu = 0; cpu < ncpus; ++cpu) {
> > +        for (idx = 0; idx < ret.nparams; ++idx) {
> > +            src = cpu * ret.nparams + idx;
> > +            dest = cpu * nparams + idx;
> > +
> > +            if (info[src].value.type == 0) /* skip zeroed ones */
> > +                continue;
> 
> Again, open coding things is not nice.  If we write the server to never
> send zero entries (which is a good thing - less data over the wire),
> then the client needs to re-create zero entries by calling
> deserialization in a loop - deserialize ret.nparams entries at every
> nparams slot.
> 
> > +
> > +    rv = ret.nparams;
> > +cleanup:
> > +    if (rv < 0) {
> > +        int max = nparams * ncpus;
> > +        int i;
> > +
> > +        for (i = 0; i < max; i++) {
> > +            if (params[i].type == VIR_TYPED_PARAM_STRING)
> > +                VIR_FREE(params[i].value.s);
> 
> Potential free of bogus memory if anything can reach cleanup with rv < 0
> but before we sanitized the contents of params.
> 
> Here's what I'm squashing in:
> 

seems nicer ! Thank you for kindly reviewing.

Thanks,
-Kame




More information about the libvir-list mailing list