From patchwork Wed Jun 19 20:40:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 252658 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A2D212C009C for ; Thu, 20 Jun 2013 06:44:33 +1000 (EST) Received: from localhost ([::1]:52786 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UpPF1-0004ja-P0 for incoming@patchwork.ozlabs.org; Wed, 19 Jun 2013 16:44:31 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:33715) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UpPCc-0001J7-Hx for qemu-devel@nongnu.org; Wed, 19 Jun 2013 16:42:04 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UpPCa-0005FE-40 for qemu-devel@nongnu.org; Wed, 19 Jun 2013 16:42:02 -0400 Received: from e39.co.us.ibm.com ([32.97.110.160]:52514) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UpPCZ-0005Ei-On for qemu-devel@nongnu.org; Wed, 19 Jun 2013 16:41:59 -0400 Received: from /spool/local by e39.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 19 Jun 2013 14:41:59 -0600 Received: from d01dlp01.pok.ibm.com (9.56.250.166) by e39.co.us.ibm.com (192.168.1.139) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 19 Jun 2013 14:41:56 -0600 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id 0C6D338C8065; Wed, 19 Jun 2013 16:41:55 -0400 (EDT) Received: from d03av03.boulder.ibm.com (d03av03.boulder.ibm.com [9.17.195.169]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r5JKeoYm274628; Wed, 19 Jun 2013 16:40:51 -0400 Received: from d03av03.boulder.ibm.com (loopback [127.0.0.1]) by d03av03.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r5JKen33022244; Wed, 19 Jun 2013 14:40:49 -0600 Received: from titi.austin.rr.com ([9.49.221.0]) by d03av03.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id r5JKeaBc020317; Wed, 19 Jun 2013 14:40:47 -0600 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Wed, 19 Jun 2013 15:40:27 -0500 Message-Id: <1371674435-14973-5-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.8.0 In-Reply-To: <1371674435-14973-1-git-send-email-aliguori@us.ibm.com> References: <1371674435-14973-1-git-send-email-aliguori@us.ibm.com> X-TM-AS-MML: No X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13061920-3620-0000-0000-0000033485EA X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 32.97.110.160 Cc: Anthony Liguori , Alexey Kardashevskiy , Alex Graf , qemu-ppc@nongnu.org, Paul Mackerras , Andreas Faerber Subject: [Qemu-devel] [PATCH 04/12] qtest: add interface to save/restore X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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, ""); + 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);