Merge remote-tracking branch 'stefanha/block' into staging

# By Kevin Wolf (19) and others
# Via Stefan Hajnoczi
* stefanha/block: (26 commits)
  hmp: add parameters device and -v for info block
  hmp: show ImageInfo in 'info block'
  qmp: add ImageInfo in BlockDeviceInfo used by query-block
  block: add image info query function bdrv_query_image_info()
  block: add snapshot info query function bdrv_query_snapshot_info_list()
  ide-test: Add FLUSH CACHE test case
  ide: Set BSY bit during FLUSH
  ide-test: Add enum value for DEV
  blkdebug: Add BLKDBG_FLUSH_TO_OS/DISK events
  Make qemu-io commands available in HMP
  qemu-io: Use the qemu version for -V
  qemu-io: Interface cleanup
  qemu-io: Move remaining helpers from cmd.c
  qemu-io: Move command_loop() and friends
  qemu-io: Move functions for registering and running commands
  qemu-io: Move qemu_strsep() to cutils.c
  qemu-io: Move 'quit' function
  qemu-io: Move 'help' function
  qemu-io: Factor out qemuio_command
  qemu-io: Split off commands to qemu-io-cmds.c
  ...

Message-id: 1370606325-10680-1-git-send-email-stefanha@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2013-06-07 08:40:52 -05:00
commit 7387de16d0
25 changed files with 2715 additions and 2561 deletions

View File

@ -186,7 +186,7 @@ qemu-img.o: qemu-img-cmds.h
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o

View File

@ -13,6 +13,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o
block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-$(CONFIG_WIN32) += aio-win32.o
block-obj-y += block/ block-obj-y += block/
block-obj-y += qapi-types.o qapi-visit.o block-obj-y += qapi-types.o qapi-visit.o
block-obj-y += qemu-io-cmds.o
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
block-obj-y += qemu-coroutine-sleep.o block-obj-y += qemu-coroutine-sleep.o

View File

@ -3186,13 +3186,11 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
{ {
BlockDriver *drv = bs->drv; if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
if (!drv || !drv->bdrv_debug_event) {
return; return;
} }
drv->bdrv_debug_event(bs, event); bs->drv->bdrv_debug_event(bs, event);
} }
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
@ -4024,6 +4022,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
} }
/* Write back cached data to the OS even with cache=unsafe */ /* Write back cached data to the OS even with cache=unsafe */
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS);
if (bs->drv->bdrv_co_flush_to_os) { if (bs->drv->bdrv_co_flush_to_os) {
ret = bs->drv->bdrv_co_flush_to_os(bs); ret = bs->drv->bdrv_co_flush_to_os(bs);
if (ret < 0) { if (ret < 0) {
@ -4036,6 +4035,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
goto flush_parent; goto flush_parent;
} }
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK);
if (bs->drv->bdrv_co_flush_to_disk) { if (bs->drv->bdrv_co_flush_to_disk) {
ret = bs->drv->bdrv_co_flush_to_disk(bs); ret = bs->drv->bdrv_co_flush_to_disk(bs);
} else if (bs->drv->bdrv_aio_flush) { } else if (bs->drv->bdrv_aio_flush) {

View File

@ -182,6 +182,9 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
[BLKDBG_CLUSTER_FREE] = "cluster_free", [BLKDBG_CLUSTER_FREE] = "cluster_free",
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
}; };
static int get_event_by_name(const char *name, BlkDebugEvent *event) static int get_event_by_name(const char *name, BlkDebugEvent *event)

View File

@ -26,29 +26,56 @@
#include "block/block_int.h" #include "block/block_int.h"
#include "qmp-commands.h" #include "qmp-commands.h"
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info) /*
* Returns 0 on success, with *p_list either set to describe snapshot
* information, or NULL because there are no snapshots. Returns -errno on
* error, with *p_list untouched.
*/
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
SnapshotInfoList **p_list,
Error **errp)
{ {
int i, sn_count; int i, sn_count;
QEMUSnapshotInfo *sn_tab = NULL; QEMUSnapshotInfo *sn_tab = NULL;
SnapshotInfoList *info_list, *cur_item = NULL; SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL;
SnapshotInfo *info;
sn_count = bdrv_snapshot_list(bs, &sn_tab); sn_count = bdrv_snapshot_list(bs, &sn_tab);
if (sn_count < 0) {
const char *dev = bdrv_get_device_name(bs);
switch (sn_count) {
case -ENOMEDIUM:
error_setg(errp, "Device '%s' is not inserted", dev);
break;
case -ENOTSUP:
error_setg(errp,
"Device '%s' does not support internal snapshots",
dev);
break;
default:
error_setg_errno(errp, -sn_count,
"Can't list snapshots of device '%s'", dev);
break;
}
return sn_count;
}
for (i = 0; i < sn_count; i++) { for (i = 0; i < sn_count; i++) {
info->has_snapshots = true; info = g_new0(SnapshotInfo, 1);
info_list = g_new0(SnapshotInfoList, 1); info->id = g_strdup(sn_tab[i].id_str);
info->name = g_strdup(sn_tab[i].name);
info->vm_state_size = sn_tab[i].vm_state_size;
info->date_sec = sn_tab[i].date_sec;
info->date_nsec = sn_tab[i].date_nsec;
info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
info_list->value = g_new0(SnapshotInfo, 1); info_list = g_new0(SnapshotInfoList, 1);
info_list->value->id = g_strdup(sn_tab[i].id_str); info_list->value = info;
info_list->value->name = g_strdup(sn_tab[i].name);
info_list->value->vm_state_size = sn_tab[i].vm_state_size;
info_list->value->date_sec = sn_tab[i].date_sec;
info_list->value->date_nsec = sn_tab[i].date_nsec;
info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
/* XXX: waiting for the qapi to support qemu-queue.h types */ /* XXX: waiting for the qapi to support qemu-queue.h types */
if (!cur_item) { if (!cur_item) {
info->snapshots = cur_item = info_list; head = cur_item = info_list;
} else { } else {
cur_item->next = info_list; cur_item->next = info_list;
cur_item = info_list; cur_item = info_list;
@ -57,20 +84,40 @@ void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info)
} }
g_free(sn_tab); g_free(sn_tab);
*p_list = head;
return 0;
} }
void bdrv_collect_image_info(BlockDriverState *bs, /**
ImageInfo *info, * bdrv_query_image_info:
const char *filename) * @bs: block device to examine
* @p_info: location to store image information
* @errp: location to store error information
*
* Store "flat" image information in @p_info.
*
* "Flat" means it does *not* query backing image information,
* i.e. (*pinfo)->has_backing_image will be set to false and
* (*pinfo)->backing_image to NULL even when the image does in fact have
* a backing image.
*
* @p_info will be set only on success. On error, store error in @errp.
*/
void bdrv_query_image_info(BlockDriverState *bs,
ImageInfo **p_info,
Error **errp)
{ {
uint64_t total_sectors; uint64_t total_sectors;
char backing_filename[1024]; const char *backing_filename;
char backing_filename2[1024]; char backing_filename2[1024];
BlockDriverInfo bdi; BlockDriverInfo bdi;
int ret;
Error *err = NULL;
ImageInfo *info = g_new0(ImageInfo, 1);
bdrv_get_geometry(bs, &total_sectors); bdrv_get_geometry(bs, &total_sectors);
info->filename = g_strdup(filename); info->filename = g_strdup(bs->filename);
info->format = g_strdup(bdrv_get_format_name(bs)); info->format = g_strdup(bdrv_get_format_name(bs));
info->virtual_size = total_sectors * 512; info->virtual_size = total_sectors * 512;
info->actual_size = bdrv_get_allocated_file_size(bs); info->actual_size = bdrv_get_allocated_file_size(bs);
@ -87,7 +134,7 @@ void bdrv_collect_image_info(BlockDriverState *bs,
info->dirty_flag = bdi.is_dirty; info->dirty_flag = bdi.is_dirty;
info->has_dirty_flag = true; info->has_dirty_flag = true;
} }
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); backing_filename = bs->backing_file;
if (backing_filename[0] != '\0') { if (backing_filename[0] != '\0') {
info->backing_filename = g_strdup(backing_filename); info->backing_filename = g_strdup(backing_filename);
info->has_backing_filename = true; info->has_backing_filename = true;
@ -105,11 +152,37 @@ void bdrv_collect_image_info(BlockDriverState *bs,
info->has_backing_filename_format = true; info->has_backing_filename_format = true;
} }
} }
ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err);
switch (ret) {
case 0:
if (info->snapshots) {
info->has_snapshots = true;
}
break;
/* recoverable error */
case -ENOMEDIUM:
case -ENOTSUP:
error_free(err);
break;
default:
error_propagate(errp, err);
qapi_free_ImageInfo(info);
return;
}
*p_info = info;
} }
BlockInfo *bdrv_query_info(BlockDriverState *bs) /* @p_info will be set only on success. */
void bdrv_query_info(BlockDriverState *bs,
BlockInfo **p_info,
Error **errp)
{ {
BlockInfo *info = g_malloc0(sizeof(*info)); BlockInfo *info = g_malloc0(sizeof(*info));
BlockDriverState *bs0;
ImageInfo **p_image_info;
Error *local_err = NULL;
info->device = g_strdup(bs->device_name); info->device = g_strdup(bs->device_name);
info->type = g_strdup("unknown"); info->type = g_strdup("unknown");
info->locked = bdrv_dev_is_medium_locked(bs); info->locked = bdrv_dev_is_medium_locked(bs);
@ -163,8 +236,30 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs)
info->inserted->iops_wr = info->inserted->iops_wr =
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]; bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
} }
bs0 = bs;
p_image_info = &info->inserted->image;
while (1) {
bdrv_query_image_info(bs0, p_image_info, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto err;
}
if (bs0->drv && bs0->backing_hd) {
bs0 = bs0->backing_hd;
(*p_image_info)->has_backing_image = true;
p_image_info = &((*p_image_info)->backing_image);
} else {
break;
}
}
} }
return info;
*p_info = info;
return;
err:
qapi_free_BlockInfo(info);
} }
BlockStats *bdrv_query_stats(const BlockDriverState *bs) BlockStats *bdrv_query_stats(const BlockDriverState *bs)
@ -201,16 +296,25 @@ BlockInfoList *qmp_query_block(Error **errp)
{ {
BlockInfoList *head = NULL, **p_next = &head; BlockInfoList *head = NULL, **p_next = &head;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
Error *local_err = NULL;
while ((bs = bdrv_next(bs))) { while ((bs = bdrv_next(bs))) {
BlockInfoList *info = g_malloc0(sizeof(*info)); BlockInfoList *info = g_malloc0(sizeof(*info));
info->value = bdrv_query_info(bs); bdrv_query_info(bs, &info->value, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto err;
}
*p_next = info; *p_next = info;
p_next = &info->next; p_next = &info->next;
} }
return head; return head;
err:
qapi_free_BlockInfoList(head);
return NULL;
} }
BlockStatsList *qmp_query_blockstats(Error **errp) BlockStatsList *qmp_query_blockstats(Error **errp)

View File

@ -1180,6 +1180,10 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
*/ */
if (bdrv_get_attached_dev(bs)) { if (bdrv_get_attached_dev(bs)) {
bdrv_make_anon(bs); bdrv_make_anon(bs);
/* Further I/O must not pause the guest */
bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT);
} else { } else {
drive_uninit(drive_get_by_blockdev(bs)); drive_uninit(drive_get_by_blockdev(bs));
} }

612
cmd.c
View File

@ -1,612 +0,0 @@
/*
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>
#include <getopt.h>
#include "cmd.h"
#include "block/aio.h"
#include "qemu/main-loop.h"
#define _(x) x /* not gettext support yet */
/* from libxcmd/command.c */
cmdinfo_t *cmdtab;
int ncmds;
static argsfunc_t args_func;
static checkfunc_t check_func;
static int ncmdline;
static char **cmdline;
static int
compare(const void *a, const void *b)
{
return strcmp(((const cmdinfo_t *)a)->name,
((const cmdinfo_t *)b)->name);
}
void add_command(const cmdinfo_t *ci)
{
cmdtab = g_realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
cmdtab[ncmds - 1] = *ci;
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
}
static int
check_command(
const cmdinfo_t *ci)
{
if (check_func)
return check_func(ci);
return 1;
}
void
add_check_command(
checkfunc_t cf)
{
check_func = cf;
}
int
command_usage(
const cmdinfo_t *ci)
{
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
return 0;
}
int
command(
const cmdinfo_t *ct,
int argc,
char **argv)
{
char *cmd = argv[0];
if (!check_command(ct))
return 0;
if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
if (ct->argmax == -1)
fprintf(stderr,
_("bad argument count %d to %s, expected at least %d arguments\n"),
argc-1, cmd, ct->argmin);
else if (ct->argmin == ct->argmax)
fprintf(stderr,
_("bad argument count %d to %s, expected %d arguments\n"),
argc-1, cmd, ct->argmin);
else
fprintf(stderr,
_("bad argument count %d to %s, expected between %d and %d arguments\n"),
argc-1, cmd, ct->argmin, ct->argmax);
return 0;
}
optind = 0;
return ct->cfunc(argc, argv);
}
const cmdinfo_t *
find_command(
const char *cmd)
{
cmdinfo_t *ct;
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
if (strcmp(ct->name, cmd) == 0 ||
(ct->altname && strcmp(ct->altname, cmd) == 0))
return (const cmdinfo_t *)ct;
}
return NULL;
}
void add_user_command(char *optarg)
{
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *));
cmdline[ncmdline-1] = optarg;
}
static int
args_command(
int index)
{
if (args_func)
return args_func(index);
return 0;
}
void
add_args_command(
argsfunc_t af)
{
args_func = af;
}
static void prep_fetchline(void *opaque)
{
int *fetchable = opaque;
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
*fetchable= 1;
}
static char *get_prompt(void);
void command_loop(void)
{
int c, i, j = 0, done = 0, fetchable = 0, prompted = 0;
char *input;
char **v;
const cmdinfo_t *ct;
for (i = 0; !done && i < ncmdline; i++) {
input = strdup(cmdline[i]);
if (!input) {
fprintf(stderr, _("cannot strdup command '%s': %s\n"),
cmdline[i], strerror(errno));
exit(1);
}
v = breakline(input, &c);
if (c) {
ct = find_command(v[0]);
if (ct) {
if (ct->flags & CMD_FLAG_GLOBAL) {
done = command(ct, c, v);
} else {
j = 0;
while (!done && (j = args_command(j))) {
done = command(ct, c, v);
}
}
} else {
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
}
}
doneline(input, v);
}
if (cmdline) {
g_free(cmdline);
return;
}
while (!done) {
if (!prompted) {
printf("%s", get_prompt());
fflush(stdout);
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
prompted = 1;
}
main_loop_wait(false);
if (!fetchable) {
continue;
}
input = fetchline();
if (input == NULL) {
break;
}
v = breakline(input, &c);
if (c) {
ct = find_command(v[0]);
if (ct) {
done = command(ct, c, v);
} else {
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
}
}
doneline(input, v);
prompted = 0;
fetchable = 0;
}
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
}
/* from libxcmd/input.c */
#if defined(ENABLE_READLINE)
# include <readline/history.h>
# include <readline/readline.h>
#elif defined(ENABLE_EDITLINE)
# include <histedit.h>
#endif
static char *
get_prompt(void)
{
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
if (!prompt[0])
snprintf(prompt, sizeof(prompt), "%s> ", progname);
return prompt;
}
#if defined(ENABLE_READLINE)
char *
fetchline(void)
{
char *line;
line = readline(get_prompt());
if (line && *line)
add_history(line);
return line;
}
#elif defined(ENABLE_EDITLINE)
static char *el_get_prompt(EditLine *e) { return get_prompt(); }
char *
fetchline(void)
{
static EditLine *el;
static History *hist;
HistEvent hevent;
char *line;
int count;
if (!el) {
hist = history_init();
history(hist, &hevent, H_SETSIZE, 100);
el = el_init(progname, stdin, stdout, stderr);
el_source(el, NULL);
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_PROMPT, el_get_prompt);
el_set(el, EL_HIST, history, (const char *)hist);
}
line = strdup(el_gets(el, &count));
if (line) {
if (count > 0)
line[count-1] = '\0';
if (*line)
history(hist, &hevent, H_ENTER, line);
}
return line;
}
#else
# define MAXREADLINESZ 1024
char *
fetchline(void)
{
char *p, *line = malloc(MAXREADLINESZ);
if (!line)
return NULL;
if (!fgets(line, MAXREADLINESZ, stdin)) {
free(line);
return NULL;
}
p = line + strlen(line);
if (p != line && p[-1] == '\n')
p[-1] = '\0';
return line;
}
#endif
static char *qemu_strsep(char **input, const char *delim)
{
char *result = *input;
if (result != NULL) {
char *p;
for (p = result; *p != '\0'; p++) {
if (strchr(delim, *p)) {
break;
}
}
if (*p == '\0') {
*input = NULL;
} else {
*p = '\0';
*input = p + 1;
}
}
return result;
}
char **breakline(char *input, int *count)
{
int c = 0;
char *p;
char **rval = calloc(sizeof(char *), 1);
char **tmp;
while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
if (!*p) {
continue;
}
c++;
tmp = realloc(rval, sizeof(*rval) * (c + 1));
if (!tmp) {
free(rval);
rval = NULL;
c = 0;
break;
} else {
rval = tmp;
}
rval[c - 1] = p;
rval[c] = NULL;
}
*count = c;
return rval;
}
void
doneline(
char *input,
char **vec)
{
free(input);
free(vec);
}
#define EXABYTES(x) ((long long)(x) << 60)
#define PETABYTES(x) ((long long)(x) << 50)
#define TERABYTES(x) ((long long)(x) << 40)
#define GIGABYTES(x) ((long long)(x) << 30)
#define MEGABYTES(x) ((long long)(x) << 20)
#define KILOBYTES(x) ((long long)(x) << 10)
long long
cvtnum(
char *s)
{
long long i;
char *sp;
int c;
i = strtoll(s, &sp, 0);
if (i == 0 && sp == s)
return -1LL;
if (*sp == '\0')
return i;
if (sp[1] != '\0')
return -1LL;
c = qemu_tolower(*sp);
switch (c) {
default:
return i;
case 'k':
return KILOBYTES(i);
case 'm':
return MEGABYTES(i);
case 'g':
return GIGABYTES(i);
case 't':
return TERABYTES(i);
case 'p':
return PETABYTES(i);
case 'e':
return EXABYTES(i);
}
return -1LL;
}
#define TO_EXABYTES(x) ((x) / EXABYTES(1))
#define TO_PETABYTES(x) ((x) / PETABYTES(1))
#define TO_TERABYTES(x) ((x) / TERABYTES(1))
#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
void
cvtstr(
double value,
char *str,
size_t size)
{
char *trim;
const char *suffix;
if (value >= EXABYTES(1)) {
suffix = " EiB";
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
} else if (value >= PETABYTES(1)) {
suffix = " PiB";
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
} else if (value >= TERABYTES(1)) {
suffix = " TiB";
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
} else if (value >= GIGABYTES(1)) {
suffix = " GiB";
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
} else if (value >= MEGABYTES(1)) {
suffix = " MiB";
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
} else if (value >= KILOBYTES(1)) {
suffix = " KiB";
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
} else {
suffix = " bytes";
snprintf(str, size - 6, "%f", value);
}
trim = strstr(str, ".000");
if (trim) {
strcpy(trim, suffix);
} else {
strcat(str, suffix);
}
}
struct timeval
tsub(struct timeval t1, struct timeval t2)
{
t1.tv_usec -= t2.tv_usec;
if (t1.tv_usec < 0) {
t1.tv_usec += 1000000;
t1.tv_sec--;
}
t1.tv_sec -= t2.tv_sec;
return t1;
}
double
tdiv(double value, struct timeval tv)
{
return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
}
#define HOURS(sec) ((sec) / (60 * 60))
#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
#define SECONDS(sec) ((sec) % 60)
void
timestr(
struct timeval *tv,
char *ts,
size_t size,
int format)
{
double usec = (double)tv->tv_usec / 1000000.0;
if (format & TERSE_FIXED_TIME) {
if (!HOURS(tv->tv_sec)) {
snprintf(ts, size, "%u:%02u.%02u",
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
return;
}
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
}
if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
snprintf(ts, size, "%u:%02u:%02u.%02u",
(unsigned int) HOURS(tv->tv_sec),
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
} else {
snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
}
}
/* from libxcmd/quit.c */
static cmdinfo_t quit_cmd;
/* ARGSUSED */
static int
quit_f(
int argc,
char **argv)
{
return 1;
}
void
quit_init(void)
{
quit_cmd.name = _("quit");
quit_cmd.altname = _("q");
quit_cmd.cfunc = quit_f;
quit_cmd.argmin = -1;
quit_cmd.argmax = -1;
quit_cmd.flags = CMD_FLAG_GLOBAL;
quit_cmd.oneline = _("exit the program");
add_command(&quit_cmd);
}
/* from libxcmd/help.c */
static cmdinfo_t help_cmd;
static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
static void help_oneline(const char *cmd, const cmdinfo_t *ct);
static void
help_all(void)
{
const cmdinfo_t *ct;
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
help_oneline(ct->name, ct);
printf(_("\nUse 'help commandname' for extended help.\n"));
}
static int
help_f(
int argc,
char **argv)
{
const cmdinfo_t *ct;
if (argc == 1) {
help_all();
return 0;
}
ct = find_command(argv[1]);
if (ct == NULL) {
printf(_("command %s not found\n"), argv[1]);
return 0;
}
help_onecmd(argv[1], ct);
return 0;
}
static void
help_onecmd(
const char *cmd,
const cmdinfo_t *ct)
{
help_oneline(cmd, ct);
if (ct->help)
ct->help();
}
static void
help_oneline(
const char *cmd,
const cmdinfo_t *ct)
{
if (cmd)
printf("%s ", cmd);
else {
printf("%s ", ct->name);
if (ct->altname)
printf("(or %s) ", ct->altname);
}
if (ct->args)
printf("%s ", ct->args);
printf("-- %s\n", ct->oneline);
}
void
help_init(void)
{
help_cmd.name = _("help");
help_cmd.altname = _("?");
help_cmd.cfunc = help_f;
help_cmd.argmin = 0;
help_cmd.argmax = 1;
help_cmd.flags = CMD_FLAG_GLOBAL;
help_cmd.args = _("[command]");
help_cmd.oneline = _("help for one or all commands");
add_command(&help_cmd);
}

79
cmd.h
View File

@ -1,79 +0,0 @@
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __COMMAND_H__
#define __COMMAND_H__
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
typedef int (*cfunc_t)(int argc, char **argv);
typedef void (*helpfunc_t)(void);
typedef struct cmdinfo {
const char *name;
const char *altname;
cfunc_t cfunc;
int argmin;
int argmax;
int canpush;
int flags;
const char *args;
const char *oneline;
helpfunc_t help;
} cmdinfo_t;
extern cmdinfo_t *cmdtab;
extern int ncmds;
void help_init(void);
void quit_init(void);
typedef int (*argsfunc_t)(int index);
typedef int (*checkfunc_t)(const cmdinfo_t *ci);
void add_command(const cmdinfo_t *ci);
void add_user_command(char *optarg);
void add_args_command(argsfunc_t af);
void add_check_command(checkfunc_t cf);
const cmdinfo_t *find_command(const char *cmd);
void command_loop(void);
int command_usage(const cmdinfo_t *ci);
int command(const cmdinfo_t *ci, int argc, char **argv);
/* from input.h */
char **breakline(char *input, int *count);
void doneline(char *input, char **vec);
char *fetchline(void);
long long cvtnum(char *s);
void cvtstr(double value, char *str, size_t sz);
struct timeval tsub(struct timeval t1, struct timeval t2);
double tdiv(double value, struct timeval tv);
enum {
DEFAULT_TIME = 0x0,
TERSE_FIXED_TIME = 0x1,
VERBOSE_FIXED_TIME = 0x2
};
void timestr(struct timeval *tv, char *str, size_t sz, int flags);
extern char *progname;
#endif /* __COMMAND_H__ */

View File

@ -185,6 +185,8 @@ Remove host block device. The result is that guest generated IO is no longer
submitted against the host device underlying the disk. Once a drive has submitted against the host device underlying the disk. Once a drive has
been deleted, the QEMU Block layer returns -EIO which results in IO been deleted, the QEMU Block layer returns -EIO which results in IO
errors in the guest for applications that are reading/writing to the device. errors in the guest for applications that are reading/writing to the device.
These errors are always reported to the guest, regardless of the drive's error
actions (drive options rerror, werror).
ETEXI ETEXI
{ {
@ -1548,6 +1550,22 @@ STEXI
Removes the chardev @var{id}. Removes the chardev @var{id}.
ETEXI
{
.name = "qemu-io",
.args_type = "device:B,command:s",
.params = "[device] \"[command]\"",
.help = "run a qemu-io command on a block device",
.mhandler.cmd = hmp_qemu_io,
},
STEXI
@item qemu-io @var{device} @var{command}
@findex qemu-io
Executes a qemu-io command on the given block device.
ETEXI ETEXI
{ {

39
hmp.c
View File

@ -22,6 +22,8 @@
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "ui/console.h" #include "ui/console.h"
#include "block/qapi.h"
#include "qemu-io.h"
static void hmp_handle_error(Monitor *mon, Error **errp) static void hmp_handle_error(Monitor *mon, Error **errp)
{ {
@ -277,10 +279,16 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
void hmp_info_block(Monitor *mon, const QDict *qdict) void hmp_info_block(Monitor *mon, const QDict *qdict)
{ {
BlockInfoList *block_list, *info; BlockInfoList *block_list, *info;
ImageInfo *image_info;
const char *device = qdict_get_try_str(qdict, "device");
bool verbose = qdict_get_try_bool(qdict, "verbose", 0);
block_list = qmp_query_block(NULL); block_list = qmp_query_block(NULL);
for (info = block_list; info; info = info->next) { for (info = block_list; info; info = info->next) {
if (device && strcmp(device, info->value->device)) {
continue;
}
monitor_printf(mon, "%s: removable=%d", monitor_printf(mon, "%s: removable=%d",
info->value->device, info->value->removable); info->value->device, info->value->removable);
@ -318,6 +326,20 @@ void hmp_info_block(Monitor *mon, const QDict *qdict)
info->value->inserted->iops, info->value->inserted->iops,
info->value->inserted->iops_rd, info->value->inserted->iops_rd,
info->value->inserted->iops_wr); info->value->inserted->iops_wr);
if (verbose) {
monitor_printf(mon, " images:\n");
image_info = info->value->inserted->image;
while (1) {
bdrv_image_info_dump((fprintf_function)monitor_printf,
mon, image_info);
if (image_info->has_backing_image) {
image_info = image_info->backing_image;
} else {
break;
}
}
}
} else { } else {
monitor_printf(mon, " [not inserted]"); monitor_printf(mon, " [not inserted]");
} }
@ -1425,3 +1447,20 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
hmp_handle_error(mon, &local_err); hmp_handle_error(mon, &local_err);
} }
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{
BlockDriverState *bs;
const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command");
Error *err = NULL;
bs = bdrv_find(device);
if (bs) {
qemuio_command(bs, command);
} else {
error_set(&err, QERR_DEVICE_NOT_FOUND, device);
}
hmp_handle_error(mon, &err);
}

1
hmp.h
View File

@ -85,5 +85,6 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
void hmp_chardev_add(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict);
void hmp_chardev_remove(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
void hmp_qemu_io(Monitor *mon, const QDict *qdict);
#endif #endif

View File

@ -814,6 +814,7 @@ void ide_flush_cache(IDEState *s)
return; return;
} }
s->status |= BUSY_STAT;
bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH); bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH);
bdrv_aio_flush(s->bs, ide_flush_cb, s); bdrv_aio_flush(s->bs, ide_flush_cb, s);
} }

View File

@ -424,6 +424,9 @@ typedef enum {
BLKDBG_CLUSTER_ALLOC_BYTES, BLKDBG_CLUSTER_ALLOC_BYTES,
BLKDBG_CLUSTER_FREE, BLKDBG_CLUSTER_FREE,
BLKDBG_FLUSH_TO_OS,
BLKDBG_FLUSH_TO_DISK,
BLKDBG_EVENT_MAX, BLKDBG_EVENT_MAX,
} BlkDebugEvent; } BlkDebugEvent;

View File

@ -29,11 +29,15 @@
#include "block/block.h" #include "block/block.h"
#include "block/snapshot.h" #include "block/snapshot.h"
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info); int bdrv_query_snapshot_info_list(BlockDriverState *bs,
void bdrv_collect_image_info(BlockDriverState *bs, SnapshotInfoList **p_list,
ImageInfo *info, Error **errp);
const char *filename); void bdrv_query_image_info(BlockDriverState *bs,
BlockInfo *bdrv_query_info(BlockDriverState *s); ImageInfo **p_info,
Error **errp);
void bdrv_query_info(BlockDriverState *bs,
BlockInfo **p_info,
Error **errp);
BlockStats *bdrv_query_stats(const BlockDriverState *bs); BlockStats *bdrv_query_stats(const BlockDriverState *bs);
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,

View File

@ -174,6 +174,7 @@ char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr); int strstart(const char *str, const char *val, const char **ptr);
int stristart(const char *str, const char *val, const char **ptr); int stristart(const char *str, const char *val, const char **ptr);
int qemu_strnlen(const char *s, int max_len); int qemu_strnlen(const char *s, int max_len);
char *qemu_strsep(char **input, const char *delim);
time_t mktimegm(struct tm *tm); time_t mktimegm(struct tm *tm);
int qemu_fls(int i); int qemu_fls(int i);
int qemu_fdatasync(int fd); int qemu_fdatasync(int fd);
@ -191,6 +192,8 @@ int parse_uint_full(const char *s, unsigned long long *value, int base);
* A-Z, as strtosz() will use qemu_toupper() on the given argument * A-Z, as strtosz() will use qemu_toupper() on the given argument
* prior to comparison. * prior to comparison.
*/ */
#define STRTOSZ_DEFSUFFIX_EB 'E'
#define STRTOSZ_DEFSUFFIX_PB 'P'
#define STRTOSZ_DEFSUFFIX_TB 'T' #define STRTOSZ_DEFSUFFIX_TB 'T'
#define STRTOSZ_DEFSUFFIX_GB 'G' #define STRTOSZ_DEFSUFFIX_GB 'G'
#define STRTOSZ_DEFSUFFIX_MB 'M' #define STRTOSZ_DEFSUFFIX_MB 'M'

46
include/qemu-io.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_IO_H
#define QEMU_IO_H
#include "qemu-common.h"
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
typedef int (*cfunc_t)(BlockDriverState *bs, int argc, char **argv);
typedef void (*helpfunc_t)(void);
typedef struct cmdinfo {
const char* name;
const char* altname;
cfunc_t cfunc;
int argmin;
int argmax;
int canpush;
int flags;
const char *args;
const char *oneline;
helpfunc_t help;
} cmdinfo_t;
bool qemuio_command(BlockDriverState *bs, const char *cmd);
void qemuio_add_command(const cmdinfo_t *ci);
int qemuio_command_usage(const cmdinfo_t *ci);
#endif /* QEMU_IO_H */

View File

@ -93,10 +93,10 @@
* 'M' Non-negative target long (32 or 64 bit), in user mode the * 'M' Non-negative target long (32 or 64 bit), in user mode the
* value is multiplied by 2^20 (think Mebibyte) * value is multiplied by 2^20 (think Mebibyte)
* 'o' octets (aka bytes) * 'o' octets (aka bytes)
* user mode accepts an optional T, t, G, g, M, m, K, k * user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
* suffix, which multiplies the value by 2^40 for * K, k suffix, which multiplies the value by 2^60 for suffixes E
* suffixes T and t, 2^30 for suffixes G and g, 2^20 for * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
* M and m, 2^10 for K and k * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
* 'T' double * 'T' double
* user mode accepts an optional ms, us, ns suffix, * user mode accepts an optional ms, us, ns suffix,
* which divides the value by 1e3, 1e6, 1e9, respectively * which divides the value by 1e3, 1e6, 1e9, respectively
@ -2472,9 +2472,10 @@ static mon_cmd_t info_cmds[] = {
}, },
{ {
.name = "block", .name = "block",
.args_type = "", .args_type = "verbose:-v,device:B?",
.params = "", .params = "[-v] [device]",
.help = "show the block devices", .help = "show info of one block device or all block devices "
"(and details of images with -v option)",
.mhandler.cmd = hmp_info_block, .mhandler.cmd = hmp_info_block,
}, },
{ {

View File

@ -236,6 +236,8 @@
# #
# @snapshots: #optional list of VM snapshots # @snapshots: #optional list of VM snapshots
# #
# @backing-image: #optional info of the backing image (since 1.6)
#
# Since: 1.3 # Since: 1.3
# #
## ##
@ -245,7 +247,8 @@
'*actual-size': 'int', 'virtual-size': 'int', '*actual-size': 'int', 'virtual-size': 'int',
'*cluster-size': 'int', '*encrypted': 'bool', '*cluster-size': 'int', '*encrypted': 'bool',
'*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } } '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
'*backing-image': 'ImageInfo' } }
## ##
# @ImageCheck: # @ImageCheck:
@ -756,6 +759,8 @@
# #
# @iops_wr: write I/O operations per second is specified # @iops_wr: write I/O operations per second is specified
# #
# @image: the info of image used (since: 1.6)
#
# Since: 0.14.0 # Since: 0.14.0
# #
# Notes: This interface is only found in @BlockInfo. # Notes: This interface is only found in @BlockInfo.
@ -765,7 +770,8 @@
'*backing_file': 'str', 'backing_file_depth': 'int', '*backing_file': 'str', 'backing_file_depth': 'int',
'encrypted': 'bool', 'encryption_key_missing': 'bool', 'encrypted': 'bool', 'encryption_key_missing': 'bool',
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int'} } 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
'image': 'ImageInfo' } }
## ##
# @BlockDeviceIoStatus: # @BlockDeviceIoStatus:

View File

@ -85,8 +85,9 @@ static void help(void)
" options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n"
" 'directsync' and 'unsafe' (default for convert)\n" " 'directsync' and 'unsafe' (default for convert)\n"
" 'size' is the disk image size in bytes. Optional suffixes\n" " 'size' is the disk image size in bytes. Optional suffixes\n"
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n"
" and T (terabyte, 1024G) are supported. 'b' is ignored.\n" " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n"
" supported. 'b' is ignored.\n"
" 'output_filename' is the destination disk image filename\n" " 'output_filename' is the destination disk image filename\n"
" 'output_fmt' is the destination format\n" " 'output_fmt' is the destination format\n"
" 'options' is a comma separated list of format specific options in a\n" " 'options' is a comma separated list of format specific options in a\n"
@ -387,8 +388,9 @@ static int img_create(int argc, char **argv)
error_report("Image size must be less than 8 EiB!"); error_report("Image size must be less than 8 EiB!");
} else { } else {
error_report("Invalid image size specified! You may use k, M, " error_report("Invalid image size specified! You may use k, M, "
"G or T suffixes for "); "G, T, P or E suffixes for ");
error_report("kilobytes, megabytes, gigabytes and terabytes."); error_report("kilobytes, megabytes, gigabytes, terabytes, "
"petabytes and exabytes.");
} }
return 1; return 1;
} }
@ -1642,6 +1644,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
ImageInfoList *head = NULL; ImageInfoList *head = NULL;
ImageInfoList **last = &head; ImageInfoList **last = &head;
GHashTable *filenames; GHashTable *filenames;
Error *err = NULL;
filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL);
@ -1663,9 +1666,12 @@ static ImageInfoList *collect_image_info_list(const char *filename,
goto err; goto err;
} }
info = g_new0(ImageInfo, 1); bdrv_query_image_info(bs, &info, &err);
bdrv_collect_image_info(bs, info, filename); if (error_is_set(&err)) {
bdrv_collect_snapshots(bs, info); error_report("%s", error_get_pretty(err));
error_free(err);
goto err;
}
elem = g_new0(ImageInfoList, 1); elem = g_new0(ImageInfoList, 1);
elem->value = info; elem->value = info;

2118
qemu-io-cmds.c Normal file

File diff suppressed because it is too large Load Diff

1986
qemu-io.c

File diff suppressed because it is too large Load Diff

View File

@ -1704,6 +1704,47 @@ Each json-object contain the following:
- "iops": limit total I/O operations per second (json-int) - "iops": limit total I/O operations per second (json-int)
- "iops_rd": limit read operations per second (json-int) - "iops_rd": limit read operations per second (json-int)
- "iops_wr": limit write operations per second (json-int) - "iops_wr": limit write operations per second (json-int)
- "image": the detail of the image, it is a json-object containing
the following:
- "filename": image file name (json-string)
- "format": image format (json-string)
- "virtual-size": image capacity in bytes (json-int)
- "dirty-flag": true if image is not cleanly closed, not present
means clean (json-bool, optional)
- "actual-size": actual size on disk in bytes of the image, not
present when image does not support thin
provision (json-int, optional)
- "cluster-size": size of a cluster in bytes, not present if image
format does not support it (json-int, optional)
- "encrypted": true if the image is encrypted, not present means
false or the image format does not support
encryption (json-bool, optional)
- "backing_file": backing file name, not present means no backing
file is used or the image format does not
support backing file chain
(json-string, optional)
- "full-backing-filename": full path of the backing file, not
present if it equals backing_file or no
backing file is used
(json-string, optional)
- "backing-filename-format": the format of the backing file, not
present means unknown or no backing
file (json-string, optional)
- "snapshots": the internal snapshot info, it is an optional list
of json-object containing the following:
- "id": unique snapshot id (json-string)
- "name": snapshot name (json-string)
- "vm-state-size": size of the VM state in bytes (json-int)
- "date-sec": UTC date of the snapshot in seconds (json-int)
- "date-nsec": fractional part in nanoseconds to be used with
date-sec(json-int)
- "vm-clock-sec": VM clock relative to boot in seconds
(json-int)
- "vm-clock-nsec": fractional part in nanoseconds to be used
with vm-clock-sec (json-int)
- "backing-image": the detail of the backing image, it is an
optional json-object only present when a
backing image present for this image
- "io-status": I/O operation status, only present if the device supports it - "io-status": I/O operation status, only present if the device supports it
and the VM is configured to stop on errors. It's always reset and the VM is configured to stop on errors. It's always reset
@ -1724,14 +1765,38 @@ Example:
"ro":false, "ro":false,
"drv":"qcow2", "drv":"qcow2",
"encrypted":false, "encrypted":false,
"file":"disks/test.img", "file":"disks/test.qcow2",
"backing_file_depth":0, "backing_file_depth":1,
"bps":1000000, "bps":1000000,
"bps_rd":0, "bps_rd":0,
"bps_wr":0, "bps_wr":0,
"iops":1000000, "iops":1000000,
"iops_rd":0, "iops_rd":0,
"iops_wr":0, "iops_wr":0,
"image":{
"filename":"disks/test.qcow2",
"format":"qcow2",
"virtual-size":2048000,
"backing_file":"base.qcow2",
"full-backing-filename":"disks/base.qcow2",
"backing-filename-format:"qcow2",
"snapshots":[
{
"id": "1",
"name": "snapshot1",
"vm-state-size": 0,
"date-sec": 10000200,
"date-nsec": 12,
"vm-clock-sec": 206,
"vm-clock-nsec": 30
}
],
"backing-image":{
"filename":"disks/base.qcow2",
"format":"qcow2",
"virtual-size":2048000
}
}
}, },
"type":"unknown" "type":"unknown"
}, },

View File

@ -64,6 +64,7 @@ enum {
}; };
enum { enum {
DEV = 0x10,
LBA = 0x40, LBA = 0x40,
}; };
@ -76,6 +77,7 @@ enum {
enum { enum {
CMD_READ_DMA = 0xc8, CMD_READ_DMA = 0xc8,
CMD_WRITE_DMA = 0xca, CMD_WRITE_DMA = 0xca,
CMD_FLUSH_CACHE = 0xe7,
CMD_IDENTIFY = 0xec, CMD_IDENTIFY = 0xec,
CMDF_ABORT = 0x100, CMDF_ABORT = 0x100,
@ -394,7 +396,7 @@ static void test_identify(void)
/* Read in the IDENTIFY buffer and check registers */ /* Read in the IDENTIFY buffer and check registers */
data = inb(IDE_BASE + reg_device); data = inb(IDE_BASE + reg_device);
g_assert_cmpint(data & 0x10, ==, 0); g_assert_cmpint(data & DEV, ==, 0);
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
data = inb(IDE_BASE + reg_status); data = inb(IDE_BASE + reg_status);
@ -423,6 +425,43 @@ static void test_identify(void)
ide_test_quit(); ide_test_quit();
} }
static void test_flush(void)
{
uint8_t data;
ide_test_start(
"-vnc none "
"-drive file=blkdebug::%s,if=ide,cache=writeback",
tmp_path);
/* Delay the completion of the flush request until we explicitly do it */
qmp("{'execute':'human-monitor-command', 'arguments': { "
"'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
/* FLUSH CACHE command on device 0*/
outb(IDE_BASE + reg_device, 0);
outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
/* Check status while request is in flight*/
data = inb(IDE_BASE + reg_status);
assert_bit_set(data, BSY | DRDY);
assert_bit_clear(data, DF | ERR | DRQ);
/* Complete the command */
qmp("{'execute':'human-monitor-command', 'arguments': { "
"'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }");
/* Check registers */
data = inb(IDE_BASE + reg_device);
g_assert_cmpint(data & DEV, ==, 0);
data = inb(IDE_BASE + reg_status);
assert_bit_set(data, DRDY);
assert_bit_clear(data, BSY | DF | ERR | DRQ);
ide_test_quit();
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *arch = qtest_get_arch(); const char *arch = qtest_get_arch();
@ -453,6 +492,8 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
qtest_add_func("/ide/flush", test_flush);
ret = g_test_run(); ret = g_test_run();
/* Cleanup */ /* Cleanup */

View File

@ -108,15 +108,15 @@ qemu-img: Formatting or formatting option not supported for file format 'qcow2'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
qemu-img: kilobytes, megabytes, gigabytes and terabytes. qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
qemu-img: kilobytes, megabytes, gigabytes and terabytes. qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
qemu-img: Parameter 'size' expects a size qemu-img: Parameter 'size' expects a size

View File

@ -107,6 +107,27 @@ int qemu_strnlen(const char *s, int max_len)
return i; return i;
} }
char *qemu_strsep(char **input, const char *delim)
{
char *result = *input;
if (result != NULL) {
char *p;
for (p = result; *p != '\0'; p++) {
if (strchr(delim, *p)) {
break;
}
}
if (*p == '\0') {
*input = NULL;
} else {
*p = '\0';
*input = p + 1;
}
}
return result;
}
time_t mktimegm(struct tm *tm) time_t mktimegm(struct tm *tm)
{ {
time_t t; time_t t;
@ -267,6 +288,10 @@ static int64_t suffix_mul(char suffix, int64_t unit)
return unit * unit * unit; return unit * unit * unit;
case STRTOSZ_DEFSUFFIX_TB: case STRTOSZ_DEFSUFFIX_TB:
return unit * unit * unit * unit; return unit * unit * unit * unit;
case STRTOSZ_DEFSUFFIX_PB:
return unit * unit * unit * unit * unit;
case STRTOSZ_DEFSUFFIX_EB:
return unit * unit * unit * unit * unit * unit;
} }
return -1; return -1;
} }