提交 ffb13b11 编写于 作者: J Jiri Denemark 提交者: Daniel Veillard

Implement CPU selection in QEMU driver

* src/qemu/qemu_conf.c src/qemu/qemu_conf.h src/qemu/qemu_driver.c:
  add the new entry point, extend capabilities and code to interract
  with qemu
上级 d5ef0a69
......@@ -52,6 +52,7 @@
#include "nodeinfo.h"
#include "logging.h"
#include "network.h"
#include "cpu/cpu.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
......@@ -608,6 +609,151 @@ qemudGetOldMachines(const char *ostype,
return 0;
}
typedef int
(*qemudParseCPUModels)(const char *output,
unsigned int *retcount,
const char ***retcpus);
/* Format:
* <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) {
if (VIR_REALLOC_N(cpus, count + 1) < 0)
goto error;
if (next)
cpus[count] = strndup(p, next - p - 1);
else
cpus[count] = strdup(p);
if (!cpus[count])
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,
const char *arch,
unsigned int *count,
const char ***cpus)
{
const char *const qemuarg[] = { qemu, "-cpu", "?", 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(NULL, 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(NULL, errno, "%s",
_("Unable to read QEMU supported CPU models"));
goto cleanup;
}
if (parse(output, count, cpus) < 0) {
virReportOOMError(NULL);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(output);
if (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,
......@@ -624,6 +770,7 @@ qemudCapsInitGuest(virCapsPtr caps,
virCapsGuestMachinePtr *machines = NULL;
int nmachines = 0;
struct stat st;
unsigned int ncpus;
/* Check for existance of base emulator, or alternate base
* which can be used with magic cpu choice
......@@ -725,6 +872,11 @@ qemudCapsInitGuest(virCapsPtr caps,
guest->arch.defaultInfo.emulator_mtime = binary_mtime;
if (qemudProbeCPUModels(binary, info->arch, &ncpus, NULL) == 0
&& ncpus > 0
&& !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0))
return -1;
if (hvm) {
if (virCapabilitiesAddGuestDomain(guest,
"qemu",
......@@ -808,6 +960,49 @@ qemudCapsInitGuest(virCapsPtr caps,
return 0;
}
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(NULL);
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(NULL, arch))
|| cpuDecode(NULL, cpu, data, 0, NULL) < 0)
goto error;
caps->host.cpu = cpu;
ret = 0;
cleanup:
cpuDataFree(NULL, arch, data);
return ret;
error:
virCPUDefFree(cpu);
goto cleanup;
}
virCapsPtr qemudCapsInit(virCapsPtr old_caps) {
struct utsname utsname;
virCapsPtr caps;
......@@ -832,6 +1027,15 @@ virCapsPtr qemudCapsInit(virCapsPtr old_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");
......@@ -1563,6 +1767,98 @@ static void qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
}
}
static int
qemudBuildCommandLineCPU(virConnectPtr conn,
const struct qemud_driver *driver,
const virDomainDefPtr def,
const char *emulator,
const struct utsname *ut,
char **opt)
{
const virCPUDefPtr host = driver->caps->host.cpu;
virCPUDefPtr guest = NULL;
unsigned int ncpus;
const char **cpus = NULL;
union cpuData *data = NULL;
int ret = -1;
virBuffer buf = VIR_BUFFER_INITIALIZER;
int i;
if (qemudProbeCPUModels(emulator, ut->machine, &ncpus, &cpus) < 0)
goto cleanup;
if (ncpus > 0 && host && def->cpu && def->cpu->model) {
virCPUCompareResult cmp;
cmp = cpuGuestData(conn, host, def->cpu, &data);
switch (cmp) {
case VIR_CPU_COMPARE_INCOMPATIBLE:
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("guest CPU is not compatible with host CPU"));
/* fall through */
case VIR_CPU_COMPARE_ERROR:
goto cleanup;
default:
break;
}
if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine)))
goto no_memory;
if (cpuDecode(conn, guest, data, ncpus, cpus) < 0)
goto cleanup;
virBufferVSprintf(&buf, "%s", guest->model);
for (i = 0; i < guest->nfeatures; i++)
virBufferVSprintf(&buf, ",+%s", guest->features[i].name);
}
else {
/*
* Need to force a 32-bit guest CPU type if
*
* 1. guest OS is i686
* 2. host OS is x86_64
* 3. emulator is qemu-kvm or kvm
*
* Or
*
* 1. guest OS is i686
* 2. emulator is qemu-system-x86_64
*/
if (STREQ(def->os.arch, "i686") &&
((STREQ(ut->machine, "x86_64") &&
strstr(emulator, "kvm")) ||
strstr(emulator, "x86_64")))
virBufferAddLit(&buf, "qemu32");
}
if (virBufferError(&buf))
goto no_memory;
*opt = virBufferContentAndReset(&buf);
ret = 0;
cleanup:
virCPUDefFree(guest);
cpuDataFree(conn, ut->machine, data);
if (cpus) {
for (i = 0; i < ncpus; i++)
VIR_FREE(cpus[i]);
VIR_FREE(cpus);
}
return ret;
no_memory:
virReportOOMError(conn);
goto cleanup;
}
#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
......@@ -1610,7 +1906,7 @@ int qemudBuildCommandLine(virConnectPtr conn,
const char *emulator;
char uuid[VIR_UUID_STRING_BUFLEN];
char domid[50];
const char *cpu = NULL;
char *cpu;
uname_normalize(&ut);
......@@ -1670,24 +1966,6 @@ int qemudBuildCommandLine(virConnectPtr conn,
def->virtType == VIR_DOMAIN_VIRT_KVM)
enableKVM = 1;
/*
* Need to force a 32-bit guest CPU type if
*
* 1. guest OS is i686
* 2. host OS is x86_64
* 3. emulator is qemu-kvm or kvm
*
* Or
*
* 1. guest OS is i686
* 2. emulator is qemu-system-x86_64
*/
if (STREQ(def->os.arch, "i686") &&
((STREQ(ut.machine, "x86_64") &&
strstr(emulator, "kvm")) ||
strstr(emulator, "x86_64")))
cpu = "qemu32";
#define ADD_ARG_SPACE \
do { \
if (qargc == qarga) { \
......@@ -1788,9 +2066,14 @@ int qemudBuildCommandLine(virConnectPtr conn,
ADD_ARG_LIT("-M");
ADD_ARG_LIT(def->os.machine);
}
if (qemudBuildCommandLineCPU(conn, driver, def, emulator, &ut, &cpu) < 0)
goto error;
if (cpu) {
ADD_ARG_LIT("-cpu");
ADD_ARG_LIT(cpu);
VIR_FREE(cpu);
}
if (disableKQEMU)
......@@ -3429,6 +3712,79 @@ error:
return NULL;
}
static int
qemuParseCommandLineCPU(virConnectPtr conn,
virDomainDefPtr dom,
const char *val)
{
virCPUDefPtr cpu;
const char *p = val;
const char *next;
if (VIR_ALLOC(cpu) < 0)
goto no_memory;
cpu->type = VIR_CPU_TYPE_GUEST;
do {
if (*p == '\0' || *p == ',')
goto syntax;
if ((next = strchr(p, ',')))
next++;
if (!cpu->model) {
if (next)
cpu->model = strndup(p, next - p - 1);
else
cpu->model = strdup(p);
if (!cpu->model)
goto no_memory;
}
else if (*p == '+' || *p == '-') {
char *feature;
int policy;
int ret;
if (*p == '+')
policy = VIR_CPU_FEATURE_REQUIRE;
else
policy = VIR_CPU_FEATURE_DISABLE;
p++;
if (*p == '\0' || *p == ',')
goto syntax;
if (next)
feature = strndup(p, next - p - 1);
else
feature = strdup(p);
ret = virCPUDefAddFeature(conn, cpu, feature, policy);
VIR_FREE(feature);
if (ret < 0)
goto error;
}
} while ((p = next));
dom->cpu = cpu;
return 0;
syntax:
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unknown CPU syntax '%s'"), val);
goto error;
no_memory:
virReportOOMError(conn);
error:
virCPUDefFree(cpu);
return -1;
}
/*
* Analyse the env and argv settings and reconstruct a
* virDomainDefPtr representing these settings as closely
......@@ -3856,6 +4212,10 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn,
_("unknown video adapter type '%s'"), val);
goto error;
}
} else if (STREQ(arg, "-cpu")) {
WANT_VALUE();
if (qemuParseCommandLineCPU(conn, def, val) < 0)
goto error;
} else if (STREQ(arg, "-domid")) {
WANT_VALUE();
/* ignore, generted on the fly */
......
......@@ -37,6 +37,8 @@
#include "security/security_driver.h"
#include "cgroup.h"
#include "pci.h"
#include "cpu_conf.h"
#include "driver.h"
#define qemudDebug(fmt, ...) do {} while(0)
......@@ -203,6 +205,11 @@ int qemudProbeMachineTypes (const char *binary,
virCapsGuestMachinePtr **machines,
int *nmachines);
int qemudProbeCPUModels (const char *qemu,
const char *arch,
unsigned int *count,
const char ***cpus);
int qemudCanonicalizeMachine (struct qemud_driver *driver,
virDomainDefPtr def);
......
......@@ -73,6 +73,7 @@
#include "cgroup.h"
#include "libvirt_internal.h"
#include "xml.h"
#include "cpu/cpu.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
......@@ -2844,24 +2845,19 @@ static int qemudGetMaxVCPUs(virConnectPtr conn, const char *type) {
static char *qemudGetCapabilities(virConnectPtr conn) {
struct qemud_driver *driver = conn->privateData;
virCapsPtr caps;
virCapsPtr caps = NULL;
char *xml = NULL;
qemuDriverLock(driver);
if ((caps = qemudCapsInit(qemu_driver->caps)) == NULL) {
virReportOOMError(conn);
goto cleanup;
}
if ((caps = qemudCapsInit(qemu_driver->caps)) == NULL)
goto no_memory;
caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc;
caps->privateDataFreeFunc = qemuDomainObjPrivateFree;
if (qemu_driver->securityDriver &&
qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0) {
virCapabilitiesFree(caps);
virReportOOMError(conn);
goto cleanup;
}
qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0)
goto no_memory;
virCapabilitiesFree(qemu_driver->caps);
qemu_driver->caps = caps;
......@@ -2873,6 +2869,11 @@ cleanup:
qemuDriverUnlock(driver);
return xml;
no_memory:
virCapabilitiesFree(caps);
virReportOOMError(conn);
goto cleanup;
}
......@@ -7849,6 +7850,28 @@ out:
return ret;
}
static int
qemuCPUCompare(virConnectPtr conn,
const char *xmlDesc,
unsigned int flags ATTRIBUTE_UNUSED)
{
struct qemud_driver *driver = conn->privateData;
int ret = VIR_CPU_COMPARE_ERROR;
qemuDriverLock(driver);
if (!driver->caps || !driver->caps->host.cpu) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
"%s", _("cannot get host CPU capabilities"));
}
else
ret = cpuCompareXML(conn, driver->caps->host.cpu, xmlDesc);
qemuDriverUnlock(driver);
return ret;
}
static virDriver qemuDriver = {
VIR_DRV_QEMU,
"QEMU",
......@@ -7923,7 +7946,7 @@ static virDriver qemuDriver = {
qemuIsSecure,
qemuDomainIsActive,
qemuDomainIsPersistent,
NULL, /* cpuCompare */
qemuCPUCompare, /* cpuCompare */
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册