]> xenbits.xensource.com Git - libvirt.git/commitdiff
rpc: allow truncated return for virDomainGetCPUStats
authorEric Blake <eblake@redhat.com>
Wed, 7 Mar 2012 04:36:53 +0000 (21:36 -0700)
committerEric Blake <eblake@redhat.com>
Wed, 7 Mar 2012 14:14:11 +0000 (07:14 -0700)
The RPC code assumed that the array returned by the driver would be
fully populated; that is, ncpus on entry resulted in ncpus * return
value on exit.  However, while we don't support holes in the middle
of ncpus, we do want to permit the case of ncpus on entry being
longer than the array returned by the driver (that is, it should be
safe for the caller to pass ncpus=128 on entry, and the driver will
stop populating the array when it hits max_id).

Additionally, a successful return implies that the caller will then
use virTypedParamArrayClear on the entire array; for this to not
free uninitialized memory, the driver must ensure that all skipped
entries are explicitly zeroed (the RPC driver did this, but not
the qemu driver).

There are now three cases:
server 0.9.10 and client 0.9.10 or newer: No impact - there were no
hypervisor drivers that supported cpu stats

server 0.9.11 or newer and client 0.9.10: if the client calls with
ncpus beyond the max, then the rpc call will fail on the client side
and disconnect the client, but the server is no worse for the wear

server 0.9.11 or newer and client 0.9.11: the server can return a
truncated array and the client will do just fine

I reproduced the problem by using a host with 2 CPUs, and doing:
virsh cpu-stats $dom --start 1 --count 2

* daemon/remote.c (remoteDispatchDomainGetCPUStats): Allow driver
to omit tail of array.
* src/remote/remote_driver.c (remoteDomainGetCPUStats):
Accommodate driver that omits tail of array.
* src/libvirt.c (virDomainGetCPUStats): Document this.
* src/qemu/qemu_driver.c (qemuDomainGetPercpuStats): Clear all
unpopulated entries.

daemon/remote.c
src/libvirt.c
src/qemu/qemu_driver.c
src/remote/remote_driver.c

index 74a5f16f2294f16a77cca0378de5e967ca8ddf37..39302cc3bd30d4abd336efdbd32e8b98c7ee09c0 100644 (file)
@@ -3574,11 +3574,17 @@ remoteDispatchDomainGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED,
                                        args->flags) < 0)
         goto cleanup;
 
-    percpu_len = ret->params.params_len / args->ncpus;
-
 success:
     rv = 0;
     ret->nparams = percpu_len;
+    if (args->nparams && !(args->flags & VIR_TYPED_PARAM_STRING_OKAY)) {
+        int i;
+
+        for (i = 0; i < percpu_len; i++) {
+            if (params[i].type == VIR_TYPED_PARAM_STRING)
+                ret->nparams--;
+        }
+    }
 
 cleanup:
     if (rv < 0)
index d98741b9e65327bcd3c2e6ef7774df81f6230867..c7e4673fe2d3eab608be046775616a90b02f61c9 100644 (file)
@@ -18534,7 +18534,9 @@ error:
  * whole).  Otherwise, @start_cpu represents which cpu to start
  * with, and @ncpus represents how many consecutive processors to
  * query, with statistics attributable per processor (such as
- * per-cpu usage).
+ * per-cpu usage).  If @ncpus is larger than the number of cpus
+ * available to query, then the trailing part of the array will
+ * be unpopulated.
  *
  * The remote driver imposes a limit of 128 @ncpus and 16 @nparams;
  * the number of parameters per cpu should not exceed 16, but if you
@@ -18579,8 +18581,10 @@ error:
  * number of populated @params, unless @ncpus was 1; and may be
  * less than @nparams).  The populated parameters start at each
  * stride of @nparams, which means the results may be discontiguous;
- * any unpopulated parameters will be zeroed on success.  The caller
- * is responsible for freeing any returned string parameters.
+ * any unpopulated parameters will be zeroed on success (this includes
+ * skipped elements if @nparams is too large, and tail elements if
+ * @ncpus is too large).  The caller is responsible for freeing any
+ * returned string parameters.
  */
 int virDomainGetCPUStats(virDomainPtr domain,
                          virTypedParameterPtr params,
index 538a4190ce5325bbe137e4fb58def36a840fd12c..ddb381a65556a3f35f28904fb87e3a50f4073148 100644 (file)
@@ -12162,6 +12162,7 @@ qemuDomainGetPercpuStats(virDomainPtr domain,
     if (virCgroupGetCpuacctPercpuUsage(group, &buf))
         goto cleanup;
     pos = buf;
+    memset(params, 0, nparams * ncpus);
 
     if (max_id - start_cpu > ncpus - 1)
         max_id = start_cpu + ncpus - 1;
index 9e74cea3119639795d2d3865c08d618b2dc4a3ad..031167ad71a953a9fc3dd7cba2e90965a5e0b32a 100644 (file)
@@ -2382,7 +2382,7 @@ static int remoteDomainGetCPUStats(virDomainPtr domain,
     /* Check the length of the returned list carefully. */
     if (ret.params.params_len > nparams * ncpus ||
         (ret.params.params_len &&
-         ret.nparams * ncpus != ret.params.params_len)) {
+         ((ret.params.params_len % ret.nparams) || ret.nparams > nparams))) {
         remoteError(VIR_ERR_RPC, "%s",
                     _("remoteDomainGetCPUStats: "
                       "returned number of stats exceeds limit"));
@@ -2399,9 +2399,11 @@ static int remoteDomainGetCPUStats(virDomainPtr domain,
     }
 
     /* The remote side did not send back any zero entries, so we have
-     * to expand things back into a possibly sparse array.
+     * to expand things back into a possibly sparse array, where the
+     * tail of the array may be omitted.
      */
     memset(params, 0, sizeof(*params) * nparams * ncpus);
+    ncpus = ret.params.params_len / ret.nparams;
     for (cpu = 0; cpu < ncpus; cpu++) {
         int tmp = nparams;
         remote_typed_param *stride = &ret.params.params_val[cpu * ret.nparams];