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:
commit
7387de16d0
2
Makefile
2
Makefile
@ -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-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
|
||||
|
||||
|
@ -13,6 +13,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||
block-obj-y += block/
|
||||
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-sleep.o
|
||||
|
8
block.c
8
block.c
@ -3186,13 +3186,11 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (!drv || !drv->bdrv_debug_event) {
|
||||
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
|
||||
return;
|
||||
}
|
||||
|
||||
drv->bdrv_debug_event(bs, event);
|
||||
bs->drv->bdrv_debug_event(bs, 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 */
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS);
|
||||
if (bs->drv->bdrv_co_flush_to_os) {
|
||||
ret = bs->drv->bdrv_co_flush_to_os(bs);
|
||||
if (ret < 0) {
|
||||
@ -4036,6 +4035,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
goto flush_parent;
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK);
|
||||
if (bs->drv->bdrv_co_flush_to_disk) {
|
||||
ret = bs->drv->bdrv_co_flush_to_disk(bs);
|
||||
} else if (bs->drv->bdrv_aio_flush) {
|
||||
|
@ -182,6 +182,9 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
|
||||
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
|
||||
[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)
|
||||
|
148
block/qapi.c
148
block/qapi.c
@ -26,29 +26,56 @@
|
||||
#include "block/block_int.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;
|
||||
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);
|
||||
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++) {
|
||||
info->has_snapshots = true;
|
||||
info_list = g_new0(SnapshotInfoList, 1);
|
||||
info = g_new0(SnapshotInfo, 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->value->id = g_strdup(sn_tab[i].id_str);
|
||||
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;
|
||||
info_list = g_new0(SnapshotInfoList, 1);
|
||||
info_list->value = info;
|
||||
|
||||
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
||||
if (!cur_item) {
|
||||
info->snapshots = cur_item = info_list;
|
||||
head = cur_item = info_list;
|
||||
} else {
|
||||
cur_item->next = info_list;
|
||||
cur_item = info_list;
|
||||
@ -57,20 +84,40 @@ void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info)
|
||||
}
|
||||
|
||||
g_free(sn_tab);
|
||||
*p_list = head;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_collect_image_info(BlockDriverState *bs,
|
||||
ImageInfo *info,
|
||||
const char *filename)
|
||||
/**
|
||||
* bdrv_query_image_info:
|
||||
* @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;
|
||||
char backing_filename[1024];
|
||||
const char *backing_filename;
|
||||
char backing_filename2[1024];
|
||||
BlockDriverInfo bdi;
|
||||
int ret;
|
||||
Error *err = NULL;
|
||||
ImageInfo *info = g_new0(ImageInfo, 1);
|
||||
|
||||
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->virtual_size = total_sectors * 512;
|
||||
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->has_dirty_flag = true;
|
||||
}
|
||||
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
|
||||
backing_filename = bs->backing_file;
|
||||
if (backing_filename[0] != '\0') {
|
||||
info->backing_filename = g_strdup(backing_filename);
|
||||
info->has_backing_filename = true;
|
||||
@ -105,11 +152,37 @@ void bdrv_collect_image_info(BlockDriverState *bs,
|
||||
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));
|
||||
BlockDriverState *bs0;
|
||||
ImageInfo **p_image_info;
|
||||
Error *local_err = NULL;
|
||||
info->device = g_strdup(bs->device_name);
|
||||
info->type = g_strdup("unknown");
|
||||
info->locked = bdrv_dev_is_medium_locked(bs);
|
||||
@ -163,8 +236,30 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
||||
info->inserted->iops_wr =
|
||||
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)
|
||||
@ -201,16 +296,25 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
{
|
||||
BlockInfoList *head = NULL, **p_next = &head;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
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->next;
|
||||
}
|
||||
|
||||
return head;
|
||||
|
||||
err:
|
||||
qapi_free_BlockInfoList(head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BlockStatsList *qmp_query_blockstats(Error **errp)
|
||||
|
@ -1180,6 +1180,10 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
*/
|
||||
if (bdrv_get_attached_dev(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 {
|
||||
drive_uninit(drive_get_by_blockdev(bs));
|
||||
}
|
||||
|
612
cmd.c
612
cmd.c
@ -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
79
cmd.h
@ -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__ */
|
@ -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
|
||||
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.
|
||||
These errors are always reported to the guest, regardless of the drive's error
|
||||
actions (drive options rerror, werror).
|
||||
ETEXI
|
||||
|
||||
{
|
||||
@ -1548,6 +1550,22 @@ STEXI
|
||||
|
||||
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
|
||||
|
||||
{
|
||||
|
39
hmp.c
39
hmp.c
@ -22,6 +22,8 @@
|
||||
#include "qemu/sockets.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "ui/console.h"
|
||||
#include "block/qapi.h"
|
||||
#include "qemu-io.h"
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
for (info = block_list; info; info = info->next) {
|
||||
if (device && strcmp(device, info->value->device)) {
|
||||
continue;
|
||||
}
|
||||
monitor_printf(mon, "%s: removable=%d",
|
||||
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_rd,
|
||||
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 {
|
||||
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);
|
||||
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
1
hmp.h
@ -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_chardev_add(Monitor *mon, const QDict *qdict);
|
||||
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
|
||||
void hmp_qemu_io(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
@ -814,6 +814,7 @@ void ide_flush_cache(IDEState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
s->status |= BUSY_STAT;
|
||||
bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH);
|
||||
bdrv_aio_flush(s->bs, ide_flush_cb, s);
|
||||
}
|
||||
|
@ -424,6 +424,9 @@ typedef enum {
|
||||
BLKDBG_CLUSTER_ALLOC_BYTES,
|
||||
BLKDBG_CLUSTER_FREE,
|
||||
|
||||
BLKDBG_FLUSH_TO_OS,
|
||||
BLKDBG_FLUSH_TO_DISK,
|
||||
|
||||
BLKDBG_EVENT_MAX,
|
||||
} BlkDebugEvent;
|
||||
|
||||
|
@ -29,11 +29,15 @@
|
||||
#include "block/block.h"
|
||||
#include "block/snapshot.h"
|
||||
|
||||
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info);
|
||||
void bdrv_collect_image_info(BlockDriverState *bs,
|
||||
ImageInfo *info,
|
||||
const char *filename);
|
||||
BlockInfo *bdrv_query_info(BlockDriverState *s);
|
||||
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
||||
SnapshotInfoList **p_list,
|
||||
Error **errp);
|
||||
void bdrv_query_image_info(BlockDriverState *bs,
|
||||
ImageInfo **p_info,
|
||||
Error **errp);
|
||||
void bdrv_query_info(BlockDriverState *bs,
|
||||
BlockInfo **p_info,
|
||||
Error **errp);
|
||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
||||
|
||||
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||
|
@ -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 stristart(const char *str, const char *val, const char **ptr);
|
||||
int qemu_strnlen(const char *s, int max_len);
|
||||
char *qemu_strsep(char **input, const char *delim);
|
||||
time_t mktimegm(struct tm *tm);
|
||||
int qemu_fls(int i);
|
||||
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
|
||||
* prior to comparison.
|
||||
*/
|
||||
#define STRTOSZ_DEFSUFFIX_EB 'E'
|
||||
#define STRTOSZ_DEFSUFFIX_PB 'P'
|
||||
#define STRTOSZ_DEFSUFFIX_TB 'T'
|
||||
#define STRTOSZ_DEFSUFFIX_GB 'G'
|
||||
#define STRTOSZ_DEFSUFFIX_MB 'M'
|
||||
|
46
include/qemu-io.h
Normal file
46
include/qemu-io.h
Normal 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 */
|
15
monitor.c
15
monitor.c
@ -93,10 +93,10 @@
|
||||
* 'M' Non-negative target long (32 or 64 bit), in user mode the
|
||||
* value is multiplied by 2^20 (think Mebibyte)
|
||||
* 'o' octets (aka bytes)
|
||||
* user mode accepts an optional T, t, G, g, M, m, K, k
|
||||
* suffix, which multiplies the value by 2^40 for
|
||||
* suffixes T and t, 2^30 for suffixes G and g, 2^20 for
|
||||
* M and m, 2^10 for K and k
|
||||
* user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
|
||||
* K, k suffix, which multiplies the value by 2^60 for suffixes E
|
||||
* and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
|
||||
* 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
|
||||
* 'T' double
|
||||
* user mode accepts an optional ms, us, ns suffix,
|
||||
* which divides the value by 1e3, 1e6, 1e9, respectively
|
||||
@ -2472,9 +2472,10 @@ static mon_cmd_t info_cmds[] = {
|
||||
},
|
||||
{
|
||||
.name = "block",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show the block devices",
|
||||
.args_type = "verbose:-v,device:B?",
|
||||
.params = "[-v] [device]",
|
||||
.help = "show info of one block device or all block devices "
|
||||
"(and details of images with -v option)",
|
||||
.mhandler.cmd = hmp_info_block,
|
||||
},
|
||||
{
|
||||
|
@ -236,6 +236,8 @@
|
||||
#
|
||||
# @snapshots: #optional list of VM snapshots
|
||||
#
|
||||
# @backing-image: #optional info of the backing image (since 1.6)
|
||||
#
|
||||
# Since: 1.3
|
||||
#
|
||||
##
|
||||
@ -245,7 +247,8 @@
|
||||
'*actual-size': 'int', 'virtual-size': 'int',
|
||||
'*cluster-size': 'int', '*encrypted': 'bool',
|
||||
'*backing-filename': 'str', '*full-backing-filename': 'str',
|
||||
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } }
|
||||
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
|
||||
'*backing-image': 'ImageInfo' } }
|
||||
|
||||
##
|
||||
# @ImageCheck:
|
||||
@ -756,6 +759,8 @@
|
||||
#
|
||||
# @iops_wr: write I/O operations per second is specified
|
||||
#
|
||||
# @image: the info of image used (since: 1.6)
|
||||
#
|
||||
# Since: 0.14.0
|
||||
#
|
||||
# Notes: This interface is only found in @BlockInfo.
|
||||
@ -765,7 +770,8 @@
|
||||
'*backing_file': 'str', 'backing_file_depth': 'int',
|
||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||
'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:
|
||||
|
20
qemu-img.c
20
qemu-img.c
@ -85,8 +85,9 @@ static void help(void)
|
||||
" options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n"
|
||||
" 'directsync' and 'unsafe' (default for convert)\n"
|
||||
" 'size' is the disk image size in bytes. Optional suffixes\n"
|
||||
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n"
|
||||
" and T (terabyte, 1024G) are supported. 'b' is ignored.\n"
|
||||
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\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_fmt' is the destination format\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!");
|
||||
} else {
|
||||
error_report("Invalid image size specified! You may use k, M, "
|
||||
"G or T suffixes for ");
|
||||
error_report("kilobytes, megabytes, gigabytes and terabytes.");
|
||||
"G, T, P or E suffixes for ");
|
||||
error_report("kilobytes, megabytes, gigabytes, terabytes, "
|
||||
"petabytes and exabytes.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -1642,6 +1644,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
|
||||
ImageInfoList *head = NULL;
|
||||
ImageInfoList **last = &head;
|
||||
GHashTable *filenames;
|
||||
Error *err = 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;
|
||||
}
|
||||
|
||||
info = g_new0(ImageInfo, 1);
|
||||
bdrv_collect_image_info(bs, info, filename);
|
||||
bdrv_collect_snapshots(bs, info);
|
||||
bdrv_query_image_info(bs, &info, &err);
|
||||
if (error_is_set(&err)) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
error_free(err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
elem = g_new0(ImageInfoList, 1);
|
||||
elem->value = info;
|
||||
|
2118
qemu-io-cmds.c
Normal file
2118
qemu-io-cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1704,6 +1704,47 @@ Each json-object contain the following:
|
||||
- "iops": limit total I/O operations per second (json-int)
|
||||
- "iops_rd": limit read 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
|
||||
and the VM is configured to stop on errors. It's always reset
|
||||
@ -1724,14 +1765,38 @@ Example:
|
||||
"ro":false,
|
||||
"drv":"qcow2",
|
||||
"encrypted":false,
|
||||
"file":"disks/test.img",
|
||||
"backing_file_depth":0,
|
||||
"file":"disks/test.qcow2",
|
||||
"backing_file_depth":1,
|
||||
"bps":1000000,
|
||||
"bps_rd":0,
|
||||
"bps_wr":0,
|
||||
"iops":1000000,
|
||||
"iops_rd":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"
|
||||
},
|
||||
|
@ -64,6 +64,7 @@ enum {
|
||||
};
|
||||
|
||||
enum {
|
||||
DEV = 0x10,
|
||||
LBA = 0x40,
|
||||
};
|
||||
|
||||
@ -76,6 +77,7 @@ enum {
|
||||
enum {
|
||||
CMD_READ_DMA = 0xc8,
|
||||
CMD_WRITE_DMA = 0xca,
|
||||
CMD_FLUSH_CACHE = 0xe7,
|
||||
CMD_IDENTIFY = 0xec,
|
||||
|
||||
CMDF_ABORT = 0x100,
|
||||
@ -394,7 +396,7 @@ static void test_identify(void)
|
||||
|
||||
/* Read in the IDENTIFY buffer and check registers */
|
||||
data = inb(IDE_BASE + reg_device);
|
||||
g_assert_cmpint(data & 0x10, ==, 0);
|
||||
g_assert_cmpint(data & DEV, ==, 0);
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
data = inb(IDE_BASE + reg_status);
|
||||
@ -423,6 +425,43 @@ static void test_identify(void)
|
||||
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)
|
||||
{
|
||||
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/teardown", test_bmdma_teardown);
|
||||
|
||||
qtest_add_func("/ide/flush", test_flush);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
/* Cleanup */
|
||||
|
@ -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
|
||||
|
||||
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: kilobytes, megabytes, gigabytes and terabytes.
|
||||
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
|
||||
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
|
||||
|
||||
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: kilobytes, megabytes, gigabytes and terabytes.
|
||||
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
|
||||
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
|
||||
qemu-img: Parameter 'size' expects a size
|
||||
|
@ -107,6 +107,27 @@ int qemu_strnlen(const char *s, int max_len)
|
||||
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 t;
|
||||
@ -267,6 +288,10 @@ static int64_t suffix_mul(char suffix, int64_t unit)
|
||||
return unit * unit * unit;
|
||||
case STRTOSZ_DEFSUFFIX_TB:
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user