From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59179) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XAhc5-0000Of-D5 for qemu-devel@nongnu.org; Fri, 25 Jul 2014 11:41:02 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XAhbw-0001QL-6C for qemu-devel@nongnu.org; Fri, 25 Jul 2014 11:40:53 -0400 Received: from mail-pa0-x233.google.com ([2607:f8b0:400e:c03::233]:42956) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XAhbv-0001QB-Pg for qemu-devel@nongnu.org; Fri, 25 Jul 2014 11:40:44 -0400 Received: by mail-pa0-f51.google.com with SMTP id ey11so6326066pad.10 for ; Fri, 25 Jul 2014 08:40:42 -0700 (PDT) From: Sanidhya Kashyap Date: Fri, 25 Jul 2014 21:09:29 +0530 Message-Id: <1406302776-2306-6-git-send-email-sanidhya.iiith@gmail.com> In-Reply-To: <1406302776-2306-1-git-send-email-sanidhya.iiith@gmail.com> References: <1406302776-2306-1-git-send-email-sanidhya.iiith@gmail.com> Subject: [Qemu-devel] [PATCH RFC v2 05/12] VMstate test: basic VMState testing mechanism List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu list Cc: Sanidhya Kashyap , "Dr. David Alan Gilbert" , Juan Quintela In this patch, I have made the following changes: * changed the DPRINT statement. * renamed the variables. * added noqdev variable which decides which option to use for resetting. * added devices option which can help in resetting one or many devices (only qdevified ones). * updated the documentation. Signed-off-by: Sanidhya Kashyap --- qapi-schema.json | 26 ++++++ qmp-commands.hx | 37 ++++++++ savevm.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+) diff --git a/qapi-schema.json b/qapi-schema.json index 996e6b5..ec48977 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3502,3 +3502,29 @@ ## { 'command': 'query-qdev-devices', 'returns': 'VMStatesQdevDevices' } + +## +# @test-vmstates +# +# tests the vmstates' value by dumping and loading in memory +# +# @iterations: (optional) The total iterations for vmstate testing. +# The min and max defind value is 10 and 100 respectively. +# +# @period: (optional) sleep interval between iteration (in milliseconds). +# The default interval is 100 milliseconds with min and max being +# 1 and 10000 respectively. +# +# @noqdev: boolean variable which decides whether to use qdevified devices +# or not. Will be removed when all the devices have been qdevified. +# +# @devices: (optional) helps in resetting particular qdevified decices +# that have been registered with SaveStateEntry +# +# Since 2.2 +## +{ 'command': 'test-vmstates', + 'data': {'*iterations': 'int', + '*period': 'int', + 'noqdev': 'bool', + '*qdevices': 'VMStatesQdevDevices' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 2e20032..6210f56 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3778,5 +3778,42 @@ Example (1): } ] } +EQMP + + { + .name = "test-vmstates", + .args_type = "iterations:i?,period:i?,noqdev:b,qdevices:O?", + .mhandler.cmd_new = qmp_marshal_input_test_vmstates, + }, + +SQMP +test-vmstates +------------- + +Tests the vmstates' entry by dumping and loading in/from memory + +Arguments: +- "iterations": (optional) The total iterations for vmstate testing. + The min and max defined value is 10 and 100 respectively. + +- "period": (optional) sleep interval between iteration (in milliseconds). + The default interval is 100 milliseconds with min and max being + 1 and 10000 respectively. + +- "noqdev": boolean variable which decides whether to use qdev or not. + Will be removed when all the devices have been qdevified. + +- "devices": (optional) helps in resetting particular qdevified decices + that have been registered with SaveStateEntry + + +Example: + +-> { "execute": "test-vmstates", + "arguments": { + "iterations": 10, + "period": 100, + "noqdev": false } } +<- { "return": {} } EQMP diff --git a/savevm.c b/savevm.c index 7c1600a..304d8fc 100644 --- a/savevm.c +++ b/savevm.c @@ -1201,6 +1201,257 @@ VMStatesQdevDevices *qmp_query_qdev_devices(Error **errp) return qdev_devices; } +#define DEBUG_TEST_VMSTATES 1 + +#ifndef DEBUG_TEST_VMSTATES +#define DEBUG_TEST_VMSTATES 0 +#endif + +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_TEST_VMSTATES) { \ + printf("vmstate_test: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TEST_VMSTATE_MIN_TIMES 10 +#define TEST_VMSTATE_MAX_TIMES 1000 + +#define TEST_VMSTATE_MIN_INTERVAL_MS 1 +#define TEST_VMSTATE_DEFAULT_INTERVAL_MS 100 +#define TEST_VMSTATE_MAX_INTERVAL_MS 10000 + +typedef struct VMStateLogState VMStateLogState; + +struct VMStateLogState { + int64_t current_iteration; + int64_t iterations; + int64_t period; + bool active_state; + bool noqdev; + VMStatesQdevDevices *qdevices; + QEMUTimer *timer; + + QTAILQ_HEAD(qdev_list, VMStatesQdevResetEntry) qdev_list; +}; + +static VMStateLogState *vmstate_current_state(void) +{ + static VMStateLogState current_state = { + .active_state = false, + }; + + return ¤t_state; +} + +static inline void test_vmstates_clear_qdev_entries(VMStateLogState *v) +{ + VMStatesQdevResetEntry *qre, *new_qre; + QTAILQ_FOREACH_SAFE(qre, &v->qdev_list, entry, new_qre) { + QTAILQ_REMOVE(&v->qdev_list, qre, entry); + } +} + +static inline bool check_device_name(VMStateLogState *v, + VMStatesQdevDevices *qdevices, + Error **errp) +{ + VMStatesQdevResetEntry *qre; + strList *devices_name = qdevices->device; + QTAILQ_INIT(&v->qdev_list); + bool device_present; + + /* now, checking against each one */ + for (; devices_name; devices_name = devices_name->next) { + device_present = false; + VMStatesQdevResetEntry *new_qre; + QTAILQ_FOREACH(qre, &vmstate_reset_handlers, entry) { + if (!strcmp(qre->device_name, devices_name->value)) { + + device_present = true; + + new_qre = g_malloc0(sizeof(VMStatesQdevResetEntry)); + new_qre->dev = qre->dev; + strcpy(new_qre->device_name, qre->device_name); + QTAILQ_INSERT_TAIL(&v->qdev_list, new_qre, entry); + + break; + } + } + if (!device_present) { + test_vmstates_clear_qdev_entries(v); + error_setg(errp, "Incorrect device name - %s\n", + devices_name->value); + return false; + } + } + return true; +} + +static inline void test_vmstates_reset_devices(VMStateLogState *v) +{ + VMStatesQdevResetEntry *qre; + + if (v->noqdev) { + DPRINTF("resetting all devices\n"); + qemu_system_reset(VMRESET_SILENT); + } else if (!v->qdevices) { + QTAILQ_FOREACH(qre, &vmstate_reset_handlers, entry) { + DPRINTF("resetting device: %s\n", qre->device_name); + device_reset(qre->dev); + } + } else { + QTAILQ_FOREACH(qre, &v->qdev_list, entry) { + DPRINTF("resetting device: %s\n", qre->device_name); + device_reset(qre->dev); + } + } +} + +static void vmstate_test_cb(void *opaque) +{ + VMStateLogState *v = opaque; + int saved_vm_running = runstate_is_running(); + const QEMUSizedBuffer *qsb; + QEMUFile *f; + int ret; + int64_t save_vmstates_duration, load_vmstates_duration; + int64_t start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* executing the steps for a single time with the help of timer */ + if (++(v->current_iteration) <= v->iterations) { + saved_vm_running = runstate_is_running(); + + /* stopping the VM before dumping the vmstates */ + vm_stop(RUN_STATE_SAVE_VM); + + f = qemu_bufopen("w", NULL); + if (!f) { + goto testing_end; + } + + cpu_synchronize_all_states(); + + /* saving the vmsates to memory buffer */ + ret = qemu_save_device_state(f); + if (ret < 0) { + goto testing_end; + } + save_vmstates_duration = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + start_time; + DPRINTF("iteration: %ld, save time (ms): %ld\n", + v->current_iteration, save_vmstates_duration); + + /* clearing the states of the guest */ + test_vmstates_reset_devices(v); + + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + qsb = qemu_buf_get(f); + f = qemu_bufopen("r", (QEMUSizedBuffer *)qsb); + if (!f) { + goto testing_end; + } + + /* loading the device states from the saved buffer */ + ret = qemu_loadvm_state(f); + qemu_fclose(f); + if (ret < 0) { + goto testing_end; + } + load_vmstates_duration = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + start_time; + DPRINTF("iteration: %ld, load time (ms): %ld\n", + v->current_iteration, load_vmstates_duration); + + if (saved_vm_running) { + vm_start(); + } + timer_mod(v->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + + v->period); + return; + } + + testing_end: + if (saved_vm_running) { + vm_start(); + } + timer_del(v->timer); + timer_free(v->timer); + test_vmstates_clear_qdev_entries(v); + v->active_state = false; + return; +} + +void qmp_test_vmstates(bool has_iterations, int64_t iterations, + bool has_period, int64_t period, bool noqdev, + bool has_qdevices, VMStatesQdevDevices *qdevices, + Error **errp) +{ + VMStateLogState *v = vmstate_current_state(); + Error *local_err; + + if (v->active_state) { + error_setg(errp, "VMState testing already in progress\n"); + return; + } + + v->active_state = true; + + /* checking the value of total iterations to be in the defined range */ + if (!has_iterations) { + v->iterations = TEST_VMSTATE_MIN_TIMES; + } else if (iterations >= TEST_VMSTATE_MIN_TIMES && + iterations <= TEST_VMSTATE_MAX_TIMES) { + v->iterations = iterations; + } else { + error_setg(errp, "iterations value must be in the range [%d, %d]\n", + TEST_VMSTATE_MIN_TIMES, TEST_VMSTATE_MAX_TIMES); + v->active_state = false; + return; + } + + /* checking for the value of period to be in the defined range */ + if (!has_period) { + v->period = TEST_VMSTATE_DEFAULT_INTERVAL_MS; + } else if (period >= TEST_VMSTATE_MIN_INTERVAL_MS && + period <= TEST_VMSTATE_MAX_INTERVAL_MS) { + v->period = period; + } else { + error_setg(errp, "sleep interval (period) value must be " + "in the defined range [%d, %d](ms)\n", + TEST_VMSTATE_MIN_INTERVAL_MS, TEST_VMSTATE_MAX_INTERVAL_MS); + v->active_state = false; + return; + } + + /* + * checking the devices information + * if no devices have been selected, then all the devices will be tested + * noqdev -> if true -- then use qemu_system_reset + * -> if false -- then use qdevified devices + */ + if (noqdev && has_qdevices) { + error_setg(errp, "either qdev or non-qdev devices allowed, not both\n"); + return; + } else if (!noqdev && !has_qdevices) { + v->qdevices = NULL; + } else if (has_qdevices) { + if (check_device_name(v, qdevices, &local_err)) { + v->qdevices = qdevices; + } else { + if (local_err) { + error_propagate(errp, local_err); + } + return; + } + } + + v->noqdev = noqdev; + v->current_iteration = 0; + v->timer = timer_new_ms(QEMU_CLOCK_REALTIME, vmstate_test_cb, v); + timer_mod(v->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); +} + void qmp_xen_save_devices_state(const char *filename, Error **errp) { QEMUFile *f; -- 1.9.3