On Sat, 28 Jan 2012 11:16:03 -0700
Eric Blake <eblake(a)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