The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the capabilities code into its own file * src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: New capabilities management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete capabilities code * src/qemu/qemu_conf.h: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_capabilities.c --- src/Makefile.am | 1 + src/qemu/qemu_capabilities.c | 1253 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 113 ++++ src/qemu/qemu_conf.c | 1191 +--------------------------------------- src/qemu/qemu_conf.h | 81 --- src/qemu/qemu_driver.c | 55 +- 6 files changed, 1398 insertions(+), 1296 deletions(-) create mode 100644 src/qemu/qemu_capabilities.c create mode 100644 src/qemu/qemu_capabilities.h diff --git a/src/Makefile.am b/src/Makefile.am index 196d8af..4ce0b35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -267,6 +267,7 @@ VBOX_DRIVER_SOURCES = \ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README QEMU_DRIVER_SOURCES = \ + qemu/qemu_capabilities.c qemu/qemu_capabilities.h\ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c new file mode 100644 index 0000000..913fbf7 --- /dev/null +++ b/src/qemu/qemu_capabilities.c @@ -0,0 +1,1253 @@ +/* + * qemu_capabilities.c: QEMU capabilities generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include "qemu_capabilities.h" +#include "memory.h" +#include "logging.h" +#include "virterror_internal.h" +#include "util.h" +#include "files.h" +#include "nodeinfo.h" +#include "cpu/cpu.h" +#include "domain_conf.h" +#include "qemu_conf.h" + +#include <sys/stat.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/utsname.h> + +#define VIR_FROM_THIS VIR_FROM_QEMU + +struct qemu_feature_flags { + const char *name; + const int default_on; + const int toggle; +}; + +struct qemu_arch_info { + const char *arch; + int wordsize; + const char *machine; + const char *binary; + const char *altbinary; + const struct qemu_feature_flags *flags; + int nflags; +}; + +/* Feature flags for the architecture info */ +static const struct qemu_feature_flags const arch_info_i686_flags [] = { + { "pae", 1, 0 }, + { "nonpae", 1, 0 }, + { "acpi", 1, 1 }, + { "apic", 1, 0 }, +}; + +static const struct qemu_feature_flags const arch_info_x86_64_flags [] = { + { "acpi", 1, 1 }, + { "apic", 1, 0 }, +}; + +/* The archicture tables for supported QEMU archs */ +static const struct qemu_arch_info const arch_info_hvm[] = { + { "i686", 32, NULL, "qemu", + "qemu-system-x86_64", arch_info_i686_flags, 4 }, + { "x86_64", 64, NULL, "qemu-system-x86_64", + NULL, arch_info_x86_64_flags, 2 }, + { "arm", 32, NULL, "qemu-system-arm", NULL, NULL, 0 }, + { "mips", 32, NULL, "qemu-system-mips", NULL, NULL, 0 }, + { "mipsel", 32, NULL, "qemu-system-mipsel", NULL, NULL, 0 }, + { "sparc", 32, NULL, "qemu-system-sparc", NULL, NULL, 0 }, + { "ppc", 32, NULL, "qemu-system-ppc", NULL, NULL, 0 }, + { "itanium", 64, NULL, "qemu-system-ia64", NULL, NULL, 0 }, + { "s390x", 64, NULL, "qemu-system-s390x", NULL, NULL, 0 }, +}; + +static const struct qemu_arch_info const arch_info_xen[] = { + { "i686", 32, "xenner", "xenner", NULL, arch_info_i686_flags, 4 }, + { "x86_64", 64, "xenner", "xenner", NULL, arch_info_x86_64_flags, 2 }, +}; + + +/* Format is: + * <machine> <desc> [(default)|(alias of <canonical>)] + */ +static int +qemuCapsParseMachineTypesStr(const char *output, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + const char *p = output; + const char *next; + virCapsGuestMachinePtr *list = NULL; + int nitems = 0; + + do { + const char *t; + virCapsGuestMachinePtr machine; + + if ((next = strchr(p, '\n'))) + ++next; + + if (STRPREFIX(p, "Supported machines are:")) + continue; + + if (!(t = strchr(p, ' ')) || (next && t >= next)) + continue; + + if (VIR_ALLOC(machine) < 0) + goto no_memory; + + if (!(machine->name = strndup(p, t - p))) { + VIR_FREE(machine); + goto no_memory; + } + + if (VIR_REALLOC_N(list, nitems + 1) < 0) { + VIR_FREE(machine->name); + VIR_FREE(machine); + goto no_memory; + } + + p = t; + if (!(t = strstr(p, "(default)")) || (next && t >= next)) { + list[nitems++] = machine; + } else { + /* put the default first in the list */ + memmove(list + 1, list, sizeof(*list) * nitems); + list[0] = machine; + nitems++; + } + + if ((t = strstr(p, "(alias of ")) && (!next || t < next)) { + p = t + strlen("(alias of "); + if (!(t = strchr(p, ')')) || (next && t >= next)) + continue; + + if (!(machine->canonical = strndup(p, t - p))) + goto no_memory; + } + } while ((p = next)); + + *machines = list; + *nmachines = nitems; + + return 0; + + no_memory: + virReportOOMError(); + virCapabilitiesFreeMachines(list, nitems); + return -1; +} + +int +qemuCapsProbeMachineTypes(const char *binary, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + const char *const qemuarg[] = { binary, "-M", "?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + char *output; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + pid_t child; + int newstdout = -1, len; + int ret = -1, status; + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(errno, "%s", + _("Unable to read 'qemu -M ?' output")); + goto cleanup; + } + + if (qemuCapsParseMachineTypesStr(output, machines, nmachines) < 0) + goto cleanup2; + + ret = 0; + +cleanup2: + VIR_FREE(output); +cleanup: + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + +static int +qemuCapsGetOldMachinesFromInfo(virCapsGuestDomainInfoPtr info, + const char *emulator, + time_t emulator_mtime, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + virCapsGuestMachinePtr *list; + int i; + + if (!info->nmachines) + return 0; + + if (!info->emulator || !STREQ(emulator, info->emulator)) + return 0; + + if (emulator_mtime != info->emulator_mtime) { + VIR_DEBUG("mtime on %s has changed, refreshing machine types", + info->emulator); + return 0; + } + + if (VIR_ALLOC_N(list, info->nmachines) < 0) { + virReportOOMError(); + return 0; + } + + for (i = 0; i < info->nmachines; i++) { + if (VIR_ALLOC(list[i]) < 0) { + goto no_memory; + } + if (info->machines[i]->name && + !(list[i]->name = strdup(info->machines[i]->name))) { + goto no_memory; + } + if (info->machines[i]->canonical && + !(list[i]->canonical = strdup(info->machines[i]->canonical))) { + goto no_memory; + } + } + + *machines = list; + *nmachines = info->nmachines; + + return 1; + + no_memory: + virReportOOMError(); + virCapabilitiesFreeMachines(list, info->nmachines); + return 0; +} + +static int +qemuCapsGetOldMachines(const char *ostype, + const char *arch, + int wordsize, + const char *emulator, + time_t emulator_mtime, + virCapsPtr old_caps, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + int i; + + for (i = 0; i < old_caps->nguests; i++) { + virCapsGuestPtr guest = old_caps->guests[i]; + int j; + + if (!STREQ(ostype, guest->ostype) || + !STREQ(arch, guest->arch.name) || + wordsize != guest->arch.wordsize) + continue; + + for (j = 0; j < guest->arch.ndomains; j++) { + virCapsGuestDomainPtr dom = guest->arch.domains[j]; + + if (qemuCapsGetOldMachinesFromInfo(&dom->info, + emulator, emulator_mtime, + machines, nmachines)) + return 1; + } + + if (qemuCapsGetOldMachinesFromInfo(&guest->arch.defaultInfo, + emulator, emulator_mtime, + machines, nmachines)) + return 1; + } + + return 0; +} + + +typedef int +(*qemuCapsParseCPUModels)(const char *output, + unsigned int *retcount, + const char ***retcpus); + +/* Format: + * <arch> <model> + * qemu-0.13 encloses some model names in []: + * <arch> [<model>] + */ +static int +qemuCapsParseX86Models(const char *output, + unsigned int *retcount, + const char ***retcpus) +{ + const char *p = output; + const char *next; + unsigned int count = 0; + const char **cpus = NULL; + int i; + + do { + const char *t; + + if ((next = strchr(p, '\n'))) + next++; + + if (!(t = strchr(p, ' ')) || (next && t >= next)) + continue; + + if (!STRPREFIX(p, "x86")) + continue; + + p = t; + while (*p == ' ') + p++; + + if (*p == '\0' || *p == '\n') + continue; + + if (retcpus) { + unsigned int len; + + if (VIR_REALLOC_N(cpus, count + 1) < 0) + goto error; + + if (next) + len = next - p - 1; + else + len = strlen(p); + + if (len > 2 && *p == '[' && p[len - 1] == ']') { + p++; + len -= 2; + } + + if (!(cpus[count] = strndup(p, len))) + goto error; + } + count++; + } while ((p = next)); + + if (retcount) + *retcount = count; + if (retcpus) + *retcpus = cpus; + + return 0; + +error: + if (cpus) { + for (i = 0; i < count; i++) + VIR_FREE(cpus[i]); + } + VIR_FREE(cpus); + + return -1; +} + + +int +qemuCapsProbeCPUModels(const char *qemu, + unsigned long long qemuCmdFlags, + const char *arch, + unsigned int *count, + const char ***cpus) +{ + const char *const qemuarg[] = { + qemu, + "-cpu", "?", + (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) ? "-nodefconfig" : NULL, + NULL + }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + char *output = NULL; + int newstdout = -1; + int ret = -1; + pid_t child; + int status; + int len; + qemuCapsParseCPUModels parse; + + if (count) + *count = 0; + if (cpus) + *cpus = NULL; + + if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) + parse = qemuCapsParseX86Models; + else { + VIR_DEBUG("don't know how to parse %s CPU models", arch); + return 0; + } + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(errno, "%s", + _("Unable to read QEMU supported CPU models")); + goto cleanup; + } + + if (parse(output, count, cpus) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(output); + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + + +static int +qemuCapsInitGuest(virCapsPtr caps, + virCapsPtr old_caps, + const char *hostmachine, + const struct qemu_arch_info *info, + int hvm) +{ + virCapsGuestPtr guest; + int i; + int haskvm = 0; + int haskqemu = 0; + char *kvmbin = NULL; + char *binary = NULL; + time_t binary_mtime; + virCapsGuestMachinePtr *machines = NULL; + int nmachines = 0; + struct stat st; + unsigned int ncpus; + int ret = -1; + + /* Check for existance of base emulator, or alternate base + * which can be used with magic cpu choice + */ + binary = virFindFileInPath(info->binary); + + if (binary == NULL || access(binary, X_OK) != 0) { + VIR_FREE(binary); + binary = virFindFileInPath(info->altbinary); + + if (binary != NULL && access(binary, X_OK) != 0) + VIR_FREE(binary); + } + + /* Can use acceleration for KVM/KQEMU if + * - host & guest arches match + * Or + * - hostarch is x86_64 and guest arch is i686 + * The latter simply needs "-cpu qemu32" + */ + if (STREQ(info->arch, hostmachine) || + (STREQ(hostmachine, "x86_64") && STREQ(info->arch, "i686"))) { + if (access("/dev/kvm", F_OK) == 0) { + const char *const kvmbins[] = { "/usr/libexec/qemu-kvm", /* RHEL */ + "qemu-kvm", /* Fedora */ + "kvm" }; /* Upstream .spec */ + + for (i = 0; i < ARRAY_CARDINALITY(kvmbins); ++i) { + kvmbin = virFindFileInPath(kvmbins[i]); + + if (kvmbin == NULL || access(kvmbin, X_OK) != 0) { + VIR_FREE(kvmbin); + continue; + } + + haskvm = 1; + if (!binary) + binary = kvmbin; + + break; + } + } + + if (access("/dev/kqemu", F_OK) == 0) + haskqemu = 1; + } + + if (!binary) + return 0; + + if (stat(binary, &st) == 0) { + binary_mtime = st.st_mtime; + } else { + char ebuf[1024]; + VIR_WARN("Failed to stat %s, most peculiar : %s", + binary, virStrerror(errno, ebuf, sizeof(ebuf))); + binary_mtime = 0; + } + + if (info->machine) { + virCapsGuestMachinePtr machine; + + if (VIR_ALLOC(machine) < 0) { + goto no_memory; + } + + if (!(machine->name = strdup(info->machine))) { + VIR_FREE(machine); + goto no_memory; + } + + nmachines = 1; + + if (VIR_ALLOC_N(machines, nmachines) < 0) { + VIR_FREE(machine->name); + VIR_FREE(machine); + goto no_memory; + } + + machines[0] = machine; + } else { + int probe = 1; + if (old_caps && binary_mtime) + probe = !qemuCapsGetOldMachines(hvm ? "hvm" : "xen", info->arch, + info->wordsize, binary, binary_mtime, + old_caps, &machines, &nmachines); + if (probe && + qemuCapsProbeMachineTypes(binary, &machines, &nmachines) < 0) + goto error; + } + + /* We register kvm as the base emulator too, since we can + * just give -no-kvm to disable acceleration if required */ + if ((guest = virCapabilitiesAddGuest(caps, + hvm ? "hvm" : "xen", + info->arch, + info->wordsize, + binary, + NULL, + nmachines, + machines)) == NULL) + goto error; + + machines = NULL; + nmachines = 0; + + guest->arch.defaultInfo.emulator_mtime = binary_mtime; + + if (caps->host.cpu && + qemuCapsProbeCPUModels(binary, 0, info->arch, &ncpus, NULL) == 0 && + ncpus > 0 && + !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) + goto error; + + if (hvm) { + if (virCapabilitiesAddGuestDomain(guest, + "qemu", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + + if (haskqemu && + virCapabilitiesAddGuestDomain(guest, + "kqemu", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + + if (haskvm) { + virCapsGuestDomainPtr dom; + + if (stat(kvmbin, &st) == 0) { + binary_mtime = st.st_mtime; + } else { + char ebuf[1024]; + VIR_WARN("Failed to stat %s, most peculiar : %s", + binary, virStrerror(errno, ebuf, sizeof(ebuf))); + binary_mtime = 0; + } + + if (!STREQ(binary, kvmbin)) { + int probe = 1; + if (old_caps && binary_mtime) + probe = !qemuCapsGetOldMachines("hvm", info->arch, info->wordsize, + kvmbin, binary_mtime, + old_caps, &machines, &nmachines); + if (probe && + qemuCapsProbeMachineTypes(kvmbin, &machines, &nmachines) < 0) + goto error; + } + + if ((dom = virCapabilitiesAddGuestDomain(guest, + "kvm", + kvmbin, + NULL, + nmachines, + machines)) == NULL) { + goto error; + } + + machines = NULL; + nmachines = 0; + + dom->info.emulator_mtime = binary_mtime; + } + } else { + if (virCapabilitiesAddGuestDomain(guest, + "kvm", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + } + + if (info->nflags) { + for (i = 0 ; i < info->nflags ; i++) { + if (virCapabilitiesAddGuestFeature(guest, + info->flags[i].name, + info->flags[i].default_on, + info->flags[i].toggle) == NULL) + goto error; + } + } + + ret = 0; + +cleanup: + if (binary == kvmbin) { + /* don't double free */ + VIR_FREE(binary); + } else { + VIR_FREE(binary); + VIR_FREE(kvmbin); + } + + return ret; + +no_memory: + virReportOOMError(); + +error: + virCapabilitiesFreeMachines(machines, nmachines); + + goto cleanup; +} + + +static int +qemuCapsInitCPU(virCapsPtr caps, + const char *arch) +{ + virCPUDefPtr cpu = NULL; + union cpuData *data = NULL; + virNodeInfo nodeinfo; + int ret = -1; + + if (VIR_ALLOC(cpu) < 0 + || !(cpu->arch = strdup(arch))) { + virReportOOMError(); + goto error; + } + + if (nodeGetInfo(NULL, &nodeinfo)) + goto error; + + cpu->type = VIR_CPU_TYPE_HOST; + cpu->sockets = nodeinfo.sockets; + cpu->cores = nodeinfo.cores; + cpu->threads = nodeinfo.threads; + + if (!(data = cpuNodeData(arch)) + || cpuDecode(cpu, data, NULL, 0, NULL) < 0) + goto error; + + caps->host.cpu = cpu; + + ret = 0; + +cleanup: + cpuDataFree(arch, data); + + return ret; + +error: + virCPUDefFree(cpu); + goto cleanup; +} + + +virCapsPtr qemuCapsInit(virCapsPtr old_caps) +{ + struct utsname utsname; + virCapsPtr caps; + int i; + char *xenner = NULL; + + /* Really, this never fails - look at the man-page. */ + uname (&utsname); + + if ((caps = virCapabilitiesNew(utsname.machine, + 1, 1)) == NULL) + goto no_memory; + + /* Using KVM's mac prefix for QEMU too */ + virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x52, 0x54, 0x00 }); + + /* Some machines have problematic NUMA toplogy causing + * unexpected failures. We don't want to break the QEMU + * driver in this scenario, so log errors & carry on + */ + if (nodeCapsInitNUMA(caps) < 0) { + virCapabilitiesFreeNUMAInfo(caps); + VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); + } + + if (old_caps == NULL || old_caps->host.cpu == NULL) { + if (qemuCapsInitCPU(caps, utsname.machine) < 0) + VIR_WARN0("Failed to get host CPU"); + } + else { + caps->host.cpu = old_caps->host.cpu; + old_caps->host.cpu = NULL; + } + + virCapabilitiesAddHostMigrateTransport(caps, + "tcp"); + + /* First the pure HVM guests */ + for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) + if (qemuCapsInitGuest(caps, old_caps, + utsname.machine, + &arch_info_hvm[i], 1) < 0) + goto no_memory; + + /* Then possibly the Xen paravirt guests (ie Xenner */ + xenner = virFindFileInPath("xenner"); + + if (xenner != NULL && access(xenner, X_OK) == 0 && + access("/dev/kvm", F_OK) == 0) { + for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_xen) ; i++) + /* Allow Xen 32-on-32, 32-on-64 and 64-on-64 */ + if (STREQ(arch_info_xen[i].arch, utsname.machine) || + (STREQ(utsname.machine, "x86_64") && + STREQ(arch_info_xen[i].arch, "i686"))) { + if (qemuCapsInitGuest(caps, old_caps, + utsname.machine, + &arch_info_xen[i], 0) < 0) + goto no_memory; + } + } + + VIR_FREE(xenner); + + /* QEMU Requires an emulator in the XML */ + virCapabilitiesSetEmulatorRequired(caps); + + caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; + + return caps; + + no_memory: + VIR_FREE(xenner); + virCapabilitiesFree(caps); + return NULL; +} + + +static unsigned long long +qemuCapsComputeCmdFlags(const char *help, + unsigned int version, + unsigned int is_kvm, + unsigned int kvm_version) +{ + unsigned long long flags = 0; + const char *p; + + if (strstr(help, "-no-kqemu")) + flags |= QEMUD_CMD_FLAG_KQEMU; + if (strstr(help, "-enable-kqemu")) + flags |= QEMUD_CMD_FLAG_ENABLE_KQEMU; + if (strstr(help, "-no-kvm")) + flags |= QEMUD_CMD_FLAG_KVM; + if (strstr(help, "-enable-kvm")) + flags |= QEMUD_CMD_FLAG_ENABLE_KVM; + if (strstr(help, "-no-reboot")) + flags |= QEMUD_CMD_FLAG_NO_REBOOT; + if (strstr(help, "-name")) { + flags |= QEMUD_CMD_FLAG_NAME; + if (strstr(help, ",process=")) + flags |= QEMUD_CMD_FLAG_NAME_PROCESS; + } + if (strstr(help, "-uuid")) + flags |= QEMUD_CMD_FLAG_UUID; + if (strstr(help, "-xen-domid")) + flags |= QEMUD_CMD_FLAG_XEN_DOMID; + else if (strstr(help, "-domid")) + flags |= QEMUD_CMD_FLAG_DOMID; + if (strstr(help, "-drive")) { + flags |= QEMUD_CMD_FLAG_DRIVE; + if (strstr(help, "cache=") && + !strstr(help, "cache=on|off")) + flags |= QEMUD_CMD_FLAG_DRIVE_CACHE_V2; + if (strstr(help, "format=")) + flags |= QEMUD_CMD_FLAG_DRIVE_FORMAT; + if (strstr(help, "readonly=")) + flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; + } + if ((p = strstr(help, "-vga")) && !strstr(help, "-std-vga")) { + const char *nl = strstr(p, "\n"); + + flags |= QEMUD_CMD_FLAG_VGA; + + if (strstr(p, "|qxl")) + flags |= QEMUD_CMD_FLAG_VGA_QXL; + if ((p = strstr(p, "|none")) && p < nl) + flags |= QEMUD_CMD_FLAG_VGA_NONE; + } + if (strstr(help, "-spice")) + flags |= QEMUD_CMD_FLAG_SPICE; + if (strstr(help, "boot=on")) + flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; + if (strstr(help, "serial=s")) + flags |= QEMUD_CMD_FLAG_DRIVE_SERIAL; + if (strstr(help, "-pcidevice")) + flags |= QEMUD_CMD_FLAG_PCIDEVICE; + if (strstr(help, "-mem-path")) + flags |= QEMUD_CMD_FLAG_MEM_PATH; + if (strstr(help, "-chardev")) + flags |= QEMUD_CMD_FLAG_CHARDEV; + if (strstr(help, "-balloon")) + flags |= QEMUD_CMD_FLAG_BALLOON; + if (strstr(help, "-device")) { + flags |= QEMUD_CMD_FLAG_DEVICE; + /* + * When -device was introduced, qemu already supported drive's + * readonly option but didn't advertise that. + */ + flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; + } + if (strstr(help, "-nodefconfig")) + flags |= QEMUD_CMD_FLAG_NODEFCONFIG; + /* The trailing ' ' is important to avoid a bogus match */ + if (strstr(help, "-rtc ")) + flags |= QEMUD_CMD_FLAG_RTC; + /* to wit */ + if (strstr(help, "-rtc-td-hack")) + flags |= QEMUD_CMD_FLAG_RTC_TD_HACK; + if (strstr(help, "-no-hpet")) + flags |= QEMUD_CMD_FLAG_NO_HPET; + if (strstr(help, "-no-kvm-pit-reinjection")) + flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; + if (strstr(help, "-tdf")) + flags |= QEMUD_CMD_FLAG_TDF; + if (strstr(help, "-enable-nesting")) + flags |= QEMUD_CMD_FLAG_NESTING; + if (strstr(help, ",menu=on")) + flags |= QEMUD_CMD_FLAG_BOOT_MENU; + if (strstr(help, "-fsdev")) + flags |= QEMUD_CMD_FLAG_FSDEV; + if (strstr(help, "-smbios type")) + flags |= QEMUD_CMD_FLAG_SMBIOS_TYPE; + + if (strstr(help, "-netdev")) { + /* Disable -netdev on 0.12 since although it exists, + * the corresponding netdev_add/remove monitor commands + * do not, and we need them to be able todo hotplug */ + if (version >= 13000) + flags |= QEMUD_CMD_FLAG_NETDEV; + } + + if (strstr(help, "-sdl")) + flags |= QEMUD_CMD_FLAG_SDL; + if (strstr(help, "cores=") && + strstr(help, "threads=") && + strstr(help, "sockets=")) + flags |= QEMUD_CMD_FLAG_SMP_TOPOLOGY; + + if (version >= 9000) + flags |= QEMUD_CMD_FLAG_VNC_COLON; + + if (is_kvm && (version >= 10000 || kvm_version >= 74)) + flags |= QEMUD_CMD_FLAG_VNET_HDR; + + if (is_kvm && strstr(help, ",vhost=")) { + flags |= QEMUD_CMD_FLAG_VNET_HOST; + } + + /* + * Handling of -incoming arg with varying features + * -incoming tcp (kvm >= 79, qemu >= 0.10.0) + * -incoming exec (kvm >= 80, qemu >= 0.10.0) + * -incoming stdio (all earlier kvm) + * + * NB, there was a pre-kvm-79 'tcp' support, but it + * was broken, because it blocked the monitor console + * while waiting for data, so pretend it doesn't exist + */ + if (version >= 10000) { + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; + if (version >= 12000) + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX; + } else if (kvm_version >= 79) { + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; + if (kvm_version >= 80) + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; + } else if (kvm_version > 0) { + flags |= QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO; + } + + if (version >= 10000) + flags |= QEMUD_CMD_FLAG_0_10; + + /* While JSON mode was available in 0.12.0, it was too + * incomplete to contemplate using. The 0.13.0 release + * is good enough to use, even though it lacks one or + * two features. The benefits of JSON mode now outweigh + * the downside. + */ + if (version >= 13000) + flags |= QEMUD_CMD_FLAG_MONITOR_JSON; + + return flags; +} + +/* We parse the output of 'qemu -help' to get the QEMU + * version number. The first bit is easy, just parse + * 'QEMU PC emulator version x.y.z' + * or + * 'QEMU emulator version x.y.z'. + * + * With qemu-kvm, however, that is followed by a string + * in parenthesis as follows: + * - qemu-kvm-x.y.z in stable releases + * - kvm-XX for kvm versions up to kvm-85 + * - qemu-kvm-devel-XX for kvm version kvm-86 and later + * + * For qemu-kvm versions before 0.10.z, we need to detect + * the KVM version number for some features. With 0.10.z + * and later, we just need the QEMU version number and + * whether it is KVM QEMU or mainline QEMU. + */ +#define QEMU_VERSION_STR_1 "QEMU emulator version" +#define QEMU_VERSION_STR_2 "QEMU PC emulator version" +#define QEMU_KVM_VER_PREFIX "(qemu-kvm-" +#define KVM_VER_PREFIX "(kvm-" + +#define SKIP_BLANKS(p) do { while ((*(p) == ' ') || (*(p) == '\t')) (p)++; } while (0) + +int qemuCapsParseHelpStr(const char *qemu, + const char *help, + unsigned long long *flags, + unsigned int *version, + unsigned int *is_kvm, + unsigned int *kvm_version) +{ + unsigned major, minor, micro; + const char *p = help; + + *flags = *version = *is_kvm = *kvm_version = 0; + + if (STRPREFIX(p, QEMU_VERSION_STR_1)) + p += strlen(QEMU_VERSION_STR_1); + else if (STRPREFIX(p, QEMU_VERSION_STR_2)) + p += strlen(QEMU_VERSION_STR_2); + else + goto fail; + + SKIP_BLANKS(p); + + major = virParseNumber(&p); + if (major == -1 || *p != '.') + goto fail; + + ++p; + + minor = virParseNumber(&p); + if (minor == -1 || *p != '.') + goto fail; + + ++p; + + micro = virParseNumber(&p); + if (micro == -1) + goto fail; + + SKIP_BLANKS(p); + + if (STRPREFIX(p, QEMU_KVM_VER_PREFIX)) { + *is_kvm = 1; + p += strlen(QEMU_KVM_VER_PREFIX); + } else if (STRPREFIX(p, KVM_VER_PREFIX)) { + int ret; + + *is_kvm = 1; + p += strlen(KVM_VER_PREFIX); + + ret = virParseNumber(&p); + if (ret == -1) + goto fail; + + *kvm_version = ret; + } + + *version = (major * 1000 * 1000) + (minor * 1000) + micro; + + *flags = qemuCapsComputeCmdFlags(help, *version, *is_kvm, *kvm_version); + + VIR_DEBUG("Version %u.%u.%u, cooked version %u, flags 0x%llx", + major, minor, micro, *version, *flags); + if (*kvm_version) + VIR_DEBUG("KVM version %d detected", *kvm_version); + else if (*is_kvm) + VIR_DEBUG("qemu-kvm version %u.%u.%u detected", major, minor, micro); + + return 0; + +fail: + p = strchr(help, '\n'); + if (p) + p = strndup(help, p - help); + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse %s version number in '%s'"), + qemu, p ? p : help); + + VIR_FREE(p); + + return -1; +} + +static void +qemuCapsParsePCIDeviceStrs(const char *qemu, + unsigned long long *flags) +{ + const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + pid_t child; + int status; + int newstderr = -1; + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0) + return; + + char *pciassign = NULL; + enum { MAX_PCI_OUTPUT_SIZE = 1024*4 }; + int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign); + if (len < 0) { + virReportSystemError(errno, + _("Unable to read %s pci-assign device output"), + qemu); + goto cleanup; + } + + if (strstr(pciassign, "pci-assign.configfd")) + *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD; + +cleanup: + VIR_FREE(pciassign); + VIR_FORCE_CLOSE(newstderr); +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + } + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } +} + +int qemuCapsExtractVersionInfo(const char *qemu, + unsigned int *retversion, + unsigned long long *retflags) +{ + const char *const qemuarg[] = { qemu, "-help", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + pid_t child; + int newstdout = -1; + int ret = -1, status; + unsigned int version, is_kvm, kvm_version; + unsigned long long flags = 0; + + if (retflags) + *retflags = 0; + if (retversion) + *retversion = 0; + + /* Make sure the binary we are about to try exec'ing exists. + * Technically we could catch the exec() failure, but that's + * in a sub-process so it's hard to feed back a useful error. + */ + if (access(qemu, X_OK) < 0) { + virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu); + return -1; + } + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + char *help = NULL; + enum { MAX_HELP_OUTPUT_SIZE = 1024*64 }; + int len = virFileReadLimFD(newstdout, MAX_HELP_OUTPUT_SIZE, &help); + if (len < 0) { + virReportSystemError(errno, + _("Unable to read %s help output"), qemu); + goto cleanup2; + } + + if (qemuCapsParseHelpStr(qemu, help, &flags, + &version, &is_kvm, &kvm_version) == -1) + goto cleanup2; + + if (flags & QEMUD_CMD_FLAG_DEVICE) + qemuCapsParsePCIDeviceStrs(qemu, &flags); + + if (retversion) + *retversion = version; + if (retflags) + *retflags = flags; + + ret = 0; + +cleanup2: + VIR_FREE(help); + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + +static void +uname_normalize (struct utsname *ut) +{ + uname(ut); + + /* Map i386, i486, i586 to i686. */ + if (ut->machine[0] == 'i' && + ut->machine[1] != '\0' && + ut->machine[2] == '8' && + ut->machine[3] == '6' && + ut->machine[4] == '\0') + ut->machine[1] = '6'; +} + +int qemuCapsExtractVersion(virCapsPtr caps, + unsigned int *version) +{ + const char *binary; + struct stat sb; + struct utsname ut; + + if (*version > 0) + return 0; + + uname_normalize(&ut); + if ((binary = virCapabilitiesDefaultGuestEmulator(caps, + "hvm", + ut.machine, + "qemu")) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find suitable emulator for %s"), ut.machine); + return -1; + } + + if (stat(binary, &sb) < 0) { + virReportSystemError(errno, + _("Cannot find QEMU binary %s"), binary); + return -1; + } + + if (qemuCapsExtractVersionInfo(binary, version, NULL) < 0) { + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h new file mode 100644 index 0000000..83afd9b --- /dev/null +++ b/src/qemu/qemu_capabilities.h @@ -0,0 +1,113 @@ +/* + * qemu_capabilities.h: QEMU capabilities generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __QEMU_CAPABILITIES_H__ +# define __QEMU_CAPABILITIES_H__ + +# include "capabilities.h" + +/* Internal flags to keep track of qemu command line capabilities */ +enum qemuCapsFlags { + QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ + QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ + QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ + QEMUD_CMD_FLAG_DRIVE = (1 << 3), /* Is the new -drive arg available */ + QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), /* Does -drive support boot=on */ + QEMUD_CMD_FLAG_NAME = (1 << 5), /* Is the -name flag available */ + QEMUD_CMD_FLAG_UUID = (1 << 6), /* Is the -uuid flag available */ + QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only, special -domid flag available */ + QEMUD_CMD_FLAG_VNET_HDR = (1 << 8), + QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO = (1 << 9), /* Original migration code from KVM. Also had tcp, but we can't use that + * since it had a design bug blocking the entire monitor console */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP = (1 << 10), /* New migration syntax after merge to QEMU with TCP transport */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC = (1 << 11), /* New migration syntax after merge to QEMU with EXEC transport */ + QEMUD_CMD_FLAG_DRIVE_CACHE_V2 = (1 << 12), /* Is the cache= flag wanting new v2 values */ + QEMUD_CMD_FLAG_KVM = (1 << 13), /* Whether KVM is compiled in */ + QEMUD_CMD_FLAG_DRIVE_FORMAT = (1 << 14), /* Is -drive format= avail */ + QEMUD_CMD_FLAG_VGA = (1 << 15), /* Is -vga avail */ + + /* features added in qemu-0.10.0 or later */ + QEMUD_CMD_FLAG_0_10 = (1 << 16), + QEMUD_CMD_FLAG_NET_NAME = QEMUD_CMD_FLAG_0_10, /* -net ...,name=str */ + QEMUD_CMD_FLAG_HOST_NET_ADD = QEMUD_CMD_FLAG_0_10, /* host_net_add monitor command */ + + QEMUD_CMD_FLAG_PCIDEVICE = (1 << 17), /* PCI device assignment only supported by qemu-kvm */ + QEMUD_CMD_FLAG_MEM_PATH = (1 << 18), /* mmap'ped guest backing supported */ + QEMUD_CMD_FLAG_DRIVE_SERIAL = (1 << 19), /* -driver serial= available */ + QEMUD_CMD_FLAG_XEN_DOMID = (1 << 20), /* -xen-domid (new style xen integration) */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ + QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ + QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ + QEMUD_CMD_FLAG_MONITOR_JSON = (1 << 24), /* JSON mode for monitor */ + QEMUD_CMD_FLAG_BALLOON = (1 << 25), /* -balloon available */ + QEMUD_CMD_FLAG_DEVICE = (1 << 26), /* Is the new -device arg available */ + QEMUD_CMD_FLAG_SDL = (1 << 27), /* Is the new -sdl arg available */ + QEMUD_CMD_FLAG_SMP_TOPOLOGY = (1 << 28), /* Is sockets=s,cores=c,threads=t available for -smp? */ + QEMUD_CMD_FLAG_NETDEV = (1 << 29), /* The -netdev flag & netdev_add/remove monitor commands */ + QEMUD_CMD_FLAG_RTC = (1 << 30), /* The -rtc flag for clock options */ + QEMUD_CMD_FLAG_VNET_HOST = (1LL << 31), /* vnet-host support is available in qemu */ + QEMUD_CMD_FLAG_RTC_TD_HACK = (1LL << 32), /* -rtc-td-hack available */ + QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */ + QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ + QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ + QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */ + QEMUD_CMD_FLAG_NODEFCONFIG = (1LL << 37), /* -nodefconfig */ + QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ + QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ + QEMUD_CMD_FLAG_FSDEV = (1LL << 40), /* -fstype filesystem passthrough */ + QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ + QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ + QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ + QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ + QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ + QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ + QEMUD_CMD_FLAG_VGA_NONE = (1LL << 47), /* The 'none' arg for '-vga' */ +}; + +virCapsPtr qemuCapsInit(virCapsPtr old_caps); + +int qemuCapsProbeMachineTypes(const char *binary, + virCapsGuestMachinePtr **machines, + int *nmachines); + +int qemuCapsProbeCPUModels(const char *qemu, + unsigned long long qemuCmdFlags, + const char *arch, + unsigned int *count, + const char ***cpus); + +int qemuCapsExtractVersion(virCapsPtr caps, + unsigned int *version); +int qemuCapsExtractVersionInfo(const char *qemu, + unsigned int *version, + unsigned long long *qemuCmdFlags); + +int qemuCapsParseHelpStr(const char *qemu, + const char *str, + unsigned long long *qemuCmdFlags, + unsigned int *version, + unsigned int *is_kvm, + unsigned int *kvm_version); + + +#endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index f4b524e..0c9c676 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -40,6 +40,7 @@ #include "c-ctype.h" #include "virterror_internal.h" #include "qemu_conf.h" +#include "qemu_capabilities.h" #include "qemu_bridge_filter.h" #include "uuid.h" #include "buf.h" @@ -450,1162 +451,6 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, return 0; } -struct qemu_feature_flags { - const char *name; - const int default_on; - const int toggle; -}; - -struct qemu_arch_info { - const char *arch; - int wordsize; - const char *machine; - const char *binary; - const char *altbinary; - const struct qemu_feature_flags *flags; - int nflags; -}; - -/* Feature flags for the architecture info */ -static const struct qemu_feature_flags const arch_info_i686_flags [] = { - { "pae", 1, 0 }, - { "nonpae", 1, 0 }, - { "acpi", 1, 1 }, - { "apic", 1, 0 }, -}; - -static const struct qemu_feature_flags const arch_info_x86_64_flags [] = { - { "acpi", 1, 1 }, - { "apic", 1, 0 }, -}; - -/* The archicture tables for supported QEMU archs */ -static const struct qemu_arch_info const arch_info_hvm[] = { - { "i686", 32, NULL, "qemu", - "qemu-system-x86_64", arch_info_i686_flags, 4 }, - { "x86_64", 64, NULL, "qemu-system-x86_64", - NULL, arch_info_x86_64_flags, 2 }, - { "arm", 32, NULL, "qemu-system-arm", NULL, NULL, 0 }, - { "mips", 32, NULL, "qemu-system-mips", NULL, NULL, 0 }, - { "mipsel", 32, NULL, "qemu-system-mipsel", NULL, NULL, 0 }, - { "sparc", 32, NULL, "qemu-system-sparc", NULL, NULL, 0 }, - { "ppc", 32, NULL, "qemu-system-ppc", NULL, NULL, 0 }, - { "itanium", 64, NULL, "qemu-system-ia64", NULL, NULL, 0 }, - { "s390x", 64, NULL, "qemu-system-s390x", NULL, NULL, 0 }, -}; - -static const struct qemu_arch_info const arch_info_xen[] = { - { "i686", 32, "xenner", "xenner", NULL, arch_info_i686_flags, 4 }, - { "x86_64", 64, "xenner", "xenner", NULL, arch_info_x86_64_flags, 2 }, -}; - - -/* Format is: - * <machine> <desc> [(default)|(alias of <canonical>)] - */ -static int -qemudParseMachineTypesStr(const char *output, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - const char *p = output; - const char *next; - virCapsGuestMachinePtr *list = NULL; - int nitems = 0; - - do { - const char *t; - virCapsGuestMachinePtr machine; - - if ((next = strchr(p, '\n'))) - ++next; - - if (STRPREFIX(p, "Supported machines are:")) - continue; - - if (!(t = strchr(p, ' ')) || (next && t >= next)) - continue; - - if (VIR_ALLOC(machine) < 0) - goto no_memory; - - if (!(machine->name = strndup(p, t - p))) { - VIR_FREE(machine); - goto no_memory; - } - - if (VIR_REALLOC_N(list, nitems + 1) < 0) { - VIR_FREE(machine->name); - VIR_FREE(machine); - goto no_memory; - } - - p = t; - if (!(t = strstr(p, "(default)")) || (next && t >= next)) { - list[nitems++] = machine; - } else { - /* put the default first in the list */ - memmove(list + 1, list, sizeof(*list) * nitems); - list[0] = machine; - nitems++; - } - - if ((t = strstr(p, "(alias of ")) && (!next || t < next)) { - p = t + strlen("(alias of "); - if (!(t = strchr(p, ')')) || (next && t >= next)) - continue; - - if (!(machine->canonical = strndup(p, t - p))) - goto no_memory; - } - } while ((p = next)); - - *machines = list; - *nmachines = nitems; - - return 0; - - no_memory: - virReportOOMError(); - virCapabilitiesFreeMachines(list, nitems); - return -1; -} - -int -qemudProbeMachineTypes(const char *binary, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - const char *const qemuarg[] = { binary, "-M", "?", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - char *output; - enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; - pid_t child; - int newstdout = -1, len; - int ret = -1, status; - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); - if (len < 0) { - virReportSystemError(errno, "%s", - _("Unable to read 'qemu -M ?' output")); - goto cleanup; - } - - if (qemudParseMachineTypesStr(output, machines, nmachines) < 0) - goto cleanup2; - - ret = 0; - -cleanup2: - VIR_FREE(output); -cleanup: - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} - -static int -qemudGetOldMachinesFromInfo(virCapsGuestDomainInfoPtr info, - const char *emulator, - time_t emulator_mtime, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - virCapsGuestMachinePtr *list; - int i; - - if (!info->nmachines) - return 0; - - if (!info->emulator || !STREQ(emulator, info->emulator)) - return 0; - - if (emulator_mtime != info->emulator_mtime) { - VIR_DEBUG("mtime on %s has changed, refreshing machine types", - info->emulator); - return 0; - } - - if (VIR_ALLOC_N(list, info->nmachines) < 0) { - virReportOOMError(); - return 0; - } - - for (i = 0; i < info->nmachines; i++) { - if (VIR_ALLOC(list[i]) < 0) { - goto no_memory; - } - if (info->machines[i]->name && - !(list[i]->name = strdup(info->machines[i]->name))) { - goto no_memory; - } - if (info->machines[i]->canonical && - !(list[i]->canonical = strdup(info->machines[i]->canonical))) { - goto no_memory; - } - } - - *machines = list; - *nmachines = info->nmachines; - - return 1; - - no_memory: - virReportOOMError(); - virCapabilitiesFreeMachines(list, info->nmachines); - return 0; -} - -static int -qemudGetOldMachines(const char *ostype, - const char *arch, - int wordsize, - const char *emulator, - time_t emulator_mtime, - virCapsPtr old_caps, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - int i; - - for (i = 0; i < old_caps->nguests; i++) { - virCapsGuestPtr guest = old_caps->guests[i]; - int j; - - if (!STREQ(ostype, guest->ostype) || - !STREQ(arch, guest->arch.name) || - wordsize != guest->arch.wordsize) - continue; - - for (j = 0; j < guest->arch.ndomains; j++) { - virCapsGuestDomainPtr dom = guest->arch.domains[j]; - - if (qemudGetOldMachinesFromInfo(&dom->info, - emulator, emulator_mtime, - machines, nmachines)) - return 1; - } - - if (qemudGetOldMachinesFromInfo(&guest->arch.defaultInfo, - emulator, emulator_mtime, - machines, nmachines)) - return 1; - } - - return 0; -} - - -typedef int -(*qemudParseCPUModels)(const char *output, - unsigned int *retcount, - const char ***retcpus); - -/* Format: - * <arch> <model> - * qemu-0.13 encloses some model names in []: - * <arch> [<model>] - */ -static int -qemudParseX86Models(const char *output, - unsigned int *retcount, - const char ***retcpus) -{ - const char *p = output; - const char *next; - unsigned int count = 0; - const char **cpus = NULL; - int i; - - do { - const char *t; - - if ((next = strchr(p, '\n'))) - next++; - - if (!(t = strchr(p, ' ')) || (next && t >= next)) - continue; - - if (!STRPREFIX(p, "x86")) - continue; - - p = t; - while (*p == ' ') - p++; - - if (*p == '\0' || *p == '\n') - continue; - - if (retcpus) { - unsigned int len; - - if (VIR_REALLOC_N(cpus, count + 1) < 0) - goto error; - - if (next) - len = next - p - 1; - else - len = strlen(p); - - if (len > 2 && *p == '[' && p[len - 1] == ']') { - p++; - len -= 2; - } - - if (!(cpus[count] = strndup(p, len))) - goto error; - } - count++; - } while ((p = next)); - - if (retcount) - *retcount = count; - if (retcpus) - *retcpus = cpus; - - return 0; - -error: - if (cpus) { - for (i = 0; i < count; i++) - VIR_FREE(cpus[i]); - } - VIR_FREE(cpus); - - return -1; -} - - -int -qemudProbeCPUModels(const char *qemu, - unsigned long long qemuCmdFlags, - const char *arch, - unsigned int *count, - const char ***cpus) -{ - const char *const qemuarg[] = { - qemu, - "-cpu", "?", - (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) ? "-nodefconfig" : NULL, - NULL - }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; - char *output = NULL; - int newstdout = -1; - int ret = -1; - pid_t child; - int status; - int len; - qemudParseCPUModels parse; - - if (count) - *count = 0; - if (cpus) - *cpus = NULL; - - if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) - parse = qemudParseX86Models; - else { - VIR_DEBUG("don't know how to parse %s CPU models", arch); - return 0; - } - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); - if (len < 0) { - virReportSystemError(errno, "%s", - _("Unable to read QEMU supported CPU models")); - goto cleanup; - } - - if (parse(output, count, cpus) < 0) { - virReportOOMError(); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FREE(output); - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} - - -static int -qemudCapsInitGuest(virCapsPtr caps, - virCapsPtr old_caps, - const char *hostmachine, - const struct qemu_arch_info *info, - int hvm) { - virCapsGuestPtr guest; - int i; - int haskvm = 0; - int haskqemu = 0; - char *kvmbin = NULL; - char *binary = NULL; - time_t binary_mtime; - virCapsGuestMachinePtr *machines = NULL; - int nmachines = 0; - struct stat st; - unsigned int ncpus; - int ret = -1; - - /* Check for existance of base emulator, or alternate base - * which can be used with magic cpu choice - */ - binary = virFindFileInPath(info->binary); - - if (binary == NULL || access(binary, X_OK) != 0) { - VIR_FREE(binary); - binary = virFindFileInPath(info->altbinary); - - if (binary != NULL && access(binary, X_OK) != 0) - VIR_FREE(binary); - } - - /* Can use acceleration for KVM/KQEMU if - * - host & guest arches match - * Or - * - hostarch is x86_64 and guest arch is i686 - * The latter simply needs "-cpu qemu32" - */ - if (STREQ(info->arch, hostmachine) || - (STREQ(hostmachine, "x86_64") && STREQ(info->arch, "i686"))) { - if (access("/dev/kvm", F_OK) == 0) { - const char *const kvmbins[] = { "/usr/libexec/qemu-kvm", /* RHEL */ - "qemu-kvm", /* Fedora */ - "kvm" }; /* Upstream .spec */ - - for (i = 0; i < ARRAY_CARDINALITY(kvmbins); ++i) { - kvmbin = virFindFileInPath(kvmbins[i]); - - if (kvmbin == NULL || access(kvmbin, X_OK) != 0) { - VIR_FREE(kvmbin); - continue; - } - - haskvm = 1; - if (!binary) - binary = kvmbin; - - break; - } - } - - if (access("/dev/kqemu", F_OK) == 0) - haskqemu = 1; - } - - if (!binary) - return 0; - - if (stat(binary, &st) == 0) { - binary_mtime = st.st_mtime; - } else { - char ebuf[1024]; - VIR_WARN("Failed to stat %s, most peculiar : %s", - binary, virStrerror(errno, ebuf, sizeof(ebuf))); - binary_mtime = 0; - } - - if (info->machine) { - virCapsGuestMachinePtr machine; - - if (VIR_ALLOC(machine) < 0) { - goto no_memory; - } - - if (!(machine->name = strdup(info->machine))) { - VIR_FREE(machine); - goto no_memory; - } - - nmachines = 1; - - if (VIR_ALLOC_N(machines, nmachines) < 0) { - VIR_FREE(machine->name); - VIR_FREE(machine); - goto no_memory; - } - - machines[0] = machine; - } else { - int probe = 1; - if (old_caps && binary_mtime) - probe = !qemudGetOldMachines(hvm ? "hvm" : "xen", info->arch, - info->wordsize, binary, binary_mtime, - old_caps, &machines, &nmachines); - if (probe && - qemudProbeMachineTypes(binary, &machines, &nmachines) < 0) - goto error; - } - - /* We register kvm as the base emulator too, since we can - * just give -no-kvm to disable acceleration if required */ - if ((guest = virCapabilitiesAddGuest(caps, - hvm ? "hvm" : "xen", - info->arch, - info->wordsize, - binary, - NULL, - nmachines, - machines)) == NULL) - goto error; - - machines = NULL; - nmachines = 0; - - guest->arch.defaultInfo.emulator_mtime = binary_mtime; - - if (caps->host.cpu && - qemudProbeCPUModels(binary, 0, info->arch, &ncpus, NULL) == 0 && - ncpus > 0 && - !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) - goto error; - - if (hvm) { - if (virCapabilitiesAddGuestDomain(guest, - "qemu", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - - if (haskqemu && - virCapabilitiesAddGuestDomain(guest, - "kqemu", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - - if (haskvm) { - virCapsGuestDomainPtr dom; - - if (stat(kvmbin, &st) == 0) { - binary_mtime = st.st_mtime; - } else { - char ebuf[1024]; - VIR_WARN("Failed to stat %s, most peculiar : %s", - binary, virStrerror(errno, ebuf, sizeof(ebuf))); - binary_mtime = 0; - } - - if (!STREQ(binary, kvmbin)) { - int probe = 1; - if (old_caps && binary_mtime) - probe = !qemudGetOldMachines("hvm", info->arch, info->wordsize, - kvmbin, binary_mtime, - old_caps, &machines, &nmachines); - if (probe && - qemudProbeMachineTypes(kvmbin, &machines, &nmachines) < 0) - goto error; - } - - if ((dom = virCapabilitiesAddGuestDomain(guest, - "kvm", - kvmbin, - NULL, - nmachines, - machines)) == NULL) { - goto error; - } - - machines = NULL; - nmachines = 0; - - dom->info.emulator_mtime = binary_mtime; - } - } else { - if (virCapabilitiesAddGuestDomain(guest, - "kvm", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - } - - if (info->nflags) { - for (i = 0 ; i < info->nflags ; i++) { - if (virCapabilitiesAddGuestFeature(guest, - info->flags[i].name, - info->flags[i].default_on, - info->flags[i].toggle) == NULL) - goto error; - } - } - - ret = 0; - -cleanup: - if (binary == kvmbin) { - /* don't double free */ - VIR_FREE(binary); - } else { - VIR_FREE(binary); - VIR_FREE(kvmbin); - } - - return ret; - -no_memory: - virReportOOMError(); - -error: - virCapabilitiesFreeMachines(machines, nmachines); - - goto cleanup; -} - - -static int -qemudCapsInitCPU(virCapsPtr caps, - const char *arch) -{ - virCPUDefPtr cpu = NULL; - union cpuData *data = NULL; - virNodeInfo nodeinfo; - int ret = -1; - - if (VIR_ALLOC(cpu) < 0 - || !(cpu->arch = strdup(arch))) { - virReportOOMError(); - goto error; - } - - if (nodeGetInfo(NULL, &nodeinfo)) - goto error; - - cpu->type = VIR_CPU_TYPE_HOST; - cpu->sockets = nodeinfo.sockets; - cpu->cores = nodeinfo.cores; - cpu->threads = nodeinfo.threads; - - if (!(data = cpuNodeData(arch)) - || cpuDecode(cpu, data, NULL, 0, NULL) < 0) - goto error; - - caps->host.cpu = cpu; - - ret = 0; - -cleanup: - cpuDataFree(arch, data); - - return ret; - -error: - virCPUDefFree(cpu); - goto cleanup; -} - - -virCapsPtr qemudCapsInit(virCapsPtr old_caps) { - struct utsname utsname; - virCapsPtr caps; - int i; - char *xenner = NULL; - - /* Really, this never fails - look at the man-page. */ - uname (&utsname); - - if ((caps = virCapabilitiesNew(utsname.machine, - 1, 1)) == NULL) - goto no_memory; - - /* Using KVM's mac prefix for QEMU too */ - virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x52, 0x54, 0x00 }); - - /* Some machines have problematic NUMA toplogy causing - * unexpected failures. We don't want to break the QEMU - * driver in this scenario, so log errors & carry on - */ - if (nodeCapsInitNUMA(caps) < 0) { - virCapabilitiesFreeNUMAInfo(caps); - VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); - } - - if (old_caps == NULL || old_caps->host.cpu == NULL) { - if (qemudCapsInitCPU(caps, utsname.machine) < 0) - VIR_WARN0("Failed to get host CPU"); - } - else { - caps->host.cpu = old_caps->host.cpu; - old_caps->host.cpu = NULL; - } - - virCapabilitiesAddHostMigrateTransport(caps, - "tcp"); - - /* First the pure HVM guests */ - for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) - if (qemudCapsInitGuest(caps, old_caps, - utsname.machine, - &arch_info_hvm[i], 1) < 0) - goto no_memory; - - /* Then possibly the Xen paravirt guests (ie Xenner */ - xenner = virFindFileInPath("xenner"); - - if (xenner != NULL && access(xenner, X_OK) == 0 && - access("/dev/kvm", F_OK) == 0) { - for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_xen) ; i++) - /* Allow Xen 32-on-32, 32-on-64 and 64-on-64 */ - if (STREQ(arch_info_xen[i].arch, utsname.machine) || - (STREQ(utsname.machine, "x86_64") && - STREQ(arch_info_xen[i].arch, "i686"))) { - if (qemudCapsInitGuest(caps, old_caps, - utsname.machine, - &arch_info_xen[i], 0) < 0) - goto no_memory; - } - } - - VIR_FREE(xenner); - - /* QEMU Requires an emulator in the XML */ - virCapabilitiesSetEmulatorRequired(caps); - - caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; - - return caps; - - no_memory: - VIR_FREE(xenner); - virCapabilitiesFree(caps); - return NULL; -} - -static unsigned long long qemudComputeCmdFlags(const char *help, - unsigned int version, - unsigned int is_kvm, - unsigned int kvm_version) -{ - unsigned long long flags = 0; - const char *p; - - if (strstr(help, "-no-kqemu")) - flags |= QEMUD_CMD_FLAG_KQEMU; - if (strstr(help, "-enable-kqemu")) - flags |= QEMUD_CMD_FLAG_ENABLE_KQEMU; - if (strstr(help, "-no-kvm")) - flags |= QEMUD_CMD_FLAG_KVM; - if (strstr(help, "-enable-kvm")) - flags |= QEMUD_CMD_FLAG_ENABLE_KVM; - if (strstr(help, "-no-reboot")) - flags |= QEMUD_CMD_FLAG_NO_REBOOT; - if (strstr(help, "-name")) { - flags |= QEMUD_CMD_FLAG_NAME; - if (strstr(help, ",process=")) - flags |= QEMUD_CMD_FLAG_NAME_PROCESS; - } - if (strstr(help, "-uuid")) - flags |= QEMUD_CMD_FLAG_UUID; - if (strstr(help, "-xen-domid")) - flags |= QEMUD_CMD_FLAG_XEN_DOMID; - else if (strstr(help, "-domid")) - flags |= QEMUD_CMD_FLAG_DOMID; - if (strstr(help, "-drive")) { - flags |= QEMUD_CMD_FLAG_DRIVE; - if (strstr(help, "cache=") && - !strstr(help, "cache=on|off")) - flags |= QEMUD_CMD_FLAG_DRIVE_CACHE_V2; - if (strstr(help, "format=")) - flags |= QEMUD_CMD_FLAG_DRIVE_FORMAT; - if (strstr(help, "readonly=")) - flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; - } - if ((p = strstr(help, "-vga")) && !strstr(help, "-std-vga")) { - const char *nl = strstr(p, "\n"); - - flags |= QEMUD_CMD_FLAG_VGA; - - if (strstr(p, "|qxl")) - flags |= QEMUD_CMD_FLAG_VGA_QXL; - if ((p = strstr(p, "|none")) && p < nl) - flags |= QEMUD_CMD_FLAG_VGA_NONE; - } - if (strstr(help, "-spice")) - flags |= QEMUD_CMD_FLAG_SPICE; - if (strstr(help, "boot=on")) - flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; - if (strstr(help, "serial=s")) - flags |= QEMUD_CMD_FLAG_DRIVE_SERIAL; - if (strstr(help, "-pcidevice")) - flags |= QEMUD_CMD_FLAG_PCIDEVICE; - if (strstr(help, "-mem-path")) - flags |= QEMUD_CMD_FLAG_MEM_PATH; - if (strstr(help, "-chardev")) - flags |= QEMUD_CMD_FLAG_CHARDEV; - if (strstr(help, "-balloon")) - flags |= QEMUD_CMD_FLAG_BALLOON; - if (strstr(help, "-device")) { - flags |= QEMUD_CMD_FLAG_DEVICE; - /* - * When -device was introduced, qemu already supported drive's - * readonly option but didn't advertise that. - */ - flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; - } - if (strstr(help, "-nodefconfig")) - flags |= QEMUD_CMD_FLAG_NODEFCONFIG; - /* The trailing ' ' is important to avoid a bogus match */ - if (strstr(help, "-rtc ")) - flags |= QEMUD_CMD_FLAG_RTC; - /* to wit */ - if (strstr(help, "-rtc-td-hack")) - flags |= QEMUD_CMD_FLAG_RTC_TD_HACK; - if (strstr(help, "-no-hpet")) - flags |= QEMUD_CMD_FLAG_NO_HPET; - if (strstr(help, "-no-kvm-pit-reinjection")) - flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; - if (strstr(help, "-tdf")) - flags |= QEMUD_CMD_FLAG_TDF; - if (strstr(help, "-enable-nesting")) - flags |= QEMUD_CMD_FLAG_NESTING; - if (strstr(help, ",menu=on")) - flags |= QEMUD_CMD_FLAG_BOOT_MENU; - if (strstr(help, "-fsdev")) - flags |= QEMUD_CMD_FLAG_FSDEV; - if (strstr(help, "-smbios type")) - flags |= QEMUD_CMD_FLAG_SMBIOS_TYPE; - - if (strstr(help, "-netdev")) { - /* Disable -netdev on 0.12 since although it exists, - * the corresponding netdev_add/remove monitor commands - * do not, and we need them to be able todo hotplug */ - if (version >= 13000) - flags |= QEMUD_CMD_FLAG_NETDEV; - } - - if (strstr(help, "-sdl")) - flags |= QEMUD_CMD_FLAG_SDL; - if (strstr(help, "cores=") && - strstr(help, "threads=") && - strstr(help, "sockets=")) - flags |= QEMUD_CMD_FLAG_SMP_TOPOLOGY; - - if (version >= 9000) - flags |= QEMUD_CMD_FLAG_VNC_COLON; - - if (is_kvm && (version >= 10000 || kvm_version >= 74)) - flags |= QEMUD_CMD_FLAG_VNET_HDR; - - if (is_kvm && strstr(help, ",vhost=")) { - flags |= QEMUD_CMD_FLAG_VNET_HOST; - } - - /* - * Handling of -incoming arg with varying features - * -incoming tcp (kvm >= 79, qemu >= 0.10.0) - * -incoming exec (kvm >= 80, qemu >= 0.10.0) - * -incoming stdio (all earlier kvm) - * - * NB, there was a pre-kvm-79 'tcp' support, but it - * was broken, because it blocked the monitor console - * while waiting for data, so pretend it doesn't exist - */ - if (version >= 10000) { - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; - if (version >= 12000) - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX; - } else if (kvm_version >= 79) { - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; - if (kvm_version >= 80) - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; - } else if (kvm_version > 0) { - flags |= QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO; - } - - if (version >= 10000) - flags |= QEMUD_CMD_FLAG_0_10; - - /* While JSON mode was available in 0.12.0, it was too - * incomplete to contemplate using. The 0.13.0 release - * is good enough to use, even though it lacks one or - * two features. The benefits of JSON mode now outweigh - * the downside. - */ - if (version >= 13000) - flags |= QEMUD_CMD_FLAG_MONITOR_JSON; - - return flags; -} - -/* We parse the output of 'qemu -help' to get the QEMU - * version number. The first bit is easy, just parse - * 'QEMU PC emulator version x.y.z' - * or - * 'QEMU emulator version x.y.z'. - * - * With qemu-kvm, however, that is followed by a string - * in parenthesis as follows: - * - qemu-kvm-x.y.z in stable releases - * - kvm-XX for kvm versions up to kvm-85 - * - qemu-kvm-devel-XX for kvm version kvm-86 and later - * - * For qemu-kvm versions before 0.10.z, we need to detect - * the KVM version number for some features. With 0.10.z - * and later, we just need the QEMU version number and - * whether it is KVM QEMU or mainline QEMU. - */ -#define QEMU_VERSION_STR_1 "QEMU emulator version" -#define QEMU_VERSION_STR_2 "QEMU PC emulator version" -#define QEMU_KVM_VER_PREFIX "(qemu-kvm-" -#define KVM_VER_PREFIX "(kvm-" - -#define SKIP_BLANKS(p) do { while ((*(p) == ' ') || (*(p) == '\t')) (p)++; } while (0) - -int qemudParseHelpStr(const char *qemu, - const char *help, - unsigned long long *flags, - unsigned int *version, - unsigned int *is_kvm, - unsigned int *kvm_version) -{ - unsigned major, minor, micro; - const char *p = help; - - *flags = *version = *is_kvm = *kvm_version = 0; - - if (STRPREFIX(p, QEMU_VERSION_STR_1)) - p += strlen(QEMU_VERSION_STR_1); - else if (STRPREFIX(p, QEMU_VERSION_STR_2)) - p += strlen(QEMU_VERSION_STR_2); - else - goto fail; - - SKIP_BLANKS(p); - - major = virParseNumber(&p); - if (major == -1 || *p != '.') - goto fail; - - ++p; - - minor = virParseNumber(&p); - if (minor == -1 || *p != '.') - goto fail; - - ++p; - - micro = virParseNumber(&p); - if (micro == -1) - goto fail; - - SKIP_BLANKS(p); - - if (STRPREFIX(p, QEMU_KVM_VER_PREFIX)) { - *is_kvm = 1; - p += strlen(QEMU_KVM_VER_PREFIX); - } else if (STRPREFIX(p, KVM_VER_PREFIX)) { - int ret; - - *is_kvm = 1; - p += strlen(KVM_VER_PREFIX); - - ret = virParseNumber(&p); - if (ret == -1) - goto fail; - - *kvm_version = ret; - } - - *version = (major * 1000 * 1000) + (minor * 1000) + micro; - - *flags = qemudComputeCmdFlags(help, *version, *is_kvm, *kvm_version); - - VIR_DEBUG("Version %u.%u.%u, cooked version %u, flags 0x%llx", - major, minor, micro, *version, *flags); - if (*kvm_version) - VIR_DEBUG("KVM version %d detected", *kvm_version); - else if (*is_kvm) - VIR_DEBUG("qemu-kvm version %u.%u.%u detected", major, minor, micro); - - return 0; - -fail: - p = strchr(help, '\n'); - if (p) - p = strndup(help, p - help); - - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse %s version number in '%s'"), - qemu, p ? p : help); - - VIR_FREE(p); - - return -1; -} - -static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags) -{ - const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - pid_t child; - int status; - int newstderr = -1; - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0) - return; - - char *pciassign = NULL; - enum { MAX_PCI_OUTPUT_SIZE = 1024*4 }; - int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign); - if (len < 0) { - virReportSystemError(errno, - _("Unable to read %s pci-assign device output"), - qemu); - goto cleanup; - } - - if (strstr(pciassign, "pci-assign.configfd")) - *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD; - -cleanup: - VIR_FREE(pciassign); - VIR_FORCE_CLOSE(newstderr); -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - } - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } -} - -int qemudExtractVersionInfo(const char *qemu, - unsigned int *retversion, - unsigned long long *retflags) { - const char *const qemuarg[] = { qemu, "-help", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - pid_t child; - int newstdout = -1; - int ret = -1, status; - unsigned int version, is_kvm, kvm_version; - unsigned long long flags = 0; - - if (retflags) - *retflags = 0; - if (retversion) - *retversion = 0; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so it's hard to feed back a useful error. - */ - if (access(qemu, X_OK) < 0) { - virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu); - return -1; - } - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - char *help = NULL; - enum { MAX_HELP_OUTPUT_SIZE = 1024*64 }; - int len = virFileReadLimFD(newstdout, MAX_HELP_OUTPUT_SIZE, &help); - if (len < 0) { - virReportSystemError(errno, - _("Unable to read %s help output"), qemu); - goto cleanup2; - } - - if (qemudParseHelpStr(qemu, help, &flags, - &version, &is_kvm, &kvm_version) == -1) - goto cleanup2; - - if (flags & QEMUD_CMD_FLAG_DEVICE) - qemudParsePCIDeviceStrs(qemu, &flags); - - if (retversion) - *retversion = version; - if (retflags) - *retflags = flags; - - ret = 0; - -cleanup2: - VIR_FREE(help); - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} static void uname_normalize (struct utsname *ut) @@ -1621,36 +466,6 @@ uname_normalize (struct utsname *ut) ut->machine[1] = '6'; } -int qemudExtractVersion(struct qemud_driver *driver) { - const char *binary; - struct stat sb; - struct utsname ut; - - if (driver->qemuVersion > 0) - return 0; - - uname_normalize(&ut); - if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps, - "hvm", - ut.machine, - "qemu")) == NULL) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot find suitable emulator for %s"), ut.machine); - return -1; - } - - if (stat(binary, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), binary); - return -1; - } - - if (qemudExtractVersionInfo(binary, &driver->qemuVersion, NULL) < 0) { - return -1; - } - - return 0; -} /** * qemudPhysIfaceConnect: @@ -3848,8 +2663,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, *hasHwVirt = false; if (def->cpu && def->cpu->model) { - if (qemudProbeCPUModels(emulator, qemuCmdFlags, ut->machine, - &ncpus, &cpus) < 0) + if (qemuCapsProbeCPUModels(emulator, qemuCmdFlags, ut->machine, + &ncpus, &cpus) < 0) goto cleanup; if (!ncpus || !host) { diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e6b413d..86c65a6 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -47,63 +47,6 @@ # define QEMUD_CPUMASK_LEN CPU_SETSIZE -/* Internal flags to keep track of qemu command line capabilities */ -enum qemud_cmd_flags { - QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ - QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ - QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ - QEMUD_CMD_FLAG_DRIVE = (1 << 3), /* Is the new -drive arg available */ - QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), /* Does -drive support boot=on */ - QEMUD_CMD_FLAG_NAME = (1 << 5), /* Is the -name flag available */ - QEMUD_CMD_FLAG_UUID = (1 << 6), /* Is the -uuid flag available */ - QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only, special -domid flag available */ - QEMUD_CMD_FLAG_VNET_HDR = (1 << 8), - QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO = (1 << 9), /* Original migration code from KVM. Also had tcp, but we can't use that - * since it had a design bug blocking the entire monitor console */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP = (1 << 10), /* New migration syntax after merge to QEMU with TCP transport */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC = (1 << 11), /* New migration syntax after merge to QEMU with EXEC transport */ - QEMUD_CMD_FLAG_DRIVE_CACHE_V2 = (1 << 12), /* Is the cache= flag wanting new v2 values */ - QEMUD_CMD_FLAG_KVM = (1 << 13), /* Whether KVM is compiled in */ - QEMUD_CMD_FLAG_DRIVE_FORMAT = (1 << 14), /* Is -drive format= avail */ - QEMUD_CMD_FLAG_VGA = (1 << 15), /* Is -vga avail */ - - /* features added in qemu-0.10.0 or later */ - QEMUD_CMD_FLAG_0_10 = (1 << 16), - QEMUD_CMD_FLAG_NET_NAME = QEMUD_CMD_FLAG_0_10, /* -net ...,name=str */ - QEMUD_CMD_FLAG_HOST_NET_ADD = QEMUD_CMD_FLAG_0_10, /* host_net_add monitor command */ - - QEMUD_CMD_FLAG_PCIDEVICE = (1 << 17), /* PCI device assignment only supported by qemu-kvm */ - QEMUD_CMD_FLAG_MEM_PATH = (1 << 18), /* mmap'ped guest backing supported */ - QEMUD_CMD_FLAG_DRIVE_SERIAL = (1 << 19), /* -driver serial= available */ - QEMUD_CMD_FLAG_XEN_DOMID = (1 << 20), /* -xen-domid (new style xen integration) */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ - QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ - QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ - QEMUD_CMD_FLAG_MONITOR_JSON = (1 << 24), /* JSON mode for monitor */ - QEMUD_CMD_FLAG_BALLOON = (1 << 25), /* -balloon available */ - QEMUD_CMD_FLAG_DEVICE = (1 << 26), /* Is the new -device arg available */ - QEMUD_CMD_FLAG_SDL = (1 << 27), /* Is the new -sdl arg available */ - QEMUD_CMD_FLAG_SMP_TOPOLOGY = (1 << 28), /* Is sockets=s,cores=c,threads=t available for -smp? */ - QEMUD_CMD_FLAG_NETDEV = (1 << 29), /* The -netdev flag & netdev_add/remove monitor commands */ - QEMUD_CMD_FLAG_RTC = (1 << 30), /* The -rtc flag for clock options */ - QEMUD_CMD_FLAG_VNET_HOST = (1LL << 31), /* vnet-host support is available in qemu */ - QEMUD_CMD_FLAG_RTC_TD_HACK = (1LL << 32), /* -rtc-td-hack available */ - QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */ - QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ - QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ - QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */ - QEMUD_CMD_FLAG_NODEFCONFIG = (1LL << 37), /* -nodefconfig */ - QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ - QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ - QEMUD_CMD_FLAG_FSDEV = (1LL << 40), /* -fstype filesystem passthrough */ - QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ - QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ - QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ - QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ - QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ - QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ - QEMUD_CMD_FLAG_VGA_NONE = (1LL << 47), /* The 'none' arg for '-vga' */ -}; /* Main driver state */ struct qemud_driver { @@ -220,20 +163,6 @@ struct _qemuDomainCmdlineDef { int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename); -virCapsPtr qemudCapsInit (virCapsPtr old_caps); - -int qemudExtractVersion (struct qemud_driver *driver); -int qemudExtractVersionInfo (const char *qemu, - unsigned int *version, - unsigned long long *qemuCmdFlags); - -int qemudParseHelpStr (const char *qemu, - const char *str, - unsigned long long *qemuCmdFlags, - unsigned int *version, - unsigned int *is_kvm, - unsigned int *kvm_version); - virCommandPtr qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, @@ -324,16 +253,6 @@ int qemudPhysIfaceConnect(virConnectPtr conn, const unsigned char *vmuuid, enum virVMOperationType vmop); -int qemudProbeMachineTypes (const char *binary, - virCapsGuestMachinePtr **machines, - int *nmachines); - -int qemudProbeCPUModels (const char *qemu, - unsigned long long qemuCmdFlags, - const char *arch, - unsigned int *count, - const char ***cpus); - int qemudCanonicalizeMachine (struct qemud_driver *driver, virDomainDefPtr def); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c096890..f303075 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -54,6 +54,7 @@ #include "datatypes.h" #include "qemu_driver.h" #include "qemu_conf.h" +#include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "c-ctype.h" @@ -1553,9 +1554,9 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq /* XXX we should be persisting the original flags in the XML * not re-detecting them, since the binary may have changed * since launch time */ - if (qemudExtractVersionInfo(obj->def->emulator, - NULL, - &qemuCmdFlags) >= 0 && + if (qemuCapsExtractVersionInfo(obj->def->emulator, + NULL, + &qemuCmdFlags) >= 0 && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { priv->persistentAddrs = 1; @@ -1644,7 +1645,7 @@ qemuCreateCapabilities(virCapsPtr oldcaps, virCapsPtr caps; /* Basic host arch / guest machine capabilities */ - if (!(caps = qemudCapsInit(oldcaps))) { + if (!(caps = qemuCapsInit(oldcaps))) { virReportOOMError(); return NULL; } @@ -3118,9 +3119,9 @@ qemuAssignPCIAddresses(virDomainDefPtr def) unsigned long long qemuCmdFlags = 0; qemuDomainPCIAddressSetPtr addrs = NULL; - if (qemudExtractVersionInfo(def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { @@ -4063,9 +4064,9 @@ static int qemudStartVMDaemon(virConnectPtr conn, goto cleanup; DEBUG0("Determining emulator version"); - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; DEBUG0("Setting up domain cgroup (if required)"); @@ -4851,10 +4852,10 @@ static int qemudGetVersion(virConnectPtr conn, unsigned long *version) { int ret = -1; qemuDriverLock(driver); - if (qemudExtractVersion(driver) < 0) + if (qemuCapsExtractVersion(driver->caps, &driver->qemuVersion) < 0) goto cleanup; - *version = qemu_driver->qemuVersion; + *version = driver->qemuVersion; ret = 0; cleanup: @@ -7465,9 +7466,9 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, def->graphics[i]->data.vnc.port = QEMU_VNC_PORT_MIN; } - if (qemudExtractVersionInfo(def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0) @@ -7637,7 +7638,7 @@ qemudCanonicalizeMachineDirect(virDomainDefPtr def, char **canonical) virCapsGuestMachinePtr *machines = NULL; int i, nmachines = 0; - if (qemudProbeMachineTypes(def->emulator, &machines, &nmachines) < 0) { + if (qemuCapsProbeMachineTypes(def->emulator, &machines, &nmachines) < 0) { virReportOOMError(); return -1; } @@ -8826,9 +8827,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK) { @@ -9073,9 +9074,9 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; switch (dev->type) { @@ -9793,9 +9794,9 @@ static int qemudDomainDetachDevice(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK && @@ -11060,7 +11061,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, unlink(unixfile); /* check that this qemu version supports the interactive exec */ - if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { + if (qemuCapsExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot determine QEMU argv syntax %s"), vm->def->emulator); @@ -11571,7 +11572,7 @@ static int doTunnelMigrate(virDomainPtr dom, } /* check that this qemu version supports the unix migration */ - if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { + if (qemuCapsExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot extract Qemu version from '%s'"), vm->def->emulator); -- 1.7.2.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list