mirror of https://github.com/xemu-project/xemu.git
Block patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJS4peVAAoJEH8JsnLIjy/WIfcP/0fezyrowrBSMNpqpbOI9bpy mjbo7tI1ZdvR2GlAsxZ8ollMNYtC611MHbnq48ywhRHJPMBPN9CYPJqwGtyzpA00 f3gw2Lct9YMPRu0wiRclFaSSdPXVohqdL16AFRcsz8FVTD8MQkgAMu0q4+dHhs6p G2BvIyo3fjuS7jLJuXUmyyB4cOHI9/gw+B/ANF1BFWl0llcb5jLVxy44NGLwqovH Nxe4GbnMb6QYUhwibzGwMnImm8nrLHbocv0IMyX4zqBLap7BfQUJ9zs4kPUnd1rB /sB5dBKj5r7l8cuOyCZRkGo4gc1FyddjWlU5UpcqBuk92PXavsRM74C6H42CS3y1 4pcEu0fAYBdfEaDAINUdbfT1eyMVjiNiFZT1NAgcz5JkTnuG3dThaDAKQEx7FCuq iP2n1nlpwSs9mzeifnZH6INyyEjoJzvBh3cIdBi3yEIJ4OVSLyrxi2djTEnRvAeh YXyMuJlscjelJpwsMCsWnkKp4vAuiXrefdQbKqZ9HwPD5oJmWi4PNvRndBiVj+Na CJfZROwAej3sAusUHbCB5pt5v+Fdb64bmTO5ph42ChnKIG9T2Piz2IQJPPTeVe9h cdtkd3tIV5xB6iXjy2xb4Y6y6Xqs1qEs91dyjQE1GszeBcairutwUmI0oYwHY5eJ tVyL8ptpNvfj9vKnDvE+ =wLul -----END PGP SIGNATURE----- Merge remote-tracking branch 'kwolf/tags/for-anthony' into staging Block patches # gpg: Signature made Fri 24 Jan 2014 08:40:53 AM PST using RSA key ID C88F2FD6 # gpg: Can't check signature: public key not found * kwolf/tags/for-anthony: (93 commits) block: Switch bdrv_io_limits_intercept() to byte granularity qemu-iotests: Test pwritev RMW logic qemu-io: New command 'sleep' blkdebug: Make required alignment configurable iscsi: Set bs->request_alignment block: Make bdrv_pwrite() a bdrv_prwv_co() wrapper block: Make bdrv_pread() a bdrv_prwv_co() wrapper block: Change coroutine wrapper to byte granularity block: Assert serialisation assumptions in pwritev block: Align requests in bdrv_co_do_pwritev() block: Allow wait_serialising_requests() at any point block: Make overlap range for serialisation dynamic block: Generalise and optimise COR serialisation block: Make zero-after-EOF work with larger alignment block: Allow waiting for overlapping requests between begin/end block: Switch BdrvTrackedRequest to byte granularity block: Introduce bdrv_co_do_pwritev() block: write: Handle COR dependency after I/O throttling block: Introduce bdrv_aligned_pwritev() block: Introduce bdrv_co_do_preadv() ... Message-id: 1390584136-24703-1-git-send-email-kwolf@redhat.com Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
commit
0d688cf7d8
|
@ -43,7 +43,6 @@ libcacard-y += libcacard/vcardt.o
|
||||||
ifeq ($(CONFIG_SOFTMMU),y)
|
ifeq ($(CONFIG_SOFTMMU),y)
|
||||||
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
||||||
common-obj-y += net/
|
common-obj-y += net/
|
||||||
common-obj-y += readline.o
|
|
||||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||||
|
|
|
@ -181,8 +181,13 @@ static int coroutine_fn backup_before_write_notify(
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
BdrvTrackedRequest *req = opaque;
|
BdrvTrackedRequest *req = opaque;
|
||||||
|
int64_t sector_num = req->offset >> BDRV_SECTOR_BITS;
|
||||||
|
int nb_sectors = req->bytes >> BDRV_SECTOR_BITS;
|
||||||
|
|
||||||
return backup_do_cow(req->bs, req->sector_num, req->nb_sectors, NULL);
|
assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||||
|
assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||||
|
|
||||||
|
return backup_do_cow(req->bs, sector_num, nb_sectors, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||||
|
|
|
@ -186,6 +186,14 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||||
|
|
||||||
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
||||||
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
||||||
|
|
||||||
|
[BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head",
|
||||||
|
[BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head",
|
||||||
|
[BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail",
|
||||||
|
[BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail",
|
||||||
|
[BLKDBG_PWRITEV] = "pwritev",
|
||||||
|
[BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
|
||||||
|
[BLKDBG_PWRITEV_DONE] = "pwritev_done",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||||
|
@ -271,19 +279,33 @@ static void remove_rule(BlkdebugRule *rule)
|
||||||
g_free(rule);
|
g_free(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_config(BDRVBlkdebugState *s, const char *filename)
|
static int read_config(BDRVBlkdebugState *s, const char *filename,
|
||||||
|
QDict *options, Error **errp)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
struct add_rule_data d;
|
struct add_rule_data d;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
f = fopen(filename, "r");
|
if (filename) {
|
||||||
if (f == NULL) {
|
f = fopen(filename, "r");
|
||||||
return -errno;
|
if (f == NULL) {
|
||||||
|
error_setg_errno(errp, errno, "Could not read blkdebug config file");
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qemu_config_parse(f, config_groups, filename);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Could not parse blkdebug config file");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemu_config_parse(f, config_groups, filename);
|
qemu_config_parse_qdict(options, config_groups, &local_err);
|
||||||
if (ret < 0) {
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +320,9 @@ static int read_config(BDRVBlkdebugState *s, const char *filename)
|
||||||
fail:
|
fail:
|
||||||
qemu_opts_reset(&inject_error_opts);
|
qemu_opts_reset(&inject_error_opts);
|
||||||
qemu_opts_reset(&set_state_opts);
|
qemu_opts_reset(&set_state_opts);
|
||||||
fclose(f);
|
if (f) {
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +334,9 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
||||||
|
|
||||||
/* Parse the blkdebug: prefix */
|
/* Parse the blkdebug: prefix */
|
||||||
if (!strstart(filename, "blkdebug:", &filename)) {
|
if (!strstart(filename, "blkdebug:", &filename)) {
|
||||||
error_setg(errp, "File name string must start with 'blkdebug:'");
|
/* There was no prefix; therefore, all options have to be already
|
||||||
|
present in the QDict (except for the filename) */
|
||||||
|
qdict_put(options, "x-image", qstring_from_str(filename));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,6 +372,11 @@ static QemuOptsList runtime_opts = {
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "[internal use only, will be removed]",
|
.help = "[internal use only, will be removed]",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "align",
|
||||||
|
.type = QEMU_OPT_SIZE,
|
||||||
|
.help = "Required alignment in bytes",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -356,7 +387,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
BDRVBlkdebugState *s = bs->opaque;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *filename, *config;
|
const char *config;
|
||||||
|
uint64_t align;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
|
@ -367,30 +399,31 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read rules from config file */
|
/* Read rules from config file or command line options */
|
||||||
config = qemu_opt_get(opts, "config");
|
config = qemu_opt_get(opts, "config");
|
||||||
if (config) {
|
ret = read_config(s, config, options, errp);
|
||||||
ret = read_config(s, config);
|
if (ret) {
|
||||||
if (ret < 0) {
|
goto fail;
|
||||||
error_setg_errno(errp, -ret, "Could not read blkdebug config file");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set initial state */
|
/* Set initial state */
|
||||||
s->state = 1;
|
s->state = 1;
|
||||||
|
|
||||||
/* Open the backing file */
|
/* Open the backing file */
|
||||||
filename = qemu_opt_get(opts, "x-image");
|
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||||
if (filename == NULL) {
|
flags, true, false, &local_err);
|
||||||
error_setg(errp, "Could not retrieve image file name");
|
if (ret < 0) {
|
||||||
ret = -EINVAL;
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
|
/* Set request alignment */
|
||||||
if (ret < 0) {
|
align = qemu_opt_get_size(opts, "align", bs->request_alignment);
|
||||||
error_propagate(errp, local_err);
|
if (align > 0 && align < INT_MAX && !(align & (align - 1))) {
|
||||||
|
bs->request_alignment = align;
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Invalid alignment");
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,9 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
||||||
|
|
||||||
/* Parse the blkverify: prefix */
|
/* Parse the blkverify: prefix */
|
||||||
if (!strstart(filename, "blkverify:", &filename)) {
|
if (!strstart(filename, "blkverify:", &filename)) {
|
||||||
error_setg(errp, "File name string must start with 'blkverify:'");
|
/* There was no prefix; therefore, all options have to be already
|
||||||
|
present in the QDict (except for the filename) */
|
||||||
|
qdict_put(options, "x-image", qstring_from_str(filename));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +124,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
BDRVBlkverifyState *s = bs->opaque;
|
BDRVBlkverifyState *s = bs->opaque;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *filename, *raw;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
|
@ -133,33 +134,19 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse the raw image filename */
|
/* Open the raw file */
|
||||||
raw = qemu_opt_get(opts, "x-raw");
|
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||||
if (raw == NULL) {
|
"raw", flags, true, false, &local_err);
|
||||||
error_setg(errp, "Could not retrieve raw image filename");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open the test file */
|
/* Open the test file */
|
||||||
filename = qemu_opt_get(opts, "x-image");
|
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||||
if (filename == NULL) {
|
"test", flags, false, false, &local_err);
|
||||||
error_setg(errp, "Could not retrieve test image filename");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->test_file = bdrv_new("");
|
|
||||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
bdrv_unref(s->test_file);
|
|
||||||
s->test_file = NULL;
|
s->test_file = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -417,7 +404,7 @@ static BlockDriver bdrv_blkverify = {
|
||||||
.bdrv_aio_writev = blkverify_aio_writev,
|
.bdrv_aio_writev = blkverify_aio_writev,
|
||||||
.bdrv_aio_flush = blkverify_aio_flush,
|
.bdrv_aio_flush = blkverify_aio_flush,
|
||||||
|
|
||||||
.bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
|
.authorizations = { true, false },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_blkverify_init(void)
|
static void bdrv_blkverify_init(void)
|
||||||
|
|
|
@ -351,7 +351,8 @@ static int cow_create(const char *filename, QEMUOptionParameter *options,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
|
81
block/curl.c
81
block/curl.c
|
@ -34,6 +34,11 @@
|
||||||
#define DPRINTF(fmt, ...) do { } while (0)
|
#define DPRINTF(fmt, ...) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LIBCURL_VERSION_NUM >= 0x071000
|
||||||
|
/* The multi interface timer callback was introduced in 7.16.0 */
|
||||||
|
#define NEED_CURL_TIMER_CALLBACK
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
||||||
CURLPROTO_FTP | CURLPROTO_FTPS | \
|
CURLPROTO_FTP | CURLPROTO_FTPS | \
|
||||||
CURLPROTO_TFTP)
|
CURLPROTO_TFTP)
|
||||||
|
@ -77,6 +82,7 @@ typedef struct CURLState
|
||||||
|
|
||||||
typedef struct BDRVCURLState {
|
typedef struct BDRVCURLState {
|
||||||
CURLM *multi;
|
CURLM *multi;
|
||||||
|
QEMUTimer timer;
|
||||||
size_t len;
|
size_t len;
|
||||||
CURLState states[CURL_NUM_STATES];
|
CURLState states[CURL_NUM_STATES];
|
||||||
char *url;
|
char *url;
|
||||||
|
@ -87,6 +93,23 @@ typedef struct BDRVCURLState {
|
||||||
static void curl_clean_state(CURLState *s);
|
static void curl_clean_state(CURLState *s);
|
||||||
static void curl_multi_do(void *arg);
|
static void curl_multi_do(void *arg);
|
||||||
|
|
||||||
|
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||||
|
static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||||
|
{
|
||||||
|
BDRVCURLState *s = opaque;
|
||||||
|
|
||||||
|
DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
|
||||||
|
if (timeout_ms == -1) {
|
||||||
|
timer_del(&s->timer);
|
||||||
|
} else {
|
||||||
|
int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000;
|
||||||
|
timer_mod(&s->timer,
|
||||||
|
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||||
void *s, void *sp)
|
void *s, void *sp)
|
||||||
{
|
{
|
||||||
|
@ -209,20 +232,10 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
|
||||||
return FIND_RET_NONE;
|
return FIND_RET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void curl_multi_do(void *arg)
|
static void curl_multi_read(BDRVCURLState *s)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s = (BDRVCURLState *)arg;
|
|
||||||
int running;
|
|
||||||
int r;
|
|
||||||
int msgs_in_queue;
|
int msgs_in_queue;
|
||||||
|
|
||||||
if (!s->multi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
do {
|
|
||||||
r = curl_multi_socket_all(s->multi, &running);
|
|
||||||
} while(r == CURLM_CALL_MULTI_PERFORM);
|
|
||||||
|
|
||||||
/* Try to find done transfers, so we can free the easy
|
/* Try to find done transfers, so we can free the easy
|
||||||
* handle again. */
|
* handle again. */
|
||||||
do {
|
do {
|
||||||
|
@ -266,6 +279,41 @@ static void curl_multi_do(void *arg)
|
||||||
} while(msgs_in_queue);
|
} while(msgs_in_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void curl_multi_do(void *arg)
|
||||||
|
{
|
||||||
|
BDRVCURLState *s = (BDRVCURLState *)arg;
|
||||||
|
int running;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!s->multi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
r = curl_multi_socket_all(s->multi, &running);
|
||||||
|
} while(r == CURLM_CALL_MULTI_PERFORM);
|
||||||
|
|
||||||
|
curl_multi_read(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void curl_multi_timeout_do(void *arg)
|
||||||
|
{
|
||||||
|
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||||
|
BDRVCURLState *s = (BDRVCURLState *)arg;
|
||||||
|
int running;
|
||||||
|
|
||||||
|
if (!s->multi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
||||||
|
|
||||||
|
curl_multi_read(s);
|
||||||
|
#else
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static CURLState *curl_init_state(BDRVCURLState *s)
|
static CURLState *curl_init_state(BDRVCURLState *s)
|
||||||
{
|
{
|
||||||
CURLState *state = NULL;
|
CURLState *state = NULL;
|
||||||
|
@ -473,12 +521,20 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
curl_easy_cleanup(state->curl);
|
curl_easy_cleanup(state->curl);
|
||||||
state->curl = NULL;
|
state->curl = NULL;
|
||||||
|
|
||||||
|
aio_timer_init(bdrv_get_aio_context(bs), &s->timer,
|
||||||
|
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||||
|
curl_multi_timeout_do, s);
|
||||||
|
|
||||||
// Now we know the file exists and its size, so let's
|
// Now we know the file exists and its size, so let's
|
||||||
// initialize the multi interface!
|
// initialize the multi interface!
|
||||||
|
|
||||||
s->multi = curl_multi_init();
|
s->multi = curl_multi_init();
|
||||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
|
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
|
||||||
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
|
||||||
|
#ifdef NEED_CURL_TIMER_CALLBACK
|
||||||
|
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
|
||||||
|
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
|
||||||
|
#endif
|
||||||
curl_multi_do(s);
|
curl_multi_do(s);
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
|
@ -597,6 +653,9 @@ static void curl_close(BlockDriverState *bs)
|
||||||
}
|
}
|
||||||
if (s->multi)
|
if (s->multi)
|
||||||
curl_multi_cleanup(s->multi);
|
curl_multi_cleanup(s->multi);
|
||||||
|
|
||||||
|
timer_del(&s->timer);
|
||||||
|
|
||||||
g_free(s->url);
|
g_free(s->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
314
block/gluster.c
314
block/gluster.c
|
@ -21,19 +21,15 @@
|
||||||
#include "qemu/uri.h"
|
#include "qemu/uri.h"
|
||||||
|
|
||||||
typedef struct GlusterAIOCB {
|
typedef struct GlusterAIOCB {
|
||||||
BlockDriverAIOCB common;
|
|
||||||
int64_t size;
|
int64_t size;
|
||||||
int ret;
|
int ret;
|
||||||
bool *finished;
|
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
|
Coroutine *coroutine;
|
||||||
} GlusterAIOCB;
|
} GlusterAIOCB;
|
||||||
|
|
||||||
typedef struct BDRVGlusterState {
|
typedef struct BDRVGlusterState {
|
||||||
struct glfs *glfs;
|
struct glfs *glfs;
|
||||||
int fds[2];
|
|
||||||
struct glfs_fd *fd;
|
struct glfs_fd *fd;
|
||||||
int event_reader_pos;
|
|
||||||
GlusterAIOCB *event_acb;
|
|
||||||
} BDRVGlusterState;
|
} BDRVGlusterState;
|
||||||
|
|
||||||
#define GLUSTER_FD_READ 0
|
#define GLUSTER_FD_READ 0
|
||||||
|
@ -231,46 +227,32 @@ out:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_gluster_complete_aio(GlusterAIOCB *acb, BDRVGlusterState *s)
|
static void qemu_gluster_complete_aio(void *opaque)
|
||||||
{
|
{
|
||||||
int ret;
|
GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
|
||||||
bool *finished = acb->finished;
|
|
||||||
BlockDriverCompletionFunc *cb = acb->common.cb;
|
|
||||||
void *opaque = acb->common.opaque;
|
|
||||||
|
|
||||||
if (!acb->ret || acb->ret == acb->size) {
|
qemu_bh_delete(acb->bh);
|
||||||
ret = 0; /* Success */
|
acb->bh = NULL;
|
||||||
} else if (acb->ret < 0) {
|
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||||
ret = acb->ret; /* Read/Write failed */
|
|
||||||
} else {
|
|
||||||
ret = -EIO; /* Partial read/write - fail it */
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_aio_release(acb);
|
|
||||||
cb(opaque, ret);
|
|
||||||
if (finished) {
|
|
||||||
*finished = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_gluster_aio_event_reader(void *opaque)
|
/*
|
||||||
|
* AIO callback routine called from GlusterFS thread.
|
||||||
|
*/
|
||||||
|
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||||
{
|
{
|
||||||
BDRVGlusterState *s = opaque;
|
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
do {
|
if (!ret || ret == acb->size) {
|
||||||
char *p = (char *)&s->event_acb;
|
acb->ret = 0; /* Success */
|
||||||
|
} else if (ret < 0) {
|
||||||
|
acb->ret = ret; /* Read/Write failed */
|
||||||
|
} else {
|
||||||
|
acb->ret = -EIO; /* Partial read/write - fail it */
|
||||||
|
}
|
||||||
|
|
||||||
ret = read(s->fds[GLUSTER_FD_READ], p + s->event_reader_pos,
|
acb->bh = qemu_bh_new(qemu_gluster_complete_aio, acb);
|
||||||
sizeof(s->event_acb) - s->event_reader_pos);
|
qemu_bh_schedule(acb->bh);
|
||||||
if (ret > 0) {
|
|
||||||
s->event_reader_pos += ret;
|
|
||||||
if (s->event_reader_pos == sizeof(s->event_acb)) {
|
|
||||||
s->event_reader_pos = 0;
|
|
||||||
qemu_gluster_complete_aio(s->event_acb, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (ret < 0 && errno == EINTR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Convert to fine grained options */
|
/* TODO Convert to fine grained options */
|
||||||
|
@ -309,7 +291,6 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
|
||||||
|
|
||||||
s->glfs = qemu_gluster_init(gconf, filename);
|
s->glfs = qemu_gluster_init(gconf, filename);
|
||||||
if (!s->glfs) {
|
if (!s->glfs) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
|
@ -329,18 +310,8 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||||
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
||||||
if (!s->fd) {
|
if (!s->fd) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemu_pipe(s->fds);
|
|
||||||
if (ret < 0) {
|
|
||||||
ret = -errno;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
fcntl(s->fds[GLUSTER_FD_READ], F_SETFL, O_NONBLOCK);
|
|
||||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ],
|
|
||||||
qemu_gluster_aio_event_reader, NULL, s);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
qemu_gluster_gconf_free(gconf);
|
qemu_gluster_gconf_free(gconf);
|
||||||
|
@ -356,12 +327,65 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||||
|
BDRVGlusterState *s = bs->opaque;
|
||||||
|
off_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
|
acb->size = size;
|
||||||
|
acb->ret = 0;
|
||||||
|
acb->coroutine = qemu_coroutine_self();
|
||||||
|
|
||||||
|
ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||||
|
if (ret < 0) {
|
||||||
|
ret = -errno;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
ret = acb->ret;
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_slice_free(GlusterAIOCB, acb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool gluster_supports_zerofill(void)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
||||||
|
int64_t size)
|
||||||
|
{
|
||||||
|
return glfs_zerofill(fd, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline bool gluster_supports_zerofill(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
|
||||||
|
int64_t size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int qemu_gluster_create(const char *filename,
|
static int qemu_gluster_create(const char *filename,
|
||||||
QEMUOptionParameter *options, Error **errp)
|
QEMUOptionParameter *options, Error **errp)
|
||||||
{
|
{
|
||||||
struct glfs *glfs;
|
struct glfs *glfs;
|
||||||
struct glfs_fd *fd;
|
struct glfs_fd *fd;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int prealloc = 0;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||||
|
|
||||||
|
@ -374,6 +398,19 @@ static int qemu_gluster_create(const char *filename,
|
||||||
while (options && options->name) {
|
while (options && options->name) {
|
||||||
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
|
||||||
total_size = options->value.n / BDRV_SECTOR_SIZE;
|
total_size = options->value.n / BDRV_SECTOR_SIZE;
|
||||||
|
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
|
||||||
|
if (!options->value.s || !strcmp(options->value.s, "off")) {
|
||||||
|
prealloc = 0;
|
||||||
|
} else if (!strcmp(options->value.s, "full") &&
|
||||||
|
gluster_supports_zerofill()) {
|
||||||
|
prealloc = 1;
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Invalid preallocation mode: '%s'"
|
||||||
|
" or GlusterFS doesn't support zerofill API",
|
||||||
|
options->value.s);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
options++;
|
options++;
|
||||||
}
|
}
|
||||||
|
@ -383,9 +420,15 @@ static int qemu_gluster_create(const char *filename,
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
} else {
|
} else {
|
||||||
if (glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
|
if (!glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE)) {
|
||||||
|
if (prealloc && qemu_gluster_zerofill(fd, 0,
|
||||||
|
total_size * BDRV_SECTOR_SIZE)) {
|
||||||
|
ret = -errno;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glfs_close(fd) != 0) {
|
if (glfs_close(fd) != 0) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
}
|
}
|
||||||
|
@ -398,58 +441,18 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_gluster_aio_cancel(BlockDriverAIOCB *blockacb)
|
static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
||||||
{
|
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
|
||||||
GlusterAIOCB *acb = (GlusterAIOCB *)blockacb;
|
|
||||||
bool finished = false;
|
|
||||||
|
|
||||||
acb->finished = &finished;
|
|
||||||
while (!finished) {
|
|
||||||
qemu_aio_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AIOCBInfo gluster_aiocb_info = {
|
|
||||||
.aiocb_size = sizeof(GlusterAIOCB),
|
|
||||||
.cancel = qemu_gluster_aio_cancel,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
|
||||||
{
|
|
||||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
|
||||||
BlockDriverState *bs = acb->common.bs;
|
|
||||||
BDRVGlusterState *s = bs->opaque;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
acb->ret = ret;
|
|
||||||
retval = qemu_write_full(s->fds[GLUSTER_FD_WRITE], &acb, sizeof(acb));
|
|
||||||
if (retval != sizeof(acb)) {
|
|
||||||
/*
|
|
||||||
* Gluster AIO callback thread failed to notify the waiting
|
|
||||||
* QEMU thread about IO completion.
|
|
||||||
*/
|
|
||||||
error_report("Gluster AIO completion failed: %s", strerror(errno));
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
|
|
||||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
|
||||||
BlockDriverCompletionFunc *cb, void *opaque, int write)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
GlusterAIOCB *acb;
|
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
size_t size;
|
size_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
off_t offset;
|
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
|
||||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
|
||||||
acb->size = size;
|
acb->size = size;
|
||||||
acb->ret = 0;
|
acb->ret = 0;
|
||||||
acb->finished = NULL;
|
acb->coroutine = qemu_coroutine_self();
|
||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
|
||||||
|
@ -460,13 +463,16 @@ static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
ret = -errno;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return &acb->common;
|
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
ret = acb->ret;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_aio_release(acb);
|
g_slice_free(GlusterAIOCB, acb);
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
|
@ -482,71 +488,68 @@ static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriverAIOCB *qemu_gluster_aio_readv(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
|
||||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||||
BlockDriverCompletionFunc *cb, void *opaque)
|
|
||||||
{
|
{
|
||||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
|
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriverAIOCB *qemu_gluster_aio_writev(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||||
BlockDriverCompletionFunc *cb, void *opaque)
|
|
||||||
{
|
{
|
||||||
return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
|
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriverAIOCB *qemu_gluster_aio_flush(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
|
||||||
BlockDriverCompletionFunc *cb, void *opaque)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
GlusterAIOCB *acb;
|
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
|
|
||||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
|
||||||
acb->size = 0;
|
acb->size = 0;
|
||||||
acb->ret = 0;
|
acb->ret = 0;
|
||||||
acb->finished = NULL;
|
acb->coroutine = qemu_coroutine_self();
|
||||||
|
|
||||||
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
ret = -errno;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return &acb->common;
|
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
ret = acb->ret;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_aio_release(acb);
|
g_slice_free(GlusterAIOCB, acb);
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||||
static BlockDriverAIOCB *qemu_gluster_aio_discard(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, BlockDriverCompletionFunc *cb,
|
int64_t sector_num, int nb_sectors)
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
GlusterAIOCB *acb;
|
GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
size_t size;
|
size_t size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
off_t offset;
|
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
offset = sector_num * BDRV_SECTOR_SIZE;
|
|
||||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
|
|
||||||
acb->size = 0;
|
acb->size = 0;
|
||||||
acb->ret = 0;
|
acb->ret = 0;
|
||||||
acb->finished = NULL;
|
acb->coroutine = qemu_coroutine_self();
|
||||||
|
|
||||||
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
ret = -errno;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return &acb->common;
|
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
ret = acb->ret;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_aio_release(acb);
|
g_slice_free(GlusterAIOCB, acb);
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -581,10 +584,6 @@ static void qemu_gluster_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
|
|
||||||
close(s->fds[GLUSTER_FD_READ]);
|
|
||||||
close(s->fds[GLUSTER_FD_WRITE]);
|
|
||||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (s->fd) {
|
if (s->fd) {
|
||||||
glfs_close(s->fd);
|
glfs_close(s->fd);
|
||||||
s->fd = NULL;
|
s->fd = NULL;
|
||||||
|
@ -604,6 +603,11 @@ static QEMUOptionParameter qemu_gluster_create_options[] = {
|
||||||
.type = OPT_SIZE,
|
.type = OPT_SIZE,
|
||||||
.help = "Virtual disk size"
|
.help = "Virtual disk size"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = BLOCK_OPT_PREALLOC,
|
||||||
|
.type = OPT_STRING,
|
||||||
|
.help = "Preallocation mode (allowed values: off, full)"
|
||||||
|
},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -618,12 +622,15 @@ static BlockDriver bdrv_gluster = {
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_truncate = qemu_gluster_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.create_options = qemu_gluster_create_options,
|
.create_options = qemu_gluster_create_options,
|
||||||
};
|
};
|
||||||
|
@ -639,12 +646,15 @@ static BlockDriver bdrv_gluster_tcp = {
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_truncate = qemu_gluster_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.create_options = qemu_gluster_create_options,
|
.create_options = qemu_gluster_create_options,
|
||||||
};
|
};
|
||||||
|
@ -660,12 +670,15 @@ static BlockDriver bdrv_gluster_unix = {
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_truncate = qemu_gluster_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.create_options = qemu_gluster_create_options,
|
.create_options = qemu_gluster_create_options,
|
||||||
};
|
};
|
||||||
|
@ -681,12 +694,15 @@ static BlockDriver bdrv_gluster_rdma = {
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||||
.bdrv_truncate = qemu_gluster_truncate,
|
.bdrv_truncate = qemu_gluster_truncate,
|
||||||
.bdrv_aio_readv = qemu_gluster_aio_readv,
|
.bdrv_co_readv = qemu_gluster_co_readv,
|
||||||
.bdrv_aio_writev = qemu_gluster_aio_writev,
|
.bdrv_co_writev = qemu_gluster_co_writev,
|
||||||
.bdrv_aio_flush = qemu_gluster_aio_flush,
|
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||||
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
|
||||||
#ifdef CONFIG_GLUSTERFS_DISCARD
|
#ifdef CONFIG_GLUSTERFS_DISCARD
|
||||||
.bdrv_aio_discard = qemu_gluster_aio_discard,
|
.bdrv_co_discard = qemu_gluster_co_discard,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
|
.bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.create_options = qemu_gluster_create_options,
|
.create_options = qemu_gluster_create_options,
|
||||||
};
|
};
|
||||||
|
|
|
@ -308,7 +308,7 @@ retry:
|
||||||
iscsi_co_generic_cb, &iTask);
|
iscsi_co_generic_cb, &iTask);
|
||||||
if (iTask.task == NULL) {
|
if (iTask.task == NULL) {
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
return -EIO;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||||
|
@ -376,7 +376,7 @@ retry:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (iTask.task == NULL) {
|
if (iTask.task == NULL) {
|
||||||
return -EIO;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||||
|
@ -419,7 +419,7 @@ static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
|
||||||
retry:
|
retry:
|
||||||
if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0,
|
if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0,
|
||||||
0, iscsi_co_generic_cb, &iTask) == NULL) {
|
0, iscsi_co_generic_cb, &iTask) == NULL) {
|
||||||
return -EIO;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
while (!iTask.complete) {
|
||||||
|
@ -669,7 +669,7 @@ retry:
|
||||||
sector_qemu2lun(sector_num, iscsilun),
|
sector_qemu2lun(sector_num, iscsilun),
|
||||||
8 + 16, iscsi_co_generic_cb,
|
8 + 16, iscsi_co_generic_cb,
|
||||||
&iTask) == NULL) {
|
&iTask) == NULL) {
|
||||||
ret = -EIO;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +753,7 @@ coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||||
retry:
|
retry:
|
||||||
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
|
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
|
||||||
iscsi_co_generic_cb, &iTask) == NULL) {
|
iscsi_co_generic_cb, &iTask) == NULL) {
|
||||||
return -EIO;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
while (!iTask.complete) {
|
||||||
|
@ -822,7 +822,7 @@ retry:
|
||||||
iscsilun->zeroblock, iscsilun->block_size,
|
iscsilun->zeroblock, iscsilun->block_size,
|
||||||
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
|
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
|
||||||
0, 0, iscsi_co_generic_cb, &iTask) == NULL) {
|
0, 0, iscsi_co_generic_cb, &iTask) == NULL) {
|
||||||
return -EIO;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
while (!iTask.complete) {
|
||||||
|
@ -1217,6 +1217,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
|
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
|
||||||
|
bs->request_alignment = iscsilun->block_size;
|
||||||
|
|
||||||
/* Medium changer or tape. We dont have any emulation for this so this must
|
/* Medium changer or tape. We dont have any emulation for this so this must
|
||||||
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
|
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
|
||||||
|
@ -1265,23 +1266,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
sizeof(struct scsi_inquiry_block_limits));
|
sizeof(struct scsi_inquiry_block_limits));
|
||||||
scsi_free_scsi_task(task);
|
scsi_free_scsi_task(task);
|
||||||
task = NULL;
|
task = NULL;
|
||||||
|
|
||||||
if (iscsilun->bl.max_unmap < 0xffffffff) {
|
|
||||||
bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap,
|
|
||||||
iscsilun);
|
|
||||||
}
|
|
||||||
bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
|
||||||
iscsilun);
|
|
||||||
|
|
||||||
if (iscsilun->bl.max_ws_len < 0xffffffff) {
|
|
||||||
bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len,
|
|
||||||
iscsilun);
|
|
||||||
}
|
|
||||||
bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
|
||||||
iscsilun);
|
|
||||||
|
|
||||||
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
|
|
||||||
iscsilun);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||||
|
@ -1326,6 +1310,34 @@ static void iscsi_close(BlockDriverState *bs)
|
||||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int iscsi_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
|
|
||||||
|
/* We don't actually refresh here, but just return data queried in
|
||||||
|
* iscsi_open(): iscsi targets don't change their limits. */
|
||||||
|
if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
|
||||||
|
if (iscsilun->bl.max_unmap < 0xffffffff) {
|
||||||
|
bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap,
|
||||||
|
iscsilun);
|
||||||
|
}
|
||||||
|
bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
||||||
|
iscsilun);
|
||||||
|
|
||||||
|
if (iscsilun->bl.max_ws_len < 0xffffffff) {
|
||||||
|
bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len,
|
||||||
|
iscsilun);
|
||||||
|
}
|
||||||
|
bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
||||||
|
iscsilun);
|
||||||
|
|
||||||
|
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
|
||||||
|
iscsilun);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
|
@ -1438,6 +1450,7 @@ static BlockDriver bdrv_iscsi = {
|
||||||
.bdrv_getlength = iscsi_getlength,
|
.bdrv_getlength = iscsi_getlength,
|
||||||
.bdrv_get_info = iscsi_get_info,
|
.bdrv_get_info = iscsi_get_info,
|
||||||
.bdrv_truncate = iscsi_truncate,
|
.bdrv_truncate = iscsi_truncate,
|
||||||
|
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
.bdrv_co_get_block_status = iscsi_co_get_block_status,
|
||||||
|
|
|
@ -96,6 +96,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_iovec_destroy(&op->qiov);
|
||||||
g_slice_free(MirrorOp, op);
|
g_slice_free(MirrorOp, op);
|
||||||
qemu_coroutine_enter(s->common.co, NULL);
|
qemu_coroutine_enter(s->common.co, NULL);
|
||||||
}
|
}
|
||||||
|
@ -630,11 +631,49 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||||
BlockDriverCompletionFunc *cb,
|
BlockDriverCompletionFunc *cb,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
|
int64_t length, base_length;
|
||||||
|
int orig_base_flags;
|
||||||
|
|
||||||
|
orig_base_flags = bdrv_get_flags(base);
|
||||||
|
|
||||||
if (bdrv_reopen(base, bs->open_flags, errp)) {
|
if (bdrv_reopen(base, bs->open_flags, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
length = bdrv_getlength(bs);
|
||||||
|
if (length < 0) {
|
||||||
|
error_setg(errp, "Unable to determine length of %s", bs->filename);
|
||||||
|
goto error_restore_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_length = bdrv_getlength(base);
|
||||||
|
if (base_length < 0) {
|
||||||
|
error_setg(errp, "Unable to determine length of %s", base->filename);
|
||||||
|
goto error_restore_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > base_length) {
|
||||||
|
if (bdrv_truncate(base, length) < 0) {
|
||||||
|
error_setg(errp, "Top image %s is larger than base image %s, and "
|
||||||
|
"resize of base image failed",
|
||||||
|
bs->filename, base->filename);
|
||||||
|
goto error_restore_flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bdrv_ref(base);
|
bdrv_ref(base);
|
||||||
mirror_start_job(bs, base, speed, 0, 0,
|
mirror_start_job(bs, base, speed, 0, 0,
|
||||||
on_error, on_error, cb, opaque, errp,
|
on_error, on_error, cb, opaque, errp,
|
||||||
&commit_active_job_driver, false, base);
|
&commit_active_job_driver, false, base);
|
||||||
|
if (error_is_set(errp)) {
|
||||||
|
goto error_restore_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error_restore_flags:
|
||||||
|
/* ignore error and errp for bdrv_reopen, because we want to propagate
|
||||||
|
* the original error */
|
||||||
|
bdrv_reopen(base, orig_base_flags, NULL);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
114
block/qapi.c
114
block/qapi.c
|
@ -29,6 +29,60 @@
|
||||||
#include "qapi/qmp-output-visitor.h"
|
#include "qapi/qmp-output-visitor.h"
|
||||||
#include "qapi/qmp/types.h"
|
#include "qapi/qmp/types.h"
|
||||||
|
|
||||||
|
BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BlockDeviceInfo *info = g_malloc0(sizeof(*info));
|
||||||
|
|
||||||
|
info->file = g_strdup(bs->filename);
|
||||||
|
info->ro = bs->read_only;
|
||||||
|
info->drv = g_strdup(bs->drv->format_name);
|
||||||
|
info->encrypted = bs->encrypted;
|
||||||
|
info->encryption_key_missing = bdrv_key_required(bs);
|
||||||
|
|
||||||
|
if (bs->node_name[0]) {
|
||||||
|
info->has_node_name = true;
|
||||||
|
info->node_name = g_strdup(bs->node_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->backing_file[0]) {
|
||||||
|
info->has_backing_file = true;
|
||||||
|
info->backing_file = g_strdup(bs->backing_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||||
|
|
||||||
|
if (bs->io_limits_enabled) {
|
||||||
|
ThrottleConfig cfg;
|
||||||
|
throttle_get_config(&bs->throttle_state, &cfg);
|
||||||
|
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||||
|
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||||
|
info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
||||||
|
|
||||||
|
info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
|
||||||
|
info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
|
||||||
|
info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
|
||||||
|
|
||||||
|
info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||||
|
info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||||
|
info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
|
||||||
|
info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
|
||||||
|
info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||||
|
info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||||
|
|
||||||
|
info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||||
|
info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||||
|
info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
|
||||||
|
info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
|
||||||
|
info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
|
info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
|
|
||||||
|
info->has_iops_size = cfg.op_size;
|
||||||
|
info->iops_size = cfg.op_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns 0 on success, with *p_list either set to describe snapshot
|
* Returns 0 on success, with *p_list either set to describe snapshot
|
||||||
* information, or NULL because there are no snapshots. Returns -errno on
|
* information, or NULL because there are no snapshots. Returns -errno on
|
||||||
|
@ -211,60 +265,7 @@ void bdrv_query_info(BlockDriverState *bs,
|
||||||
|
|
||||||
if (bs->drv) {
|
if (bs->drv) {
|
||||||
info->has_inserted = true;
|
info->has_inserted = true;
|
||||||
info->inserted = g_malloc0(sizeof(*info->inserted));
|
info->inserted = bdrv_block_device_info(bs);
|
||||||
info->inserted->file = g_strdup(bs->filename);
|
|
||||||
info->inserted->ro = bs->read_only;
|
|
||||||
info->inserted->drv = g_strdup(bs->drv->format_name);
|
|
||||||
info->inserted->encrypted = bs->encrypted;
|
|
||||||
info->inserted->encryption_key_missing = bdrv_key_required(bs);
|
|
||||||
|
|
||||||
if (bs->backing_file[0]) {
|
|
||||||
info->inserted->has_backing_file = true;
|
|
||||||
info->inserted->backing_file = g_strdup(bs->backing_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
|
||||||
|
|
||||||
if (bs->io_limits_enabled) {
|
|
||||||
ThrottleConfig cfg;
|
|
||||||
throttle_get_config(&bs->throttle_state, &cfg);
|
|
||||||
info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
|
||||||
info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
|
||||||
info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
|
||||||
|
|
||||||
info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
|
|
||||||
info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
|
|
||||||
info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
|
|
||||||
|
|
||||||
info->inserted->has_bps_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
|
||||||
info->inserted->bps_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
|
||||||
info->inserted->has_bps_rd_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_READ].max;
|
|
||||||
info->inserted->bps_rd_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_READ].max;
|
|
||||||
info->inserted->has_bps_wr_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
|
||||||
info->inserted->bps_wr_max =
|
|
||||||
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
|
||||||
|
|
||||||
info->inserted->has_iops_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
|
||||||
info->inserted->iops_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
|
||||||
info->inserted->has_iops_rd_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_READ].max;
|
|
||||||
info->inserted->iops_rd_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_READ].max;
|
|
||||||
info->inserted->has_iops_wr_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
|
||||||
info->inserted->iops_wr_max =
|
|
||||||
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
|
||||||
|
|
||||||
info->inserted->has_iops_size = cfg.op_size;
|
|
||||||
info->inserted->iops_size = cfg.op_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs0 = bs;
|
bs0 = bs;
|
||||||
p_image_info = &info->inserted->image;
|
p_image_info = &info->inserted->image;
|
||||||
|
@ -318,6 +319,11 @@ BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||||
s->parent = bdrv_query_stats(bs->file);
|
s->parent = bdrv_query_stats(bs->file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bs->backing_hd) {
|
||||||
|
s->has_backing = true;
|
||||||
|
s->backing = bdrv_query_stats(bs->backing_hd);
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -691,7 +691,8 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
|
|
@ -718,7 +718,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
|
||||||
|
|
||||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
||||||
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
||||||
|
@ -751,6 +750,15 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int qcow2_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
|
||||||
|
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
@ -1483,7 +1491,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2268,6 +2276,7 @@ static BlockDriver bdrv_qcow2 = {
|
||||||
|
|
||||||
.bdrv_change_backing_file = qcow2_change_backing_file,
|
.bdrv_change_backing_file = qcow2_change_backing_file,
|
||||||
|
|
||||||
|
.bdrv_refresh_limits = qcow2_refresh_limits,
|
||||||
.bdrv_invalidate_cache = qcow2_invalidate_cache,
|
.bdrv_invalidate_cache = qcow2_invalidate_cache,
|
||||||
|
|
||||||
.create_options = qcow2_create_options,
|
.create_options = qcow2_create_options,
|
||||||
|
|
|
@ -340,11 +340,11 @@ typedef enum QCow2MetadataOverlap {
|
||||||
#define QCOW2_OL_ALL \
|
#define QCOW2_OL_ALL \
|
||||||
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
|
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
|
||||||
|
|
||||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL
|
||||||
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL
|
||||||
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
||||||
|
|
||||||
#define REFT_OFFSET_MASK 0xffffffffffffff00ULL
|
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
|
||||||
|
|
||||||
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
|
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
|
||||||
{
|
{
|
||||||
|
|
15
block/qed.c
15
block/qed.c
|
@ -495,7 +495,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
|
|
||||||
s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||||
qed_need_check_timer_cb, s);
|
qed_need_check_timer_cb, s);
|
||||||
|
|
||||||
|
@ -507,6 +506,15 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bdrv_qed_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQEDState *s = bs->opaque;
|
||||||
|
|
||||||
|
bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* We have nothing to do for QED reopen, stubs just return
|
/* We have nothing to do for QED reopen, stubs just return
|
||||||
* success */
|
* success */
|
||||||
static int bdrv_qed_reopen_prepare(BDRVReopenState *state,
|
static int bdrv_qed_reopen_prepare(BDRVReopenState *state,
|
||||||
|
@ -563,8 +571,8 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB,
|
ret = bdrv_file_open(&bs, filename, NULL, NULL,
|
||||||
&local_err);
|
BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
@ -1616,6 +1624,7 @@ static BlockDriver bdrv_qed = {
|
||||||
.bdrv_truncate = bdrv_qed_truncate,
|
.bdrv_truncate = bdrv_qed_truncate,
|
||||||
.bdrv_getlength = bdrv_qed_getlength,
|
.bdrv_getlength = bdrv_qed_getlength,
|
||||||
.bdrv_get_info = bdrv_qed_get_info,
|
.bdrv_get_info = bdrv_qed_get_info,
|
||||||
|
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
||||||
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
|
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
|
||||||
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
|
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
|
||||||
.bdrv_check = bdrv_qed_check,
|
.bdrv_check = bdrv_qed_check,
|
||||||
|
|
|
@ -127,6 +127,8 @@ typedef struct BDRVRawState {
|
||||||
int fd;
|
int fd;
|
||||||
int type;
|
int type;
|
||||||
int open_flags;
|
int open_flags;
|
||||||
|
size_t buf_align;
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
/* linux floppy specific */
|
/* linux floppy specific */
|
||||||
int64_t fd_open_time;
|
int64_t fd_open_time;
|
||||||
|
@ -213,6 +215,76 @@ static int raw_normalize_devicepath(const char **filename)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void raw_probe_alignment(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
char *buf;
|
||||||
|
unsigned int sector_size;
|
||||||
|
|
||||||
|
/* For /dev/sg devices the alignment is not really used.
|
||||||
|
With buffered I/O, we don't have any restrictions. */
|
||||||
|
if (bs->sg || !(s->open_flags & O_DIRECT)) {
|
||||||
|
bs->request_alignment = 1;
|
||||||
|
s->buf_align = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try a few ioctls to get the right size */
|
||||||
|
bs->request_alignment = 0;
|
||||||
|
s->buf_align = 0;
|
||||||
|
|
||||||
|
#ifdef BLKSSZGET
|
||||||
|
if (ioctl(s->fd, BLKSSZGET, §or_size) >= 0) {
|
||||||
|
bs->request_alignment = sector_size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef DKIOCGETBLOCKSIZE
|
||||||
|
if (ioctl(s->fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) {
|
||||||
|
bs->request_alignment = sector_size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef DIOCGSECTORSIZE
|
||||||
|
if (ioctl(s->fd, DIOCGSECTORSIZE, §or_size) >= 0) {
|
||||||
|
bs->request_alignment = sector_size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_XFS
|
||||||
|
if (s->is_xfs) {
|
||||||
|
struct dioattr da;
|
||||||
|
if (xfsctl(NULL, s->fd, XFS_IOC_DIOINFO, &da) >= 0) {
|
||||||
|
bs->request_alignment = da.d_miniosz;
|
||||||
|
/* The kernel returns wrong information for d_mem */
|
||||||
|
/* s->buf_align = da.d_mem; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If we could not get the sizes so far, we can only guess them */
|
||||||
|
if (!s->buf_align) {
|
||||||
|
size_t align;
|
||||||
|
buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE);
|
||||||
|
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
|
||||||
|
if (pread(s->fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) {
|
||||||
|
s->buf_align = align;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qemu_vfree(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bs->request_alignment) {
|
||||||
|
size_t align;
|
||||||
|
buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE);
|
||||||
|
for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
|
||||||
|
if (pread(s->fd, buf, align, 0) >= 0) {
|
||||||
|
bs->request_alignment = align;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qemu_vfree(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void raw_parse_flags(int bdrv_flags, int *open_flags)
|
static void raw_parse_flags(int bdrv_flags, int *open_flags)
|
||||||
{
|
{
|
||||||
assert(open_flags != NULL);
|
assert(open_flags != NULL);
|
||||||
|
@ -463,7 +535,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void raw_reopen_commit(BDRVReopenState *state)
|
static void raw_reopen_commit(BDRVReopenState *state)
|
||||||
{
|
{
|
||||||
BDRVRawReopenState *raw_s = state->opaque;
|
BDRVRawReopenState *raw_s = state->opaque;
|
||||||
|
@ -499,23 +570,15 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
||||||
state->opaque = NULL;
|
state->opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int raw_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
|
||||||
/* XXX: use host sector size if necessary with:
|
raw_probe_alignment(bs);
|
||||||
#ifdef DIOCGSECTORSIZE
|
bs->bl.opt_mem_alignment = s->buf_align;
|
||||||
{
|
|
||||||
unsigned int sectorsize = 512;
|
return 0;
|
||||||
if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) &&
|
}
|
||||||
sectorsize > bufsize)
|
|
||||||
bufsize = sectorsize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_COCOA
|
|
||||||
uint32_t blockSize = 512;
|
|
||||||
if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
|
|
||||||
bufsize = blockSize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
|
||||||
{
|
{
|
||||||
|
@ -1363,6 +1426,7 @@ static BlockDriver bdrv_file = {
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
.bdrv_aio_discard = raw_aio_discard,
|
.bdrv_aio_discard = raw_aio_discard,
|
||||||
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
|
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
|
@ -1740,6 +1804,7 @@ static BlockDriver bdrv_host_device = {
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
.bdrv_aio_discard = hdev_aio_discard,
|
.bdrv_aio_discard = hdev_aio_discard,
|
||||||
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
|
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
|
@ -1871,6 +1936,7 @@ static BlockDriver bdrv_host_floppy = {
|
||||||
.bdrv_aio_readv = raw_aio_readv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
|
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
|
@ -1981,6 +2047,7 @@ static BlockDriver bdrv_host_cdrom = {
|
||||||
.bdrv_aio_readv = raw_aio_readv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
|
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
|
@ -2110,6 +2177,7 @@ static BlockDriver bdrv_host_cdrom = {
|
||||||
.bdrv_aio_readv = raw_aio_readv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
|
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_truncate = raw_truncate,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_getlength = raw_getlength,
|
||||||
|
|
|
@ -202,6 +202,35 @@ static int set_sparse(int fd)
|
||||||
NULL, 0, NULL, 0, &returned, NULL);
|
NULL, 0, NULL, 0, &returned, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void raw_probe_alignment(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
DWORD sectorsPerCluster, freeClusters, totalClusters, count;
|
||||||
|
DISK_GEOMETRY_EX dg;
|
||||||
|
BOOL status;
|
||||||
|
|
||||||
|
if (s->type == FTYPE_CD) {
|
||||||
|
bs->request_alignment = 2048;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (s->type == FTYPE_HARDDISK) {
|
||||||
|
status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
||||||
|
NULL, 0, &dg, sizeof(dg), &count, NULL);
|
||||||
|
if (status != 0) {
|
||||||
|
bs->request_alignment = dg.Geometry.BytesPerSector;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* try GetDiskFreeSpace too */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->drive_path[0]) {
|
||||||
|
GetDiskFreeSpace(s->drive_path, §orsPerCluster,
|
||||||
|
&dg.Geometry.BytesPerSector,
|
||||||
|
&freeClusters, &totalClusters);
|
||||||
|
bs->request_alignment = dg.Geometry.BytesPerSector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
|
static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
|
||||||
{
|
{
|
||||||
assert(access_flags != NULL);
|
assert(access_flags != NULL);
|
||||||
|
@ -269,6 +298,17 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filename[0] && filename[1] == ':') {
|
||||||
|
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]);
|
||||||
|
} else if (filename[0] == '\\' && filename[1] == '\\') {
|
||||||
|
s->drive_path[0] = 0;
|
||||||
|
} else {
|
||||||
|
/* Relative path. */
|
||||||
|
char buf[MAX_PATH];
|
||||||
|
GetCurrentDirectory(MAX_PATH, buf);
|
||||||
|
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]);
|
||||||
|
}
|
||||||
|
|
||||||
s->hfile = CreateFile(filename, access_flags,
|
s->hfile = CreateFile(filename, access_flags,
|
||||||
FILE_SHARE_READ, NULL,
|
FILE_SHARE_READ, NULL,
|
||||||
OPEN_EXISTING, overlapped, NULL);
|
OPEN_EXISTING, overlapped, NULL);
|
||||||
|
@ -293,6 +333,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
s->aio = aio;
|
s->aio = aio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw_probe_alignment(bs);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
|
|
130
block/rbd.c
130
block/rbd.c
|
@ -95,18 +95,13 @@ typedef struct RADOSCB {
|
||||||
#define RBD_FD_WRITE 1
|
#define RBD_FD_WRITE 1
|
||||||
|
|
||||||
typedef struct BDRVRBDState {
|
typedef struct BDRVRBDState {
|
||||||
int fds[2];
|
|
||||||
rados_t cluster;
|
rados_t cluster;
|
||||||
rados_ioctx_t io_ctx;
|
rados_ioctx_t io_ctx;
|
||||||
rbd_image_t image;
|
rbd_image_t image;
|
||||||
char name[RBD_MAX_IMAGE_NAME_SIZE];
|
char name[RBD_MAX_IMAGE_NAME_SIZE];
|
||||||
char *snap;
|
char *snap;
|
||||||
int event_reader_pos;
|
|
||||||
RADOSCB *event_rcb;
|
|
||||||
} BDRVRBDState;
|
} BDRVRBDState;
|
||||||
|
|
||||||
static void rbd_aio_bh_cb(void *opaque);
|
|
||||||
|
|
||||||
static int qemu_rbd_next_tok(char *dst, int dst_len,
|
static int qemu_rbd_next_tok(char *dst, int dst_len,
|
||||||
char *src, char delim,
|
char *src, char delim,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -369,9 +364,8 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This aio completion is being called from qemu_rbd_aio_event_reader()
|
* This aio completion is being called from rbd_finish_bh() and runs in qemu
|
||||||
* and runs in qemu context. It schedules a bh, but just in case the aio
|
* BH context.
|
||||||
* was not cancelled before.
|
|
||||||
*/
|
*/
|
||||||
static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||||
{
|
{
|
||||||
|
@ -401,36 +395,19 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||||
acb->ret = r;
|
acb->ret = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Note that acb->bh can be NULL in case where the aio was cancelled */
|
|
||||||
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
|
|
||||||
qemu_bh_schedule(acb->bh);
|
|
||||||
g_free(rcb);
|
g_free(rcb);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (acb->cmd == RBD_AIO_READ) {
|
||||||
* aio fd read handler. It runs in the qemu context and calls the
|
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
|
||||||
* completion handling of completed rados aio operations.
|
}
|
||||||
*/
|
qemu_vfree(acb->bounce);
|
||||||
static void qemu_rbd_aio_event_reader(void *opaque)
|
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||||
{
|
acb->status = 0;
|
||||||
BDRVRBDState *s = opaque;
|
|
||||||
|
|
||||||
ssize_t ret;
|
if (!acb->cancelled) {
|
||||||
|
qemu_aio_release(acb);
|
||||||
do {
|
}
|
||||||
char *p = (char *)&s->event_rcb;
|
|
||||||
|
|
||||||
/* now read the rcb pointer that was sent from a non qemu thread */
|
|
||||||
ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos,
|
|
||||||
sizeof(s->event_rcb) - s->event_reader_pos);
|
|
||||||
if (ret > 0) {
|
|
||||||
s->event_reader_pos += ret;
|
|
||||||
if (s->event_reader_pos == sizeof(s->event_rcb)) {
|
|
||||||
s->event_reader_pos = 0;
|
|
||||||
qemu_rbd_complete_aio(s->event_rcb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (ret < 0 && errno == EINTR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Convert to fine grained options */
|
/* TODO Convert to fine grained options */
|
||||||
|
@ -538,23 +515,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
|
||||||
bs->read_only = (s->snap != NULL);
|
bs->read_only = (s->snap != NULL);
|
||||||
|
|
||||||
s->event_reader_pos = 0;
|
|
||||||
r = qemu_pipe(s->fds);
|
|
||||||
if (r < 0) {
|
|
||||||
error_report("error opening eventfd");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
|
|
||||||
fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
|
|
||||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
|
|
||||||
NULL, s);
|
|
||||||
|
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
failed:
|
|
||||||
rbd_close(s->image);
|
|
||||||
failed_open:
|
failed_open:
|
||||||
rados_ioctx_destroy(s->io_ctx);
|
rados_ioctx_destroy(s->io_ctx);
|
||||||
failed_shutdown:
|
failed_shutdown:
|
||||||
|
@ -569,10 +532,6 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
|
|
||||||
close(s->fds[0]);
|
|
||||||
close(s->fds[1]);
|
|
||||||
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL);
|
|
||||||
|
|
||||||
rbd_close(s->image);
|
rbd_close(s->image);
|
||||||
rados_ioctx_destroy(s->io_ctx);
|
rados_ioctx_destroy(s->io_ctx);
|
||||||
g_free(s->snap);
|
g_free(s->snap);
|
||||||
|
@ -600,34 +559,11 @@ static const AIOCBInfo rbd_aiocb_info = {
|
||||||
.cancel = qemu_rbd_aio_cancel,
|
.cancel = qemu_rbd_aio_cancel,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
|
static void rbd_finish_bh(void *opaque)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
RADOSCB *rcb = opaque;
|
||||||
while (1) {
|
qemu_bh_delete(rcb->acb->bh);
|
||||||
fd_set wfd;
|
qemu_rbd_complete_aio(rcb);
|
||||||
int fd = s->fds[RBD_FD_WRITE];
|
|
||||||
|
|
||||||
/* send the op pointer to the qemu thread that is responsible
|
|
||||||
for the aio/op completion. Must do it in a qemu thread context */
|
|
||||||
ret = write(fd, (void *)&rcb, sizeof(rcb));
|
|
||||||
if (ret >= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (errno != EAGAIN) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
FD_ZERO(&wfd);
|
|
||||||
FD_SET(fd, &wfd);
|
|
||||||
do {
|
|
||||||
ret = select(fd + 1, NULL, &wfd, NULL, NULL);
|
|
||||||
} while (ret < 0 && errno == EINTR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -635,40 +571,18 @@ static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
|
||||||
*
|
*
|
||||||
* Note: this function is being called from a non qemu thread so
|
* Note: this function is being called from a non qemu thread so
|
||||||
* we need to be careful about what we do here. Generally we only
|
* we need to be careful about what we do here. Generally we only
|
||||||
* write to the block notification pipe, and do the rest of the
|
* schedule a BH, and do the rest of the io completion handling
|
||||||
* io completion handling from qemu_rbd_aio_event_reader() which
|
* from rbd_finish_bh() which runs in a qemu context.
|
||||||
* runs in a qemu context.
|
|
||||||
*/
|
*/
|
||||||
static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
||||||
{
|
{
|
||||||
int ret;
|
RBDAIOCB *acb = rcb->acb;
|
||||||
|
|
||||||
rcb->ret = rbd_aio_get_return_value(c);
|
rcb->ret = rbd_aio_get_return_value(c);
|
||||||
rbd_aio_release(c);
|
rbd_aio_release(c);
|
||||||
ret = qemu_rbd_send_pipe(rcb->s, rcb);
|
|
||||||
if (ret < 0) {
|
|
||||||
error_report("failed writing to acb->s->fds");
|
|
||||||
g_free(rcb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback when all queued rbd_aio requests are complete */
|
acb->bh = qemu_bh_new(rbd_finish_bh, rcb);
|
||||||
|
qemu_bh_schedule(acb->bh);
|
||||||
static void rbd_aio_bh_cb(void *opaque)
|
|
||||||
{
|
|
||||||
RBDAIOCB *acb = opaque;
|
|
||||||
|
|
||||||
if (acb->cmd == RBD_AIO_READ) {
|
|
||||||
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
|
|
||||||
}
|
|
||||||
qemu_vfree(acb->bounce);
|
|
||||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
|
||||||
qemu_bh_delete(acb->bh);
|
|
||||||
acb->bh = NULL;
|
|
||||||
acb->status = 0;
|
|
||||||
|
|
||||||
if (!acb->cancelled) {
|
|
||||||
qemu_aio_release(acb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||||
|
|
|
@ -161,7 +161,7 @@ typedef struct SheepdogVdiReq {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t data_length;
|
uint32_t data_length;
|
||||||
uint64_t vdi_size;
|
uint64_t vdi_size;
|
||||||
uint32_t vdi_id;
|
uint32_t base_vdi_id;
|
||||||
uint8_t copies;
|
uint8_t copies;
|
||||||
uint8_t copy_policy;
|
uint8_t copy_policy;
|
||||||
uint8_t reserved[2];
|
uint8_t reserved[2];
|
||||||
|
@ -1493,7 +1493,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
|
||||||
|
|
||||||
memset(&hdr, 0, sizeof(hdr));
|
memset(&hdr, 0, sizeof(hdr));
|
||||||
hdr.opcode = SD_OP_NEW_VDI;
|
hdr.opcode = SD_OP_NEW_VDI;
|
||||||
hdr.vdi_id = s->inode.vdi_id;
|
hdr.base_vdi_id = s->inode.vdi_id;
|
||||||
|
|
||||||
wlen = SD_MAX_VDI_LEN;
|
wlen = SD_MAX_VDI_LEN;
|
||||||
|
|
||||||
|
@ -1534,7 +1534,7 @@ static int sd_prealloc(const char *filename)
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
@ -1684,7 +1684,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
||||||
|
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BDRVSheepdogState *s;
|
BDRVSheepdogState *base;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
|
|
||||||
/* Currently, only Sheepdog backing image is supported. */
|
/* Currently, only Sheepdog backing image is supported. */
|
||||||
|
@ -1695,22 +1695,22 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err);
|
ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = bs->opaque;
|
base = bs->opaque;
|
||||||
|
|
||||||
if (!is_snapshot(&s->inode)) {
|
if (!is_snapshot(&base->inode)) {
|
||||||
error_report("cannot clone from a non snapshot vdi");
|
error_report("cannot clone from a non snapshot vdi");
|
||||||
bdrv_unref(bs);
|
bdrv_unref(bs);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
s->inode.vdi_id = base->inode.vdi_id;
|
||||||
bdrv_unref(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1743,7 +1743,7 @@ static void sd_close(BlockDriverState *bs)
|
||||||
memset(&hdr, 0, sizeof(hdr));
|
memset(&hdr, 0, sizeof(hdr));
|
||||||
|
|
||||||
hdr.opcode = SD_OP_RELEASE_VDI;
|
hdr.opcode = SD_OP_RELEASE_VDI;
|
||||||
hdr.vdi_id = s->inode.vdi_id;
|
hdr.base_vdi_id = s->inode.vdi_id;
|
||||||
wlen = strlen(s->name) + 1;
|
wlen = strlen(s->name) + 1;
|
||||||
hdr.data_length = wlen;
|
hdr.data_length = wlen;
|
||||||
hdr.flags = SD_FLAG_CMD_WRITE;
|
hdr.flags = SD_FLAG_CMD_WRITE;
|
||||||
|
@ -1846,7 +1846,7 @@ static bool sd_delete(BDRVSheepdogState *s)
|
||||||
unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0;
|
unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0;
|
||||||
SheepdogVdiReq hdr = {
|
SheepdogVdiReq hdr = {
|
||||||
.opcode = SD_OP_DEL_VDI,
|
.opcode = SD_OP_DEL_VDI,
|
||||||
.vdi_id = s->inode.vdi_id,
|
.base_vdi_id = s->inode.vdi_id,
|
||||||
.data_length = wlen,
|
.data_length = wlen,
|
||||||
.flags = SD_FLAG_CMD_WRITE,
|
.flags = SD_FLAG_CMD_WRITE,
|
||||||
};
|
};
|
||||||
|
@ -2442,11 +2442,12 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||||
{
|
{
|
||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
SheepdogInode *inode = &s->inode;
|
SheepdogInode *inode = &s->inode;
|
||||||
unsigned long start = sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE,
|
uint64_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
unsigned long start = offset / SD_DATA_OBJ_SIZE,
|
||||||
end = DIV_ROUND_UP((sector_num + nb_sectors) *
|
end = DIV_ROUND_UP((sector_num + nb_sectors) *
|
||||||
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
|
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
|
||||||
unsigned long idx;
|
unsigned long idx;
|
||||||
int64_t ret = BDRV_BLOCK_DATA;
|
int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
|
||||||
|
|
||||||
for (idx = start; idx < end; idx++) {
|
for (idx = start; idx < end; idx++) {
|
||||||
if (inode->data_vdi_id[idx] == 0) {
|
if (inode->data_vdi_id[idx] == 0) {
|
||||||
|
|
|
@ -75,6 +75,8 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
||||||
unused->backing_hd = NULL;
|
unused->backing_hd = NULL;
|
||||||
bdrv_unref(unused);
|
bdrv_unref(unused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_refresh_limits(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn stream_run(void *opaque)
|
static void coroutine_fn stream_run(void *opaque)
|
||||||
|
|
|
@ -1797,7 +1797,7 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
45
block/vmdk.c
45
block/vmdk.c
|
@ -428,10 +428,6 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
||||||
extent->l2_size = l2_size;
|
extent->l2_size = l2_size;
|
||||||
extent->cluster_sectors = flat ? sectors : cluster_sectors;
|
extent->cluster_sectors = flat ? sectors : cluster_sectors;
|
||||||
|
|
||||||
if (!flat) {
|
|
||||||
bs->bl.write_zeroes_alignment =
|
|
||||||
MAX(bs->bl.write_zeroes_alignment, cluster_sectors);
|
|
||||||
}
|
|
||||||
if (s->num_extents > 1) {
|
if (s->num_extents > 1) {
|
||||||
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
|
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
|
||||||
} else {
|
} else {
|
||||||
|
@ -640,6 +636,13 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||||
if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {
|
if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {
|
||||||
l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;
|
l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||||
}
|
}
|
||||||
|
if (bdrv_getlength(file) <
|
||||||
|
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
|
||||||
|
error_report("File truncated, expecting at least %lld bytes",
|
||||||
|
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ret = vmdk_add_extent(bs, file, false,
|
ret = vmdk_add_extent(bs, file, false,
|
||||||
le64_to_cpu(header.capacity),
|
le64_to_cpu(header.capacity),
|
||||||
le64_to_cpu(header.gd_offset) << 9,
|
le64_to_cpu(header.gd_offset) << 9,
|
||||||
|
@ -654,6 +657,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
extent->compressed =
|
extent->compressed =
|
||||||
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
|
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
|
||||||
|
if (extent->compressed) {
|
||||||
|
g_free(s->create_type);
|
||||||
|
s->create_type = g_strdup("streamOptimized");
|
||||||
|
}
|
||||||
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
|
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
|
||||||
extent->version = le32_to_cpu(header.version);
|
extent->version = le32_to_cpu(header.version);
|
||||||
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
|
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
|
||||||
|
@ -769,8 +776,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||||
|
|
||||||
path_combine(extent_path, sizeof(extent_path),
|
path_combine(extent_path, sizeof(extent_path),
|
||||||
desc_file_path, fname);
|
desc_file_path, fname);
|
||||||
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
|
ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL,
|
||||||
errp);
|
bs->open_flags, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -891,6 +898,23 @@ fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int vmdk_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVVmdkState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_extents; i++) {
|
||||||
|
if (!s->extents[i].flat) {
|
||||||
|
bs->bl.write_zeroes_alignment =
|
||||||
|
MAX(bs->bl.write_zeroes_alignment,
|
||||||
|
s->extents[i].cluster_sectors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_whole_cluster(BlockDriverState *bs,
|
static int get_whole_cluster(BlockDriverState *bs,
|
||||||
VmdkExtent *extent,
|
VmdkExtent *extent,
|
||||||
uint64_t cluster_offset,
|
uint64_t cluster_offset,
|
||||||
|
@ -1325,8 +1349,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||||
{
|
{
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
VmdkExtent *extent = NULL;
|
VmdkExtent *extent = NULL;
|
||||||
int n, ret;
|
int ret;
|
||||||
int64_t index_in_cluster;
|
int64_t index_in_cluster, n;
|
||||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
VmdkMetaData m_data;
|
VmdkMetaData m_data;
|
||||||
|
@ -1469,7 +1493,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -1807,7 +1831,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = bdrv_file_open(&new_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write description");
|
error_setg_errno(errp, -ret, "Could not write description");
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -2002,6 +2026,7 @@ static BlockDriver bdrv_vmdk = {
|
||||||
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
||||||
.bdrv_has_zero_init = vmdk_has_zero_init,
|
.bdrv_has_zero_init = vmdk_has_zero_init,
|
||||||
.bdrv_get_specific_info = vmdk_get_specific_info,
|
.bdrv_get_specific_info = vmdk_get_specific_info,
|
||||||
|
.bdrv_refresh_limits = vmdk_refresh_limits,
|
||||||
|
|
||||||
.create_options = vmdk_create_options,
|
.create_options = vmdk_create_options,
|
||||||
};
|
};
|
||||||
|
|
112
blockdev.c
112
blockdev.c
|
@ -307,12 +307,11 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
||||||
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
||||||
|
|
||||||
/* Takes the ownership of bs_opts */
|
/* Takes the ownership of bs_opts */
|
||||||
static DriveInfo *blockdev_init(QDict *bs_opts,
|
static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||||
BlockInterfaceType type,
|
BlockInterfaceType type,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
const char *buf;
|
const char *buf;
|
||||||
const char *file = NULL;
|
|
||||||
const char *serial;
|
const char *serial;
|
||||||
int ro = 0;
|
int ro = 0;
|
||||||
int bdrv_flags = 0;
|
int bdrv_flags = 0;
|
||||||
|
@ -354,7 +353,6 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
|
||||||
ro = qemu_opt_get_bool(opts, "read-only", 0);
|
ro = qemu_opt_get_bool(opts, "read-only", 0);
|
||||||
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
|
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
|
||||||
|
|
||||||
file = qemu_opt_get(opts, "file");
|
|
||||||
serial = qemu_opt_get(opts, "serial");
|
serial = qemu_opt_get(opts, "serial");
|
||||||
|
|
||||||
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
|
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
|
||||||
|
@ -599,6 +597,10 @@ QemuOptsList qemu_legacy_drive_opts = {
|
||||||
.name = "addr",
|
.name = "addr",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "pci address (virtio only)",
|
.help = "pci address (virtio only)",
|
||||||
|
},{
|
||||||
|
.name = "file",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "file name",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Options that are passed on, but have special semantics with -drive */
|
/* Options that are passed on, but have special semantics with -drive */
|
||||||
|
@ -629,6 +631,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||||
const char *devaddr;
|
const char *devaddr;
|
||||||
bool read_only = false;
|
bool read_only = false;
|
||||||
bool copy_on_read;
|
bool copy_on_read;
|
||||||
|
const char *filename;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
/* Change legacy command line options into QMP ones */
|
/* Change legacy command line options into QMP ones */
|
||||||
|
@ -867,8 +870,10 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filename = qemu_opt_get(legacy_opts, "file");
|
||||||
|
|
||||||
/* Actual block device init: Functionality shared with blockdev-add */
|
/* Actual block device init: Functionality shared with blockdev-add */
|
||||||
dinfo = blockdev_init(bs_opts, type, &local_err);
|
dinfo = blockdev_init(filename, bs_opts, type, &local_err);
|
||||||
if (dinfo == NULL) {
|
if (dinfo == NULL) {
|
||||||
if (error_is_set(&local_err)) {
|
if (error_is_set(&local_err)) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
|
@ -942,14 +947,22 @@ static void blockdev_do_action(int kind, void *data, Error **errp)
|
||||||
qmp_transaction(&list, errp);
|
qmp_transaction(&list, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
|
void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
|
||||||
|
bool has_node_name, const char *node_name,
|
||||||
|
const char *snapshot_file,
|
||||||
|
bool has_snapshot_node_name,
|
||||||
|
const char *snapshot_node_name,
|
||||||
bool has_format, const char *format,
|
bool has_format, const char *format,
|
||||||
bool has_mode, enum NewImageMode mode,
|
bool has_mode, NewImageMode mode, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BlockdevSnapshot snapshot = {
|
BlockdevSnapshot snapshot = {
|
||||||
|
.has_device = has_device,
|
||||||
.device = (char *) device,
|
.device = (char *) device,
|
||||||
|
.has_node_name = has_node_name,
|
||||||
|
.node_name = (char *) node_name,
|
||||||
.snapshot_file = (char *) snapshot_file,
|
.snapshot_file = (char *) snapshot_file,
|
||||||
|
.has_snapshot_node_name = has_snapshot_node_name,
|
||||||
|
.snapshot_node_name = (char *) snapshot_node_name,
|
||||||
.has_format = has_format,
|
.has_format = has_format,
|
||||||
.format = (char *) format,
|
.format = (char *) format,
|
||||||
.has_mode = has_mode,
|
.has_mode = has_mode,
|
||||||
|
@ -1187,8 +1200,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
{
|
{
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
int flags, ret;
|
int flags, ret;
|
||||||
|
QDict *options = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
bool has_device = false;
|
||||||
const char *device;
|
const char *device;
|
||||||
|
bool has_node_name = false;
|
||||||
|
const char *node_name;
|
||||||
|
bool has_snapshot_node_name = false;
|
||||||
|
const char *snapshot_node_name;
|
||||||
const char *new_image_file;
|
const char *new_image_file;
|
||||||
const char *format = "qcow2";
|
const char *format = "qcow2";
|
||||||
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||||
|
@ -1199,7 +1218,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
/* get parameters */
|
/* get parameters */
|
||||||
g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
|
g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
|
||||||
|
|
||||||
|
has_device = action->blockdev_snapshot_sync->has_device;
|
||||||
device = action->blockdev_snapshot_sync->device;
|
device = action->blockdev_snapshot_sync->device;
|
||||||
|
has_node_name = action->blockdev_snapshot_sync->has_node_name;
|
||||||
|
node_name = action->blockdev_snapshot_sync->node_name;
|
||||||
|
has_snapshot_node_name =
|
||||||
|
action->blockdev_snapshot_sync->has_snapshot_node_name;
|
||||||
|
snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name;
|
||||||
|
|
||||||
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
|
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
|
||||||
if (action->blockdev_snapshot_sync->has_format) {
|
if (action->blockdev_snapshot_sync->has_format) {
|
||||||
format = action->blockdev_snapshot_sync->format;
|
format = action->blockdev_snapshot_sync->format;
|
||||||
|
@ -1215,9 +1241,21 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->old_bs = bdrv_find(device);
|
state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
if (!state->old_bs) {
|
has_node_name ? node_name : NULL,
|
||||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
&local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_node_name && !has_snapshot_node_name) {
|
||||||
|
error_setg(errp, "New snapshot node name missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_snapshot_node_name && bdrv_find_node(snapshot_node_name)) {
|
||||||
|
error_setg(errp, "New snapshot node name already existing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1238,7 +1276,7 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) {
|
if (!bdrv_is_first_non_filter(state->old_bs)) {
|
||||||
error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
|
error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1257,15 +1295,23 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_snapshot_node_name) {
|
||||||
|
options = qdict_new();
|
||||||
|
qdict_put(options, "node-name",
|
||||||
|
qstring_from_str(snapshot_node_name));
|
||||||
|
}
|
||||||
|
|
||||||
/* We will manually add the backing_hd field to the bs later */
|
/* We will manually add the backing_hd field to the bs later */
|
||||||
state->new_bs = bdrv_new("");
|
state->new_bs = bdrv_new("");
|
||||||
/* TODO Inherit bs->options or only take explicit options with an
|
/* TODO Inherit bs->options or only take explicit options with an
|
||||||
* extended QMP command? */
|
* extended QMP command? */
|
||||||
ret = bdrv_open(state->new_bs, new_image_file, NULL,
|
ret = bdrv_open(state->new_bs, new_image_file, options,
|
||||||
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDECREF(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void external_snapshot_commit(BlkTransactionState *common)
|
static void external_snapshot_commit(BlkTransactionState *common)
|
||||||
|
@ -1476,14 +1522,19 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
|
||||||
eject_device(bs, force, errp);
|
eject_device(bs, force, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_block_passwd(const char *device, const char *password, Error **errp)
|
void qmp_block_passwd(bool has_device, const char *device,
|
||||||
|
bool has_node_name, const char *node_name,
|
||||||
|
const char *password, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
bs = bdrv_find(device);
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
if (!bs) {
|
has_node_name ? node_name : NULL,
|
||||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
&local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1673,14 +1724,24 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_block_resize(const char *device, int64_t size, Error **errp)
|
void qmp_block_resize(bool has_device, const char *device,
|
||||||
|
bool has_node_name, const char *node_name,
|
||||||
|
int64_t size, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
bs = bdrv_find(device);
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
if (!bs) {
|
has_node_name ? node_name : NULL,
|
||||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
&local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bdrv_is_first_non_filter(bs)) {
|
||||||
|
error_set(errp, QERR_FEATURE_DISABLED, "resize");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1947,6 +2008,11 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
|
||||||
|
{
|
||||||
|
return bdrv_named_nodes_list();
|
||||||
|
}
|
||||||
|
|
||||||
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
||||||
|
|
||||||
void qmp_drive_mirror(const char *device, const char *target,
|
void qmp_drive_mirror(const char *device, const char *target,
|
||||||
|
@ -2210,7 +2276,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||||
|
|
||||||
qdict_flatten(qdict);
|
qdict_flatten(qdict);
|
||||||
|
|
||||||
blockdev_init(qdict, IF_NONE, &local_err);
|
blockdev_init(NULL, qdict, IF_NONE, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (error_is_set(&local_err)) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -2250,10 +2316,6 @@ QemuOptsList qemu_common_drive_opts = {
|
||||||
.name = "snapshot",
|
.name = "snapshot",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
.help = "enable/disable snapshot mode",
|
.help = "enable/disable snapshot mode",
|
||||||
},{
|
|
||||||
.name = "file",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "disk image",
|
|
||||||
},{
|
},{
|
||||||
.name = "discard",
|
.name = "discard",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
|
|
|
@ -256,6 +256,7 @@ coroutine_pool=""
|
||||||
seccomp=""
|
seccomp=""
|
||||||
glusterfs=""
|
glusterfs=""
|
||||||
glusterfs_discard="no"
|
glusterfs_discard="no"
|
||||||
|
glusterfs_zerofill="no"
|
||||||
virtio_blk_data_plane=""
|
virtio_blk_data_plane=""
|
||||||
gtk=""
|
gtk=""
|
||||||
gtkabi="2.0"
|
gtkabi="2.0"
|
||||||
|
@ -2701,6 +2702,9 @@ if test "$glusterfs" != "no" ; then
|
||||||
if $pkg_config --atleast-version=5 glusterfs-api; then
|
if $pkg_config --atleast-version=5 glusterfs-api; then
|
||||||
glusterfs_discard="yes"
|
glusterfs_discard="yes"
|
||||||
fi
|
fi
|
||||||
|
if $pkg_config --atleast-version=6 glusterfs-api; then
|
||||||
|
glusterfs_zerofill="yes"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
if test "$glusterfs" = "yes" ; then
|
if test "$glusterfs" = "yes" ; then
|
||||||
feature_not_found "GlusterFS backend support"
|
feature_not_found "GlusterFS backend support"
|
||||||
|
@ -4229,6 +4233,10 @@ if test "$glusterfs_discard" = "yes" ; then
|
||||||
echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
|
echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$glusterfs_zerofill" = "yes" ; then
|
||||||
|
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$libssh2" = "yes" ; then
|
if test "$libssh2" = "yes" ; then
|
||||||
echo "CONFIG_LIBSSH2=y" >> $config_host_mak
|
echo "CONFIG_LIBSSH2=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -35,6 +35,11 @@ STEXI
|
||||||
@item commit
|
@item commit
|
||||||
@findex commit
|
@findex commit
|
||||||
Commit changes to the disk images (if -snapshot is used) or backing files.
|
Commit changes to the disk images (if -snapshot is used) or backing files.
|
||||||
|
If the backing file is smaller than the snapshot, then the backing file will be
|
||||||
|
resized to be the same size as the snapshot. If the snapshot is smaller than
|
||||||
|
the backing file, the backing file will not be truncated. If you want the
|
||||||
|
backing file to match the size of the smaller snapshot, you can safely truncate
|
||||||
|
it yourself once the commit operation successfully completes.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
14
hmp.c
14
hmp.c
|
@ -871,7 +871,7 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict)
|
||||||
const char *password = qdict_get_str(qdict, "password");
|
const char *password = qdict_get_str(qdict, "password");
|
||||||
Error *errp = NULL;
|
Error *errp = NULL;
|
||||||
|
|
||||||
qmp_block_passwd(device, password, &errp);
|
qmp_block_passwd(true, device, false, NULL, password, &errp);
|
||||||
hmp_handle_error(mon, &errp);
|
hmp_handle_error(mon, &errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +893,7 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
|
||||||
int64_t size = qdict_get_int(qdict, "size");
|
int64_t size = qdict_get_int(qdict, "size");
|
||||||
Error *errp = NULL;
|
Error *errp = NULL;
|
||||||
|
|
||||||
qmp_block_resize(device, size, &errp);
|
qmp_block_resize(true, device, false, NULL, size, &errp);
|
||||||
hmp_handle_error(mon, &errp);
|
hmp_handle_error(mon, &errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,7 +972,9 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
|
||||||
}
|
}
|
||||||
|
|
||||||
mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||||
qmp_blockdev_snapshot_sync(device, filename, !!format, format,
|
qmp_blockdev_snapshot_sync(true, device, false, NULL,
|
||||||
|
filename, false, NULL,
|
||||||
|
!!format, format,
|
||||||
true, mode, &errp);
|
true, mode, &errp);
|
||||||
hmp_handle_error(mon, &errp);
|
hmp_handle_error(mon, &errp);
|
||||||
}
|
}
|
||||||
|
@ -1092,11 +1094,11 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
|
||||||
hmp_handle_error(mon, &err);
|
hmp_handle_error(mon, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hmp_change_read_arg(Monitor *mon, const char *password,
|
static void hmp_change_read_arg(void *opaque, const char *password,
|
||||||
void *opaque)
|
void *readline_opaque)
|
||||||
{
|
{
|
||||||
qmp_change_vnc_password(password, NULL);
|
qmp_change_vnc_password(password, NULL);
|
||||||
monitor_read_command(mon, 1);
|
monitor_read_command(opaque, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hmp_change(Monitor *mon, const QDict *qdict)
|
void hmp_change(Monitor *mon, const QDict *qdict)
|
||||||
|
|
|
@ -731,7 +731,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||||
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
|
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
|
||||||
virtio_blk_save, virtio_blk_load, s);
|
virtio_blk_save, virtio_blk_load, s);
|
||||||
bdrv_set_dev_ops(s->bs, &virtio_block_ops, s);
|
bdrv_set_dev_ops(s->bs, &virtio_block_ops, s);
|
||||||
bdrv_set_buffer_alignment(s->bs, s->conf->logical_block_size);
|
bdrv_set_guest_block_size(s->bs, s->conf->logical_block_size);
|
||||||
|
|
||||||
bdrv_iostatus_enable(s->bs);
|
bdrv_iostatus_enable(s->bs);
|
||||||
|
|
||||||
|
|
|
@ -2103,7 +2103,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
|
||||||
s->smart_selftest_count = 0;
|
s->smart_selftest_count = 0;
|
||||||
if (kind == IDE_CD) {
|
if (kind == IDE_CD) {
|
||||||
bdrv_set_dev_ops(bs, &ide_cd_block_ops, s);
|
bdrv_set_dev_ops(bs, &ide_cd_block_ops, s);
|
||||||
bdrv_set_buffer_alignment(bs, 2048);
|
bdrv_set_guest_block_size(bs, 2048);
|
||||||
} else {
|
} else {
|
||||||
if (!bdrv_is_inserted(s->bs)) {
|
if (!bdrv_is_inserted(s->bs)) {
|
||||||
error_report("Device needs media, but drive is empty");
|
error_report("Device needs media, but drive is empty");
|
||||||
|
|
|
@ -2254,7 +2254,7 @@ static int scsi_initfn(SCSIDevice *dev)
|
||||||
} else {
|
} else {
|
||||||
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
|
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
|
||||||
}
|
}
|
||||||
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
|
bdrv_set_guest_block_size(s->qdev.conf.bs, s->qdev.blocksize);
|
||||||
|
|
||||||
bdrv_iostatus_enable(s->qdev.conf.bs);
|
bdrv_iostatus_enable(s->qdev.conf.bs);
|
||||||
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
|
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
|
||||||
|
|
|
@ -210,7 +210,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||||
s->blocksize = ldl_be_p(&r->buf[8]);
|
s->blocksize = ldl_be_p(&r->buf[8]);
|
||||||
s->max_lba = ldq_be_p(&r->buf[0]);
|
s->max_lba = ldq_be_p(&r->buf[0]);
|
||||||
}
|
}
|
||||||
bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
|
bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
|
||||||
|
|
||||||
scsi_req_data(&r->req, len);
|
scsi_req_data(&r->req, len);
|
||||||
if (!r->req.io_canceled) {
|
if (!r->req.io_canceled) {
|
||||||
|
|
|
@ -376,7 +376,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
||||||
barrier();
|
barrier();
|
||||||
|
|
||||||
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
||||||
int ret = get_indirect(vring, elem, &desc);
|
ret = get_indirect(vring, elem, &desc);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,11 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
|
||||||
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
||||||
int bdrv_parse_discard_flags(const char *mode, int *flags);
|
int bdrv_parse_discard_flags(const char *mode, int *flags);
|
||||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||||
QDict *options, int flags, Error **errp);
|
const char *reference, QDict *options, int flags,
|
||||||
|
Error **errp);
|
||||||
|
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||||
|
QDict *options, const char *bdref_key, int flags,
|
||||||
|
bool force_raw, bool allow_none, Error **errp);
|
||||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
||||||
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||||
int flags, BlockDriver *drv, Error **errp);
|
int flags, BlockDriver *drv, Error **errp);
|
||||||
|
@ -220,7 +224,6 @@ BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, int64_t sector_num
|
||||||
int nb_sectors, BdrvRequestFlags flags,
|
int nb_sectors, BdrvRequestFlags flags,
|
||||||
BlockDriverCompletionFunc *cb, void *opaque);
|
BlockDriverCompletionFunc *cb, void *opaque);
|
||||||
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
|
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
|
||||||
int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov);
|
|
||||||
int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
||||||
void *buf, int count);
|
void *buf, int count);
|
||||||
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
|
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
|
||||||
|
@ -249,6 +252,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset);
|
||||||
int64_t bdrv_getlength(BlockDriverState *bs);
|
int64_t bdrv_getlength(BlockDriverState *bs);
|
||||||
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
|
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
|
||||||
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
|
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
|
||||||
|
int bdrv_refresh_limits(BlockDriverState *bs);
|
||||||
int bdrv_commit(BlockDriverState *bs);
|
int bdrv_commit(BlockDriverState *bs);
|
||||||
int bdrv_commit_all(void);
|
int bdrv_commit_all(void);
|
||||||
int bdrv_change_backing_file(BlockDriverState *bs,
|
int bdrv_change_backing_file(BlockDriverState *bs,
|
||||||
|
@ -283,16 +287,16 @@ int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
|
||||||
/* external snapshots */
|
/* external snapshots */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EXT_SNAPSHOT_ALLOWED,
|
BS_IS_A_FILTER,
|
||||||
EXT_SNAPSHOT_FORBIDDEN,
|
BS_FILTER_PASS_DOWN,
|
||||||
} ExtSnapshotPerm;
|
BS_AUTHORIZATION_COUNT,
|
||||||
|
} BsAuthorization;
|
||||||
|
|
||||||
/* return EXT_SNAPSHOT_ALLOWED if external snapshot is allowed
|
bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
|
||||||
* return EXT_SNAPSHOT_FORBIDDEN if external snapshot is forbidden
|
BlockDriverState *candidate);
|
||||||
*/
|
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||||
ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs);
|
BlockDriverState *candidate);
|
||||||
/* helper used to forbid external snapshots like in blkverify */
|
bool bdrv_is_first_non_filter(BlockDriverState *candidate);
|
||||||
ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs);
|
|
||||||
|
|
||||||
/* async block I/O */
|
/* async block I/O */
|
||||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
|
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
|
||||||
|
@ -374,6 +378,11 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked);
|
||||||
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
|
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
|
||||||
const char *bdrv_get_format_name(BlockDriverState *bs);
|
const char *bdrv_get_format_name(BlockDriverState *bs);
|
||||||
BlockDriverState *bdrv_find(const char *name);
|
BlockDriverState *bdrv_find(const char *name);
|
||||||
|
BlockDriverState *bdrv_find_node(const char *node_name);
|
||||||
|
BlockDeviceInfoList *bdrv_named_nodes_list(void);
|
||||||
|
BlockDriverState *bdrv_lookup_bs(const char *device,
|
||||||
|
const char *node_name,
|
||||||
|
Error **errp);
|
||||||
BlockDriverState *bdrv_next(BlockDriverState *bs);
|
BlockDriverState *bdrv_next(BlockDriverState *bs);
|
||||||
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
|
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
|
||||||
void *opaque);
|
void *opaque);
|
||||||
|
@ -418,7 +427,10 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
char *options, uint64_t img_size, int flags,
|
char *options, uint64_t img_size, int flags,
|
||||||
Error **errp, bool quiet);
|
Error **errp, bool quiet);
|
||||||
|
|
||||||
void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
|
/* Returns the alignment in bytes that is required so that no bounce buffer
|
||||||
|
* is required throughout the stack */
|
||||||
|
size_t bdrv_opt_mem_align(BlockDriverState *bs);
|
||||||
|
void bdrv_set_guest_block_size(BlockDriverState *bs, int align);
|
||||||
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
||||||
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov);
|
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov);
|
||||||
|
|
||||||
|
@ -515,6 +527,14 @@ typedef enum {
|
||||||
BLKDBG_FLUSH_TO_OS,
|
BLKDBG_FLUSH_TO_OS,
|
||||||
BLKDBG_FLUSH_TO_DISK,
|
BLKDBG_FLUSH_TO_DISK,
|
||||||
|
|
||||||
|
BLKDBG_PWRITEV_RMW_HEAD,
|
||||||
|
BLKDBG_PWRITEV_RMW_AFTER_HEAD,
|
||||||
|
BLKDBG_PWRITEV_RMW_TAIL,
|
||||||
|
BLKDBG_PWRITEV_RMW_AFTER_TAIL,
|
||||||
|
BLKDBG_PWRITEV,
|
||||||
|
BLKDBG_PWRITEV_ZERO,
|
||||||
|
BLKDBG_PWRITEV_DONE,
|
||||||
|
|
||||||
BLKDBG_EVENT_MAX,
|
BLKDBG_EVENT_MAX,
|
||||||
} BlkDebugEvent;
|
} BlkDebugEvent;
|
||||||
|
|
||||||
|
|
|
@ -57,22 +57,35 @@
|
||||||
|
|
||||||
typedef struct BdrvTrackedRequest {
|
typedef struct BdrvTrackedRequest {
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
int64_t sector_num;
|
int64_t offset;
|
||||||
int nb_sectors;
|
unsigned int bytes;
|
||||||
bool is_write;
|
bool is_write;
|
||||||
|
|
||||||
|
bool serialising;
|
||||||
|
int64_t overlap_offset;
|
||||||
|
unsigned int overlap_bytes;
|
||||||
|
|
||||||
QLIST_ENTRY(BdrvTrackedRequest) list;
|
QLIST_ENTRY(BdrvTrackedRequest) list;
|
||||||
Coroutine *co; /* owner, used for deadlock detection */
|
Coroutine *co; /* owner, used for deadlock detection */
|
||||||
CoQueue wait_queue; /* coroutines blocked on this request */
|
CoQueue wait_queue; /* coroutines blocked on this request */
|
||||||
|
|
||||||
|
struct BdrvTrackedRequest *waiting_for;
|
||||||
} BdrvTrackedRequest;
|
} BdrvTrackedRequest;
|
||||||
|
|
||||||
struct BlockDriver {
|
struct BlockDriver {
|
||||||
const char *format_name;
|
const char *format_name;
|
||||||
int instance_size;
|
int instance_size;
|
||||||
|
|
||||||
/* if not defined external snapshots are allowed
|
/* this table of boolean contains authorizations for the block operations */
|
||||||
* future block filters will query their children to build the response
|
bool authorizations[BS_AUTHORIZATION_COUNT];
|
||||||
|
/* for snapshots complex block filter like Quorum can implement the
|
||||||
|
* following recursive callback instead of BS_IS_A_FILTER.
|
||||||
|
* It's purpose is to recurse on the filter children while calling
|
||||||
|
* bdrv_recurse_is_first_non_filter on them.
|
||||||
|
* For a sample implementation look in the future Quorum block filter.
|
||||||
*/
|
*/
|
||||||
ExtSnapshotPerm (*bdrv_check_ext_snapshot)(BlockDriverState *bs);
|
bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
|
||||||
|
BlockDriverState *candidate);
|
||||||
|
|
||||||
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
||||||
int (*bdrv_probe_device)(const char *filename);
|
int (*bdrv_probe_device)(const char *filename);
|
||||||
|
@ -226,6 +239,8 @@ struct BlockDriver {
|
||||||
int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag);
|
int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag);
|
||||||
bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag);
|
bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag);
|
||||||
|
|
||||||
|
int (*bdrv_refresh_limits)(BlockDriverState *bs);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns 1 if newly created images are guaranteed to contain only
|
* Returns 1 if newly created images are guaranteed to contain only
|
||||||
* zeros, 0 otherwise.
|
* zeros, 0 otherwise.
|
||||||
|
@ -250,6 +265,9 @@ typedef struct BlockLimits {
|
||||||
|
|
||||||
/* optimal transfer length in sectors */
|
/* optimal transfer length in sectors */
|
||||||
int opt_transfer_length;
|
int opt_transfer_length;
|
||||||
|
|
||||||
|
/* memory alignment so that no bounce buffer is needed */
|
||||||
|
size_t opt_mem_alignment;
|
||||||
} BlockLimits;
|
} BlockLimits;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -291,8 +309,8 @@ struct BlockDriverState {
|
||||||
/* Callback before write request is processed */
|
/* Callback before write request is processed */
|
||||||
NotifierWithReturnList before_write_notifiers;
|
NotifierWithReturnList before_write_notifiers;
|
||||||
|
|
||||||
/* number of in-flight copy-on-read requests */
|
/* number of in-flight serialising requests */
|
||||||
unsigned int copy_on_read_in_flight;
|
unsigned int serialising_in_flight;
|
||||||
|
|
||||||
/* I/O throttling */
|
/* I/O throttling */
|
||||||
ThrottleState throttle_state;
|
ThrottleState throttle_state;
|
||||||
|
@ -314,8 +332,11 @@ struct BlockDriverState {
|
||||||
/* Whether produces zeros when read beyond eof */
|
/* Whether produces zeros when read beyond eof */
|
||||||
bool zero_beyond_eof;
|
bool zero_beyond_eof;
|
||||||
|
|
||||||
/* the memory alignment required for the buffers handled by this driver */
|
/* Alignment requirement for offset/length of I/O requests */
|
||||||
int buffer_alignment;
|
unsigned int request_alignment;
|
||||||
|
|
||||||
|
/* the block size for which the guest device expects atomicity */
|
||||||
|
int guest_block_size;
|
||||||
|
|
||||||
/* do we need to tell the quest if we have a volatile write cache? */
|
/* do we need to tell the quest if we have a volatile write cache? */
|
||||||
int enable_write_cache;
|
int enable_write_cache;
|
||||||
|
@ -325,11 +346,18 @@ struct BlockDriverState {
|
||||||
BlockdevOnError on_read_error, on_write_error;
|
BlockdevOnError on_read_error, on_write_error;
|
||||||
bool iostatus_enabled;
|
bool iostatus_enabled;
|
||||||
BlockDeviceIoStatus iostatus;
|
BlockDeviceIoStatus iostatus;
|
||||||
|
|
||||||
|
/* the following member gives a name to every node on the bs graph. */
|
||||||
|
char node_name[32];
|
||||||
|
/* element of the list of named nodes building the graph */
|
||||||
|
QTAILQ_ENTRY(BlockDriverState) node_list;
|
||||||
|
/* Device name is the name associated with the "drive" the guest sees */
|
||||||
char device_name[32];
|
char device_name[32];
|
||||||
|
/* element of the list of "drives" the guest sees */
|
||||||
|
QTAILQ_ENTRY(BlockDriverState) device_list;
|
||||||
QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
|
QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
|
||||||
int refcnt;
|
int refcnt;
|
||||||
int in_use; /* users other than guest access, eg. block migration */
|
int in_use; /* users other than guest access, eg. block migration */
|
||||||
QTAILQ_ENTRY(BlockDriverState) list;
|
|
||||||
|
|
||||||
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
#include "block/snapshot.h"
|
#include "block/snapshot.h"
|
||||||
|
|
||||||
|
BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs);
|
||||||
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
||||||
SnapshotInfoList **p_list,
|
SnapshotInfoList **p_list,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
#include "monitor/readline.h"
|
#include "qemu/readline.h"
|
||||||
|
|
||||||
extern Monitor *cur_mon;
|
extern Monitor *cur_mon;
|
||||||
extern Monitor *default_mon;
|
extern Monitor *default_mon;
|
||||||
|
|
|
@ -68,5 +68,6 @@ QDict *qdict_clone_shallow(const QDict *src);
|
||||||
void qdict_flatten(QDict *qdict);
|
void qdict_flatten(QDict *qdict);
|
||||||
|
|
||||||
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
|
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
|
||||||
|
void qdict_array_split(QDict *src, QList **dst);
|
||||||
|
|
||||||
#endif /* QDICT_H */
|
#endif /* QDICT_H */
|
||||||
|
|
|
@ -42,5 +42,8 @@ bool qemuio_command(BlockDriverState *bs, const char *cmd);
|
||||||
|
|
||||||
void qemuio_add_command(const cmdinfo_t *ci);
|
void qemuio_add_command(const cmdinfo_t *ci);
|
||||||
int qemuio_command_usage(const cmdinfo_t *ci);
|
int qemuio_command_usage(const cmdinfo_t *ci);
|
||||||
|
void qemuio_complete_command(const char *input,
|
||||||
|
void (*fn)(const char *cmd, void *opaque),
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
#endif /* QEMU_IO_H */
|
#endif /* QEMU_IO_H */
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
|
||||||
QemuOptsList *qemu_find_opts(const char *group);
|
QemuOptsList *qemu_find_opts(const char *group);
|
||||||
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
|
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
|
||||||
|
@ -18,6 +19,11 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
|
||||||
|
|
||||||
int qemu_read_config_file(const char *filename);
|
int qemu_read_config_file(const char *filename);
|
||||||
|
|
||||||
|
/* Parse QDict options as a replacement for a config file (allowing multiple
|
||||||
|
enumerated (0..(n-1)) configuration "sections") */
|
||||||
|
void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
/* Read default QEMU config files
|
/* Read default QEMU config files
|
||||||
*/
|
*/
|
||||||
int qemu_read_default_config_files(bool userconfig);
|
int qemu_read_default_config_files(bool userconfig);
|
||||||
|
|
|
@ -240,4 +240,6 @@ static inline void qemu_init_auxval(char **envp) { }
|
||||||
void qemu_init_auxval(char **envp);
|
void qemu_init_auxval(char **envp);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void qemu_set_tty_echo(int fd, bool echo);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#ifndef READLINE_H
|
#ifndef READLINE_H
|
||||||
#define READLINE_H
|
#define READLINE_H
|
||||||
|
|
||||||
#include "qemu-common.h"
|
|
||||||
|
|
||||||
#define READLINE_CMD_BUF_SIZE 4095
|
#define READLINE_CMD_BUF_SIZE 4095
|
||||||
#define READLINE_MAX_CMDS 64
|
#define READLINE_MAX_CMDS 64
|
||||||
#define READLINE_MAX_COMPLETIONS 256
|
#define READLINE_MAX_COMPLETIONS 256
|
||||||
|
|
||||||
typedef void ReadLineFunc(Monitor *mon, const char *str, void *opaque);
|
typedef void ReadLinePrintfFunc(void *opaque, const char *fmt, ...);
|
||||||
typedef void ReadLineCompletionFunc(Monitor *mon,
|
typedef void ReadLineFlushFunc(void *opaque);
|
||||||
|
typedef void ReadLineFunc(void *opaque, const char *str,
|
||||||
|
void *readline_opaque);
|
||||||
|
typedef void ReadLineCompletionFunc(void *opaque,
|
||||||
const char *cmdline);
|
const char *cmdline);
|
||||||
|
|
||||||
typedef struct ReadLineState {
|
typedef struct ReadLineState {
|
||||||
|
@ -35,7 +36,10 @@ typedef struct ReadLineState {
|
||||||
void *readline_opaque;
|
void *readline_opaque;
|
||||||
int read_password;
|
int read_password;
|
||||||
char prompt[256];
|
char prompt[256];
|
||||||
Monitor *mon;
|
|
||||||
|
ReadLinePrintfFunc *printf_func;
|
||||||
|
ReadLineFlushFunc *flush_func;
|
||||||
|
void *opaque;
|
||||||
} ReadLineState;
|
} ReadLineState;
|
||||||
|
|
||||||
void readline_add_completion(ReadLineState *rs, const char *str);
|
void readline_add_completion(ReadLineState *rs, const char *str);
|
||||||
|
@ -46,11 +50,13 @@ const char *readline_get_history(ReadLineState *rs, unsigned int index);
|
||||||
void readline_handle_byte(ReadLineState *rs, int ch);
|
void readline_handle_byte(ReadLineState *rs, int ch);
|
||||||
|
|
||||||
void readline_start(ReadLineState *rs, const char *prompt, int read_password,
|
void readline_start(ReadLineState *rs, const char *prompt, int read_password,
|
||||||
ReadLineFunc *readline_func, void *opaque);
|
ReadLineFunc *readline_func, void *readline_opaque);
|
||||||
void readline_restart(ReadLineState *rs);
|
void readline_restart(ReadLineState *rs);
|
||||||
void readline_show_prompt(ReadLineState *rs);
|
void readline_show_prompt(ReadLineState *rs);
|
||||||
|
|
||||||
ReadLineState *readline_init(Monitor *mon,
|
ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
|
||||||
|
ReadLineFlushFunc *flush_func,
|
||||||
|
void *opaque,
|
||||||
ReadLineCompletionFunc *completion_finder);
|
ReadLineCompletionFunc *completion_finder);
|
||||||
|
|
||||||
#endif /* !READLINE_H */
|
#endif /* !READLINE_H */
|
41
monitor.c
41
monitor.c
|
@ -37,7 +37,7 @@
|
||||||
#include "ui/qemu-spice.h"
|
#include "ui/qemu-spice.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "monitor/readline.h"
|
#include "qemu/readline.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
|
@ -217,8 +217,8 @@ static const mon_cmd_t qmp_cmds[];
|
||||||
Monitor *cur_mon;
|
Monitor *cur_mon;
|
||||||
Monitor *default_mon;
|
Monitor *default_mon;
|
||||||
|
|
||||||
static void monitor_command_cb(Monitor *mon, const char *cmdline,
|
static void monitor_command_cb(void *opaque, const char *cmdline,
|
||||||
void *opaque);
|
void *readline_opaque);
|
||||||
|
|
||||||
static inline int qmp_cmd_mode(const Monitor *mon)
|
static inline int qmp_cmd_mode(const Monitor *mon)
|
||||||
{
|
{
|
||||||
|
@ -4338,9 +4338,10 @@ static void monitor_find_completion_by_table(Monitor *mon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitor_find_completion(Monitor *mon,
|
static void monitor_find_completion(void *opaque,
|
||||||
const char *cmdline)
|
const char *cmdline)
|
||||||
{
|
{
|
||||||
|
Monitor *mon = opaque;
|
||||||
char *args[MAX_ARGS];
|
char *args[MAX_ARGS];
|
||||||
int nb_args, len;
|
int nb_args, len;
|
||||||
|
|
||||||
|
@ -4751,8 +4752,11 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
|
||||||
cur_mon = old_mon;
|
cur_mon = old_mon;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
|
static void monitor_command_cb(void *opaque, const char *cmdline,
|
||||||
|
void *readline_opaque)
|
||||||
{
|
{
|
||||||
|
Monitor *mon = opaque;
|
||||||
|
|
||||||
monitor_suspend(mon);
|
monitor_suspend(mon);
|
||||||
handle_user_command(mon, cmdline);
|
handle_user_command(mon, cmdline);
|
||||||
monitor_resume(mon);
|
monitor_resume(mon);
|
||||||
|
@ -4881,6 +4885,22 @@ static void sortcmdlist(void)
|
||||||
* End:
|
* End:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* These functions just adapt the readline interface in a typesafe way. We
|
||||||
|
* could cast function pointers but that discards compiler checks.
|
||||||
|
*/
|
||||||
|
static void monitor_readline_printf(void *opaque, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
monitor_vprintf(opaque, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void monitor_readline_flush(void *opaque)
|
||||||
|
{
|
||||||
|
monitor_flush(opaque);
|
||||||
|
}
|
||||||
|
|
||||||
void monitor_init(CharDriverState *chr, int flags)
|
void monitor_init(CharDriverState *chr, int flags)
|
||||||
{
|
{
|
||||||
static int is_first_init = 1;
|
static int is_first_init = 1;
|
||||||
|
@ -4898,7 +4918,10 @@ void monitor_init(CharDriverState *chr, int flags)
|
||||||
mon->chr = chr;
|
mon->chr = chr;
|
||||||
mon->flags = flags;
|
mon->flags = flags;
|
||||||
if (flags & MONITOR_USE_READLINE) {
|
if (flags & MONITOR_USE_READLINE) {
|
||||||
mon->rs = readline_init(mon, monitor_find_completion);
|
mon->rs = readline_init(monitor_readline_printf,
|
||||||
|
monitor_readline_flush,
|
||||||
|
mon,
|
||||||
|
monitor_find_completion);
|
||||||
monitor_read_command(mon, 0);
|
monitor_read_command(mon, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4920,9 +4943,11 @@ void monitor_init(CharDriverState *chr, int flags)
|
||||||
default_mon = mon;
|
default_mon = mon;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
|
static void bdrv_password_cb(void *opaque, const char *password,
|
||||||
|
void *readline_opaque)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = opaque;
|
Monitor *mon = opaque;
|
||||||
|
BlockDriverState *bs = readline_opaque;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (bdrv_set_key(bs, password) != 0) {
|
if (bdrv_set_key(bs, password) != 0) {
|
||||||
|
|
183
qapi-schema.json
183
qapi-schema.json
|
@ -810,6 +810,8 @@
|
||||||
#
|
#
|
||||||
# @file: the filename of the backing device
|
# @file: the filename of the backing device
|
||||||
#
|
#
|
||||||
|
# @node-name: #optional the name of the block driver node (Since 2.0)
|
||||||
|
#
|
||||||
# @ro: true if the backing device was open read-only
|
# @ro: true if the backing device was open read-only
|
||||||
#
|
#
|
||||||
# @drv: the name of the block format used to open the backing device. As of
|
# @drv: the name of the block format used to open the backing device. As of
|
||||||
|
@ -857,10 +859,9 @@
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
#
|
#
|
||||||
# Notes: This interface is only found in @BlockInfo.
|
|
||||||
##
|
##
|
||||||
{ 'type': 'BlockDeviceInfo',
|
{ 'type': 'BlockDeviceInfo',
|
||||||
'data': { 'file': 'str', 'ro': 'bool', 'drv': 'str',
|
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
|
||||||
'*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',
|
||||||
|
@ -1022,15 +1023,17 @@
|
||||||
#
|
#
|
||||||
# @stats: A @BlockDeviceStats for the device.
|
# @stats: A @BlockDeviceStats for the device.
|
||||||
#
|
#
|
||||||
# @parent: #optional This may point to the backing block device if this is a
|
# @parent: #optional This describes the file block device if it has one.
|
||||||
# a virtual block device. If it's a backing block, this will point
|
#
|
||||||
# to the backing file is one is present.
|
# @backing: #optional This describes the backing block device if it has one.
|
||||||
|
# (Since 2.0)
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
{ 'type': 'BlockStats',
|
{ 'type': 'BlockStats',
|
||||||
'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
|
'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
|
||||||
'*parent': 'BlockStats'} }
|
'*parent': 'BlockStats',
|
||||||
|
'*backing': 'BlockStats'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-blockstats:
|
# @query-blockstats:
|
||||||
|
@ -1675,7 +1678,11 @@
|
||||||
# determine which ones are encrypted, set the passwords with this command, and
|
# determine which ones are encrypted, set the passwords with this command, and
|
||||||
# then start the guest with the @cont command.
|
# then start the guest with the @cont command.
|
||||||
#
|
#
|
||||||
# @device: the name of the device to set the password on
|
# Either @device or @node-name must be set but not both.
|
||||||
|
#
|
||||||
|
# @device: #optional the name of the block backend device to set the password on
|
||||||
|
#
|
||||||
|
# @node-name: #optional graph node name to set the password on (Since 2.0)
|
||||||
#
|
#
|
||||||
# @password: the password to use for the device
|
# @password: the password to use for the device
|
||||||
#
|
#
|
||||||
|
@ -1689,7 +1696,8 @@
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
{ 'command': 'block_passwd', 'data': {'device': 'str', 'password': 'str'} }
|
{ 'command': 'block_passwd', 'data': {'*device': 'str',
|
||||||
|
'*node-name': 'str', 'password': 'str'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @balloon:
|
# @balloon:
|
||||||
|
@ -1716,7 +1724,11 @@
|
||||||
#
|
#
|
||||||
# Resize a block image while a guest is running.
|
# Resize a block image while a guest is running.
|
||||||
#
|
#
|
||||||
# @device: the name of the device to get the image resized
|
# Either @device or @node-name must be set but not both.
|
||||||
|
#
|
||||||
|
# @device: #optional the name of the device to get the image resized
|
||||||
|
#
|
||||||
|
# @node-name: #optional graph node name to get the image resized (Since 2.0)
|
||||||
#
|
#
|
||||||
# @size: new image size in bytes
|
# @size: new image size in bytes
|
||||||
#
|
#
|
||||||
|
@ -1725,7 +1737,9 @@
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
##
|
##
|
||||||
{ 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }}
|
{ 'command': 'block_resize', 'data': { '*device': 'str',
|
||||||
|
'*node-name': 'str',
|
||||||
|
'size': 'int' }}
|
||||||
|
|
||||||
##
|
##
|
||||||
# @NewImageMode
|
# @NewImageMode
|
||||||
|
@ -1747,18 +1761,25 @@
|
||||||
##
|
##
|
||||||
# @BlockdevSnapshot
|
# @BlockdevSnapshot
|
||||||
#
|
#
|
||||||
# @device: the name of the device to generate the snapshot from.
|
# Either @device or @node-name must be set but not both.
|
||||||
|
#
|
||||||
|
# @device: #optional the name of the device to generate the snapshot from.
|
||||||
|
#
|
||||||
|
# @node-name: #optional graph node name to generate the snapshot from (Since 2.0)
|
||||||
#
|
#
|
||||||
# @snapshot-file: the target of the new image. A new file will be created.
|
# @snapshot-file: the target of the new image. A new file will be created.
|
||||||
#
|
#
|
||||||
|
# @snapshot-node-name: #optional the graph node name of the new image (Since 2.0)
|
||||||
|
#
|
||||||
# @format: #optional the format of the snapshot image, default is 'qcow2'.
|
# @format: #optional the format of the snapshot image, default is 'qcow2'.
|
||||||
#
|
#
|
||||||
# @mode: #optional whether and how QEMU should create a new image, default is
|
# @mode: #optional whether and how QEMU should create a new image, default is
|
||||||
# 'absolute-paths'.
|
# 'absolute-paths'.
|
||||||
##
|
##
|
||||||
{ 'type': 'BlockdevSnapshot',
|
{ 'type': 'BlockdevSnapshot',
|
||||||
'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
|
'data': { '*device': 'str', '*node-name': 'str',
|
||||||
'*mode': 'NewImageMode' } }
|
'snapshot-file': 'str', '*snapshot-node-name': 'str',
|
||||||
|
'*format': 'str', '*mode': 'NewImageMode' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevSnapshotInternal
|
# @BlockdevSnapshotInternal
|
||||||
|
@ -1973,6 +1994,13 @@
|
||||||
# user needs to complete the job with the block-job-complete
|
# user needs to complete the job with the block-job-complete
|
||||||
# command after getting the ready event. (Since 2.0)
|
# command after getting the ready event. (Since 2.0)
|
||||||
#
|
#
|
||||||
|
# If the base image is smaller than top, then the base image
|
||||||
|
# will be resized to be the same size as top. If top is
|
||||||
|
# smaller than the base image, the base will not be
|
||||||
|
# truncated. If you want the base image size to match the
|
||||||
|
# size of the smaller top, you can safely truncate it
|
||||||
|
# yourself once the commit operation successfully completes.
|
||||||
|
#
|
||||||
#
|
#
|
||||||
# @speed: #optional the maximum speed, in bytes per second
|
# @speed: #optional the maximum speed, in bytes per second
|
||||||
#
|
#
|
||||||
|
@ -2008,6 +2036,17 @@
|
||||||
##
|
##
|
||||||
{ 'command': 'drive-backup', 'data': 'DriveBackup' }
|
{ 'command': 'drive-backup', 'data': 'DriveBackup' }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @query-named-block-nodes
|
||||||
|
#
|
||||||
|
# Get the named block driver list
|
||||||
|
#
|
||||||
|
# Returns: the list of BlockDeviceInfo
|
||||||
|
#
|
||||||
|
# Since 2.0
|
||||||
|
##
|
||||||
|
{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @drive-mirror
|
# @drive-mirror
|
||||||
#
|
#
|
||||||
|
@ -4090,6 +4129,7 @@
|
||||||
# @id: #optional id by which the new block device can be referred to.
|
# @id: #optional id by which the new block device can be referred to.
|
||||||
# This is a required option on the top level of blockdev-add, and
|
# This is a required option on the top level of blockdev-add, and
|
||||||
# currently not allowed on any other level.
|
# currently not allowed on any other level.
|
||||||
|
# @node-name: #optional the name of a block driver state node (Since 2.0)
|
||||||
# @discard: #optional discard-related options (default: ignore)
|
# @discard: #optional discard-related options (default: ignore)
|
||||||
# @cache: #optional cache-related options
|
# @cache: #optional cache-related options
|
||||||
# @aio: #optional AIO backend (default: threads)
|
# @aio: #optional AIO backend (default: threads)
|
||||||
|
@ -4105,6 +4145,7 @@
|
||||||
{ 'type': 'BlockdevOptionsBase',
|
{ 'type': 'BlockdevOptionsBase',
|
||||||
'data': { 'driver': 'str',
|
'data': { 'driver': 'str',
|
||||||
'*id': 'str',
|
'*id': 'str',
|
||||||
|
'*node-name': 'str',
|
||||||
'*discard': 'BlockdevDiscardOptions',
|
'*discard': 'BlockdevDiscardOptions',
|
||||||
'*cache': 'BlockdevCacheOptions',
|
'*cache': 'BlockdevCacheOptions',
|
||||||
'*aio': 'BlockdevAioOptions',
|
'*aio': 'BlockdevAioOptions',
|
||||||
|
@ -4200,6 +4241,116 @@
|
||||||
'*pass-discard-snapshot': 'bool',
|
'*pass-discard-snapshot': 'bool',
|
||||||
'*pass-discard-other': 'bool' } }
|
'*pass-discard-other': 'bool' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlkdebugEvent
|
||||||
|
#
|
||||||
|
# Trigger events supported by blkdebug.
|
||||||
|
##
|
||||||
|
{ 'enum': 'BlkdebugEvent',
|
||||||
|
'data': [ 'l1_update', 'l1_grow.alloc_table', 'l1_grow.write_table',
|
||||||
|
'l1_grow.activate_table', 'l2_load', 'l2_update',
|
||||||
|
'l2_update_compressed', 'l2_alloc.cow_read', 'l2_alloc.write',
|
||||||
|
'read_aio', 'read_backing_aio', 'read_compressed', 'write_aio',
|
||||||
|
'write_compressed', 'vmstate_load', 'vmstate_save', 'cow_read',
|
||||||
|
'cow_write', 'reftable_load', 'reftable_grow', 'reftable_update',
|
||||||
|
'refblock_load', 'refblock_update', 'refblock_update_part',
|
||||||
|
'refblock_alloc', 'refblock_alloc.hookup', 'refblock_alloc.write',
|
||||||
|
'refblock_alloc.write_blocks', 'refblock_alloc.write_table',
|
||||||
|
'refblock_alloc.switch_table', 'cluster_alloc',
|
||||||
|
'cluster_alloc_bytes', 'cluster_free', 'flush_to_os',
|
||||||
|
'flush_to_disk' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlkdebugInjectErrorOptions
|
||||||
|
#
|
||||||
|
# Describes a single error injection for blkdebug.
|
||||||
|
#
|
||||||
|
# @event: trigger event
|
||||||
|
#
|
||||||
|
# @state: #optional the state identifier blkdebug needs to be in to
|
||||||
|
# actually trigger the event; defaults to "any"
|
||||||
|
#
|
||||||
|
# @errno: #optional error identifier (errno) to be returned; defaults to
|
||||||
|
# EIO
|
||||||
|
#
|
||||||
|
# @sector: #optional specifies the sector index which has to be affected
|
||||||
|
# in order to actually trigger the event; defaults to "any
|
||||||
|
# sector"
|
||||||
|
#
|
||||||
|
# @once: #optional disables further events after this one has been
|
||||||
|
# triggered; defaults to false
|
||||||
|
#
|
||||||
|
# @immediately: #optional fail immediately; defaults to false
|
||||||
|
#
|
||||||
|
# Since: 2.0
|
||||||
|
##
|
||||||
|
{ 'type': 'BlkdebugInjectErrorOptions',
|
||||||
|
'data': { 'event': 'BlkdebugEvent',
|
||||||
|
'*state': 'int',
|
||||||
|
'*errno': 'int',
|
||||||
|
'*sector': 'int',
|
||||||
|
'*once': 'bool',
|
||||||
|
'*immediately': 'bool' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlkdebugSetStateOptions
|
||||||
|
#
|
||||||
|
# Describes a single state-change event for blkdebug.
|
||||||
|
#
|
||||||
|
# @event: trigger event
|
||||||
|
#
|
||||||
|
# @state: #optional the current state identifier blkdebug needs to be in;
|
||||||
|
# defaults to "any"
|
||||||
|
#
|
||||||
|
# @new_state: the state identifier blkdebug is supposed to assume if
|
||||||
|
# this event is triggered
|
||||||
|
#
|
||||||
|
# Since: 2.0
|
||||||
|
##
|
||||||
|
{ 'type': 'BlkdebugSetStateOptions',
|
||||||
|
'data': { 'event': 'BlkdebugEvent',
|
||||||
|
'*state': 'int',
|
||||||
|
'new_state': 'int' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsBlkdebug
|
||||||
|
#
|
||||||
|
# Driver specific block device options for blkdebug.
|
||||||
|
#
|
||||||
|
# @image: underlying raw block device (or image file)
|
||||||
|
#
|
||||||
|
# @config: #optional filename of the configuration file
|
||||||
|
#
|
||||||
|
# @align: #optional required alignment for requests in bytes
|
||||||
|
#
|
||||||
|
# @inject-error: #optional array of error injection descriptions
|
||||||
|
#
|
||||||
|
# @set-state: #optional array of state-change descriptions
|
||||||
|
#
|
||||||
|
# Since: 2.0
|
||||||
|
##
|
||||||
|
{ 'type': 'BlockdevOptionsBlkdebug',
|
||||||
|
'data': { 'image': 'BlockdevRef',
|
||||||
|
'*config': 'str',
|
||||||
|
'*align': 'int',
|
||||||
|
'*inject-error': ['BlkdebugInjectErrorOptions'],
|
||||||
|
'*set-state': ['BlkdebugSetStateOptions'] } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsBlkverify
|
||||||
|
#
|
||||||
|
# Driver specific block device options for blkverify.
|
||||||
|
#
|
||||||
|
# @test: block device to be tested
|
||||||
|
#
|
||||||
|
# @raw: raw image used for verification
|
||||||
|
#
|
||||||
|
# Since: 2.0
|
||||||
|
##
|
||||||
|
{ 'type': 'BlockdevOptionsBlkverify',
|
||||||
|
'data': { 'test': 'BlockdevRef',
|
||||||
|
'raw': 'BlockdevRef' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptions
|
# @BlockdevOptions
|
||||||
#
|
#
|
||||||
|
@ -4224,10 +4375,8 @@
|
||||||
# TODO sheepdog: Wait for structured options
|
# TODO sheepdog: Wait for structured options
|
||||||
# TODO ssh: Should take InetSocketAddress for 'host'?
|
# TODO ssh: Should take InetSocketAddress for 'host'?
|
||||||
'vvfat': 'BlockdevOptionsVVFAT',
|
'vvfat': 'BlockdevOptionsVVFAT',
|
||||||
|
'blkdebug': 'BlockdevOptionsBlkdebug',
|
||||||
# TODO blkdebug: Wait for structured options
|
'blkverify': 'BlockdevOptionsBlkverify',
|
||||||
# TODO blkverify: Wait for structured options
|
|
||||||
|
|
||||||
'bochs': 'BlockdevOptionsGenericFormat',
|
'bochs': 'BlockdevOptionsGenericFormat',
|
||||||
'cloop': 'BlockdevOptionsGenericFormat',
|
'cloop': 'BlockdevOptionsGenericFormat',
|
||||||
'cow': 'BlockdevOptionsGenericCOWFormat',
|
'cow': 'BlockdevOptionsGenericCOWFormat',
|
||||||
|
|
|
@ -536,11 +536,11 @@ support of multiple VM snapshots.
|
||||||
Supported options:
|
Supported options:
|
||||||
@table @code
|
@table @code
|
||||||
@item compat
|
@item compat
|
||||||
Determines the qcow2 version to use. @code{compat=0.10} uses the traditional
|
Determines the qcow2 version to use. @code{compat=0.10} uses the
|
||||||
image format that can be read by any QEMU since 0.10 (this is the default).
|
traditional image format that can be read by any QEMU since 0.10.
|
||||||
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
|
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
|
||||||
newer understand. Amongst others, this includes zero clusters, which allow
|
newer understand (this is the default). Amongst others, this includes
|
||||||
efficient copy-on-read for sparse images.
|
zero clusters, which allow efficient copy-on-read for sparse images.
|
||||||
|
|
||||||
@item backing_file
|
@item backing_file
|
||||||
File name of a base image (see @option{create} subcommand)
|
File name of a base image (see @option{create} subcommand)
|
||||||
|
|
|
@ -57,7 +57,9 @@ indicates that target image must be compressed (qcow format only)
|
||||||
@item -h
|
@item -h
|
||||||
with or without a command shows help and lists the supported formats
|
with or without a command shows help and lists the supported formats
|
||||||
@item -p
|
@item -p
|
||||||
display progress bar (convert and rebase commands only)
|
display progress bar (compare, convert and rebase commands only).
|
||||||
|
If the @var{-p} option is not used for a command that supports it, the
|
||||||
|
progress is reported when the process receives a @code{SIGUSR1} signal.
|
||||||
@item -q
|
@item -q
|
||||||
Quiet mode - do not print any output (except errors). There's no progress bar
|
Quiet mode - do not print any output (except errors). There's no progress bar
|
||||||
in case both @var{-q} and @var{-p} options are used.
|
in case both @var{-q} and @var{-p} options are used.
|
||||||
|
@ -140,7 +142,12 @@ it doesn't need to be specified separately in this case.
|
||||||
|
|
||||||
@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
|
@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
|
||||||
|
|
||||||
Commit the changes recorded in @var{filename} in its base image.
|
Commit the changes recorded in @var{filename} in its base image or backing file.
|
||||||
|
If the backing file is smaller than the snapshot, then the backing file will be
|
||||||
|
resized to be the same size as the snapshot. If the snapshot is smaller than
|
||||||
|
the backing file, the backing file will not be truncated. If you want the
|
||||||
|
backing file to match the size of the smaller snapshot, you can safely truncate
|
||||||
|
it yourself once the commit operation successfully completes.
|
||||||
|
|
||||||
@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-s] [-q] @var{filename1} @var{filename2}
|
@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-s] [-q] @var{filename1} @var{filename2}
|
||||||
|
|
||||||
|
@ -391,11 +398,11 @@ support of multiple VM snapshots.
|
||||||
Supported options:
|
Supported options:
|
||||||
@table @code
|
@table @code
|
||||||
@item compat
|
@item compat
|
||||||
Determines the qcow2 version to use. @code{compat=0.10} uses the traditional
|
Determines the qcow2 version to use. @code{compat=0.10} uses the
|
||||||
image format that can be read by any QEMU since 0.10 (this is the default).
|
traditional image format that can be read by any QEMU since 0.10.
|
||||||
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
|
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
|
||||||
newer understand. Amongst others, this includes zero clusters, which allow
|
newer understand (this is the default). Amongst others, this includes zero
|
||||||
efficient copy-on-read for sparse images.
|
clusters, which allow efficient copy-on-read for sparse images.
|
||||||
|
|
||||||
@item backing_file
|
@item backing_file
|
||||||
File name of a base image (see @option{create} subcommand)
|
File name of a base image (see @option{create} subcommand)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qapi.h"
|
#include "block/qapi.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
#define CMD_NOFILE_OK 0x01
|
#define CMD_NOFILE_OK 0x01
|
||||||
|
|
||||||
|
@ -94,6 +95,21 @@ static const cmdinfo_t *find_command(const char *cmd)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Invoke fn() for commands with a matching prefix */
|
||||||
|
void qemuio_complete_command(const char *input,
|
||||||
|
void (*fn)(const char *cmd, void *opaque),
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
cmdinfo_t *ct;
|
||||||
|
size_t input_len = strlen(input);
|
||||||
|
|
||||||
|
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
|
||||||
|
if (strncmp(input, ct->name, input_len) == 0) {
|
||||||
|
fn(ct->name, opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char **breakline(char *input, int *count)
|
static char **breakline(char *input, int *count)
|
||||||
{
|
{
|
||||||
int c = 0;
|
int c = 0;
|
||||||
|
@ -2038,6 +2054,46 @@ static const cmdinfo_t abort_cmd = {
|
||||||
.oneline = "simulate a program crash using abort(3)",
|
.oneline = "simulate a program crash using abort(3)",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void sleep_cb(void *opaque)
|
||||||
|
{
|
||||||
|
bool *expired = opaque;
|
||||||
|
*expired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sleep_f(BlockDriverState *bs, int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
long ms;
|
||||||
|
struct QEMUTimer *timer;
|
||||||
|
bool expired = false;
|
||||||
|
|
||||||
|
ms = strtol(argv[1], &endptr, 0);
|
||||||
|
if (ms < 0 || *endptr != '\0') {
|
||||||
|
printf("%s is not a valid number\n", argv[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired);
|
||||||
|
timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_HOST) + SCALE_MS * ms);
|
||||||
|
|
||||||
|
while (!expired) {
|
||||||
|
main_loop_wait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_free(timer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const cmdinfo_t sleep_cmd = {
|
||||||
|
.name = "sleep",
|
||||||
|
.argmin = 1,
|
||||||
|
.argmax = 1,
|
||||||
|
.cfunc = sleep_f,
|
||||||
|
.flags = CMD_NOFILE_OK,
|
||||||
|
.oneline = "waits for the given value in milliseconds",
|
||||||
|
};
|
||||||
|
|
||||||
static void help_oneline(const char *cmd, const cmdinfo_t *ct)
|
static void help_oneline(const char *cmd, const cmdinfo_t *ct)
|
||||||
{
|
{
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
|
@ -2151,4 +2207,5 @@ static void __attribute((constructor)) init_qemuio_commands(void)
|
||||||
qemuio_add_command(&resume_cmd);
|
qemuio_add_command(&resume_cmd);
|
||||||
qemuio_add_command(&wait_break_cmd);
|
qemuio_add_command(&wait_break_cmd);
|
||||||
qemuio_add_command(&abort_cmd);
|
qemuio_add_command(&abort_cmd);
|
||||||
|
qemuio_add_command(&sleep_cmd);
|
||||||
}
|
}
|
||||||
|
|
129
qemu-io.c
129
qemu-io.c
|
@ -18,6 +18,7 @@
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
#include "qemu/readline.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "trace/control.h"
|
#include "trace/control.h"
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ extern int qemuio_misalign;
|
||||||
static int ncmdline;
|
static int ncmdline;
|
||||||
static char **cmdline;
|
static char **cmdline;
|
||||||
|
|
||||||
|
static ReadLineState *readline_state;
|
||||||
|
|
||||||
static int close_f(BlockDriverState *bs, int argc, char **argv)
|
static int close_f(BlockDriverState *bs, int argc, char **argv)
|
||||||
{
|
{
|
||||||
bdrv_unref(bs);
|
bdrv_unref(bs);
|
||||||
|
@ -56,7 +59,7 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (growable) {
|
if (growable) {
|
||||||
if (bdrv_file_open(&qemuio_bs, name, opts, flags, &local_err)) {
|
if (bdrv_file_open(&qemuio_bs, name, NULL, opts, flags, &local_err)) {
|
||||||
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
@ -160,11 +163,13 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
||||||
flags |= BDRV_O_RDWR;
|
flags |= BDRV_O_RDWR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind != argc - 1) {
|
if (optind == argc - 1) {
|
||||||
|
return openfile(argv[optind], flags, growable, opts);
|
||||||
|
} else if (optind == argc) {
|
||||||
|
return openfile(NULL, flags, growable, opts);
|
||||||
|
} else {
|
||||||
return qemuio_command_usage(&open_cmd);
|
return qemuio_command_usage(&open_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return openfile(argv[optind], flags, growable, opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int quit_f(BlockDriverState *bs, int argc, char **argv)
|
static int quit_f(BlockDriverState *bs, int argc, char **argv)
|
||||||
|
@ -203,14 +208,6 @@ static void usage(const char *name)
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#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 *get_prompt(void)
|
||||||
{
|
{
|
||||||
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
|
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
|
||||||
|
@ -222,52 +219,53 @@ static char *get_prompt(void)
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_READLINE)
|
static void readline_printf_func(void *opaque, const char *fmt, ...)
|
||||||
static char *fetchline(void)
|
|
||||||
{
|
{
|
||||||
char *line = readline(get_prompt());
|
va_list ap;
|
||||||
if (line && *line) {
|
va_start(ap, fmt);
|
||||||
add_history(line);
|
vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_flush_func(void *opaque)
|
||||||
|
{
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_func(void *opaque, const char *str, void *readline_opaque)
|
||||||
|
{
|
||||||
|
char **line = readline_opaque;
|
||||||
|
*line = g_strdup(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void completion_match(const char *cmd, void *opaque)
|
||||||
|
{
|
||||||
|
readline_add_completion(readline_state, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_completion_func(void *opaque, const char *str)
|
||||||
|
{
|
||||||
|
readline_set_completion_index(readline_state, strlen(str));
|
||||||
|
qemuio_complete_command(str, completion_match, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *fetchline_readline(void)
|
||||||
|
{
|
||||||
|
char *line = NULL;
|
||||||
|
|
||||||
|
readline_start(readline_state, get_prompt(), 0, readline_func, &line);
|
||||||
|
while (!line) {
|
||||||
|
int ch = getchar();
|
||||||
|
if (ch == EOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
readline_handle_byte(readline_state, ch);
|
||||||
}
|
}
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
#elif defined(ENABLE_EDITLINE)
|
|
||||||
static char *el_get_prompt(EditLine *e)
|
|
||||||
{
|
|
||||||
return get_prompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *fetchline(void)
|
#define MAXREADLINESZ 1024
|
||||||
{
|
static char *fetchline_fgets(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
|
|
||||||
static char *fetchline(void)
|
|
||||||
{
|
{
|
||||||
char *p, *line = g_malloc(MAXREADLINESZ);
|
char *p, *line = g_malloc(MAXREADLINESZ);
|
||||||
|
|
||||||
|
@ -283,7 +281,15 @@ static char *fetchline(void)
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
static char *fetchline(void)
|
||||||
|
{
|
||||||
|
if (readline_state) {
|
||||||
|
return fetchline_readline();
|
||||||
|
} else {
|
||||||
|
return fetchline_fgets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void prep_fetchline(void *opaque)
|
static void prep_fetchline(void *opaque)
|
||||||
{
|
{
|
||||||
|
@ -339,6 +345,11 @@ static void add_user_command(char *optarg)
|
||||||
cmdline[ncmdline-1] = optarg;
|
cmdline[ncmdline-1] = optarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reenable_tty_echo(void)
|
||||||
|
{
|
||||||
|
qemu_set_tty_echo(STDIN_FILENO, true);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int readonly = 0;
|
int readonly = 0;
|
||||||
|
@ -435,6 +446,15 @@ int main(int argc, char **argv)
|
||||||
qemuio_add_command(&open_cmd);
|
qemuio_add_command(&open_cmd);
|
||||||
qemuio_add_command(&close_cmd);
|
qemuio_add_command(&close_cmd);
|
||||||
|
|
||||||
|
if (isatty(STDIN_FILENO)) {
|
||||||
|
readline_state = readline_init(readline_printf_func,
|
||||||
|
readline_flush_func,
|
||||||
|
NULL,
|
||||||
|
readline_completion_func);
|
||||||
|
qemu_set_tty_echo(STDIN_FILENO, false);
|
||||||
|
atexit(reenable_tty_echo);
|
||||||
|
}
|
||||||
|
|
||||||
/* open the device */
|
/* open the device */
|
||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
flags |= BDRV_O_RDWR;
|
flags |= BDRV_O_RDWR;
|
||||||
|
@ -453,5 +473,6 @@ int main(int argc, char **argv)
|
||||||
if (qemuio_bs) {
|
if (qemuio_bs) {
|
||||||
bdrv_unref(qemuio_bs);
|
bdrv_unref(qemuio_bs);
|
||||||
}
|
}
|
||||||
|
g_free(readline_state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
117
qmp-commands.hx
117
qmp-commands.hx
|
@ -931,7 +931,7 @@ EQMP
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "block_resize",
|
.name = "block_resize",
|
||||||
.args_type = "device:B,size:o",
|
.args_type = "device:s?,node-name:s?,size:o",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_resize,
|
.mhandler.cmd_new = qmp_marshal_input_block_resize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -944,6 +944,7 @@ Resize a block image while a guest is running.
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
- "device": the device's ID, must be unique (json-string)
|
- "device": the device's ID, must be unique (json-string)
|
||||||
|
- "node-name": the node name in the block driver state graph (json-string)
|
||||||
- "size": new size
|
- "size": new size
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -965,6 +966,45 @@ EQMP
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_commit,
|
.mhandler.cmd_new = qmp_marshal_input_block_commit,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
SQMP
|
||||||
|
block-commit
|
||||||
|
------------
|
||||||
|
|
||||||
|
Live commit of data from overlay image nodes into backing nodes - i.e., writes
|
||||||
|
data between 'top' and 'base' into 'base'.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- "device": The device's ID, must be unique (json-string)
|
||||||
|
- "base": The file name of the backing image to write data into.
|
||||||
|
If not specified, this is the deepest backing image
|
||||||
|
(json-string, optional)
|
||||||
|
- "top": The file name of the backing image within the image chain,
|
||||||
|
which contains the topmost data to be committed down.
|
||||||
|
|
||||||
|
If top == base, that is an error.
|
||||||
|
If top == active, the job will not be completed by itself,
|
||||||
|
user needs to complete the job with the block-job-complete
|
||||||
|
command after getting the ready event. (Since 2.0)
|
||||||
|
|
||||||
|
If the base image is smaller than top, then the base image
|
||||||
|
will be resized to be the same size as top. If top is
|
||||||
|
smaller than the base image, the base will not be
|
||||||
|
truncated. If you want the base image size to match the
|
||||||
|
size of the smaller top, you can safely truncate it
|
||||||
|
yourself once the commit operation successfully completes.
|
||||||
|
(json-string)
|
||||||
|
- "speed": the maximum speed, in bytes per second (json-int, optional)
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
-> { "execute": "block-commit", "arguments": { "device": "virtio0",
|
||||||
|
"top": "/tmp/snap1.qcow2" } }
|
||||||
|
<- { "return": {} }
|
||||||
|
|
||||||
|
EQMP
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "drive-backup",
|
.name = "drive-backup",
|
||||||
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
|
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
|
||||||
|
@ -1088,7 +1128,9 @@ actions array:
|
||||||
- "data": a dictionary. The contents depend on the value
|
- "data": a dictionary. The contents depend on the value
|
||||||
of "type". When "type" is "blockdev-snapshot-sync":
|
of "type". When "type" is "blockdev-snapshot-sync":
|
||||||
- "device": device name to snapshot (json-string)
|
- "device": device name to snapshot (json-string)
|
||||||
|
- "node-name": graph node name to snapshot (json-string)
|
||||||
- "snapshot-file": name of new image file (json-string)
|
- "snapshot-file": name of new image file (json-string)
|
||||||
|
- "snapshot-node-name": graph node name of the new snapshot (json-string)
|
||||||
- "format": format of new image (json-string, optional)
|
- "format": format of new image (json-string, optional)
|
||||||
- "mode": whether and how QEMU should create the snapshot file
|
- "mode": whether and how QEMU should create the snapshot file
|
||||||
(NewImageMode, optional, default "absolute-paths")
|
(NewImageMode, optional, default "absolute-paths")
|
||||||
|
@ -1103,6 +1145,11 @@ Example:
|
||||||
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd0",
|
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd0",
|
||||||
"snapshot-file": "/some/place/my-image",
|
"snapshot-file": "/some/place/my-image",
|
||||||
"format": "qcow2" } },
|
"format": "qcow2" } },
|
||||||
|
{ 'type': 'blockdev-snapshot-sync', 'data' : { "node-name": "myfile",
|
||||||
|
"snapshot-file": "/some/place/my-image2",
|
||||||
|
"snapshot-node-name": "node3432",
|
||||||
|
"mode": "existing",
|
||||||
|
"format": "qcow2" } },
|
||||||
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
|
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
|
||||||
"snapshot-file": "/some/place/my-image2",
|
"snapshot-file": "/some/place/my-image2",
|
||||||
"mode": "existing",
|
"mode": "existing",
|
||||||
|
@ -1116,7 +1163,7 @@ EQMP
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "blockdev-snapshot-sync",
|
.name = "blockdev-snapshot-sync",
|
||||||
.args_type = "device:B,snapshot-file:s,format:s?,mode:s?",
|
.args_type = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
|
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1133,7 +1180,9 @@ snapshot image, default is qcow2.
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
- "device": device name to snapshot (json-string)
|
- "device": device name to snapshot (json-string)
|
||||||
|
- "node-name": graph node name to snapshot (json-string)
|
||||||
- "snapshot-file": name of new image file (json-string)
|
- "snapshot-file": name of new image file (json-string)
|
||||||
|
- "snapshot-node-name": graph node name of the new snapshot (json-string)
|
||||||
- "mode": whether and how QEMU should create the snapshot file
|
- "mode": whether and how QEMU should create the snapshot file
|
||||||
(NewImageMode, optional, default "absolute-paths")
|
(NewImageMode, optional, default "absolute-paths")
|
||||||
- "format": format of new image (json-string, optional)
|
- "format": format of new image (json-string, optional)
|
||||||
|
@ -1503,7 +1552,7 @@ EQMP
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "block_passwd",
|
.name = "block_passwd",
|
||||||
.args_type = "device:B,password:s",
|
.args_type = "device:s?,node-name:s?,password:s",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_passwd,
|
.mhandler.cmd_new = qmp_marshal_input_block_passwd,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1516,6 +1565,7 @@ Set the password of encrypted block devices.
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
- "device": device name (json-string)
|
- "device": device name (json-string)
|
||||||
|
- "node-name": name in the block driver state graph (json-string)
|
||||||
- "password": password (json-string)
|
- "password": password (json-string)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -3345,4 +3395,65 @@ Example (2):
|
||||||
|
|
||||||
<- { "return": {} }
|
<- { "return": {} }
|
||||||
|
|
||||||
|
EQMP
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "query-named-block-nodes",
|
||||||
|
.args_type = "",
|
||||||
|
.mhandler.cmd_new = qmp_marshal_input_query_named_block_nodes,
|
||||||
|
},
|
||||||
|
|
||||||
|
SQMP
|
||||||
|
@query-named-block-nodes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Return a list of BlockDeviceInfo for all the named block driver nodes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
-> { "execute": "query-named-block-nodes" }
|
||||||
|
<- { "return": [ { "ro":false,
|
||||||
|
"drv":"qcow2",
|
||||||
|
"encrypted":false,
|
||||||
|
"file":"disks/test.qcow2",
|
||||||
|
"node-name": "my-node",
|
||||||
|
"backing_file_depth":1,
|
||||||
|
"bps":1000000,
|
||||||
|
"bps_rd":0,
|
||||||
|
"bps_wr":0,
|
||||||
|
"iops":1000000,
|
||||||
|
"iops_rd":0,
|
||||||
|
"iops_wr":0,
|
||||||
|
"bps_max": 8000000,
|
||||||
|
"bps_rd_max": 0,
|
||||||
|
"bps_wr_max": 0,
|
||||||
|
"iops_max": 0,
|
||||||
|
"iops_rd_max": 0,
|
||||||
|
"iops_wr_max": 0,
|
||||||
|
"iops_size": 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
|
||||||
|
}
|
||||||
|
} } ] }
|
||||||
|
|
||||||
EQMP
|
EQMP
|
||||||
|
|
|
@ -477,7 +477,43 @@ static void qdict_destroy_obj(QObject *obj)
|
||||||
g_free(qdict);
|
g_free(qdict);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
|
static void qdict_flatten_qdict(QDict *qdict, QDict *target,
|
||||||
|
const char *prefix);
|
||||||
|
|
||||||
|
static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
|
||||||
|
{
|
||||||
|
QObject *value;
|
||||||
|
const QListEntry *entry;
|
||||||
|
char *new_key;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* This function is never called with prefix == NULL, i.e., it is always
|
||||||
|
* called from within qdict_flatten_q(list|dict)(). Therefore, it does not
|
||||||
|
* need to remove list entries during the iteration (the whole list will be
|
||||||
|
* deleted eventually anyway from qdict_flatten_qdict()). */
|
||||||
|
assert(prefix);
|
||||||
|
|
||||||
|
entry = qlist_first(qlist);
|
||||||
|
|
||||||
|
for (i = 0; entry; entry = qlist_next(entry), i++) {
|
||||||
|
value = qlist_entry_obj(entry);
|
||||||
|
new_key = g_strdup_printf("%s.%i", prefix, i);
|
||||||
|
|
||||||
|
if (qobject_type(value) == QTYPE_QDICT) {
|
||||||
|
qdict_flatten_qdict(qobject_to_qdict(value), target, new_key);
|
||||||
|
} else if (qobject_type(value) == QTYPE_QLIST) {
|
||||||
|
qdict_flatten_qlist(qobject_to_qlist(value), target, new_key);
|
||||||
|
} else {
|
||||||
|
/* All other types are moved to the target unchanged. */
|
||||||
|
qobject_incref(value);
|
||||||
|
qdict_put_obj(target, new_key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(new_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
|
||||||
{
|
{
|
||||||
QObject *value;
|
QObject *value;
|
||||||
const QDictEntry *entry, *next;
|
const QDictEntry *entry, *next;
|
||||||
|
@ -500,8 +536,12 @@ static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
|
||||||
if (qobject_type(value) == QTYPE_QDICT) {
|
if (qobject_type(value) == QTYPE_QDICT) {
|
||||||
/* Entries of QDicts are processed recursively, the QDict object
|
/* Entries of QDicts are processed recursively, the QDict object
|
||||||
* itself disappears. */
|
* itself disappears. */
|
||||||
qdict_do_flatten(qobject_to_qdict(value), target,
|
qdict_flatten_qdict(qobject_to_qdict(value), target,
|
||||||
new_key ? new_key : entry->key);
|
new_key ? new_key : entry->key);
|
||||||
|
delete = true;
|
||||||
|
} else if (qobject_type(value) == QTYPE_QLIST) {
|
||||||
|
qdict_flatten_qlist(qobject_to_qlist(value), target,
|
||||||
|
new_key ? new_key : entry->key);
|
||||||
delete = true;
|
delete = true;
|
||||||
} else if (prefix) {
|
} else if (prefix) {
|
||||||
/* All other objects are moved to the target unchanged. */
|
/* All other objects are moved to the target unchanged. */
|
||||||
|
@ -526,12 +566,14 @@ static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qdict_flatten(): For each nested QDict with key x, all fields with key y
|
* qdict_flatten(): For each nested QDict with key x, all fields with key y
|
||||||
* are moved to this QDict and their key is renamed to "x.y". This operation
|
* are moved to this QDict and their key is renamed to "x.y". For each nested
|
||||||
* is applied recursively for nested QDicts.
|
* QList with key x, the field at index y is moved to this QDict with the key
|
||||||
|
* "x.y" (i.e., the reverse of what qdict_array_split() does).
|
||||||
|
* This operation is applied recursively for nested QDicts and QLists.
|
||||||
*/
|
*/
|
||||||
void qdict_flatten(QDict *qdict)
|
void qdict_flatten(QDict *qdict)
|
||||||
{
|
{
|
||||||
qdict_do_flatten(qdict, qdict, NULL);
|
qdict_flatten_qdict(qdict, qdict, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract all the src QDict entries starting by start into dst */
|
/* extract all the src QDict entries starting by start into dst */
|
||||||
|
@ -554,3 +596,40 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
|
||||||
entry = next;
|
entry = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qdict_array_split(): This function moves array-like elements of a QDict into
|
||||||
|
* a new QList of QDicts. Every entry in the original QDict with a key prefixed
|
||||||
|
* "%u.", where %u designates an unsigned integer starting at 0 and
|
||||||
|
* incrementally counting up, will be moved to a new QDict at index %u in the
|
||||||
|
* output QList with the key prefix removed. The function terminates when there
|
||||||
|
* is no entry in the QDict with a prefix directly (incrementally) following the
|
||||||
|
* last one.
|
||||||
|
* Example: {"0.a": 42, "0.b": 23, "1.x": 0, "3.y": 1, "o.o": 7}
|
||||||
|
* (or {"1.x": 0, "3.y": 1, "0.a": 42, "o.o": 7, "0.b": 23})
|
||||||
|
* => [{"a": 42, "b": 23}, {"x": 0}]
|
||||||
|
* and {"3.y": 1, "o.o": 7} (remainder of the old QDict)
|
||||||
|
*/
|
||||||
|
void qdict_array_split(QDict *src, QList **dst)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
*dst = qlist_new();
|
||||||
|
|
||||||
|
for (i = 0; i < UINT_MAX; i++) {
|
||||||
|
QDict *subqdict;
|
||||||
|
char prefix[32];
|
||||||
|
size_t snprintf_ret;
|
||||||
|
|
||||||
|
snprintf_ret = snprintf(prefix, 32, "%u.", i);
|
||||||
|
assert(snprintf_ret < 32);
|
||||||
|
|
||||||
|
qdict_extract_subqdict(src, &subqdict, prefix);
|
||||||
|
if (!qdict_size(subqdict)) {
|
||||||
|
QDECREF(subqdict);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlist_append_obj(*dst, QOBJECT(subqdict));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ def c_var(name, protect=True):
|
||||||
'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
|
'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
|
||||||
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
|
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
|
||||||
# namespace pollution:
|
# namespace pollution:
|
||||||
polluted_words = set(['unix'])
|
polluted_words = set(['unix', 'errno'])
|
||||||
if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
|
if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
|
||||||
return "q_" + name
|
return "q_" + name
|
||||||
return name.replace('-', '_').lstrip("*")
|
return name.replace('-', '_').lstrip("*")
|
||||||
|
|
|
@ -227,6 +227,160 @@ static void qdict_iterapi_test(void)
|
||||||
QDECREF(tests_dict);
|
QDECREF(tests_dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qdict_flatten_test(void)
|
||||||
|
{
|
||||||
|
QList *list1 = qlist_new();
|
||||||
|
QList *list2 = qlist_new();
|
||||||
|
QDict *dict1 = qdict_new();
|
||||||
|
QDict *dict2 = qdict_new();
|
||||||
|
QDict *dict3 = qdict_new();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the flattening of
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "e": [
|
||||||
|
* 42,
|
||||||
|
* [
|
||||||
|
* 23,
|
||||||
|
* 66,
|
||||||
|
* {
|
||||||
|
* "a": 0,
|
||||||
|
* "b": 1
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* ],
|
||||||
|
* "f": {
|
||||||
|
* "c": 2,
|
||||||
|
* "d": 3,
|
||||||
|
* },
|
||||||
|
* "g": 4
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* to
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "e.0": 42,
|
||||||
|
* "e.1.0": 23,
|
||||||
|
* "e.1.1": 66,
|
||||||
|
* "e.1.2.a": 0,
|
||||||
|
* "e.1.2.b": 1,
|
||||||
|
* "f.c": 2,
|
||||||
|
* "f.d": 3,
|
||||||
|
* "g": 4
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
qdict_put(dict1, "a", qint_from_int(0));
|
||||||
|
qdict_put(dict1, "b", qint_from_int(1));
|
||||||
|
|
||||||
|
qlist_append_obj(list1, QOBJECT(qint_from_int(23)));
|
||||||
|
qlist_append_obj(list1, QOBJECT(qint_from_int(66)));
|
||||||
|
qlist_append_obj(list1, QOBJECT(dict1));
|
||||||
|
qlist_append_obj(list2, QOBJECT(qint_from_int(42)));
|
||||||
|
qlist_append_obj(list2, QOBJECT(list1));
|
||||||
|
|
||||||
|
qdict_put(dict2, "c", qint_from_int(2));
|
||||||
|
qdict_put(dict2, "d", qint_from_int(3));
|
||||||
|
qdict_put_obj(dict3, "e", QOBJECT(list2));
|
||||||
|
qdict_put_obj(dict3, "f", QOBJECT(dict2));
|
||||||
|
qdict_put(dict3, "g", qint_from_int(4));
|
||||||
|
|
||||||
|
qdict_flatten(dict3);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict3, "e.0") == 42);
|
||||||
|
g_assert(qdict_get_int(dict3, "e.1.0") == 23);
|
||||||
|
g_assert(qdict_get_int(dict3, "e.1.1") == 66);
|
||||||
|
g_assert(qdict_get_int(dict3, "e.1.2.a") == 0);
|
||||||
|
g_assert(qdict_get_int(dict3, "e.1.2.b") == 1);
|
||||||
|
g_assert(qdict_get_int(dict3, "f.c") == 2);
|
||||||
|
g_assert(qdict_get_int(dict3, "f.d") == 3);
|
||||||
|
g_assert(qdict_get_int(dict3, "g") == 4);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict3) == 8);
|
||||||
|
|
||||||
|
QDECREF(dict3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qdict_array_split_test(void)
|
||||||
|
{
|
||||||
|
QDict *test_dict = qdict_new();
|
||||||
|
QDict *dict1, *dict2;
|
||||||
|
QList *test_list;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the split of
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "1.x": 0,
|
||||||
|
* "3.y": 1,
|
||||||
|
* "0.a": 42,
|
||||||
|
* "o.o": 7,
|
||||||
|
* "0.b": 23
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* to
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* {
|
||||||
|
* "a": 42,
|
||||||
|
* "b": 23
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* "x": 0
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* and
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "3.y": 1,
|
||||||
|
* "o.o": 7
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* (remaining in the old QDict)
|
||||||
|
*
|
||||||
|
* This example is given in the comment of qdict_array_split().
|
||||||
|
*/
|
||||||
|
|
||||||
|
qdict_put(test_dict, "1.x", qint_from_int(0));
|
||||||
|
qdict_put(test_dict, "3.y", qint_from_int(1));
|
||||||
|
qdict_put(test_dict, "0.a", qint_from_int(42));
|
||||||
|
qdict_put(test_dict, "o.o", qint_from_int(7));
|
||||||
|
qdict_put(test_dict, "0.b", qint_from_int(23));
|
||||||
|
|
||||||
|
qdict_array_split(test_dict, &test_list);
|
||||||
|
|
||||||
|
dict1 = qobject_to_qdict(qlist_pop(test_list));
|
||||||
|
dict2 = qobject_to_qdict(qlist_pop(test_list));
|
||||||
|
|
||||||
|
g_assert(dict1);
|
||||||
|
g_assert(dict2);
|
||||||
|
g_assert(qlist_empty(test_list));
|
||||||
|
|
||||||
|
QDECREF(test_list);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict1, "a") == 42);
|
||||||
|
g_assert(qdict_get_int(dict1, "b") == 23);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict1) == 2);
|
||||||
|
|
||||||
|
QDECREF(dict1);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict2, "x") == 0);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict2) == 1);
|
||||||
|
|
||||||
|
QDECREF(dict2);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(test_dict, "3.y") == 1);
|
||||||
|
g_assert(qdict_get_int(test_dict, "o.o") == 7);
|
||||||
|
|
||||||
|
g_assert(qdict_size(test_dict) == 2);
|
||||||
|
|
||||||
|
QDECREF(test_dict);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Errors test-cases
|
* Errors test-cases
|
||||||
*/
|
*/
|
||||||
|
@ -365,6 +519,8 @@ int main(int argc, char **argv)
|
||||||
g_test_add_func("/public/del", qdict_del_test);
|
g_test_add_func("/public/del", qdict_del_test);
|
||||||
g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
|
g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
|
||||||
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
||||||
|
g_test_add_func("/public/flatten", qdict_flatten_test);
|
||||||
|
g_test_add_func("/public/array_split", qdict_array_split_test);
|
||||||
|
|
||||||
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
||||||
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
|
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
|
||||||
|
|
|
@ -518,7 +518,6 @@ static void fuzz_registers(void)
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *arch = qtest_get_arch();
|
const char *arch = qtest_get_arch();
|
||||||
char *cmdline;
|
|
||||||
int fd;
|
int fd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -538,9 +537,7 @@ int main(int argc, char **argv)
|
||||||
/* Run the tests */
|
/* Run the tests */
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
cmdline = g_strdup_printf("-vnc none ");
|
qtest_start(NULL);
|
||||||
|
|
||||||
qtest_start(cmdline);
|
|
||||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||||
qtest_add_func("/fdc/cmos", test_cmos);
|
qtest_add_func("/fdc/cmos", test_cmos);
|
||||||
qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
|
qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
|
||||||
|
|
|
@ -380,7 +380,6 @@ static void test_bmdma_no_busmaster(void)
|
||||||
static void test_bmdma_setup(void)
|
static void test_bmdma_setup(void)
|
||||||
{
|
{
|
||||||
ide_test_start(
|
ide_test_start(
|
||||||
"-vnc none "
|
|
||||||
"-drive file=%s,if=ide,serial=%s,cache=writeback "
|
"-drive file=%s,if=ide,serial=%s,cache=writeback "
|
||||||
"-global ide-hd.ver=%s",
|
"-global ide-hd.ver=%s",
|
||||||
tmp_path, "testdisk", "version");
|
tmp_path, "testdisk", "version");
|
||||||
|
@ -410,7 +409,6 @@ static void test_identify(void)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ide_test_start(
|
ide_test_start(
|
||||||
"-vnc none "
|
|
||||||
"-drive file=%s,if=ide,serial=%s,cache=writeback "
|
"-drive file=%s,if=ide,serial=%s,cache=writeback "
|
||||||
"-global ide-hd.ver=%s",
|
"-global ide-hd.ver=%s",
|
||||||
tmp_path, "testdisk", "version");
|
tmp_path, "testdisk", "version");
|
||||||
|
@ -455,7 +453,6 @@ static void test_flush(void)
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
|
|
||||||
ide_test_start(
|
ide_test_start(
|
||||||
"-vnc none "
|
|
||||||
"-drive file=blkdebug::%s,if=ide,cache=writeback",
|
"-drive file=blkdebug::%s,if=ide,cache=writeback",
|
||||||
tmp_path);
|
tmp_path);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||||
|
|
||||||
TEST_OFFSETS="0 4294967296"
|
TEST_OFFSETS="0 4294967296"
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||||
|
|
||||||
TEST_OFFSETS="0 4294967296"
|
TEST_OFFSETS="0 4294967296"
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
TEST_OFFSETS="0 4294967296"
|
TEST_OFFSETS="0 4294967296"
|
||||||
CLUSTER_SIZE=65536
|
CLUSTER_SIZE=65536
|
||||||
|
|
|
@ -45,6 +45,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
TEST_OFFSETS="0 4294967296"
|
TEST_OFFSETS="0 4294967296"
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
CLUSTER_SIZE=4k
|
CLUSTER_SIZE=4k
|
||||||
size=128M
|
size=128M
|
||||||
|
|
|
@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed
|
_supported_fmt qcow qcow2 vmdk qed
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
CLUSTER_SIZE=4k
|
CLUSTER_SIZE=4k
|
||||||
size=128M
|
size=128M
|
||||||
|
|
|
@ -222,7 +222,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
|
||||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||||
|
|
||||||
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
|
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
|
||||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
|
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
|
||||||
|
|
||||||
|
|
||||||
=== Parsing protocol from file name ===
|
=== Parsing protocol from file name ===
|
||||||
|
|
|
@ -42,6 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt vmdk
|
_supported_fmt vmdk
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
capacity_offset=16
|
capacity_offset=16
|
||||||
granularity_offset=20
|
granularity_offset=20
|
||||||
|
@ -94,11 +97,24 @@ RW 12582912 VMFS "dummy.vmdk" 1
|
||||||
EOF
|
EOF
|
||||||
_img_info
|
_img_info
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing truncated sparse ==="
|
||||||
|
IMGOPTS="subformat=monolithicSparse" _make_test_img 100G
|
||||||
|
truncate -s 10M $TEST_IMG
|
||||||
|
_img_info
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "=== Testing version 3 ==="
|
echo "=== Testing version 3 ==="
|
||||||
_use_sample_img iotest-version3.vmdk.bz2
|
_use_sample_img iotest-version3.vmdk.bz2
|
||||||
_img_info
|
_img_info
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing 4TB monolithicFlat creation and IO ==="
|
||||||
|
IMGOPTS="subformat=monolithicFlat" _make_test_img 4T
|
||||||
|
_img_info
|
||||||
|
$QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
$QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
# success, all done
|
# success, all done
|
||||||
echo "*** done"
|
echo "*** done"
|
||||||
rm -f $seq.full
|
rm -f $seq.full
|
||||||
|
|
|
@ -2043,8 +2043,87 @@ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent lines:
|
||||||
RW 12582912 VMFS "dummy.IMGFMT" 1
|
RW 12582912 VMFS "dummy.IMGFMT" 1
|
||||||
|
|
||||||
|
|
||||||
|
=== Testing truncated sparse ===
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
|
||||||
|
qemu-img: File truncated, expecting at least 13172736 bytes
|
||||||
|
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Wrong medium type
|
||||||
|
|
||||||
=== Testing version 3 ===
|
=== Testing version 3 ===
|
||||||
image: TEST_DIR/iotest-version3.IMGFMT
|
image: TEST_DIR/iotest-version3.IMGFMT
|
||||||
file format: IMGFMT
|
file format: IMGFMT
|
||||||
virtual size: 1.0G (1073741824 bytes)
|
virtual size: 1.0G (1073741824 bytes)
|
||||||
|
|
||||||
|
=== Testing 4TB monolithicFlat creation and IO ===
|
||||||
|
Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104
|
||||||
|
image: TEST_DIR/iotest-version3.IMGFMT
|
||||||
|
file format: IMGFMT
|
||||||
|
virtual size: 4.0T (4398046511104 bytes)
|
||||||
|
wrote 512/512 bytes at offset 966367641600
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
e100000000: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000010: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000020: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000030: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000040: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000050: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000060: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000070: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000080: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000090: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000a0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000b0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000c0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000d0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000e0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000000f0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000100: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000110: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000120: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000130: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000140: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000150: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000160: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000170: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000180: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000190: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001a0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001b0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001c0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001d0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001e0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e1000001f0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
|
||||||
|
e100000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000002f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e100000390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
e1000003f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
read 1024/1024 bytes at offset 966367641600
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
*** done
|
*** done
|
||||||
|
|
|
@ -44,6 +44,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt qcow qcow2 vmdk qed raw
|
_supported_fmt qcow qcow2 vmdk qed raw
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||||
|
"subformat=twoGbMaxExtentFlat" \
|
||||||
|
"subformat=twoGbMaxExtentSparse"
|
||||||
|
|
||||||
_make_test_img 4M
|
_make_test_img 4M
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
_supported_fmt cow qed qcow qcow2 vmdk
|
_supported_fmt cow qed qcow qcow2 vmdk
|
||||||
_supported_proto generic
|
_supported_proto generic
|
||||||
_supported_os Linux
|
_supported_os Linux
|
||||||
|
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||||
|
|
||||||
IMG_SIZE=128K
|
IMG_SIZE=128K
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test case for the QMP blkdebug and blkverify interfaces
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq="$(basename $0)"
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here="$PWD"
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
_supported_fmt generic
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
function do_run_qemu()
|
||||||
|
{
|
||||||
|
echo Testing: "$@" | _filter_imgfmt
|
||||||
|
$QEMU -nographic -qmp stdio -serial none "$@"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_qemu()
|
||||||
|
{
|
||||||
|
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io
|
||||||
|
}
|
||||||
|
|
||||||
|
IMG_SIZE=64M
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkverify through filename ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
|
||||||
|
_filter_imgfmt
|
||||||
|
_make_test_img $IMG_SIZE
|
||||||
|
$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
|
||||||
|
-c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
|
||||||
|
-c 'read -P 42 0 512' | _filter_qemu_io
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkverify through file blockref ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
|
||||||
|
_filter_imgfmt
|
||||||
|
_make_test_img $IMG_SIZE
|
||||||
|
$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \
|
||||||
|
-c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
|
||||||
|
-c 'read -P 42 0 512' | _filter_qemu_io
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkdebug through filename ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
$QEMU_IO -c "open -o file.driver=blkdebug,file.inject-error.event=l2_load $TEST_IMG" \
|
||||||
|
-c 'read -P 42 0x38000 512'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkdebug through file blockref ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.inject-error.event=l2_load,file.image.filename=$TEST_IMG" \
|
||||||
|
-c 'read -P 42 0x38000 512'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkdebug on existing block device ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
|
||||||
|
{ "execute": "qmp_capabilities" }
|
||||||
|
{ "execute": "blockdev-add",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"driver": "$IMGFMT",
|
||||||
|
"id": "drive0-debug",
|
||||||
|
"file": {
|
||||||
|
"driver": "blkdebug",
|
||||||
|
"image": "drive0",
|
||||||
|
"inject-error": [{
|
||||||
|
"event": "l2_load"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-debug "read 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "quit" }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkverify on existing block device ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
run_qemu -drive "file=$TEST_IMG,format=$IMGFMT,if=none,id=drive0" <<EOF
|
||||||
|
{ "execute": "qmp_capabilities" }
|
||||||
|
{ "execute": "blockdev-add",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"driver": "blkverify",
|
||||||
|
"id": "drive0-verify",
|
||||||
|
"test": "drive0",
|
||||||
|
"raw": {
|
||||||
|
"driver": "raw",
|
||||||
|
"file": {
|
||||||
|
"driver": "file",
|
||||||
|
"filename": "$TEST_IMG.base"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-verify "read 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "quit" }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkverify on existing raw block device ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
run_qemu -drive "file=$TEST_IMG.base,if=none,id=drive0" <<EOF
|
||||||
|
{ "execute": "qmp_capabilities" }
|
||||||
|
{ "execute": "blockdev-add",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"driver": "blkverify",
|
||||||
|
"id": "drive0-verify",
|
||||||
|
"test": {
|
||||||
|
"driver": "$IMGFMT",
|
||||||
|
"file": {
|
||||||
|
"driver": "file",
|
||||||
|
"filename": "$TEST_IMG"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"raw": "drive0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-verify "read 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "quit" }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkdebug's set-state through QMP ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
|
||||||
|
{ "execute": "qmp_capabilities" }
|
||||||
|
{ "execute": "blockdev-add",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"driver": "$IMGFMT",
|
||||||
|
"id": "drive0-debug",
|
||||||
|
"file": {
|
||||||
|
"driver": "blkdebug",
|
||||||
|
"image": "drive0",
|
||||||
|
"inject-error": [{
|
||||||
|
"event": "read_aio",
|
||||||
|
"state": 42
|
||||||
|
}],
|
||||||
|
"set-state": [{
|
||||||
|
"event": "write_aio",
|
||||||
|
"new_state": 42
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-debug "read 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-debug "write 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "human-monitor-command",
|
||||||
|
"arguments": {
|
||||||
|
"command-line": 'qemu-io drive0-debug "read 0 512"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ "execute": "quit" }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,90 @@
|
||||||
|
QA output created by 071
|
||||||
|
|
||||||
|
=== Testing blkverify through filename ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 229376
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 229376
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
|
||||||
|
|
||||||
|
=== Testing blkverify through file blockref ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 229376
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 229376
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
|
||||||
|
|
||||||
|
=== Testing blkdebug through filename ===
|
||||||
|
|
||||||
|
read failed: Input/output error
|
||||||
|
|
||||||
|
=== Testing blkdebug through file blockref ===
|
||||||
|
|
||||||
|
read failed: Input/output error
|
||||||
|
|
||||||
|
=== Testing blkdebug on existing block device ===
|
||||||
|
|
||||||
|
Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
|
||||||
|
QMP_VERSION
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
read failed: Input/output error
|
||||||
|
{"return": ""}
|
||||||
|
{"return": {}}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||||
|
|
||||||
|
|
||||||
|
=== Testing blkverify on existing block device ===
|
||||||
|
|
||||||
|
Testing: -drive file=TEST_DIR/t.IMGFMT,format=IMGFMT,if=none,id=drive0
|
||||||
|
QMP_VERSION
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
|
||||||
|
|
||||||
|
|
||||||
|
=== Testing blkverify on existing raw block device ===
|
||||||
|
|
||||||
|
Testing: -drive file=TEST_DIR/t.IMGFMT.base,if=none,id=drive0
|
||||||
|
QMP_VERSION
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
|
||||||
|
|
||||||
|
|
||||||
|
=== Testing blkdebug's set-state through QMP ===
|
||||||
|
|
||||||
|
Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
|
||||||
|
QMP_VERSION
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
{"return": ""}
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
{"return": ""}
|
||||||
|
read failed: Input/output error
|
||||||
|
{"return": ""}
|
||||||
|
{"return": {}}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||||
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||||
|
|
||||||
|
*** done
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test case for nested image formats
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq="$(basename $0)"
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here="$PWD"
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
IMG_SIZE=64M
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing nested image formats ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||||
|
|
||||||
|
$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
|
||||||
|
-c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
|
||||||
|
|
||||||
|
$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=$IMGFMT,file.file.filename=$TEST_IMG" \
|
||||||
|
-c 'read -P 42 0 512' -c 'read -P 23 512 512' \
|
||||||
|
-c 'read -P 66 1024 512' | _filter_qemu_io
|
||||||
|
|
||||||
|
# When not giving any format, qemu should open only one "layer". Therefore, this
|
||||||
|
# should not work for any image formats with a header.
|
||||||
|
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,21 @@
|
||||||
|
QA output created by 072
|
||||||
|
|
||||||
|
=== Testing nested image formats ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 512
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 1024
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 512
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 1024
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
Pattern verification failed at offset 0, 512 bytes
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
*** done
|
|
@ -0,0 +1,278 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test concurrent pread/pwrite
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=kwolf@redhat.com
|
||||||
|
|
||||||
|
seq=`basename $0`
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here=`pwd`
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
_supported_fmt generic
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
CLUSTER_SIZE=4k
|
||||||
|
size=128M
|
||||||
|
|
||||||
|
_make_test_img $size
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Some concurrent requests involving RMW =="
|
||||||
|
|
||||||
|
function test_io()
|
||||||
|
{
|
||||||
|
echo "open -o file.align=4k blkdebug::$TEST_IMG"
|
||||||
|
# A simple RMW request
|
||||||
|
cat <<EOF
|
||||||
|
aio_write -P 10 0x200 0x200
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Sequential RMW requests on the same physical sector
|
||||||
|
off=0x1000
|
||||||
|
for ev in "head" "after_head" "tail" "after_tail"; do
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.$ev A
|
||||||
|
aio_write -P 10 $((off + 0x200)) 0x200
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 $((off + 0x400)) 0x200
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
off=$((off + 0x1000))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Chained dependencies
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0x5000 0x200
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 0x5200 0x200
|
||||||
|
aio_write -P 12 0x5400 0x200
|
||||||
|
aio_write -P 13 0x5600 0x200
|
||||||
|
aio_write -P 14 0x5800 0x200
|
||||||
|
aio_write -P 15 0x5a00 0x200
|
||||||
|
aio_write -P 16 0x5c00 0x200
|
||||||
|
aio_write -P 17 0x5e00 0x200
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Overlapping multiple requests
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0x6000 0x200
|
||||||
|
wait_break A
|
||||||
|
break pwritev_rmw.after_head B
|
||||||
|
aio_write -P 10 0x7e00 0x200
|
||||||
|
wait_break B
|
||||||
|
aio_write -P 11 0x6800 0x1000
|
||||||
|
resume A
|
||||||
|
sleep 100
|
||||||
|
resume B
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0x8000 0x200
|
||||||
|
wait_break A
|
||||||
|
break pwritev_rmw.after_head B
|
||||||
|
aio_write -P 10 0x9e00 0x200
|
||||||
|
wait_break B
|
||||||
|
aio_write -P 11 0x8800 0x1000
|
||||||
|
resume B
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0xa000 0x200
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 0xa800 0x1000
|
||||||
|
break pwritev_rmw.after_head B
|
||||||
|
aio_write -P 10 0xbe00 0x200
|
||||||
|
wait_break B
|
||||||
|
resume A
|
||||||
|
sleep 100
|
||||||
|
resume B
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0xc000 0x200
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 0xc800 0x1000
|
||||||
|
break pwritev_rmw.after_head B
|
||||||
|
aio_write -P 10 0xde00 0x200
|
||||||
|
wait_break B
|
||||||
|
resume B
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Only RMW for the tail part
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev_rmw.after_tail A
|
||||||
|
aio_write -P 10 0xe000 0x1800
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 0xf000 0xc00
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev A
|
||||||
|
aio_write -P 10 0x10000 0x800
|
||||||
|
wait_break A
|
||||||
|
break pwritev_rmw.after_tail B
|
||||||
|
aio_write -P 11 0x10000 0x400
|
||||||
|
break pwritev_done C
|
||||||
|
resume A
|
||||||
|
wait_break C
|
||||||
|
resume C
|
||||||
|
sleep 100
|
||||||
|
wait_break B
|
||||||
|
resume B
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
break pwritev A
|
||||||
|
aio_write -P 10 0x11000 0x800
|
||||||
|
wait_break A
|
||||||
|
aio_write -P 11 0x11000 0x1000
|
||||||
|
sleep 100
|
||||||
|
resume A
|
||||||
|
aio_flush
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
test_io | $QEMU_IO | _filter_qemu_io | \
|
||||||
|
sed -e 's,[0-9/]* bytes at offset [0-9]*,XXX/XXX bytes at offset XXX,g' \
|
||||||
|
-e 's/^[0-9]* \(bytes\|KiB\)/XXX bytes/' \
|
||||||
|
-e '/Suspended/d'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Verify image content =="
|
||||||
|
|
||||||
|
function verify_io()
|
||||||
|
{
|
||||||
|
# A simple RMW request
|
||||||
|
echo read -P 0 0 0x200
|
||||||
|
echo read -P 10 0x200 0x200
|
||||||
|
echo read -P 0 0x400 0xc00
|
||||||
|
|
||||||
|
# Sequential RMW requests on the same physical sector
|
||||||
|
echo read -P 0 0x1000 0x200
|
||||||
|
echo read -P 10 0x1200 0x200
|
||||||
|
echo read -P 11 0x1400 0x200
|
||||||
|
echo read -P 0 0x1600 0xa00
|
||||||
|
|
||||||
|
echo read -P 0 0x2000 0x200
|
||||||
|
echo read -P 10 0x2200 0x200
|
||||||
|
echo read -P 11 0x2400 0x200
|
||||||
|
echo read -P 0 0x2600 0xa00
|
||||||
|
|
||||||
|
echo read -P 0 0x3000 0x200
|
||||||
|
echo read -P 10 0x3200 0x200
|
||||||
|
echo read -P 11 0x3400 0x200
|
||||||
|
echo read -P 0 0x3600 0xa00
|
||||||
|
|
||||||
|
echo read -P 0 0x4000 0x200
|
||||||
|
echo read -P 10 0x4200 0x200
|
||||||
|
echo read -P 11 0x4400 0x200
|
||||||
|
echo read -P 0 0x4600 0xa00
|
||||||
|
|
||||||
|
# Chained dependencies
|
||||||
|
echo read -P 10 0x5000 0x200
|
||||||
|
echo read -P 11 0x5200 0x200
|
||||||
|
echo read -P 12 0x5400 0x200
|
||||||
|
echo read -P 13 0x5600 0x200
|
||||||
|
echo read -P 14 0x5800 0x200
|
||||||
|
echo read -P 15 0x5a00 0x200
|
||||||
|
echo read -P 16 0x5c00 0x200
|
||||||
|
echo read -P 17 0x5e00 0x200
|
||||||
|
|
||||||
|
# Overlapping multiple requests
|
||||||
|
echo read -P 10 0x6000 0x200
|
||||||
|
echo read -P 0 0x6200 0x600
|
||||||
|
echo read -P 11 0x6800 0x1000
|
||||||
|
echo read -P 0 0x7800 0x600
|
||||||
|
echo read -P 10 0x7e00 0x200
|
||||||
|
|
||||||
|
echo read -P 10 0x8000 0x200
|
||||||
|
echo read -P 0 0x8200 0x600
|
||||||
|
echo read -P 11 0x8800 0x1000
|
||||||
|
echo read -P 0 0x9800 0x600
|
||||||
|
echo read -P 10 0x9e00 0x200
|
||||||
|
|
||||||
|
echo read -P 10 0xa000 0x200
|
||||||
|
echo read -P 0 0xa200 0x600
|
||||||
|
echo read -P 11 0xa800 0x1000
|
||||||
|
echo read -P 0 0xb800 0x600
|
||||||
|
echo read -P 10 0xbe00 0x200
|
||||||
|
|
||||||
|
echo read -P 10 0xc000 0x200
|
||||||
|
echo read -P 0 0xc200 0x600
|
||||||
|
echo read -P 11 0xc800 0x1000
|
||||||
|
echo read -P 0 0xd800 0x600
|
||||||
|
echo read -P 10 0xde00 0x200
|
||||||
|
|
||||||
|
# Only RMW for the tail part
|
||||||
|
echo read -P 10 0xe000 0x1000
|
||||||
|
echo read -P 11 0xf800 0x400
|
||||||
|
echo read -P 0 0xfc00 0x400
|
||||||
|
|
||||||
|
echo read -P 11 0x10000 0x400
|
||||||
|
echo read -P 10 0x10400 0x400
|
||||||
|
|
||||||
|
echo read -P 11 0x11800 0x800
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,202 @@
|
||||||
|
QA output created by 077
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
|
|
||||||
|
== Some concurrent requests involving RMW ==
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'B'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'B'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'B'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'B'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
blkdebug: Resuming request 'C'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'B'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
blkdebug: Resuming request 'A'
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote XXX/XXX bytes at offset XXX
|
||||||
|
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
== Verify image content ==
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 512
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 3072/3072 bytes at offset 1024
|
||||||
|
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 4096
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 4608
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 5120
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 2560/2560 bytes at offset 5632
|
||||||
|
2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 8192
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 8704
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 9216
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 2560/2560 bytes at offset 9728
|
||||||
|
2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 12288
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 12800
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 13312
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 2560/2560 bytes at offset 13824
|
||||||
|
2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 16384
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 16896
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 17408
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 2560/2560 bytes at offset 17920
|
||||||
|
2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 20480
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 20992
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 21504
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 22016
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 22528
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 23040
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 23552
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 24064
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 24576
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 25088
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 4096/4096 bytes at offset 26624
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 30720
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 32256
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 32768
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 33280
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 4096/4096 bytes at offset 34816
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 38912
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 40448
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 40960
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 41472
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 4096/4096 bytes at offset 43008
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 47104
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 48640
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 49152
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 49664
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 4096/4096 bytes at offset 51200
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1536/1536 bytes at offset 55296
|
||||||
|
1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 56832
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 4096/4096 bytes at offset 57344
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1024/1024 bytes at offset 63488
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1024/1024 bytes at offset 64512
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1024/1024 bytes at offset 65536
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 1024/1024 bytes at offset 66560
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 2048/2048 bytes at offset 71680
|
||||||
|
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
No errors were found on the image.
|
||||||
|
*** done
|
|
@ -170,6 +170,17 @@ _make_test_img()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_rm_test_img()
|
||||||
|
{
|
||||||
|
local img=$1
|
||||||
|
if [ "$IMGFMT" = "vmdk" ]; then
|
||||||
|
# Remove all the extents for vmdk
|
||||||
|
$QEMU_IMG info $img 2>/dev/null | grep 'filename:' | cut -f 2 -d: \
|
||||||
|
| xargs -I {} rm -f "{}"
|
||||||
|
fi
|
||||||
|
rm -f $img
|
||||||
|
}
|
||||||
|
|
||||||
_cleanup_test_img()
|
_cleanup_test_img()
|
||||||
{
|
{
|
||||||
case "$IMGPROTO" in
|
case "$IMGPROTO" in
|
||||||
|
@ -179,9 +190,9 @@ _cleanup_test_img()
|
||||||
rm -f "$TEST_IMG_FILE"
|
rm -f "$TEST_IMG_FILE"
|
||||||
;;
|
;;
|
||||||
file)
|
file)
|
||||||
rm -f "$TEST_DIR/t.$IMGFMT"
|
_rm_test_img "$TEST_DIR/t.$IMGFMT"
|
||||||
rm -f "$TEST_DIR/t.$IMGFMT.orig"
|
_rm_test_img "$TEST_DIR/t.$IMGFMT.orig"
|
||||||
rm -f "$TEST_DIR/t.$IMGFMT.base"
|
_rm_test_img "$TEST_DIR/t.$IMGFMT.base"
|
||||||
if [ -n "$SAMPLE_IMG_FILE" ]
|
if [ -n "$SAMPLE_IMG_FILE" ]
|
||||||
then
|
then
|
||||||
rm -f "$TEST_DIR/$SAMPLE_IMG_FILE"
|
rm -f "$TEST_DIR/$SAMPLE_IMG_FILE"
|
||||||
|
@ -406,6 +417,17 @@ _default_cache_mode()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_unsupported_imgopts()
|
||||||
|
{
|
||||||
|
for bad_opt
|
||||||
|
do
|
||||||
|
if echo "$IMGOPTS" | grep -q 2>/dev/null "$bad_opt"
|
||||||
|
then
|
||||||
|
_notrun "not suitable for image option: $bad_opt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# this test requires that a specified command (executable) exists
|
# this test requires that a specified command (executable) exists
|
||||||
#
|
#
|
||||||
_require_command()
|
_require_command()
|
||||||
|
|
|
@ -77,5 +77,8 @@
|
||||||
068 rw auto
|
068 rw auto
|
||||||
069 rw auto
|
069 rw auto
|
||||||
070 rw auto
|
070 rw auto
|
||||||
|
071 rw auto
|
||||||
|
072 rw auto
|
||||||
073 rw auto
|
073 rw auto
|
||||||
074 rw auto
|
074 rw auto
|
||||||
|
077 rw auto
|
||||||
|
|
|
@ -13,3 +13,4 @@ util-obj-y += hexdump.o
|
||||||
util-obj-y += crc32c.o
|
util-obj-y += crc32c.o
|
||||||
util-obj-y += throttle.o
|
util-obj-y += throttle.o
|
||||||
util-obj-y += getauxval.o
|
util-obj-y += getauxval.o
|
||||||
|
util-obj-y += readline.o
|
||||||
|
|
|
@ -47,6 +47,9 @@ extern int daemon(int, int);
|
||||||
# define QEMU_VMALLOC_ALIGN getpagesize()
|
# define QEMU_VMALLOC_ALIGN getpagesize()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
#include "config-host.h"
|
#include "config-host.h"
|
||||||
|
@ -85,6 +88,11 @@ void *qemu_oom_check(void *ptr)
|
||||||
void *qemu_memalign(size_t alignment, size_t size)
|
void *qemu_memalign(size_t alignment, size_t size)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
|
if (alignment < sizeof(void*)) {
|
||||||
|
alignment = sizeof(void*);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
|
#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
|
||||||
int ret;
|
int ret;
|
||||||
ret = posix_memalign(&ptr, alignment, size);
|
ret = posix_memalign(&ptr, alignment, size);
|
||||||
|
@ -251,3 +259,18 @@ qemu_get_local_state_pathname(const char *relative_pathname)
|
||||||
return g_strdup_printf("%s/%s", CONFIG_QEMU_LOCALSTATEDIR,
|
return g_strdup_printf("%s/%s", CONFIG_QEMU_LOCALSTATEDIR,
|
||||||
relative_pathname);
|
relative_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_set_tty_echo(int fd, bool echo)
|
||||||
|
{
|
||||||
|
struct termios tty;
|
||||||
|
|
||||||
|
tcgetattr(fd, &tty);
|
||||||
|
|
||||||
|
if (echo) {
|
||||||
|
tty.c_lflag |= ECHO | ECHONL | ICANON | IEXTEN;
|
||||||
|
} else {
|
||||||
|
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
tcsetattr(fd, TCSANOW, &tty);
|
||||||
|
}
|
||||||
|
|
|
@ -189,3 +189,22 @@ qemu_get_local_state_pathname(const char *relative_pathname)
|
||||||
return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path,
|
return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path,
|
||||||
relative_pathname);
|
relative_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_set_tty_echo(int fd, bool echo)
|
||||||
|
{
|
||||||
|
HANDLE handle = (HANDLE)_get_osfhandle(fd);
|
||||||
|
DWORD dwMode = 0;
|
||||||
|
|
||||||
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetConsoleMode(handle, &dwMode);
|
||||||
|
|
||||||
|
if (echo) {
|
||||||
|
SetConsoleMode(handle, dwMode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
|
||||||
|
} else {
|
||||||
|
SetConsoleMode(handle,
|
||||||
|
dwMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -356,3 +356,103 @@ int qemu_read_config_file(const char *filename)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void config_parse_qdict_section(QDict *options, QemuOptsList *opts,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QemuOpts *subopts;
|
||||||
|
QDict *subqdict;
|
||||||
|
QList *list = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
size_t orig_size, enum_size;
|
||||||
|
char *prefix;
|
||||||
|
|
||||||
|
prefix = g_strdup_printf("%s.", opts->name);
|
||||||
|
qdict_extract_subqdict(options, &subqdict, prefix);
|
||||||
|
g_free(prefix);
|
||||||
|
orig_size = qdict_size(subqdict);
|
||||||
|
if (!orig_size) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
subopts = qemu_opts_create(opts, NULL, 0, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_opts_absorb_qdict(subopts, subqdict, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_size = qdict_size(subqdict);
|
||||||
|
if (enum_size < orig_size && enum_size) {
|
||||||
|
error_setg(errp, "Unknown option '%s' for [%s]",
|
||||||
|
qdict_first(subqdict)->key, opts->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enum_size) {
|
||||||
|
/* Multiple, enumerated sections */
|
||||||
|
QListEntry *list_entry;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
/* Not required anymore */
|
||||||
|
qemu_opts_del(subopts);
|
||||||
|
|
||||||
|
qdict_array_split(subqdict, &list);
|
||||||
|
if (qdict_size(subqdict)) {
|
||||||
|
error_setg(errp, "Unused option '%s' for [%s]",
|
||||||
|
qdict_first(subqdict)->key, opts->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLIST_FOREACH_ENTRY(list, list_entry) {
|
||||||
|
QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry));
|
||||||
|
char *opt_name;
|
||||||
|
|
||||||
|
opt_name = g_strdup_printf("%s.%u", opts->name, i++);
|
||||||
|
subopts = qemu_opts_create(opts, opt_name, 1, &local_err);
|
||||||
|
g_free(opt_name);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_opts_absorb_qdict(subopts, section, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
qemu_opts_del(subopts);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qdict_size(section)) {
|
||||||
|
error_setg(errp, "[%s] section doesn't support the option '%s'",
|
||||||
|
opts->name, qdict_first(section)->key);
|
||||||
|
qemu_opts_del(subopts);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
QDECREF(subqdict);
|
||||||
|
QDECREF(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
for (i = 0; lists[i]; i++) {
|
||||||
|
config_parse_qdict_section(options, lists[i], &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "sysemu/sysemu.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
struct progress_state {
|
struct progress_state {
|
||||||
|
@ -83,12 +82,22 @@ static void progress_dummy_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_POSIX
|
#ifdef CONFIG_POSIX
|
||||||
struct sigaction action;
|
struct sigaction action;
|
||||||
|
sigset_t set;
|
||||||
|
|
||||||
memset(&action, 0, sizeof(action));
|
memset(&action, 0, sizeof(action));
|
||||||
sigfillset(&action.sa_mask);
|
sigfillset(&action.sa_mask);
|
||||||
action.sa_handler = sigusr_print;
|
action.sa_handler = sigusr_print;
|
||||||
action.sa_flags = 0;
|
action.sa_flags = 0;
|
||||||
sigaction(SIGUSR1, &action, NULL);
|
sigaction(SIGUSR1, &action, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SIGUSR1 is SIG_IPI and gets blocked in qemu_init_main_loop(). In the
|
||||||
|
* tools that use the progress report SIGUSR1 isn't used in this meaning
|
||||||
|
* and instead should print the progress, so reenable it.
|
||||||
|
*/
|
||||||
|
sigemptyset(&set);
|
||||||
|
sigaddset(&set, SIGUSR1);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
state.print = progress_dummy_print;
|
state.print = progress_dummy_print;
|
||||||
|
|
|
@ -21,21 +21,19 @@
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "monitor/readline.h"
|
|
||||||
#include "monitor/monitor.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/readline.h"
|
||||||
|
|
||||||
#define IS_NORM 0
|
#define IS_NORM 0
|
||||||
#define IS_ESC 1
|
#define IS_ESC 1
|
||||||
#define IS_CSI 2
|
#define IS_CSI 2
|
||||||
#define IS_SS3 3
|
#define IS_SS3 3
|
||||||
|
|
||||||
#undef printf
|
|
||||||
#define printf do_not_use_printf
|
|
||||||
|
|
||||||
void readline_show_prompt(ReadLineState *rs)
|
void readline_show_prompt(ReadLineState *rs)
|
||||||
{
|
{
|
||||||
monitor_printf(rs->mon, "%s", rs->prompt);
|
rs->printf_func(rs->opaque, "%s", rs->prompt);
|
||||||
monitor_flush(rs->mon);
|
rs->flush_func(rs->opaque);
|
||||||
rs->last_cmd_buf_index = 0;
|
rs->last_cmd_buf_index = 0;
|
||||||
rs->last_cmd_buf_size = 0;
|
rs->last_cmd_buf_size = 0;
|
||||||
rs->esc_state = IS_NORM;
|
rs->esc_state = IS_NORM;
|
||||||
|
@ -49,17 +47,17 @@ static void readline_update(ReadLineState *rs)
|
||||||
if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
|
if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
|
||||||
memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
|
memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
|
||||||
for(i = 0; i < rs->last_cmd_buf_index; i++) {
|
for(i = 0; i < rs->last_cmd_buf_index; i++) {
|
||||||
monitor_printf(rs->mon, "\033[D");
|
rs->printf_func(rs->opaque, "\033[D");
|
||||||
}
|
}
|
||||||
rs->cmd_buf[rs->cmd_buf_size] = '\0';
|
rs->cmd_buf[rs->cmd_buf_size] = '\0';
|
||||||
if (rs->read_password) {
|
if (rs->read_password) {
|
||||||
len = strlen(rs->cmd_buf);
|
len = strlen(rs->cmd_buf);
|
||||||
for(i = 0; i < len; i++)
|
for(i = 0; i < len; i++)
|
||||||
monitor_printf(rs->mon, "*");
|
rs->printf_func(rs->opaque, "*");
|
||||||
} else {
|
} else {
|
||||||
monitor_printf(rs->mon, "%s", rs->cmd_buf);
|
rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
|
||||||
}
|
}
|
||||||
monitor_printf(rs->mon, "\033[K");
|
rs->printf_func(rs->opaque, "\033[K");
|
||||||
memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
|
memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
|
||||||
rs->last_cmd_buf_size = rs->cmd_buf_size;
|
rs->last_cmd_buf_size = rs->cmd_buf_size;
|
||||||
rs->last_cmd_buf_index = rs->cmd_buf_size;
|
rs->last_cmd_buf_index = rs->cmd_buf_size;
|
||||||
|
@ -68,17 +66,17 @@ static void readline_update(ReadLineState *rs)
|
||||||
delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
|
delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
for(i = 0;i < delta; i++) {
|
for(i = 0;i < delta; i++) {
|
||||||
monitor_printf(rs->mon, "\033[C");
|
rs->printf_func(rs->opaque, "\033[C");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
delta = -delta;
|
delta = -delta;
|
||||||
for(i = 0;i < delta; i++) {
|
for(i = 0;i < delta; i++) {
|
||||||
monitor_printf(rs->mon, "\033[D");
|
rs->printf_func(rs->opaque, "\033[D");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs->last_cmd_buf_index = rs->cmd_buf_index;
|
rs->last_cmd_buf_index = rs->cmd_buf_index;
|
||||||
}
|
}
|
||||||
monitor_flush(rs->mon);
|
rs->flush_func(rs->opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readline_insert_char(ReadLineState *rs, int ch)
|
static void readline_insert_char(ReadLineState *rs, int ch)
|
||||||
|
@ -284,7 +282,7 @@ static void readline_completion(ReadLineState *rs)
|
||||||
cmdline = g_malloc(rs->cmd_buf_index + 1);
|
cmdline = g_malloc(rs->cmd_buf_index + 1);
|
||||||
memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
|
memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
|
||||||
cmdline[rs->cmd_buf_index] = '\0';
|
cmdline[rs->cmd_buf_index] = '\0';
|
||||||
rs->completion_finder(rs->mon, cmdline);
|
rs->completion_finder(rs->opaque, cmdline);
|
||||||
g_free(cmdline);
|
g_free(cmdline);
|
||||||
|
|
||||||
/* no completion found */
|
/* no completion found */
|
||||||
|
@ -299,7 +297,7 @@ static void readline_completion(ReadLineState *rs)
|
||||||
if (len > 0 && rs->completions[0][len - 1] != '/')
|
if (len > 0 && rs->completions[0][len - 1] != '/')
|
||||||
readline_insert_char(rs, ' ');
|
readline_insert_char(rs, ' ');
|
||||||
} else {
|
} else {
|
||||||
monitor_printf(rs->mon, "\n");
|
rs->printf_func(rs->opaque, "\n");
|
||||||
max_width = 0;
|
max_width = 0;
|
||||||
max_prefix = 0;
|
max_prefix = 0;
|
||||||
for(i = 0; i < rs->nb_completions; i++) {
|
for(i = 0; i < rs->nb_completions; i++) {
|
||||||
|
@ -329,9 +327,9 @@ static void readline_completion(ReadLineState *rs)
|
||||||
nb_cols = 80 / max_width;
|
nb_cols = 80 / max_width;
|
||||||
j = 0;
|
j = 0;
|
||||||
for(i = 0; i < rs->nb_completions; i++) {
|
for(i = 0; i < rs->nb_completions; i++) {
|
||||||
monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
|
rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
|
||||||
if (++j == nb_cols || i == (rs->nb_completions - 1)) {
|
if (++j == nb_cols || i == (rs->nb_completions - 1)) {
|
||||||
monitor_printf(rs->mon, "\n");
|
rs->printf_func(rs->opaque, "\n");
|
||||||
j = 0;
|
j = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,12 +363,12 @@ void readline_handle_byte(ReadLineState *rs, int ch)
|
||||||
rs->cmd_buf[rs->cmd_buf_size] = '\0';
|
rs->cmd_buf[rs->cmd_buf_size] = '\0';
|
||||||
if (!rs->read_password)
|
if (!rs->read_password)
|
||||||
readline_hist_add(rs, rs->cmd_buf);
|
readline_hist_add(rs, rs->cmd_buf);
|
||||||
monitor_printf(rs->mon, "\n");
|
rs->printf_func(rs->opaque, "\n");
|
||||||
rs->cmd_buf_index = 0;
|
rs->cmd_buf_index = 0;
|
||||||
rs->cmd_buf_size = 0;
|
rs->cmd_buf_size = 0;
|
||||||
rs->last_cmd_buf_index = 0;
|
rs->last_cmd_buf_index = 0;
|
||||||
rs->last_cmd_buf_size = 0;
|
rs->last_cmd_buf_size = 0;
|
||||||
rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
|
rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque);
|
||||||
break;
|
break;
|
||||||
case 23:
|
case 23:
|
||||||
/* ^W */
|
/* ^W */
|
||||||
|
@ -480,13 +478,17 @@ const char *readline_get_history(ReadLineState *rs, unsigned int index)
|
||||||
return rs->history[index];
|
return rs->history[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadLineState *readline_init(Monitor *mon,
|
ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
|
||||||
|
ReadLineFlushFunc *flush_func,
|
||||||
|
void *opaque,
|
||||||
ReadLineCompletionFunc *completion_finder)
|
ReadLineCompletionFunc *completion_finder)
|
||||||
{
|
{
|
||||||
ReadLineState *rs = g_malloc0(sizeof(*rs));
|
ReadLineState *rs = g_malloc0(sizeof(*rs));
|
||||||
|
|
||||||
rs->hist_entry = -1;
|
rs->hist_entry = -1;
|
||||||
rs->mon = mon;
|
rs->opaque = opaque;
|
||||||
|
rs->printf_func = printf_func;
|
||||||
|
rs->flush_func = flush_func;
|
||||||
rs->completion_finder = completion_finder;
|
rs->completion_finder = completion_finder;
|
||||||
|
|
||||||
return rs;
|
return rs;
|
Loading…
Reference in New Issue