[libvirt] [PATCH 0/4] Add timer element to domain clock elements

This patchset implements the XML described in http://www.redhat.com/archives/libvir-list/2010-March/msg00304.html (with one small change - the tickpolicy 'none' was changed to 'delay' after a later discussion). It also implements the qemu backend for those timer types that qemu supports. One thing to point out to 'early adopters': qemu's driftfix option is currently broken, so if you try to specify a tickpolicy=catchup in an rtc timer, the domain will give an error on startup. A patch for that has been posted by Zach Amsden to qemu-devel: http://lists.gnu.org/archive/html/qemu-devel/2010-03/msg02036.html Backends for other hypervisors have not been implemented. That is left as an exercise for later ;-)

This extension is described in http://www.redhat.com/archives/libvir-list/2010-March/msg00304.html Currently all attributes are optional, except name. * src/conf/domain_conf.h: add data definition for virDomainTimerDef and add a list of them to virDomainClockDef * src/conf/domain_conf.c: XML parser and formatter for a timer inside a clock * src/libvirt_private.syms: add new Timer enum helper functions to symbols --- src/conf/domain_conf.c | 238 +++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 58 +++++++++++ src/libvirt_private.syms | 9 ++- 3 files changed, 300 insertions(+), 5 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 651dd04..e864417 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -239,6 +239,29 @@ VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, "variable", "timezone"); +VIR_ENUM_IMPL(virDomainTimerName, VIR_DOMAIN_TIMER_NAME_LAST, + "platform", + "pit", + "rtc", + "hpet", + "tsc"); + +VIR_ENUM_IMPL(virDomainTimerWallclock, VIR_DOMAIN_TIMER_WALLCLOCK_LAST, + "host", + "guest"); + +VIR_ENUM_IMPL(virDomainTimerTickpolicy, VIR_DOMAIN_TIMER_TICKPOLICY_LAST, + "delay", + "catchup", + "merge", + "discard"); + +VIR_ENUM_IMPL(virDomainTimerMode, VIR_DOMAIN_TIMER_MODE_LAST, + "auto", + "native", + "emulate", + "paravirt"); + #define virDomainReportError(code, ...) \ virReportErrorHelper(NULL, VIR_FROM_DOMAIN, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) @@ -597,6 +620,18 @@ void virSecurityLabelDefFree(virDomainDefPtr def) VIR_FREE(def->seclabel.imagelabel); } +static void +virDomainClockDefClear(virDomainClockDefPtr def) +{ + if (def->offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE) + VIR_FREE(def->data.timezone); + + int i; + for (i = 0; i < def->ntimers; i++) + VIR_FREE(def->timers[i]); + VIR_FREE(def->timers); +} + void virDomainDefFree(virDomainDefPtr def) { unsigned int i; @@ -666,8 +701,7 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def->os.bootloader); VIR_FREE(def->os.bootloaderArgs); - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE) - VIR_FREE(def->clock.data.timezone); + virDomainClockDefClear(&def->clock); VIR_FREE(def->name); VIR_FREE(def->cpumask); @@ -2462,6 +2496,107 @@ error: } +/* Parse the XML definition for a clock timer */ +static virDomainTimerDefPtr +virDomainTimerDefParseXML(const xmlNodePtr node, + xmlXPathContextPtr ctxt, + int flags ATTRIBUTE_UNUSED) +{ + char *name = NULL; + char *present = NULL; + char *tickpolicy = NULL; + char *wallclock = NULL; + char *mode = NULL; + + virDomainTimerDefPtr def; + xmlNodePtr oldnode = ctxt->node; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + ctxt->node = node; + + name = virXMLPropString(node, "name"); + if (name == NULL) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing timer name")); + goto error; + } + if ((def->name = virDomainTimerNameTypeFromString(name)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown timer name '%s'"), name); + goto error; + } + + def->present = -1; /* unspecified */ + if ((present = virXMLPropString(node, "present")) != NULL) { + if (STREQ(present, "yes")) { + def->present = 1; + } else if (STREQ(present, "no")) { + def->present = 0; + } else { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown timer present value '%s'"), present); + goto error; + } + } + + def->tickpolicy = -1; + tickpolicy = virXMLPropString(node, "tickpolicy"); + if (tickpolicy != NULL) { + if ((def->tickpolicy = virDomainTimerTickpolicyTypeFromString(tickpolicy)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown timer tickpolicy '%s'"), tickpolicy); + goto error; + } + } + + def->wallclock = -1; + wallclock = virXMLPropString(node, "wallclock"); + if (wallclock != NULL) { + if ((def->wallclock = virDomainTimerWallclockTypeFromString(wallclock)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown timer wallclock '%s'"), wallclock); + goto error; + } + } + + int ret = virXPathULong("string(./frequency)", ctxt, &def->frequency); + if (ret == -1) { + def->frequency = 0; + } else if (ret <= 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid timer frequency")); + goto error; + } + + def->mode = -1; + mode = virXMLPropString(node, "mode"); + if (mode != NULL) { + if ((def->mode = virDomainTimerModeTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown timer mode '%s'"), mode); + goto error; + } + } + +cleanup: + VIR_FREE(name); + VIR_FREE(present); + VIR_FREE(tickpolicy); + VIR_FREE(wallclock); + VIR_FREE(mode); + ctxt->node = oldnode; + + return def; + +error: + VIR_FREE(def); + goto cleanup; +} + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { @@ -3593,7 +3728,6 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, &def->onCrash, VIR_DOMAIN_LIFECYCLE_DESTROY) < 0) goto error; - tmp = virXPathString("string(./clock/@offset)", ctxt); if (tmp) { if ((def->clock.offset = virDomainClockOffsetTypeFromString(tmp)) < 0) { @@ -3622,6 +3756,24 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, break; } + if ((n = virXPathNodeSet("./clock/timer", ctxt, &nodes)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to parse timers")); + goto error; + } + if (n && VIR_ALLOC_N(def->clock.timers, n) < 0) + goto no_memory; + for (i = 0 ; i < n ; i++) { + virDomainTimerDefPtr timer = virDomainTimerDefParseXML(nodes[i], + ctxt, + flags); + if (!timer) + goto error; + + def->clock.timers[def->clock.ntimers++] = timer; + } + VIR_FREE(nodes); + def->os.bootloader = virXPathString("string(./bootloader)", ctxt); def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); @@ -5207,6 +5359,75 @@ virDomainInputDefFormat(virBufferPtr buf, static int +virDomainTimerDefFormat(virBufferPtr buf, + virDomainTimerDefPtr def) +{ + const char *name = virDomainTimerNameTypeToString(def->name); + + if (!name) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected timer name %d"), def->name); + return -1; + } + virBufferVSprintf(buf, " <timer name='%s'", name); + + if (def->present == 0) { + virBufferAddLit(buf, " present='no'"); + } else if (def->present == 1) { + virBufferAddLit(buf, " present='yes'"); + } + + if (def->tickpolicy != -1) { + const char *tickpolicy + = virDomainTimerTickpolicyTypeToString(def->tickpolicy); + if (!tickpolicy) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected timer tickpolicy %d"), + def->tickpolicy); + return -1; + } + virBufferVSprintf(buf, " tickpolicy='%s'", tickpolicy); + } + + if ((def->name == VIR_DOMAIN_TIMER_NAME_PLATFORM) + || (def->name == VIR_DOMAIN_TIMER_NAME_RTC)) { + if (def->wallclock != -1) { + const char *wallclock + = virDomainTimerWallclockTypeToString(def->wallclock); + if (!wallclock) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected timer wallclock %d"), + def->wallclock); + return -1; + } + virBufferVSprintf(buf, " wallclock='%s'", wallclock); + } + } + + if (def->name == VIR_DOMAIN_TIMER_NAME_TSC) { + if (def->frequency > 0) { + virBufferVSprintf(buf, " frequency='%lu'", def->frequency); + } + + if (def->mode != -1) { + const char *mode + = virDomainTimerModeTypeToString(def->mode); + if (!mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected timer mode %d"), + def->mode); + return -1; + } + virBufferVSprintf(buf, " mode='%s'", mode); + } + } + + virBufferAddLit(buf, "/>\n"); + + return 0; +} + +static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, int flags) @@ -5504,7 +5725,16 @@ char *virDomainDefFormat(virDomainDefPtr def, virBufferEscapeString(&buf, " timezone='%s'", def->clock.data.timezone); break; } - virBufferAddLit(&buf, "/>\n"); + if (def->clock.ntimers == 0) { + virBufferAddLit(&buf, "/>\n"); + } else { + virBufferAddLit(&buf, ">\n"); + for (n = 0; n < def->clock.ntimers; n++) { + if (virDomainTimerDefFormat(&buf, def->clock.timers[n]) < 0) + goto cleanup; + } + virBufferAddLit(&buf, " </clock>\n"); + } if (virDomainLifecycleDefFormat(&buf, def->onPoweroff, "on_poweroff") < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 218e0a7..d5c0637 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -633,6 +633,56 @@ struct _virSecurityLabelDef { int type; }; +enum virDomainTimerNameType { + VIR_DOMAIN_TIMER_NAME_PLATFORM = 0, + VIR_DOMAIN_TIMER_NAME_PIT, + VIR_DOMAIN_TIMER_NAME_RTC, + VIR_DOMAIN_TIMER_NAME_HPET, + VIR_DOMAIN_TIMER_NAME_TSC, + + VIR_DOMAIN_TIMER_NAME_LAST, +}; + +enum virDomainTimerWallclockType { + VIR_DOMAIN_TIMER_WALLCLOCK_HOST = 0, + VIR_DOMAIN_TIMER_WALLCLOCK_GUEST, + + VIR_DOMAIN_TIMER_WALLCLOCK_LAST, +}; + +enum virDomainTimerTickpolicyType { + VIR_DOMAIN_TIMER_TICKPOLICY_DELAY = 0, + VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP, + VIR_DOMAIN_TIMER_TICKPOLICY_MERGE, + VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD, + + VIR_DOMAIN_TIMER_TICKPOLICY_LAST, +}; + +enum virDomainTimerModeType { + VIR_DOMAIN_TIMER_MODE_AUTO = 0, + VIR_DOMAIN_TIMER_MODE_NATIVE, + VIR_DOMAIN_TIMER_MODE_EMULATE, + VIR_DOMAIN_TIMER_MODE_PARAVIRT, + + VIR_DOMAIN_TIMER_MODE_LAST, +}; + +typedef struct _virDomainTimerDef virDomainTimerDef; +typedef virDomainTimerDef *virDomainTimerDefPtr; +struct _virDomainTimerDef { + int name; + int present; /* unspecified = -1, no = 0, yes = 1 */ + int tickpolicy; /* none|catchup|merge|discard */ + + /* wallclock is only valid for name='platform|rtc' */ + int wallclock; /* host|guest */ + + /* frequency & mode are only valid for name='tsc' */ + unsigned long frequency; /* in Hz, unspecified = 0 */ + int mode; /* auto|native|emulate|paravirt */ +}; + enum virDomainClockOffsetType { VIR_DOMAIN_CLOCK_OFFSET_UTC = 0, VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME = 1, @@ -656,6 +706,9 @@ struct _virDomainClockDef { * offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME */ char *timezone; } data; + + int ntimers; + virDomainTimerDefPtr *timers; }; # define VIR_DOMAIN_CPUMASK_LEN 1024 @@ -958,4 +1011,9 @@ VIR_ENUM_DECL(virDomainClockOffset) VIR_ENUM_DECL(virDomainNetdevMacvtap) +VIR_ENUM_DECL(virDomainTimerName) +VIR_ENUM_DECL(virDomainTimerWallclock) +VIR_ENUM_DECL(virDomainTimerTickpolicy) +VIR_ENUM_DECL(virDomainTimerMode) + #endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0033d2a..83059ad 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -192,7 +192,14 @@ virDomainDefClearDeviceAliases; virDomainDeviceInfoIterate; virDomainClockOffsetTypeToString; virDomainClockOffsetTypeFromString; - +virDomainTimerNameTypeToString; +virDomainTimerNameTypeFromString; +virDomainTimerWallclockTypeToString; +virDomainTimerWallclockTypeFromString; +virDomainTimerTickpolicyTypeToString; +virDomainTimerTickpolicyTypeFromString; +virDomainTimerModeTypeToString; +virDomainTimerModeTypeFromString; # domain_event.h virDomainEventCallbackListAdd; -- 1.6.6.1

timers are sub-elements of clocks. A clock can have zero or more instances of timer. Within the timer, only the name attribute is required; all other attributes are optional. A simpler representation of a timer element is: <timer name='platform|pit|rtc|hpet|tsc' wallclock='host|guest' tickpolicy='delay|catchup|merge|discard' frequency='123' mode='auto|native|emulate|paravirt' present='yes|no'/> frequency is a ulong. All other attributes are simple enums. --- docs/schemas/domain.rng | 60 ++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 59 insertions(+), 1 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 5a8c82b..1b80344 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -295,6 +295,9 @@ </interleave> </define> <define name="clock"> + <zeroOrMore> + <ref name="timer"/> + </zeroOrMore> <optional> <element name="clock"> <choice> @@ -329,7 +332,62 @@ </element> </optional> </define> - <!-- + <define name="timer"> + <element name="timer"> + <attribute name="name"> + <choice> + <value>platform</value> + <value>pit</value> + <value>rtc</value> + <value>hpet</value> + <value>tsc</value> + </choice> + </attribute> + <optional> + <attribute name="wallclock"> + <choice> + <value>host</value> + <value>guest</value> + </choice> + </attribute> + </optional> + <optional> + <attribute name="tickpolicy"> + <choice> + <value>delay</value> + <value>catchup</value> + <value>merge</value> + <value>discard</value> + </choice> + </attribute> + </optional> + <optional> + <attribute name="frequency"> + <ref name="unsignedInt"/> + </attribute> + </optional> + <optional> + <attribute name="mode"> + <choice> + <value>auto</value> + <value>native</value> + <value>emulate</value> + <value>paravirt</value> + </choice> + </attribute> + </optional> + <optional> + <attribute name="present"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> + <empty/> + </element> + </define> +<!-- A bootloader may be used to extract the OS information instead of defining the OS parameter in the instance. It points just to the binary or script used to extract the data from the first disk device. -- 1.6.6.1

* src/qemu/qemu_conf.h: define 4 new flags * src/qemu/qemu_conf.c: check the help text of qemu for presence of features indicated by each flag. * tests/qemuhelptest.c: add appropriate flags into the masks for each test --- src/qemu/qemu_conf.c | 10 ++++++++++ src/qemu/qemu_conf.h | 4 ++++ tests/qemuhelptest.c | 28 ++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b1cc834..e9197db 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1159,6 +1159,16 @@ static unsigned long long qemudComputeCmdFlags(const char *help, /* 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; + /* Keep disabled till we're actually ready to turn on netdev mode * The plan is todo it in 0.13.0 QEMU, but lets wait & see... */ #if 0 diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 2d62cc4..39518ca 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -84,6 +84,10 @@ enum qemud_cmd_flags { 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 = (1 << 31), /* vnet-host support is available in qemu */ + QEMUD_CMD_FLAG_RTC_TD_HACK = (1LL << 32), /* -rtd-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) */ }; /* Main driver state */ diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index dfdac75..b8c4b91 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -126,7 +126,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO | QEMUD_CMD_FLAG_KVM | QEMUD_CMD_FLAG_DRIVE_FORMAT | - QEMUD_CMD_FLAG_MEM_PATH, + QEMUD_CMD_FLAG_MEM_PATH | + QEMUD_CMD_FLAG_TDF, 9001, 1, 74); DO_TEST("qemu-0.10.5", QEMUD_CMD_FLAG_KQEMU | @@ -143,7 +144,9 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_0_10 | QEMUD_CMD_FLAG_ENABLE_KVM | - QEMUD_CMD_FLAG_SDL, + QEMUD_CMD_FLAG_SDL | + QEMUD_CMD_FLAG_RTC_TD_HACK | + QEMUD_CMD_FLAG_NO_HPET, 10005, 0, 0); DO_TEST("qemu-kvm-0.10.5", QEMUD_CMD_FLAG_VNC_COLON | @@ -163,7 +166,11 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_0_10 | QEMUD_CMD_FLAG_PCIDEVICE | QEMUD_CMD_FLAG_MEM_PATH | - QEMUD_CMD_FLAG_SDL, + QEMUD_CMD_FLAG_SDL | + QEMUD_CMD_FLAG_RTC_TD_HACK | + QEMUD_CMD_FLAG_NO_HPET | + QEMUD_CMD_FLAG_NO_KVM_PIT | + QEMUD_CMD_FLAG_TDF, 10005, 1, 0); DO_TEST("kvm-86", QEMUD_CMD_FLAG_VNC_COLON | @@ -182,7 +189,11 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_0_10 | QEMUD_CMD_FLAG_PCIDEVICE | - QEMUD_CMD_FLAG_SDL, + QEMUD_CMD_FLAG_SDL | + QEMUD_CMD_FLAG_RTC_TD_HACK | + QEMUD_CMD_FLAG_NO_HPET | + QEMUD_CMD_FLAG_NO_KVM_PIT | + QEMUD_CMD_FLAG_TDF, 10050, 1, 0); DO_TEST("qemu-kvm-0.11.0-rc2", QEMUD_CMD_FLAG_VNC_COLON | @@ -204,7 +215,11 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_MEM_PATH | QEMUD_CMD_FLAG_ENABLE_KVM | QEMUD_CMD_FLAG_BALLOON | - QEMUD_CMD_FLAG_SDL, + QEMUD_CMD_FLAG_SDL | + QEMUD_CMD_FLAG_RTC_TD_HACK | + QEMUD_CMD_FLAG_NO_HPET | + QEMUD_CMD_FLAG_NO_KVM_PIT | + QEMUD_CMD_FLAG_TDF, 10092, 1, 0); DO_TEST("qemu-0.12.1", QEMUD_CMD_FLAG_VNC_COLON | @@ -227,7 +242,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_BALLOON | QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_SMP_TOPOLOGY | - QEMUD_CMD_FLAG_RTC, + QEMUD_CMD_FLAG_RTC | + QEMUD_CMD_FLAG_NO_HPET, 12001, 0, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 1.6.6.1

Since the timers are defined to cover all possible config cases for several different hypervisors, many of these possibilities generate an error on qemu. Here is what is currently supported: RTC: If the -rtc commandline option is available, allow setting "clock=host" or "clock=vm" based on the rtc timer clock='host|guest' value. Also add "driftfix=slew" if the tickpolicy is 'catchup', or add nothing if tickpolicy is 'delay'. (Other tickpolicies will raise an error). If -rtc isn't available, but -rtc-td-hack is, add that option if the tickpolicy is 'catchup', add -rtc-td-hack, if it is 'delay' add nothing, and if it's anything else, raise an error. PIT: If -no-kvm-pit-reinjection is available, and tickpolicy is 'delay', add that option. if tickpolicy is 'catchup', do nothing. Anything else --> raise an error. If -no-kvm-pit-reinjection *isn't* available, but -tdf is, when tickpolicy is 'catchup' add -tdf. If it's 'delay', do nothing. Anything else --> raise an error. If neither of those commandline options is available, and tickpolicy is anything other than 'delay' (or unspecified), raise an error. HPET: If -no-hpet flag is available and present='no', add -no-hpet. If -no-hpet is not available, and present='yes', raise an error. If present is unspecified, the default is to do whatever this particular qemu does by default, so don't raise an error. All other timer types are unsupported by QEMU, so they will raise an error. --- src/qemu/qemu_conf.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 129 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e9197db..ad0468d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3149,6 +3149,36 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) goto error; } + /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */ + int i; + for (i = 0; i < def->ntimers; i++) { + if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) { + if (def->timers[i]->wallclock == VIR_DOMAIN_TIMER_WALLCLOCK_HOST) { + virBufferAddLit(&buf, ",clock=host"); + } else if (def->timers[i]->wallclock == VIR_DOMAIN_TIMER_WALLCLOCK_GUEST) { + virBufferAddLit(&buf, ",clock=vm"); + } + switch (def->timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* This is the default - missed ticks delivered when + next scheduled, at normal rate */ + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + /* deliver ticks at a faster rate until caught up */ + virBufferAddLit(&buf, ",driftfix=slew"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc timer tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy)); + goto error; + } + break; /* no need to check other timers - there is only one rtc */ + } + } + if (virBufferError(&buf)) { virReportOOMError(); goto error; @@ -3622,6 +3652,105 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ENV_PAIR("TZ", def->clock.data.timezone); } + for (i = 0; i < def->clock.ntimers; i++) { + switch (def->clock.timers[i]->name) { + default: + case VIR_DOMAIN_TIMER_NAME_PLATFORM: + case VIR_DOMAIN_TIMER_NAME_TSC: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported timer type (name) '%s'"), + virDomainTimerNameTypeToString(def->clock.timers[i]->name)); + goto error; + + case VIR_DOMAIN_TIMER_NAME_RTC: + /* This has already been taken care of (in qemuBuildClockArgStr) + if QEMUD_CMD_FLAG_RTC is set (mutually exclusive with + QEMUD_FLAG_RTC_TD_HACK) */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC_TD_HACK) { + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* the default - do nothing */ + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + ADD_ARG_LIT("-rtc-td-hack"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_RTC) + && (def->clock.timers[i]->tickpolicy + != VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) + && (def->clock.timers[i]->tickpolicy != -1)) { + /* a non-default rtc policy was given, but there is no + way to implement it in this version of qemu */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + + case VIR_DOMAIN_TIMER_NAME_PIT: + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* delay is the default if we don't have kernel + (-no-kvm-pit), otherwise, the default is catchup. */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) + ADD_ARG_LIT("-no-kvm-pit-reinjection"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) { + /* do nothing - this is default for kvm-pit */ + } else if (qemuCmdFlags & QEMUD_CMD_FLAG_TDF) { + /* -tdf switches to 'catchup' with userspace pit. */ + ADD_ARG_LIT("-tdf"); + } else { + /* can't catchup if we have neither pit mode */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + /* no way to support these modes for pit in qemu */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + + case VIR_DOMAIN_TIMER_NAME_HPET: + /* the only meaningful attribute for hpet is "present". If + * present is -1, that means it wasn't specified, and + * should be left at the default for the + * hypervisor. "default" when -no-hpet exists is "yes", + * and when -no-hpet doesn't exist is "no". "confusing"? + * "yes"! */ + + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_HPET) { + if (def->clock.timers[i]->present == 0) + ADD_ARG_LIT("-no-hpet"); + } else { + /* no hpet timer available. The only possible action + is to raise an error if present="yes" */ + if (def->clock.timers[i]->present == 1) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("pit timer is not supported")); + } + } + break; + } + } + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) ADD_ARG_LIT("-no-reboot"); -- 1.6.6.1

On Fri, Mar 26, 2010 at 02:21:36PM -0400, Laine Stump wrote:
This patchset implements the XML described in
http://www.redhat.com/archives/libvir-list/2010-March/msg00304.html
(with one small change - the tickpolicy 'none' was changed to 'delay' after a later discussion).
It also implements the qemu backend for those timer types that qemu supports.
One thing to point out to 'early adopters': qemu's driftfix option is currently broken, so if you try to specify a tickpolicy=catchup in an rtc timer, the domain will give an error on startup. A patch for that has been posted by Zach Amsden to qemu-devel:
http://lists.gnu.org/archive/html/qemu-devel/2010-03/msg02036.html
Backends for other hypervisors have not been implemented. That is left as an exercise for later ;-)
Okay, patch set looks fine so applied and commited. One thing we miss is documentation now, so hopefully you can add description to the domain format page this week, thanks ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (2)
-
Daniel Veillard
-
Laine Stump