}
+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);
}
}
+/**
+ * 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)
+{
+ char *tmp;
+ 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;
+
+ /* trim '/thread...' suffix from the data returned by query-cpus */
+ for (i = 0; i < ncpuentries; i++) {
+ if (cpuentries[i].qom_path &&
+ (tmp = strstr(cpuentries[i].qom_path, "/thread")))
+ *tmp = '\0';
+ }
+
+ if (totalvcpus != maxvcpus) {
+ VIR_DEBUG("expected '%zu' total vcpus got '%zu'", maxvcpus, totalvcpus);
+ return -1;
+ }
+
+ /* 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 ||
+ STRNEQ(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;
+ }
+ }
+
+ /* 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 &&
+ STREQ(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
* @vcpus: 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
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
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;
}