Am 19.06.2013 22:40, schrieb Anthony Liguori: > The idea here is pretty simple. We have a synchronous interface > that when called, does a migration to a file, kills the QEMU > instance, and spawns a new one using the saved file state. > > We an then sprinkle calls to qtest_save_restore() thorough test > cases to validate that we are properly saving and restoring state. > > Signed-off-by: Anthony Liguori <aligu...@us.ibm.com> > --- > tests/libqtest.c | 65 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/libqtest.h | 46 +++++++++++++++++++++++++++++++++++++++ > 2 files changed, 111 insertions(+) > > diff --git a/tests/libqtest.c b/tests/libqtest.c > index 235ec62..bc2e84e 100644 > --- a/tests/libqtest.c > +++ b/tests/libqtest.c > @@ -44,6 +44,7 @@ struct QTestState > gchar *pid_file; /* QEMU PID file */ > int child_pid; /* Child process created to execute QEMU */ > char *socket_path, *qmp_socket_path; > + char *extra_args; > }; > > #define g_assert_no_errno(ret) do { \ > @@ -104,6 +105,14 @@ static pid_t qtest_qemu_pid(QTestState *s) > return pid; > } > > +void qtest_qmp_wait_event(QTestState *s, const char *event) > +{ > + char *d; > + /* This is cheating */ > + d = qtest_qmp(s, "");
This reminds me that I was unable to use GCC_FMT_ATTR(2, 3) on qtest_qmp() because of the "" argument that gcc would warn about. Otherwise code looks okay, although I'm not too familiar with the events. Regards, Andreas > + g_free(d); > +} > + > QTestState *qtest_init(const char *extra_args) > { > QTestState *s; > @@ -118,6 +127,7 @@ QTestState *qtest_init(const char *extra_args) > > s = g_malloc(sizeof(*s)); > > + s->extra_args = g_strdup(extra_args); > s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); > s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); > pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid()); > @@ -177,6 +187,61 @@ void qtest_quit(QTestState *s) > g_free(s->pid_file); > g_free(s->socket_path); > g_free(s->qmp_socket_path); > + g_free(s->extra_args); > +} > + > +QTestState *qtest_save_restore(QTestState *s) > +{ > + char *filename; > + char *d, *p, *extra_args; > + char *n; > + > + filename = g_strdup_printf("/tmp/qtest-%d.savevm", getpid()); > + > + /* Start migration to a temporary file */ > + d = qtest_qmp(s, > + "{ 'execute': 'migrate', " > + " 'arguments': { 'uri': 'exec:dd of=%s 2>/dev/null' } }", > + filename); > + g_free(d); > + > + /* Wait for critical section to be entered */ > + qtest_qmp_wait_event(s, "STOP"); > + > + /* Not strictly needed as we can't possibly respond to this command until > + * we've completed migration by virtue of the fact that STOP has been > sent > + * but it's good to be rigorious. */ > + do { > + d = qtest_qmp(s, "{ 'execute': 'query-migrate' }"); > + p = strstr(d, "\"status\": \"completed\","); > + g_free(d); > + if (!p) { > + g_usleep(100); > + } > + } while (p == NULL); > + > + /* Save arguments to this qtest instance */ > + extra_args = s->extra_args; > + s->extra_args = NULL; > + > + /* Quit src instance */ > + qtest_quit(s); > + > + /* Spawn destination */ > + n = g_strdup_printf("%s -incoming exec:\"dd if=%s 2>/dev/null\"", > + extra_args, filename); > + s = qtest_init(n); > + > + /* Wait for incoming migration to complete */ > + qtest_qmp_wait_event(s, "RESUME"); > + > + /* Fixup extra arg so we can call repeatedly */ > + g_free(s->extra_args); > + s->extra_args = extra_args; > + > + g_free(filename); > + > + return s; > } > > static void socket_sendf(int fd, const char *fmt, va_list ap) > diff --git a/tests/libqtest.h b/tests/libqtest.h > index 5cdcae7..f2c6e52 100644 > --- a/tests/libqtest.h > +++ b/tests/libqtest.h > @@ -67,6 +67,15 @@ char *qtest_qmp(QTestState *s, const char *fmt, ...); > char *qtest_qmpv(QTestState *s, const char *fmt, va_list ap); > > /** > + * qtest_qmp_wait_event: > + * @s: #QTestState instance to operate on. > + * @event: the event to wait for. > + * > + * Waits for a specific QMP event to occur. > + */ > +void qtest_qmp_wait_event(QTestState *s, const char *event); > + > +/** > * qtest_get_irq: > * @s: #QTestState instance to operate on. > * @num: Interrupt to observe. > @@ -291,6 +300,19 @@ int64_t qtest_clock_step(QTestState *s, int64_t step); > int64_t qtest_clock_set(QTestState *s, int64_t val); > > /** > + * qtest_save_restore: > + * @s: QTest instance to operate on. > + * > + * This function will save and restore the state of the running QEMU > + * instance. If the savevm code is implemented correctly for a device, > + * this function should behave like a nop. If a test case fails because > + * this function is called, the savevm code for the device is broken. > + * > + * Returns: the new QTest instance > + */ > +QTestState *qtest_save_restore(QTestState *s); > + > +/** > * qtest_spapr_hcall9: > * @s: QTestState instance to operate on. > * @nr: The hypercall index > @@ -337,6 +359,17 @@ static inline QTestState *qtest_start(const char *args) > } > > /** > + * qmp_wait_event: > + * @event: the event to wait for. > + * > + * Waits for a specific QMP event to occur. > + */ > +static inline void qmp_wait_event(const char *event) > +{ > + qtest_qmp_wait_event(global_qtest, event); > +} > + > +/** > * qmp: > * @fmt...: QMP message to send to qemu > * > @@ -628,6 +661,19 @@ static inline int64_t clock_set(int64_t val) > return qtest_clock_set(global_qtest, val); > } > > +/** > + * save_restore: > + * > + * This function will save and restore the state of the running QEMU > + * instance. If the savevm code is implemented correctly for a device, > + * this function should behave like a nop. If a test case fails because > + * this function is called, the savevm code for the device is broken. > + */ > +static inline void save_restore(void) > +{ > + global_qtest = qtest_save_restore(global_qtest); > +} > + > static inline uint64_t spapr_hcall0(uint64_t nr) > { > return qtest_spapr_hcall9(global_qtest, nr, 0, 0, 0, 0, 0, 0, 0, 0, 0); > -- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg