[libvirt] [PATCH v2 08/23] qemu: monitor: Add algorithm for combining query-(hotpluggable-)-cpus data

John Ferlan jferlan at redhat.com
Fri Aug 19 18:33:42 UTC 2016



On 08/19/2016 10:38 AM, Peter Krempa wrote:
> For hotplug purposes it's necessary to retrieve data using
> query-hotpluggable-cpus while the old query-cpus API report thread IDs
> and order of hotplug.
> 
> This patch adds code that merges the data using a rather non-trivial
> algorithm and fills the data to the qemuMonitorCPUInfo structure for
> adding to appropriate place in the domain definition.
> ---
> 
> Notes:
>     v2:
>     - fixed loop in the fallback info populator function
>     - fixed debug message
> 
>  src/qemu/qemu_domain.c  |   2 +-
>  src/qemu/qemu_monitor.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++--
>  src/qemu/qemu_monitor.h |  23 +++++-
>  3 files changed, 212 insertions(+), 10 deletions(-)
> 
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index c56dc75..45ded03 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -5804,7 +5804,7 @@ qemuDomainRefreshVcpuInfo(virQEMUDriverPtr driver,
>      if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
>          return -1;
> 
> -    rc = qemuMonitorGetCPUInfo(qemuDomainGetMonitor(vm), &info, maxvcpus);
> +    rc = qemuMonitorGetCPUInfo(qemuDomainGetMonitor(vm), &info, maxvcpus, false);
> 
>      if (qemuDomainObjExitMonitor(driver, vm) < 0)
>          goto cleanup;
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index f87f431..451786b 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -1656,13 +1656,36 @@ qemuMonitorSystemReset(qemuMonitorPtr mon)
>  }
> 
> 
> +static void
> +qemuMonitorCPUInfoClear(qemuMonitorCPUInfoPtr cpus,
> +                        size_t ncpus)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < ncpus; i++) {
> +        cpus[i].id = 0;
> +        cpus[i].socket_id = -1;
> +        cpus[i].core_id = -1;
> +        cpus[i].thread_id = -1;
> +        cpus[i].vcpus = 0;
> +        cpus[i].tid = 0;
> +
> +        VIR_FREE(cpus[i].qom_path);
> +        VIR_FREE(cpus[i].alias);
> +        VIR_FREE(cpus[i].type);
> +    }
> +}
> +
> +
>  void
>  qemuMonitorCPUInfoFree(qemuMonitorCPUInfoPtr cpus,
> -                       size_t ncpus ATTRIBUTE_UNUSED)
> +                       size_t ncpus)
>  {
>      if (!cpus)
>          return;
> 
> +    qemuMonitorCPUInfoClear(cpus, ncpus);
> +
>      VIR_FREE(cpus);
>  }
> 
> @@ -1683,10 +1706,148 @@ qemuMonitorQueryCpusFree(struct qemuMonitorQueryCpusEntry *entries,
> 
> 
>  /**
> + * Legacy approach doesn't allow out of order cpus, thus no complex matching
> + * algorithm is necessary */
> +static void
> +qemuMonitorGetCPUInfoLegacy(struct qemuMonitorQueryCpusEntry *cpuentries,
> +                            size_t ncpuentries,
> +                            qemuMonitorCPUInfoPtr vcpus,
> +                            size_t maxvcpus)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < maxvcpus; i++) {
> +        if (i < ncpuentries)
> +            vcpus[i].tid = cpuentries[i].tid;
> +
> +        /* for legacy hotplug to work we need to fake the vcpu count added by
> +         * enabling a given vcpu */
> +        vcpus[i].vcpus = 1;
> +    }
> +}
> +
> +
> +/**
> + * qemuMonitorGetCPUInfoHotplug:
> + *
> + * This function stitches together data retrieved via query-hotpluggable-cpus
> + * which returns entities on the hotpluggable level (which may describe more
> + * than one guest logical vcpu) with the output of query-cpus, having an entry
> + * per enabled guest logical vcpu.
> + *
> + * query-hotpluggable-cpus conveys following information:
> + * - topology information and number of logical vcpus this entry creates
> + * - device type name of the entry that needs to be used when hotplugging
> + * - qom path in qemu which can be used to map the entry against query-cpus
> + *
> + * query-cpus conveys following information:
> + * - thread id of a given guest logical vcpu
> + * - order in which the vcpus were inserted
> + * - qom path to allow mapping the two together
> + *
> + * The libvirt's internal structure has an entry for each possible (even
> + * disabled) guest vcpu. The purpose is to map the data together so that we are
> + * certain of the thread id mapping and the information required for vcpu
> + * hotplug.
> + *
> + * This function returns 0 on success and -1 on error, but does not report
> + * libvirt errors so that fallback approach can be used.
> + */
> +static int
> +qemuMonitorGetCPUInfoHotplug(struct qemuMonitorQueryHotpluggableCpusEntry *hotplugvcpus,
> +                             size_t nhotplugvcpus,
> +                             struct qemuMonitorQueryCpusEntry *cpuentries,
> +                             size_t ncpuentries,
> +                             qemuMonitorCPUInfoPtr vcpus,
> +                             size_t maxvcpus)
> +{
> +    int order = 1;
> +    size_t totalvcpus = 0;
> +    size_t i;
> +    size_t j;
> +
> +    /* ensure that the total vcpu count reported by query-hotpluggable-cpus equals
> +     * to the libvirt maximum cpu count */
> +    for (i = 0; i < nhotplugvcpus; i++)
> +        totalvcpus += hotplugvcpus[i].vcpus;
> +
> +    if (totalvcpus != maxvcpus) {
> +        VIR_DEBUG("expected '%zu' total vcpus got '%zu'", maxvcpus, totalvcpus);
> +        return -1;
> +    }

Still trying to come to grips with 'nhotplugvcpus', 'ncpuentries', and
'maxvcpus'

> +
> +    /* Note the order in which the hotpluggable entities are inserted by
> +     * matching them to the query-cpus entries */
> +    for (i = 0; i < ncpuentries; i++) {
> +        for (j = 0; j < nhotplugvcpus; j++) {
> +            if (!cpuentries[i].qom_path ||
> +                !hotplugvcpus[j].qom_path ||
> +                !STRPREFIX(cpuentries[i].qom_path, hotplugvcpus[j].qom_path))
> +                continue;
> +
> +            /* add ordering info for hotpluggable entries */
> +            if (hotplugvcpus[j].enable_id == 0)
> +                hotplugvcpus[j].enable_id = order++;
> +
> +            break;

So enable_id always == 1 (or 0 I supposed) and order never gets beyond
2?  Or am I missing something not obvious.

I guess I thought you were trying to figure out the order of
nhotplugvcpus so that you could fill in vcpus in the correct order, but
that doesn't match the subsequent algorithm.

John
> +        }
> +    }
> +
> +    /* transfer appropriate data from the hotpluggable list to corresponding
> +     * entries. the entries returned by qemu may in fact describe multiple
> +     * logical vcpus in the guest */
> +    j = 0;
> +    for (i = 0; i < nhotplugvcpus; i++) {
> +        vcpus[j].socket_id = hotplugvcpus[i].socket_id;
> +        vcpus[j].core_id = hotplugvcpus[i].core_id;
> +        vcpus[j].thread_id = hotplugvcpus[i].thread_id;
> +        vcpus[j].vcpus = hotplugvcpus[i].vcpus;
> +        VIR_STEAL_PTR(vcpus[j].qom_path, hotplugvcpus[i].qom_path);
> +        VIR_STEAL_PTR(vcpus[j].alias, hotplugvcpus[i].alias);
> +        VIR_STEAL_PTR(vcpus[j].type, hotplugvcpus[i].type);
> +        vcpus[j].id = hotplugvcpus[i].enable_id;
> +
> +        /* skip over vcpu entries covered by this hotpluggable entry */
> +        j += hotplugvcpus[i].vcpus;
> +    }
> +
> +    /* match entries from query cpus to the output array taking into account
> +     * multi-vcpu objects */
> +    for (j = 0; j < ncpuentries; j++) {
> +        /* find the correct entry or beginning of group of entries */
> +        for (i = 0; i < maxvcpus; i++) {
> +            if (cpuentries[j].qom_path && vcpus[i].qom_path &&
> +                STRPREFIX(cpuentries[j].qom_path, vcpus[i].qom_path))
> +                break;
> +        }
> +
> +        if (i == maxvcpus) {
> +            VIR_DEBUG("too many query-cpus entries for a given "
> +                      "query-hotpluggable-cpus entry");
> +            return -1;
> +        }
> +
> +        if (vcpus[i].vcpus != 1) {
> +            /* find a possibly empty vcpu thread for core granularity systems */
> +            for (; i < maxvcpus; i++) {
> +                if (vcpus[i].tid == 0)
> +                    break;
> +            }
> +        }
> +
> +        vcpus[i].tid = cpuentries[j].tid;
> +    }
> +
> +    return 0;
> +}
> +
> +
> +/**
>   * qemuMonitorGetCPUInfo:
>   * @mon: monitor
>   * @cpus: pointer filled by array of qemuMonitorCPUInfo structures
>   * @maxvcpus: total possible number of vcpus
> + * @hotplug: query data relevant for hotplug support
>   *
>   * Detects VCPU information. If qemu doesn't support or fails reporting
>   * information this function will return success as other parts of libvirt
> @@ -1698,20 +1859,32 @@ qemuMonitorQueryCpusFree(struct qemuMonitorQueryCpusEntry *entries,
>  int
>  qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
>                        qemuMonitorCPUInfoPtr *vcpus,
> -                      size_t maxvcpus)
> +                      size_t maxvcpus,
> +                      bool hotplug)
>  {
> -    qemuMonitorCPUInfoPtr info = NULL;
> +    struct qemuMonitorQueryHotpluggableCpusEntry *hotplugcpus = NULL;
> +    size_t nhotplugcpus = 0;
>      struct qemuMonitorQueryCpusEntry *cpuentries = NULL;
>      size_t ncpuentries = 0;
> -    size_t i;
>      int ret = -1;
>      int rc;
> +    qemuMonitorCPUInfoPtr info = NULL;
> 
> -    QEMU_CHECK_MONITOR(mon);
> +    if (hotplug)
> +        QEMU_CHECK_MONITOR_JSON(mon);
> +    else
> +        QEMU_CHECK_MONITOR(mon);
> 
>      if (VIR_ALLOC_N(info, maxvcpus) < 0)
>          return -1;
> 
> +    /* initialize a few non-zero defaults */
> +    qemuMonitorCPUInfoClear(info, maxvcpus);
> +
> +    if (hotplug &&
> +        (qemuMonitorJSONGetHotpluggableCPUs(mon, &hotplugcpus, &nhotplugcpus)) < 0)
> +        goto cleanup;
> +
>      if (mon->json)
>          rc = qemuMonitorJSONQueryCPUs(mon, &cpuentries, &ncpuentries);
>      else
> @@ -1726,15 +1899,23 @@ qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
>          goto cleanup;
>      }
> 
> -    for (i = 0; i < ncpuentries; i++)
> -        info[i].tid = cpuentries[i].tid;
> +    if (!hotplugcpus ||
> +        qemuMonitorGetCPUInfoHotplug(hotplugcpus, nhotplugcpus,
> +                                     cpuentries, ncpuentries,
> +                                     info, maxvcpus) < 0) {
> +        /* Fallback to the legacy algorithm. Hotplug paths will make sure that
> +         * the apropriate data is present */
> +        qemuMonitorCPUInfoClear(info, maxvcpus);
> +        qemuMonitorGetCPUInfoLegacy(cpuentries, ncpuentries, info, maxvcpus);
> +    }
> 
>      VIR_STEAL_PTR(*vcpus, info);
>      ret = 0;
> 
>   cleanup:
> -    qemuMonitorCPUInfoFree(info, maxvcpus);
> +    qemuMonitorQueryHotpluggableCpusFree(hotplugcpus, nhotplugcpus);
>      qemuMonitorQueryCpusFree(cpuentries, ncpuentries);
> +    qemuMonitorCPUInfoFree(info, maxvcpus);
>      return ret;
>  }
> 
> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
> index 58f8327..b838725 100644
> --- a/src/qemu/qemu_monitor.h
> +++ b/src/qemu/qemu_monitor.h
> @@ -409,6 +409,9 @@ struct qemuMonitorQueryHotpluggableCpusEntry {
>      int socket_id;
>      int core_id;
>      int thread_id;
> +
> +    /* internal data */
> +    int enable_id;
>  };
>  void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries,
>                                            size_t nentries);
> @@ -416,6 +419,23 @@ void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpu
> 
>  struct _qemuMonitorCPUInfo {
>      pid_t tid;
> +    int id; /* order of enabling of the given cpu */
> +
> +    /* topology info for hotplug purposes. Hotplug of given vcpu impossible if
> +     * all entries are -1 */
> +    int socket_id;
> +    int core_id;
> +    int thread_id;
> +    unsigned int vcpus; /* number of vcpus added if given entry is hotplugged */
> +
> +    /* name of the qemu type to add in case of hotplug */
> +    char *type;
> +
> +    /* alias of an hotpluggable entry. Entries with alias can be hot-unplugged */
> +    char *alias;
> +
> +    /* internal for use in the matching code */
> +    char *qom_path;
>  };
>  typedef struct _qemuMonitorCPUInfo qemuMonitorCPUInfo;
>  typedef qemuMonitorCPUInfo *qemuMonitorCPUInfoPtr;
> @@ -424,7 +444,8 @@ void qemuMonitorCPUInfoFree(qemuMonitorCPUInfoPtr list,
>                              size_t nitems);
>  int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
>                            qemuMonitorCPUInfoPtr *vcpus,
> -                          size_t maxvcpus);
> +                          size_t maxvcpus,
> +                          bool hotplug);
> 
>  int qemuMonitorGetVirtType(qemuMonitorPtr mon,
>                             virDomainVirtType *virtType);
> 




More information about the libvir-list mailing list