Merge remote-tracking branch 'kwolf/for-anthony' into staging
* kwolf/for-anthony: (43 commits) qcow2: Factor out handle_dependencies() qcow2: Execute run_dependent_requests() without lock qcow2: Enable dirty flag in qcow2_alloc_cluster_link_l2 qcow2: Allocate l2meta only for cluster allocations qcow2: Drop l2meta.cluster_offset qcow2: Allocate l2meta dynamically qcow2: Introduce Qcow2COWRegion qcow2: Round QCowL2Meta.offset down to cluster boundary atapi: reset cdrom tray statuses on ide_reset qemu-iotests: Test concurrent cluster allocations qcow2: Move BLKDBG_EVENT out of the lock qemu-io: Add AIO debugging commands blkdebug: Implement suspend/resume of AIO requests blkdebug: Factor out remove_rule() blkdebug: Allow usage without config file create new function: qemu_opt_set_number use qemu_opts_create_nofail introduce qemu_opts_create_nofail function qemu-option: qemu_opt_set_bool(): fix code duplication qemu-option: qemu_opts_validate(): fix duplicated code ... Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
e376a788ae
5
async.c
5
async.c
@ -215,8 +215,3 @@ void aio_context_unref(AioContext *ctx)
|
||||
{
|
||||
g_source_unref(&ctx->source);
|
||||
}
|
||||
|
||||
void aio_flush(AioContext *ctx)
|
||||
{
|
||||
while (aio_poll(ctx, true));
|
||||
}
|
||||
|
227
block.c
227
block.c
@ -518,22 +518,16 @@ BlockDriver *bdrv_find_protocol(const char *filename)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_image_format(const char *filename, BlockDriver **pdrv)
|
||||
static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
BlockDriver **pdrv)
|
||||
{
|
||||
int ret, score, score_max;
|
||||
int score, score_max;
|
||||
BlockDriver *drv1, *drv;
|
||||
uint8_t buf[2048];
|
||||
BlockDriverState *bs;
|
||||
|
||||
ret = bdrv_file_open(&bs, filename, 0);
|
||||
if (ret < 0) {
|
||||
*pdrv = NULL;
|
||||
return ret;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
|
||||
if (bs->sg || !bdrv_is_inserted(bs)) {
|
||||
bdrv_delete(bs);
|
||||
drv = bdrv_find_format("raw");
|
||||
if (!drv) {
|
||||
ret = -ENOENT;
|
||||
@ -543,7 +537,6 @@ static int find_image_format(const char *filename, BlockDriver **pdrv)
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
||||
bdrv_delete(bs);
|
||||
if (ret < 0) {
|
||||
*pdrv = NULL;
|
||||
return ret;
|
||||
@ -634,10 +627,31 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
bs->copy_on_read--;
|
||||
}
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
{
|
||||
int open_flags = flags | BDRV_O_CACHE_WB;
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
* image.
|
||||
*/
|
||||
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
/*
|
||||
* Snapshots should be writable.
|
||||
*/
|
||||
if (bs->is_temporary) {
|
||||
open_flags |= BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
return open_flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*/
|
||||
static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
const char *filename,
|
||||
int flags, BlockDriver *drv)
|
||||
{
|
||||
int ret, open_flags;
|
||||
@ -665,31 +679,22 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
bs->opaque = g_malloc0(drv->instance_size);
|
||||
|
||||
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
||||
open_flags = flags | BDRV_O_CACHE_WB;
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
* image.
|
||||
*/
|
||||
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
/*
|
||||
* Snapshots should be writable.
|
||||
*/
|
||||
if (bs->is_temporary) {
|
||||
open_flags |= BDRV_O_RDWR;
|
||||
}
|
||||
open_flags = bdrv_open_flags(bs, flags);
|
||||
|
||||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
/* Open the image, either directly or using a protocol */
|
||||
if (drv->bdrv_file_open) {
|
||||
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
||||
} else {
|
||||
ret = bdrv_file_open(&bs->file, filename, open_flags);
|
||||
if (ret >= 0) {
|
||||
ret = drv->bdrv_open(bs, open_flags);
|
||||
if (file != NULL) {
|
||||
bdrv_swap(file, bs);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
||||
}
|
||||
} else {
|
||||
assert(file != NULL);
|
||||
bs->file = file;
|
||||
ret = drv->bdrv_open(bs, open_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
@ -709,10 +714,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
return 0;
|
||||
|
||||
free_and_fail:
|
||||
if (bs->file) {
|
||||
bdrv_delete(bs->file);
|
||||
bs->file = NULL;
|
||||
}
|
||||
bs->file = NULL;
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bs->drv = NULL;
|
||||
@ -734,7 +736,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
|
||||
}
|
||||
|
||||
bs = bdrv_new("");
|
||||
ret = bdrv_open_common(bs, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, NULL, filename, flags, drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs);
|
||||
return ret;
|
||||
@ -789,6 +791,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
int ret;
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
char tmp_filename[PATH_MAX + 1];
|
||||
BlockDriverState *file = NULL;
|
||||
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
BlockDriverState *bs1;
|
||||
@ -848,25 +851,36 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
bs->is_temporary = 1;
|
||||
}
|
||||
|
||||
/* Find the right image format driver */
|
||||
if (!drv) {
|
||||
ret = find_image_format(filename, &drv);
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
/* Open image file without format layer */
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
flags |= BDRV_O_ALLOW_RDWR;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&file, filename, bdrv_open_flags(bs, flags));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find the right image format driver */
|
||||
if (!drv) {
|
||||
ret = find_image_format(file, filename, &drv);
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, file, filename, flags, drv);
|
||||
if (ret < 0) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
if (bs->file != file) {
|
||||
bdrv_delete(file);
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
/* If there is a backing file, use it */
|
||||
if ((flags & BDRV_O_NO_BACKING) == 0) {
|
||||
ret = bdrv_open_backing_file(bs);
|
||||
@ -888,6 +902,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
return 0;
|
||||
|
||||
unlink_and_fail:
|
||||
if (file != NULL) {
|
||||
bdrv_delete(file);
|
||||
}
|
||||
if (bs->is_temporary) {
|
||||
unlink(filename);
|
||||
}
|
||||
@ -3028,7 +3045,46 @@ void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
}
|
||||
|
||||
drv->bdrv_debug_event(bs, event);
|
||||
}
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
|
||||
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_resume) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_resume) {
|
||||
return bs->drv->bdrv_debug_resume(bs, tag);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) {
|
||||
return bs->drv->bdrv_debug_is_suspended(bs, tag);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
@ -3778,12 +3834,20 @@ typedef struct BlockDriverAIOCBCoroutine {
|
||||
BlockDriverAIOCB common;
|
||||
BlockRequest req;
|
||||
bool is_write;
|
||||
bool *done;
|
||||
QEMUBH* bh;
|
||||
} BlockDriverAIOCBCoroutine;
|
||||
|
||||
static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
qemu_aio_flush();
|
||||
BlockDriverAIOCBCoroutine *acb =
|
||||
container_of(blockacb, BlockDriverAIOCBCoroutine, common);
|
||||
bool done = false;
|
||||
|
||||
acb->done = &done;
|
||||
while (!done) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo bdrv_em_co_aiocb_info = {
|
||||
@ -3796,6 +3860,11 @@ static void bdrv_co_em_bh(void *opaque)
|
||||
BlockDriverAIOCBCoroutine *acb = opaque;
|
||||
|
||||
acb->common.cb(acb->common.opaque, acb->req.error);
|
||||
|
||||
if (acb->done) {
|
||||
*acb->done = true;
|
||||
}
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
@ -3834,6 +3903,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->req.qiov = qiov;
|
||||
acb->is_write = is_write;
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_do_rw);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
@ -3860,6 +3930,8 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverAIOCBCoroutine *acb;
|
||||
|
||||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
@ -3888,6 +3960,7 @@ BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
|
||||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->req.sector = sector_num;
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->done = NULL;
|
||||
co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
@ -4408,9 +4481,9 @@ bdrv_acct_done(BlockDriverState *bs, BlockAcctCookie *cookie)
|
||||
bs->total_time_ns[cookie->type] += get_clock() - cookie->start_time_ns;
|
||||
}
|
||||
|
||||
int bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags)
|
||||
void bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags, Error **errp)
|
||||
{
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *backing_fmt, *backing_file, *size;
|
||||
@ -4422,16 +4495,14 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
/* Find driver and parse its options */
|
||||
drv = bdrv_find_format(fmt);
|
||||
if (!drv) {
|
||||
error_report("Unknown file format '%s'", fmt);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
error_setg(errp, "Unknown file format '%s'", fmt);
|
||||
return;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(filename);
|
||||
if (!proto_drv) {
|
||||
error_report("Unknown protocol '%s'", filename);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
error_setg(errp, "Unknown protocol '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
create_options = append_option_parameters(create_options,
|
||||
@ -4448,8 +4519,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (options) {
|
||||
param = parse_option_parameters(options, create_options, param);
|
||||
if (param == NULL) {
|
||||
error_report("Invalid options for file format '%s'.", fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Invalid options for file format '%s'.", fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4457,18 +4527,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (base_filename) {
|
||||
if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
|
||||
base_filename)) {
|
||||
error_report("Backing file not supported for file format '%s'",
|
||||
fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Backing file not supported for file format '%s'",
|
||||
fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (base_fmt) {
|
||||
if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
|
||||
error_report("Backing file format not supported for file "
|
||||
"format '%s'", fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Backing file format not supported for file "
|
||||
"format '%s'", fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4476,9 +4544,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
|
||||
if (backing_file && backing_file->value.s) {
|
||||
if (!strcmp(filename, backing_file->value.s)) {
|
||||
error_report("Error: Trying to create an image with the "
|
||||
"same filename as the backing file");
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Error: Trying to create an image with the "
|
||||
"same filename as the backing file");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4487,9 +4554,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (backing_fmt && backing_fmt->value.s) {
|
||||
backing_drv = bdrv_find_format(backing_fmt->value.s);
|
||||
if (!backing_drv) {
|
||||
error_report("Unknown backing file format '%s'",
|
||||
backing_fmt->value.s);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Unknown backing file format '%s'",
|
||||
backing_fmt->value.s);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4511,7 +4577,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
|
||||
ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv);
|
||||
if (ret < 0) {
|
||||
error_report("Could not open '%s'", backing_file->value.s);
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'",
|
||||
backing_file->value.s);
|
||||
goto out;
|
||||
}
|
||||
bdrv_get_geometry(bs, &size);
|
||||
@ -4520,8 +4587,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
snprintf(buf, sizeof(buf), "%" PRId64, size);
|
||||
set_option_parameter(param, BLOCK_OPT_SIZE, buf);
|
||||
} else {
|
||||
error_report("Image creation needs a size parameter");
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Image creation needs a size parameter");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4531,17 +4597,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
puts("");
|
||||
|
||||
ret = bdrv_create(drv, filename, param);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
error_report("Formatting or formatting option not supported for "
|
||||
"file format '%s'", fmt);
|
||||
error_setg(errp,"Formatting or formatting option not supported for "
|
||||
"file format '%s'", fmt);
|
||||
} else if (ret == -EFBIG) {
|
||||
error_report("The image size is too large for file format '%s'",
|
||||
fmt);
|
||||
error_setg(errp, "The image size is too large for file format '%s'",
|
||||
fmt);
|
||||
} else {
|
||||
error_report("%s: error while creating %s: %s", filename, fmt,
|
||||
strerror(-ret));
|
||||
error_setg(errp, "%s: error while creating %s: %s", filename, fmt,
|
||||
strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4552,6 +4617,4 @@ out:
|
||||
if (bs) {
|
||||
bdrv_delete(bs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
11
block.h
11
block.h
@ -343,9 +343,9 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
||||
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
|
||||
int bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags);
|
||||
void bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags, Error **errp);
|
||||
|
||||
void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
|
||||
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
||||
@ -431,4 +431,9 @@ typedef enum {
|
||||
#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag);
|
||||
int bdrv_debug_resume(BlockDriverState *bs, const char *tag);
|
||||
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag);
|
||||
|
||||
#endif
|
||||
|
128
block/blkdebug.c
128
block/blkdebug.c
@ -29,8 +29,10 @@
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
|
||||
} BDRVBlkdebugState;
|
||||
|
||||
typedef struct BlkdebugAIOCB {
|
||||
@ -39,6 +41,12 @@ typedef struct BlkdebugAIOCB {
|
||||
int ret;
|
||||
} BlkdebugAIOCB;
|
||||
|
||||
typedef struct BlkdebugSuspendedReq {
|
||||
Coroutine *co;
|
||||
char *tag;
|
||||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
@ -49,6 +57,7 @@ static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
enum {
|
||||
ACTION_INJECT_ERROR,
|
||||
ACTION_SET_STATE,
|
||||
ACTION_SUSPEND,
|
||||
};
|
||||
|
||||
typedef struct BlkdebugRule {
|
||||
@ -65,6 +74,9 @@ typedef struct BlkdebugRule {
|
||||
struct {
|
||||
int new_state;
|
||||
} set_state;
|
||||
struct {
|
||||
char *tag;
|
||||
} suspend;
|
||||
} options;
|
||||
QLIST_ENTRY(BlkdebugRule) next;
|
||||
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
|
||||
@ -226,6 +238,11 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
rule->options.set_state.new_state =
|
||||
qemu_opt_get_number(opts, "new_state", 0);
|
||||
break;
|
||||
|
||||
case ACTION_SUSPEND:
|
||||
rule->options.suspend.tag =
|
||||
g_strdup(qemu_opt_get(opts, "tag"));
|
||||
break;
|
||||
};
|
||||
|
||||
/* Add the rule */
|
||||
@ -234,12 +251,32 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_rule(BlkdebugRule *rule)
|
||||
{
|
||||
switch (rule->action) {
|
||||
case ACTION_INJECT_ERROR:
|
||||
case ACTION_SET_STATE:
|
||||
break;
|
||||
case ACTION_SUSPEND:
|
||||
g_free(rule->options.suspend.tag);
|
||||
break;
|
||||
}
|
||||
|
||||
QLIST_REMOVE(rule, next);
|
||||
g_free(rule);
|
||||
}
|
||||
|
||||
static int read_config(BDRVBlkdebugState *s, const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
int ret;
|
||||
struct add_rule_data d;
|
||||
|
||||
/* Allow usage without config file */
|
||||
if (!*filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
return -errno;
|
||||
@ -389,6 +426,7 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
@ -397,12 +435,32 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
QLIST_REMOVE(rule, next);
|
||||
g_free(rule);
|
||||
remove_rule(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq r;
|
||||
|
||||
r = (BlkdebugSuspendedReq) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.tag = g_strdup(rule->options.suspend.tag),
|
||||
};
|
||||
|
||||
remove_rule(rule);
|
||||
QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
|
||||
|
||||
printf("blkdebug: Suspended request '%s'\n", r.tag);
|
||||
qemu_coroutine_yield();
|
||||
printf("blkdebug: Resuming request '%s'\n", r.tag);
|
||||
|
||||
QLIST_REMOVE(&r, next);
|
||||
g_free(r.tag);
|
||||
}
|
||||
|
||||
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
bool injected)
|
||||
{
|
||||
@ -426,6 +484,10 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
case ACTION_SET_STATE:
|
||||
s->new_state = rule->options.set_state.new_state;
|
||||
break;
|
||||
|
||||
case ACTION_SUSPEND:
|
||||
suspend_request(bs, rule);
|
||||
break;
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
@ -433,19 +495,72 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
struct BlkdebugRule *rule, *next;
|
||||
bool injected;
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
QLIST_FOREACH(rule, &s->rules[event], next) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
|
||||
injected = process_rule(bs, rule, injected);
|
||||
}
|
||||
s->state = s->new_state;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
BlkDebugEvent blkdebug_event;
|
||||
|
||||
if (get_event_by_name(event, &blkdebug_event) < 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
rule = g_malloc(sizeof(*rule));
|
||||
*rule = (struct BlkdebugRule) {
|
||||
.event = blkdebug_event,
|
||||
.action = ACTION_SUSPEND,
|
||||
.state = 0,
|
||||
.options.suspend.tag = g_strdup(tag),
|
||||
};
|
||||
|
||||
QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file);
|
||||
@ -464,7 +579,10 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
.bdrv_debug_resume = blkdebug_debug_resume,
|
||||
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
||||
};
|
||||
|
||||
static void bdrv_blkdebug_init(void)
|
||||
|
@ -103,7 +103,7 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
@ -205,7 +205,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
@ -615,57 +615,67 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, start_sect, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
bool cow = false;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
|
||||
if (m->nb_clusters == 0)
|
||||
if (r->nb_sectors == 0) {
|
||||
return 0;
|
||||
|
||||
old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
|
||||
if (m->n_start) {
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (m->nb_available & (s->cluster_sectors - 1)) {
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, m->nb_available,
|
||||
align_offset(m->nb_available, s->cluster_sectors));
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, m->offset / BDRV_SECTOR_SIZE, m->alloc_offset,
|
||||
r->offset / BDRV_SECTOR_SIZE,
|
||||
r->offset / BDRV_SECTOR_SIZE + r->nb_sectors);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update L2 table.
|
||||
*
|
||||
* Before we update the L2 table to actually point to the new cluster, we
|
||||
* need to be sure that the refcounts have been increased and COW was
|
||||
* handled.
|
||||
*/
|
||||
if (cow) {
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
assert(m->nb_clusters > 0);
|
||||
|
||||
old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
ret = perform_cow(bs, m, &m->cow_start);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = perform_cow(bs, m, &m->cow_end);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Update L2 table. */
|
||||
if (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||
s->refcount_block_cache);
|
||||
}
|
||||
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
@ -743,38 +753,16 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new clusters for the given guest_offset.
|
||||
*
|
||||
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
|
||||
* contain the number of clusters that have been allocated and are contiguous
|
||||
* in the image file.
|
||||
*
|
||||
* If *host_offset is non-zero, it specifies the offset in the image file at
|
||||
* which the new clusters must start. *nb_clusters can be 0 on return in this
|
||||
* case if the cluster at host_offset is already in use. If *host_offset is
|
||||
* zero, the clusters can be allocated anywhere in the image file.
|
||||
*
|
||||
* *host_offset is updated to contain the offset into the image file at which
|
||||
* the first allocated cluster starts.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases. -EAGAIN means that the
|
||||
* function has been waiting for another request and the allocation must be
|
||||
* restarted, but the whole request should not be failed.
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
||||
static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||
unsigned int *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowL2Meta *old_alloc;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
|
||||
/*
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
|
||||
|
||||
uint64_t start = guest_offset >> s->cluster_bits;
|
||||
@ -807,6 +795,42 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new clusters for the given guest_offset.
|
||||
*
|
||||
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
|
||||
* contain the number of clusters that have been allocated and are contiguous
|
||||
* in the image file.
|
||||
*
|
||||
* If *host_offset is non-zero, it specifies the offset in the image file at
|
||||
* which the new clusters must start. *nb_clusters can be 0 on return in this
|
||||
* case if the cluster at host_offset is already in use. If *host_offset is
|
||||
* zero, the clusters can be allocated anywhere in the image file.
|
||||
*
|
||||
* *host_offset is updated to contain the offset into the image file at which
|
||||
* the first allocated cluster starts.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases. -EAGAIN means that the
|
||||
* function has been waiting for another request and the allocation must be
|
||||
* restarted, but the whole request should not be failed.
|
||||
*/
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
|
||||
ret = handle_dependencies(bs, guest_offset, nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate new clusters */
|
||||
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
|
||||
if (*host_offset == 0) {
|
||||
@ -818,7 +842,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
*host_offset = cluster_offset;
|
||||
return 0;
|
||||
} else {
|
||||
int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||
ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -847,7 +871,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
* Return 0 on success and -errno in error cases
|
||||
*/
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m)
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret, sectors;
|
||||
@ -919,12 +943,6 @@ again:
|
||||
}
|
||||
|
||||
/* If there is something left to allocate, do that now */
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = cluster_offset,
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
|
||||
if (nb_clusters > 0) {
|
||||
uint64_t alloc_offset;
|
||||
uint64_t alloc_cluster_offset;
|
||||
@ -957,22 +975,40 @@ again:
|
||||
*
|
||||
* avail_sectors: Number of sectors from the start of the first
|
||||
* newly allocated to the end of the last newly allocated cluster.
|
||||
*
|
||||
* nb_sectors: The number of sectors from the start of the first
|
||||
* newly allocated cluster to the end of the aread that the write
|
||||
* request actually writes to (excluding COW at the end)
|
||||
*/
|
||||
int requested_sectors = n_end - keep_clusters * s->cluster_sectors;
|
||||
int avail_sectors = nb_clusters
|
||||
<< (s->cluster_bits - BDRV_SECTOR_BITS);
|
||||
int alloc_n_start = keep_clusters == 0 ? n_start : 0;
|
||||
int nb_sectors = MIN(requested_sectors, avail_sectors);
|
||||
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = keep_clusters == 0 ?
|
||||
alloc_cluster_offset : cluster_offset,
|
||||
if (keep_clusters == 0) {
|
||||
cluster_offset = alloc_cluster_offset;
|
||||
}
|
||||
|
||||
*m = g_malloc0(sizeof(**m));
|
||||
|
||||
**m = (QCowL2Meta) {
|
||||
.alloc_offset = alloc_cluster_offset,
|
||||
.offset = alloc_offset,
|
||||
.n_start = keep_clusters == 0 ? n_start : 0,
|
||||
.offset = alloc_offset & ~(s->cluster_size - 1),
|
||||
.nb_clusters = nb_clusters,
|
||||
.nb_available = MIN(requested_sectors, avail_sectors),
|
||||
.nb_available = nb_sectors,
|
||||
|
||||
.cow_start = {
|
||||
.offset = 0,
|
||||
.nb_sectors = alloc_n_start,
|
||||
},
|
||||
.cow_end = {
|
||||
.offset = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
.nb_sectors = avail_sectors - nb_sectors,
|
||||
},
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
|
||||
qemu_co_queue_init(&(*m)->dependent_requests);
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
@ -984,12 +1020,13 @@ again:
|
||||
|
||||
assert(sectors > n_start);
|
||||
*num = sectors - n_start;
|
||||
*host_offset = cluster_offset;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (m->nb_clusters > 0) {
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
if (*m && (*m)->nb_clusters > 0) {
|
||||
QLIST_REMOVE(*m, next_in_flight);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ static void report_unsupported_feature(BlockDriverState *bs,
|
||||
* updated successfully. Therefore it is not required to check the return
|
||||
* value of this function.
|
||||
*/
|
||||
static int qcow2_mark_dirty(BlockDriverState *bs)
|
||||
int qcow2_mark_dirty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t val;
|
||||
@ -745,21 +745,6 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m)
|
||||
{
|
||||
/* Take the request off the list of running requests */
|
||||
if (m->nb_clusters != 0) {
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
}
|
||||
|
||||
/* Restart all dependent requests */
|
||||
if (!qemu_co_queue_empty(&m->dependent_requests)) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_restart_all(&m->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int remaining_sectors,
|
||||
@ -774,15 +759,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
QEMUIOVector hd_qiov;
|
||||
uint64_t bytes_done = 0;
|
||||
uint8_t *cluster_data = NULL;
|
||||
QCowL2Meta l2meta = {
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
QCowL2Meta *l2meta;
|
||||
|
||||
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
|
||||
remaining_sectors);
|
||||
|
||||
qemu_co_queue_init(&l2meta.dependent_requests);
|
||||
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
@ -791,6 +772,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
while (remaining_sectors != 0) {
|
||||
|
||||
l2meta = NULL;
|
||||
|
||||
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n_end = index_in_cluster + remaining_sectors;
|
||||
@ -800,17 +783,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
|
||||
index_in_cluster, n_end, &cur_nr_sectors, &l2meta);
|
||||
index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (l2meta.nb_clusters > 0 &&
|
||||
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
|
||||
cluster_offset = l2meta.cluster_offset;
|
||||
assert((cluster_offset & 511) == 0);
|
||||
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
@ -835,8 +812,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
cur_nr_sectors * 512);
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
trace_qcow2_writev_data(qemu_coroutine_self(),
|
||||
(cluster_offset >> 9) + index_in_cluster);
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
@ -847,12 +824,24 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (l2meta != NULL) {
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
run_dependent_requests(s, &l2meta);
|
||||
/* Take the request off the list of running requests */
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
g_free(l2meta);
|
||||
l2meta = NULL;
|
||||
}
|
||||
|
||||
remaining_sectors -= cur_nr_sectors;
|
||||
sector_num += cur_nr_sectors;
|
||||
@ -862,10 +851,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
run_dependent_requests(s, &l2meta);
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
if (l2meta != NULL) {
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
g_free(l2meta);
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&hd_qiov);
|
||||
qemu_vfree(cluster_data);
|
||||
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
||||
@ -1128,31 +1123,33 @@ static int preallocate(BlockDriverState *bs)
|
||||
{
|
||||
uint64_t nb_sectors;
|
||||
uint64_t offset;
|
||||
uint64_t host_offset = 0;
|
||||
int num;
|
||||
int ret;
|
||||
QCowL2Meta meta;
|
||||
QCowL2Meta *meta;
|
||||
|
||||
nb_sectors = bdrv_getlength(bs) >> 9;
|
||||
offset = 0;
|
||||
qemu_co_queue_init(&meta.dependent_requests);
|
||||
meta.cluster_offset = 0;
|
||||
|
||||
while (nb_sectors) {
|
||||
num = MIN(nb_sectors, INT_MAX >> 9);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, &meta);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
|
||||
&host_offset, &meta);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, &meta);
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||
if (ret < 0) {
|
||||
qcow2_free_any_clusters(bs, meta.cluster_offset, meta.nb_clusters);
|
||||
qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* There are no dependent requests, but we need to remove our request
|
||||
* from the list of in-flight requests */
|
||||
run_dependent_requests(bs->opaque, &meta);
|
||||
if (meta != NULL) {
|
||||
QLIST_REMOVE(meta, next_in_flight);
|
||||
}
|
||||
|
||||
/* TODO Preallocate data if requested */
|
||||
|
||||
@ -1165,10 +1162,10 @@ static int preallocate(BlockDriverState *bs)
|
||||
* all of the allocated clusters (otherwise we get failing reads after
|
||||
* EOF). Extend the image to the last allocated sector.
|
||||
*/
|
||||
if (meta.cluster_offset != 0) {
|
||||
if (host_offset != 0) {
|
||||
uint8_t buf[512];
|
||||
memset(buf, 0, 512);
|
||||
ret = bdrv_write(bs->file, (meta.cluster_offset >> 9) + num - 1, buf, 1);
|
||||
ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -196,17 +196,56 @@ typedef struct QCowCreateState {
|
||||
|
||||
struct QCowAIOCB;
|
||||
|
||||
/* XXX This could be private for qcow2-cluster.c */
|
||||
typedef struct Qcow2COWRegion {
|
||||
/**
|
||||
* Offset of the COW region in bytes from the start of the first cluster
|
||||
* touched by the request.
|
||||
*/
|
||||
uint64_t offset;
|
||||
|
||||
/** Number of sectors to copy */
|
||||
int nb_sectors;
|
||||
} Qcow2COWRegion;
|
||||
|
||||
/**
|
||||
* Describes an in-flight (part of a) write request that writes to clusters
|
||||
* that are not referenced in their L2 table yet.
|
||||
*/
|
||||
typedef struct QCowL2Meta
|
||||
{
|
||||
/** Guest offset of the first newly allocated cluster */
|
||||
uint64_t offset;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
/** Host offset of the first newly allocated cluster */
|
||||
uint64_t alloc_offset;
|
||||
int n_start;
|
||||
|
||||
/**
|
||||
* Number of sectors from the start of the first allocated cluster to
|
||||
* the end of the (possibly shortened) request
|
||||
*/
|
||||
int nb_available;
|
||||
|
||||
/** Number of newly allocated clusters */
|
||||
int nb_clusters;
|
||||
|
||||
/**
|
||||
* Requests that overlap with this allocation and wait to be restarted
|
||||
* when the allocating request has completed.
|
||||
*/
|
||||
CoQueue dependent_requests;
|
||||
|
||||
/**
|
||||
* The COW Region between the start of the first allocated cluster and the
|
||||
* area the guest actually writes to.
|
||||
*/
|
||||
Qcow2COWRegion cow_start;
|
||||
|
||||
/**
|
||||
* The COW Region between the area the guest actually writes to and the
|
||||
* end of the last allocated cluster.
|
||||
*/
|
||||
Qcow2COWRegion cow_end;
|
||||
|
||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||
} QCowL2Meta;
|
||||
|
||||
@ -264,6 +303,8 @@ static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
|
||||
/* qcow2.c functions */
|
||||
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
|
||||
int qcow2_mark_dirty(BlockDriverState *bs);
|
||||
int qcow2_update_header(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
@ -297,7 +338,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset);
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m);
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m);
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size);
|
||||
|
@ -708,22 +708,6 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = fd;
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
@ -1346,10 +1330,19 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawPosixAIOData *acb;
|
||||
|
||||
if (fd_open(bs) < 0)
|
||||
return NULL;
|
||||
return paio_ioctl(bs, s->fd, req, buf, cb, opaque);
|
||||
|
||||
acb = g_slice_new(RawPosixAIOData);
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = s->fd;
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
@ -303,13 +303,24 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
LONG low, high;
|
||||
DWORD dwPtrLow;
|
||||
|
||||
low = offset;
|
||||
high = offset >> 32;
|
||||
if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN))
|
||||
return -EIO;
|
||||
if (!SetEndOfFile(s->hfile))
|
||||
|
||||
/*
|
||||
* An error has occurred if the return value is INVALID_SET_FILE_POINTER
|
||||
* and GetLastError doesn't return NO_ERROR.
|
||||
*/
|
||||
dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN);
|
||||
if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||
fprintf(stderr, "SetFilePointer error: %d\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
if (SetEndOfFile(s->hfile) == 0) {
|
||||
fprintf(stderr, "SetEndOfFile error: %d\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
20
block/rbd.c
20
block/rbd.c
@ -77,6 +77,7 @@ typedef struct RBDAIOCB {
|
||||
int error;
|
||||
struct BDRVRBDState *s;
|
||||
int cancelled;
|
||||
int status;
|
||||
} RBDAIOCB;
|
||||
|
||||
typedef struct RADOSCB {
|
||||
@ -376,12 +377,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
RBDAIOCB *acb = rcb->acb;
|
||||
int64_t r;
|
||||
|
||||
if (acb->cancelled) {
|
||||
qemu_vfree(acb->bounce);
|
||||
qemu_aio_release(acb);
|
||||
goto done;
|
||||
}
|
||||
|
||||
r = rcb->ret;
|
||||
|
||||
if (acb->cmd == RBD_AIO_WRITE ||
|
||||
@ -409,7 +404,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
/* 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);
|
||||
done:
|
||||
g_free(rcb);
|
||||
}
|
||||
|
||||
@ -568,6 +562,12 @@ static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
|
||||
acb->cancelled = 1;
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
@ -639,8 +639,11 @@ static void rbd_aio_bh_cb(void *opaque)
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
acb->status = 0;
|
||||
|
||||
qemu_aio_release(acb);
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||
@ -685,6 +688,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
acb->s = s;
|
||||
acb->cancelled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
if (cmd == RBD_AIO_WRITE) {
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);
|
||||
|
@ -108,7 +108,7 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
24
block/vpc.c
24
block/vpc.c
@ -26,6 +26,9 @@
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "migration.h"
|
||||
#if defined(CONFIG_UUID)
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@ -198,7 +201,8 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
bs->total_sectors = (int64_t)
|
||||
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||
|
||||
if (bs->total_sectors >= 65535 * 16 * 255) {
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
if (bs->total_sectors >= 65535LL * 255 * 255) {
|
||||
err = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
@ -524,19 +528,27 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
* Note that the geometry doesn't always exactly match total_sectors but
|
||||
* may round it down.
|
||||
*
|
||||
* Returns 0 on success, -EFBIG if the size is larger than 127 GB
|
||||
* Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override
|
||||
* the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB)
|
||||
* and instead allow up to 255 heads.
|
||||
*/
|
||||
static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
uint8_t* heads, uint8_t* secs_per_cyl)
|
||||
{
|
||||
uint32_t cyls_times_heads;
|
||||
|
||||
if (total_sectors > 65535 * 16 * 255)
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
if (total_sectors > 65535LL * 255 * 255) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (total_sectors > 65535 * 16 * 63) {
|
||||
*secs_per_cyl = 255;
|
||||
*heads = 16;
|
||||
if (total_sectors > 65535 * 16 * 255) {
|
||||
*heads = 255;
|
||||
} else {
|
||||
*heads = 16;
|
||||
}
|
||||
cyls_times_heads = total_sectors / *secs_per_cyl;
|
||||
} else {
|
||||
*secs_per_cyl = 17;
|
||||
@ -739,7 +751,9 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
|
||||
footer->type = be32_to_cpu(disk_type);
|
||||
|
||||
/* TODO uuid is missing */
|
||||
#if defined(CONFIG_UUID)
|
||||
uuid_generate(footer->uuid);
|
||||
#endif
|
||||
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
|
@ -190,6 +190,12 @@ struct BlockDriver {
|
||||
|
||||
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
/* TODO Better pass a option string/QDict/QemuOpts to add any rule? */
|
||||
int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event,
|
||||
const char *tag);
|
||||
int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag);
|
||||
bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag);
|
||||
|
||||
/*
|
||||
* Returns 1 if newly created images are guaranteed to contain only
|
||||
* zeros, 0 otherwise.
|
||||
|
35
blockdev.c
35
blockdev.c
@ -275,7 +275,7 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
|
||||
return true;
|
||||
}
|
||||
|
||||
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
||||
{
|
||||
const char *buf;
|
||||
const char *file = NULL;
|
||||
@ -325,7 +325,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
type = default_to_scsi ? IF_SCSI : IF_IDE;
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
max_devs = if_max_devs[type];
|
||||
@ -568,7 +568,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
@ -707,6 +707,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
int ret = 0;
|
||||
BlockdevActionList *dev_entry = dev_list;
|
||||
BlkTransactionStates *states, *next;
|
||||
Error *local_err = NULL;
|
||||
|
||||
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
|
||||
QSIMPLEQ_INIT(&snap_bdrv_states);
|
||||
@ -786,12 +787,12 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
|
||||
/* create new image w/backing file */
|
||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
ret = bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
|
||||
bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
}
|
||||
@ -1263,8 +1264,8 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
assert(format && drv);
|
||||
bdrv_get_geometry(bs, &size);
|
||||
size *= 512;
|
||||
ret = bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags);
|
||||
bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags, &local_err);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case NEW_IMAGE_MODE_EXISTING:
|
||||
@ -1272,18 +1273,18 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
break;
|
||||
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
||||
/* create new image with backing file */
|
||||
ret = bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, target);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,13 @@ void blockdev_auto_del(BlockDriverState *bs);
|
||||
|
||||
typedef enum {
|
||||
IF_DEFAULT = -1, /* for use with drive_add() only */
|
||||
/*
|
||||
* IF_IDE must be zero, because we want QEMUMachine member
|
||||
* block_default_type to default-initialize to IF_IDE
|
||||
*/
|
||||
IF_IDE = 0,
|
||||
IF_NONE,
|
||||
IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
|
||||
IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
|
||||
IF_COUNT
|
||||
} BlockInterfaceType;
|
||||
|
||||
@ -51,7 +56,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
|
||||
QemuOpts *drive_def(const char *optstr);
|
||||
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
||||
const char *optstr);
|
||||
DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
|
||||
DriveInfo *drive_init(QemuOpts *arg, BlockInterfaceType block_default_type);
|
||||
|
||||
/* device-hotplug */
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#ifndef HW_BOARDS_H
|
||||
#define HW_BOARDS_H
|
||||
|
||||
#include "blockdev.h"
|
||||
#include "qdev.h"
|
||||
|
||||
typedef struct QEMUMachineInitArgs {
|
||||
@ -24,7 +25,7 @@ typedef struct QEMUMachine {
|
||||
const char *desc;
|
||||
QEMUMachineInitFunc *init;
|
||||
QEMUMachineResetFunc *reset;
|
||||
int use_scsi;
|
||||
BlockInterfaceType block_default_type;
|
||||
int max_cpus;
|
||||
unsigned int no_serial:1,
|
||||
no_parallel:1,
|
||||
|
@ -39,7 +39,7 @@ DriveInfo *add_init_drive(const char *optstr)
|
||||
if (!opts)
|
||||
return NULL;
|
||||
|
||||
dinfo = drive_init(opts, current_machine->use_scsi);
|
||||
dinfo = drive_init(opts, current_machine->block_default_type);
|
||||
if (!dinfo) {
|
||||
qemu_opts_del(opts);
|
||||
return NULL;
|
||||
|
@ -329,7 +329,7 @@ static QEMUMachine highbank_machine = {
|
||||
.name = "highbank",
|
||||
.desc = "Calxeda Highbank (ECX-1000)",
|
||||
.init = highbank_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
|
@ -1869,6 +1869,8 @@ static void ide_reset(IDEState *s)
|
||||
s->io_buffer_index = 0;
|
||||
s->cd_sector_size = 0;
|
||||
s->atapi_dma = 0;
|
||||
s->tray_locked = 0;
|
||||
s->tray_open = 0;
|
||||
/* ATA DMA state */
|
||||
s->io_buffer_size = 0;
|
||||
s->req_nb_sectors = 0;
|
||||
|
@ -212,7 +212,6 @@ static QEMUMachine leon3_generic_machine = {
|
||||
.name = "leon3_generic",
|
||||
.desc = "Leon-3 generic",
|
||||
.init = leon3_generic_hw_init,
|
||||
.use_scsi = 0,
|
||||
};
|
||||
|
||||
static void leon3_machine_init(void)
|
||||
|
@ -324,14 +324,14 @@ static QEMUMachine mips_magnum_machine = {
|
||||
.name = "magnum",
|
||||
.desc = "MIPS Magnum",
|
||||
.init = mips_magnum_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine mips_pica61_machine = {
|
||||
.name = "pica61",
|
||||
.desc = "Acer Pica 61",
|
||||
.init = mips_pica61_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static void mips_jazz_machine_init(void)
|
||||
|
@ -98,7 +98,7 @@ static void pc_fw_add_pflash_drv(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!drive_init(opts, machine->use_scsi)) {
|
||||
if (!drive_init(opts, machine->block_default_type)) {
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,6 @@ static QEMUMachine puv3_machine = {
|
||||
.desc = "PKUnity Version-3 based on UniCore32",
|
||||
.init = puv3_init,
|
||||
.is_default = 1,
|
||||
.use_scsi = 0,
|
||||
};
|
||||
|
||||
static void puv3_machine_init(void)
|
||||
|
@ -364,14 +364,14 @@ static QEMUMachine realview_eb_machine = {
|
||||
.name = "realview-eb",
|
||||
.desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
|
||||
.init = realview_eb_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine realview_eb_mpcore_machine = {
|
||||
.name = "realview-eb-mpcore",
|
||||
.desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
|
||||
.init = realview_eb_mpcore_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
@ -385,7 +385,7 @@ static QEMUMachine realview_pbx_a9_machine = {
|
||||
.name = "realview-pbx-a9",
|
||||
.desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
|
||||
.init = realview_pbx_a9_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
|
@ -314,21 +314,6 @@ static void s390_init(QEMUMachineInitArgs *args)
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
|
||||
/* Create VirtIO disk drives */
|
||||
for(i = 0; i < MAX_BLK_DEVS; i++) {
|
||||
DriveInfo *dinfo;
|
||||
DeviceState *dev;
|
||||
|
||||
dinfo = drive_get(IF_IDE, 0, i);
|
||||
if (!dinfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390");
|
||||
qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static QEMUMachine s390_machine = {
|
||||
@ -336,6 +321,7 @@ static QEMUMachine s390_machine = {
|
||||
.alias = "s390",
|
||||
.desc = "VirtIO based S390 machine",
|
||||
.init = s390_init,
|
||||
.block_default_type = IF_VIRTIO,
|
||||
.no_cdrom = 1,
|
||||
.no_floppy = 1,
|
||||
.no_serial = 1,
|
||||
@ -352,3 +338,4 @@ static void s390_machine_init(void)
|
||||
}
|
||||
|
||||
machine_init(s390_machine_init);
|
||||
|
||||
|
@ -924,9 +924,9 @@ static QEMUMachine spapr_machine = {
|
||||
.desc = "pSeries Logical Partition (PAPR compliant)",
|
||||
.init = ppc_spapr_init,
|
||||
.reset = ppc_spapr_reset,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = MAX_CPUS,
|
||||
.no_parallel = 1,
|
||||
.use_scsi = 1,
|
||||
};
|
||||
|
||||
static void spapr_machine_init(void)
|
||||
|
24
hw/sun4m.c
24
hw/sun4m.c
@ -1426,7 +1426,7 @@ static QEMUMachine ss5_machine = {
|
||||
.name = "SS-5",
|
||||
.desc = "Sun4m platform, SPARCstation 5",
|
||||
.init = ss5_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.is_default = 1,
|
||||
};
|
||||
|
||||
@ -1434,7 +1434,7 @@ static QEMUMachine ss10_machine = {
|
||||
.name = "SS-10",
|
||||
.desc = "Sun4m platform, SPARCstation 10",
|
||||
.init = ss10_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
@ -1442,7 +1442,7 @@ static QEMUMachine ss600mp_machine = {
|
||||
.name = "SS-600MP",
|
||||
.desc = "Sun4m platform, SPARCserver 600MP",
|
||||
.init = ss600mp_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
@ -1450,7 +1450,7 @@ static QEMUMachine ss20_machine = {
|
||||
.name = "SS-20",
|
||||
.desc = "Sun4m platform, SPARCstation 20",
|
||||
.init = ss20_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
@ -1458,35 +1458,35 @@ static QEMUMachine voyager_machine = {
|
||||
.name = "Voyager",
|
||||
.desc = "Sun4m platform, SPARCstation Voyager",
|
||||
.init = vger_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine ss_lx_machine = {
|
||||
.name = "LX",
|
||||
.desc = "Sun4m platform, SPARCstation LX",
|
||||
.init = ss_lx_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine ss4_machine = {
|
||||
.name = "SS-4",
|
||||
.desc = "Sun4m platform, SPARCstation 4",
|
||||
.init = ss4_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine scls_machine = {
|
||||
.name = "SPARCClassic",
|
||||
.desc = "Sun4m platform, SPARCClassic",
|
||||
.init = scls_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine sbook_machine = {
|
||||
.name = "SPARCbook",
|
||||
.desc = "Sun4m platform, SPARCbook",
|
||||
.init = sbook_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static const struct sun4d_hwdef sun4d_hwdefs[] = {
|
||||
@ -1709,7 +1709,7 @@ static QEMUMachine ss1000_machine = {
|
||||
.name = "SS-1000",
|
||||
.desc = "Sun4d platform, SPARCserver 1000",
|
||||
.init = ss1000_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 8,
|
||||
};
|
||||
|
||||
@ -1717,7 +1717,7 @@ static QEMUMachine ss2000_machine = {
|
||||
.name = "SS-2000",
|
||||
.desc = "Sun4d platform, SPARCcenter 2000",
|
||||
.init = ss2000_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 20,
|
||||
};
|
||||
|
||||
@ -1896,7 +1896,7 @@ static QEMUMachine ss2_machine = {
|
||||
.name = "SS-2",
|
||||
.desc = "Sun4c platform, SPARCstation 2",
|
||||
.init = ss2_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static void sun4m_register_types(void)
|
||||
|
@ -358,14 +358,14 @@ static QEMUMachine versatilepb_machine = {
|
||||
.name = "versatilepb",
|
||||
.desc = "ARM Versatile/PB (ARM926EJ-S)",
|
||||
.init = vpb_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static QEMUMachine versatileab_machine = {
|
||||
.name = "versatileab",
|
||||
.desc = "ARM Versatile/AB (ARM926EJ-S)",
|
||||
.init = vab_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
};
|
||||
|
||||
static void versatile_machine_init(void)
|
||||
|
@ -477,7 +477,7 @@ static QEMUMachine vexpress_a9_machine = {
|
||||
.name = "vexpress-a9",
|
||||
.desc = "ARM Versatile Express for Cortex-A9",
|
||||
.init = vexpress_a9_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
@ -485,7 +485,7 @@ static QEMUMachine vexpress_a15_machine = {
|
||||
.name = "vexpress-a15",
|
||||
.desc = "ARM Versatile Express for Cortex-A15",
|
||||
.init = vexpress_a15_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,6 @@ struct VirtIOBlkConf
|
||||
BlockConf conf;
|
||||
char *serial;
|
||||
uint32_t scsi;
|
||||
uint32_t config_wce;
|
||||
};
|
||||
|
||||
#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
|
||||
|
@ -895,7 +895,6 @@ static Property virtio_blk_properties[] = {
|
||||
#ifdef __linux__
|
||||
DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true),
|
||||
#endif
|
||||
DEFINE_PROP_BIT("config-wce", VirtIOPCIProxy, blk.config_wce, 0, true),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
|
||||
|
@ -66,7 +66,7 @@ int select_watchdog(const char *p)
|
||||
QLIST_FOREACH(model, &watchdog_list, entry) {
|
||||
if (strcasecmp(model->wdt_name, p) == 0) {
|
||||
/* add the device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
qemu_opt_set(opts, "driver", p);
|
||||
return 0;
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ static QEMUMachine zynq_machine = {
|
||||
.name = "xilinx-zynq-a9",
|
||||
.desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
|
||||
.init = zynq_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 1,
|
||||
.no_sdcard = 1
|
||||
};
|
||||
|
@ -432,11 +432,6 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
|
||||
return aio_bh_new(qemu_aio_context, cb, opaque);
|
||||
}
|
||||
|
||||
void qemu_aio_flush(void)
|
||||
{
|
||||
aio_flush(qemu_aio_context);
|
||||
}
|
||||
|
||||
bool qemu_aio_wait(void)
|
||||
{
|
||||
return aio_poll(qemu_aio_context, true);
|
||||
|
@ -162,10 +162,6 @@ void qemu_bh_cancel(QEMUBH *bh);
|
||||
*/
|
||||
void qemu_bh_delete(QEMUBH *bh);
|
||||
|
||||
/* Flush any pending AIO operation. This function will block until all
|
||||
* outstanding AIO operations have been completed or cancelled. */
|
||||
void aio_flush(AioContext *ctx);
|
||||
|
||||
/* Return whether there are any pending callbacks from the GSource
|
||||
* attached to the AioContext.
|
||||
*
|
||||
@ -196,7 +192,7 @@ typedef int (AioFlushHandler)(void *opaque);
|
||||
|
||||
/* Register a file descriptor and associated callbacks. Behaves very similarly
|
||||
* to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will
|
||||
* be invoked when using either qemu_aio_wait() or qemu_aio_flush().
|
||||
* be invoked when using qemu_aio_wait().
|
||||
*
|
||||
* Code that invokes AIO completion functions should rely on this function
|
||||
* instead of qemu_set_fd_handler[2].
|
||||
@ -211,7 +207,7 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
|
||||
/* Register an event notifier and associated callbacks. Behaves very similarly
|
||||
* to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks
|
||||
* will be invoked when using either qemu_aio_wait() or qemu_aio_flush().
|
||||
* will be invoked when using qemu_aio_wait().
|
||||
*
|
||||
* Code that invokes AIO completion functions should rely on this function
|
||||
* instead of event_notifier_set_handler.
|
||||
@ -228,7 +224,6 @@ GSource *aio_get_g_source(AioContext *ctx);
|
||||
|
||||
/* Functions to operate on the main QEMU AioContext. */
|
||||
|
||||
void qemu_aio_flush(void);
|
||||
bool qemu_aio_wait(void);
|
||||
void qemu_aio_set_event_notifier(EventNotifier *notifier,
|
||||
EventNotifierHandler *io_read,
|
||||
|
@ -756,7 +756,7 @@ int qemu_global_option(const char *str)
|
||||
return -1;
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&qemu_global_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&qemu_global_opts);
|
||||
qemu_opt_set(opts, "driver", driver);
|
||||
qemu_opt_set(opts, "property", property);
|
||||
qemu_opt_set(opts, "value", str+offset+1);
|
||||
@ -843,7 +843,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
|
||||
error_free(local_err);
|
||||
goto out;
|
||||
}
|
||||
opts = qemu_opts_create(list, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(list);
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
|
||||
|
21
qemu-img.c
21
qemu-img.c
@ -294,13 +294,14 @@ static int add_old_style_options(const char *fmt, QEMUOptionParameter *list,
|
||||
|
||||
static int img_create(int argc, char **argv)
|
||||
{
|
||||
int c, ret = 0;
|
||||
int c;
|
||||
uint64_t img_size = -1;
|
||||
const char *fmt = "raw";
|
||||
const char *base_fmt = NULL;
|
||||
const char *filename;
|
||||
const char *base_filename = NULL;
|
||||
char *options = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "F:b:f:he6o:");
|
||||
@ -350,23 +351,23 @@ static int img_create(int argc, char **argv)
|
||||
error_report("Invalid image size specified! You may use k, M, G or "
|
||||
"T suffixes for ");
|
||||
error_report("kilobytes, megabytes, gigabytes and terabytes.");
|
||||
ret = -1;
|
||||
goto out;
|
||||
return 1;
|
||||
}
|
||||
img_size = (uint64_t)sval;
|
||||
}
|
||||
|
||||
if (options && is_help_option(options)) {
|
||||
ret = print_block_option_help(filename, fmt);
|
||||
goto out;
|
||||
return print_block_option_help(filename, fmt);
|
||||
}
|
||||
|
||||
ret = bdrv_img_create(filename, fmt, base_filename, base_fmt,
|
||||
options, img_size, BDRV_O_FLAGS);
|
||||
out:
|
||||
if (ret) {
|
||||
bdrv_img_create(filename, fmt, base_filename, base_fmt,
|
||||
options, img_size, BDRV_O_FLAGS, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1933,7 +1934,7 @@ static int img_resize(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Parse size */
|
||||
param = qemu_opts_create(&resize_options, NULL, 0, NULL);
|
||||
param = qemu_opts_create_nofail(&resize_options);
|
||||
if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) {
|
||||
/* Error message already printed when size parsing fails */
|
||||
ret = -1;
|
||||
|
87
qemu-io.c
87
qemu-io.c
@ -265,6 +265,18 @@ static int do_co_write_zeroes(int64_t offset, int count, int *total)
|
||||
}
|
||||
}
|
||||
|
||||
static int do_write_compressed(char *buf, int64_t offset, int count, int *total)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
*total = count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_load_vmstate(char *buf, int64_t offset, int count, int *total)
|
||||
{
|
||||
*total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
|
||||
@ -687,6 +699,7 @@ static void write_help(void)
|
||||
" Writes into a segment of the currently open file, using a buffer\n"
|
||||
" filled with a set pattern (0xcdcdcdcd).\n"
|
||||
" -b, -- write to the VM state rather than the virtual disk\n"
|
||||
" -c, -- write compressed data with bdrv_write_compressed\n"
|
||||
" -p, -- use bdrv_pwrite to write the file\n"
|
||||
" -P, -- use different pattern to fill file\n"
|
||||
" -C, -- report statistics in a machine parsable format\n"
|
||||
@ -703,7 +716,7 @@ static const cmdinfo_t write_cmd = {
|
||||
.cfunc = write_f,
|
||||
.argmin = 2,
|
||||
.argmax = -1,
|
||||
.args = "[-bCpqz] [-P pattern ] off len",
|
||||
.args = "[-bcCpqz] [-P pattern ] off len",
|
||||
.oneline = "writes a number of bytes at a specified offset",
|
||||
.help = write_help,
|
||||
};
|
||||
@ -712,6 +725,7 @@ static int write_f(int argc, char **argv)
|
||||
{
|
||||
struct timeval t1, t2;
|
||||
int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0;
|
||||
int cflag = 0;
|
||||
int c, cnt;
|
||||
char *buf = NULL;
|
||||
int64_t offset;
|
||||
@ -720,11 +734,14 @@ static int write_f(int argc, char **argv)
|
||||
int total = 0;
|
||||
int pattern = 0xcd;
|
||||
|
||||
while ((c = getopt(argc, argv, "bCpP:qz")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "bcCpP:qz")) != EOF) {
|
||||
switch (c) {
|
||||
case 'b':
|
||||
bflag = 1;
|
||||
break;
|
||||
case 'c':
|
||||
cflag = 1;
|
||||
break;
|
||||
case 'C':
|
||||
Cflag = 1;
|
||||
break;
|
||||
@ -801,6 +818,8 @@ static int write_f(int argc, char **argv)
|
||||
cnt = do_save_vmstate(buf, offset, count, &total);
|
||||
} else if (zflag) {
|
||||
cnt = do_co_write_zeroes(offset, count, &total);
|
||||
} else if (cflag) {
|
||||
cnt = do_write_compressed(buf, offset, count, &total);
|
||||
} else {
|
||||
cnt = do_write(buf, offset, count, &total);
|
||||
}
|
||||
@ -1652,6 +1671,67 @@ static const cmdinfo_t map_cmd = {
|
||||
.oneline = "prints the allocated areas of a file",
|
||||
};
|
||||
|
||||
static int break_f(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_debug_breakpoint(bs, argv[1], argv[2]);
|
||||
if (ret < 0) {
|
||||
printf("Could not set breakpoint: %s\n", strerror(-ret));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const cmdinfo_t break_cmd = {
|
||||
.name = "break",
|
||||
.argmin = 2,
|
||||
.argmax = 2,
|
||||
.cfunc = break_f,
|
||||
.args = "event tag",
|
||||
.oneline = "sets a breakpoint on event and tags the stopped "
|
||||
"request as tag",
|
||||
};
|
||||
|
||||
static int resume_f(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_debug_resume(bs, argv[1]);
|
||||
if (ret < 0) {
|
||||
printf("Could not resume request: %s\n", strerror(-ret));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const cmdinfo_t resume_cmd = {
|
||||
.name = "resume",
|
||||
.argmin = 1,
|
||||
.argmax = 1,
|
||||
.cfunc = resume_f,
|
||||
.args = "tag",
|
||||
.oneline = "resumes the request tagged as tag",
|
||||
};
|
||||
|
||||
static int wait_break_f(int argc, char **argv)
|
||||
{
|
||||
while (!bdrv_debug_is_suspended(bs, argv[1])) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const cmdinfo_t wait_break_cmd = {
|
||||
.name = "wait_break",
|
||||
.argmin = 1,
|
||||
.argmax = 1,
|
||||
.cfunc = wait_break_f,
|
||||
.args = "tag",
|
||||
.oneline = "waits for the suspension of a request",
|
||||
};
|
||||
|
||||
static int abort_f(int argc, char **argv)
|
||||
{
|
||||
abort();
|
||||
@ -1915,6 +1995,9 @@ int main(int argc, char **argv)
|
||||
add_command(&discard_cmd);
|
||||
add_command(&alloc_cmd);
|
||||
add_command(&map_cmd);
|
||||
add_command(&break_cmd);
|
||||
add_command(&resume_cmd);
|
||||
add_command(&wait_break_cmd);
|
||||
add_command(&abort_cmd);
|
||||
|
||||
add_args_command(init_args_command);
|
||||
|
114
qemu-option.c
114
qemu-option.c
@ -602,26 +602,36 @@ static void qemu_opt_del(QemuOpt *opt)
|
||||
g_free(opt);
|
||||
}
|
||||
|
||||
static void opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
bool prepend, Error **errp)
|
||||
static bool opts_accepts_any(const QemuOpts *opts)
|
||||
{
|
||||
return opts->list->desc[0].name == NULL;
|
||||
}
|
||||
|
||||
static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc,
|
||||
const char *name)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
const QemuOptDesc *desc = opts->list->desc;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; desc[i].name != NULL; i++) {
|
||||
if (strcmp(desc[i].name, name) == 0) {
|
||||
break;
|
||||
return &desc[i];
|
||||
}
|
||||
}
|
||||
if (desc[i].name == NULL) {
|
||||
if (i == 0) {
|
||||
/* empty list -> allow any */;
|
||||
} else {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, name);
|
||||
return;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
bool prepend, Error **errp)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
const QemuOptDesc *desc;
|
||||
Error *local_err = NULL;
|
||||
|
||||
desc = find_desc_by_name(opts->list->desc, name);
|
||||
if (!desc && !opts_accepts_any(opts)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, name);
|
||||
return;
|
||||
}
|
||||
|
||||
opt = g_malloc0(sizeof(*opt));
|
||||
@ -632,9 +642,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value,
|
||||
} else {
|
||||
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
|
||||
}
|
||||
if (desc[i].name != NULL) {
|
||||
opt->desc = desc+i;
|
||||
}
|
||||
opt->desc = desc;
|
||||
if (value) {
|
||||
opt->str = g_strdup(value);
|
||||
}
|
||||
@ -669,30 +677,43 @@ int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
const QemuOptDesc *desc = opts->list->desc;
|
||||
int i;
|
||||
|
||||
for (i = 0; desc[i].name != NULL; i++) {
|
||||
if (strcmp(desc[i].name, name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (desc[i].name == NULL) {
|
||||
if (i == 0) {
|
||||
/* empty list -> allow any */;
|
||||
} else {
|
||||
qerror_report(QERR_INVALID_PARAMETER, name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
opt = g_malloc0(sizeof(*opt));
|
||||
opt->desc = find_desc_by_name(desc, name);
|
||||
if (!opt->desc && !opts_accepts_any(opts)) {
|
||||
qerror_report(QERR_INVALID_PARAMETER, name);
|
||||
g_free(opt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
opt->name = g_strdup(name);
|
||||
opt->opts = opts;
|
||||
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
|
||||
if (desc[i].name != NULL) {
|
||||
opt->desc = desc+i;
|
||||
}
|
||||
opt->value.boolean = !!val;
|
||||
opt->str = g_strdup(val ? "on" : "off");
|
||||
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
const QemuOptDesc *desc = opts->list->desc;
|
||||
|
||||
opt = g_malloc0(sizeof(*opt));
|
||||
opt->desc = find_desc_by_name(desc, name);
|
||||
if (!opt->desc && !opts_accepts_any(opts)) {
|
||||
qerror_report(QERR_INVALID_PARAMETER, name);
|
||||
g_free(opt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
opt->name = g_strdup(name);
|
||||
opt->opts = opts;
|
||||
opt->value.uint = val;
|
||||
opt->str = g_strdup_printf("%" PRId64, val);
|
||||
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -781,6 +802,15 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
||||
return opts;
|
||||
}
|
||||
|
||||
QemuOpts *qemu_opts_create_nofail(QemuOptsList *list)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
Error *errp = NULL;
|
||||
opts = qemu_opts_create(list, NULL, 0, &errp);
|
||||
assert_no_error(errp);
|
||||
return opts;
|
||||
}
|
||||
|
||||
void qemu_opts_reset(QemuOptsList *list)
|
||||
{
|
||||
QemuOpts *opts, *next_opts;
|
||||
@ -1068,23 +1098,15 @@ void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
|
||||
QemuOpt *opt;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(opts->list->desc[0].name == NULL);
|
||||
assert(opts_accepts_any(opts));
|
||||
|
||||
QTAILQ_FOREACH(opt, &opts->head, next) {
|
||||
int i;
|
||||
|
||||
for (i = 0; desc[i].name != NULL; i++) {
|
||||
if (strcmp(desc[i].name, opt->name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (desc[i].name == NULL) {
|
||||
opt->desc = find_desc_by_name(desc, opt->name);
|
||||
if (!opt->desc) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, opt->name);
|
||||
return;
|
||||
}
|
||||
|
||||
opt->desc = &desc[i];
|
||||
|
||||
qemu_opt_parse(opt, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -126,6 +126,7 @@ int qemu_opt_set(QemuOpts *opts, const char *name, const char *value);
|
||||
void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value,
|
||||
Error **errp);
|
||||
int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val);
|
||||
int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val);
|
||||
typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque);
|
||||
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
|
||||
int abort_on_failure);
|
||||
@ -133,6 +134,7 @@ int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
|
||||
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
|
||||
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
||||
int fail_if_exists, Error **errp);
|
||||
QemuOpts *qemu_opts_create_nofail(QemuOptsList *list);
|
||||
void qemu_opts_reset(QemuOptsList *list);
|
||||
void qemu_opts_loc_restore(QemuOpts *opts);
|
||||
int qemu_opts_set(QemuOptsList *list, const char *id,
|
||||
|
@ -579,7 +579,7 @@ int inet_listen(const char *str, char *ostr, int olen,
|
||||
|
||||
addr = inet_parse(str, errp);
|
||||
if (addr != NULL) {
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
inet_addr_to_opts(opts, addr);
|
||||
qapi_free_InetSocketAddress(addr);
|
||||
sock = inet_listen_opts(opts, port_offset, errp);
|
||||
@ -618,7 +618,7 @@ int inet_connect(const char *str, Error **errp)
|
||||
|
||||
addr = inet_parse(str, errp);
|
||||
if (addr != NULL) {
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
inet_addr_to_opts(opts, addr);
|
||||
qapi_free_InetSocketAddress(addr);
|
||||
sock = inet_connect_opts(opts, errp, NULL, NULL);
|
||||
@ -652,7 +652,7 @@ int inet_nonblocking_connect(const char *str,
|
||||
|
||||
addr = inet_parse(str, errp);
|
||||
if (addr != NULL) {
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
inet_addr_to_opts(opts, addr);
|
||||
qapi_free_InetSocketAddress(addr);
|
||||
sock = inet_connect_opts(opts, errp, callback, opaque);
|
||||
@ -795,7 +795,7 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp)
|
||||
char *path, *optstr;
|
||||
int sock, len;
|
||||
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
|
||||
optstr = strchr(str, ',');
|
||||
if (optstr) {
|
||||
@ -823,7 +823,7 @@ int unix_connect(const char *path, Error **errp)
|
||||
QemuOpts *opts;
|
||||
int sock;
|
||||
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
qemu_opt_set(opts, "path", path);
|
||||
sock = unix_connect_opts(opts, errp, NULL, NULL);
|
||||
qemu_opts_del(opts);
|
||||
@ -840,7 +840,7 @@ int unix_nonblocking_connect(const char *path,
|
||||
|
||||
g_assert(callback != NULL);
|
||||
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
qemu_opt_set(opts, "path", path);
|
||||
sock = unix_connect_opts(opts, errp, callback, opaque);
|
||||
qemu_opts_del(opts);
|
||||
@ -891,7 +891,7 @@ int socket_connect(SocketAddress *addr, Error **errp,
|
||||
QemuOpts *opts;
|
||||
int fd;
|
||||
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
switch (addr->kind) {
|
||||
case SOCKET_ADDRESS_KIND_INET:
|
||||
inet_addr_to_opts(opts, addr->inet);
|
||||
@ -922,7 +922,7 @@ int socket_listen(SocketAddress *addr, Error **errp)
|
||||
QemuOpts *opts;
|
||||
int fd;
|
||||
|
||||
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(&dummy_opts);
|
||||
switch (addr->kind) {
|
||||
case SOCKET_ADDRESS_KIND_INET:
|
||||
inet_addr_to_opts(opts, addr->inet);
|
||||
|
129
tests/qemu-iotests/045
Executable file
129
tests/qemu-iotests/045
Executable file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Tests for fdsets.
|
||||
#
|
||||
# Copyright (C) 2012 IBM Corp.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import iotests
|
||||
from iotests import qemu_img
|
||||
|
||||
image0 = os.path.join(iotests.test_dir, 'image0')
|
||||
image1 = os.path.join(iotests.test_dir, 'image1')
|
||||
image2 = os.path.join(iotests.test_dir, 'image2')
|
||||
image3 = os.path.join(iotests.test_dir, 'image3')
|
||||
image4 = os.path.join(iotests.test_dir, 'image4')
|
||||
|
||||
class TestFdSets(iotests.QMPTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.vm = iotests.VM()
|
||||
qemu_img('create', '-f', iotests.imgfmt, image0, '128K')
|
||||
qemu_img('create', '-f', iotests.imgfmt, image1, '128K')
|
||||
qemu_img('create', '-f', iotests.imgfmt, image2, '128K')
|
||||
qemu_img('create', '-f', iotests.imgfmt, image3, '128K')
|
||||
qemu_img('create', '-f', iotests.imgfmt, image4, '128K')
|
||||
self.file0 = open(image0, 'r')
|
||||
self.file1 = open(image1, 'w+')
|
||||
self.file2 = open(image2, 'r')
|
||||
self.file3 = open(image3, 'r')
|
||||
self.file4 = open(image4, 'r')
|
||||
self.vm.add_fd(self.file0.fileno(), 1, 'image0:r')
|
||||
self.vm.add_fd(self.file1.fileno(), 1, 'image1:w+')
|
||||
self.vm.add_fd(self.file2.fileno(), 0, 'image2:r')
|
||||
self.vm.add_fd(self.file3.fileno(), 2, 'image3:r')
|
||||
self.vm.add_fd(self.file4.fileno(), 2, 'image4:r')
|
||||
self.vm.add_drive("/dev/fdset/1")
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
self.file0.close()
|
||||
self.file1.close()
|
||||
self.file2.close()
|
||||
self.file3.close()
|
||||
self.file4.close()
|
||||
os.remove(image0)
|
||||
os.remove(image1)
|
||||
os.remove(image2)
|
||||
os.remove(image3)
|
||||
os.remove(image4)
|
||||
|
||||
def test_query_fdset(self):
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
self.assert_qmp(result, 'return[0]/fdset-id', 2)
|
||||
self.assert_qmp(result, 'return[1]/fdset-id', 1)
|
||||
self.assert_qmp(result, 'return[2]/fdset-id', 0)
|
||||
self.assert_qmp(result, 'return[0]/fds[0]/opaque', 'image3:r')
|
||||
self.assert_qmp(result, 'return[0]/fds[1]/opaque', 'image4:r')
|
||||
self.assert_qmp(result, 'return[1]/fds[0]/opaque', 'image0:r')
|
||||
self.assert_qmp(result, 'return[1]/fds[1]/opaque', 'image1:w+')
|
||||
self.assert_qmp(result, 'return[2]/fds[0]/opaque', 'image2:r')
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_remove_fdset(self):
|
||||
result = self.vm.qmp('remove-fd', fdset_id=2)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
self.assert_qmp(result, 'return[0]/fdset-id', 1)
|
||||
self.assert_qmp(result, 'return[1]/fdset-id', 0)
|
||||
self.assert_qmp(result, 'return[0]/fds[0]/opaque', 'image0:r')
|
||||
self.assert_qmp(result, 'return[0]/fds[1]/opaque', 'image1:w+')
|
||||
self.assert_qmp(result, 'return[1]/fds[0]/opaque', 'image2:r')
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_remove_fd(self):
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
fd_image3 = result['return'][0]['fds'][0]['fd']
|
||||
result = self.vm.qmp('remove-fd', fdset_id=2, fd=fd_image3)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
self.assert_qmp(result, 'return[0]/fdset-id', 2)
|
||||
self.assert_qmp(result, 'return[1]/fdset-id', 1)
|
||||
self.assert_qmp(result, 'return[2]/fdset-id', 0)
|
||||
self.assert_qmp(result, 'return[0]/fds[0]/opaque', 'image4:r')
|
||||
self.assert_qmp(result, 'return[1]/fds[0]/opaque', 'image0:r')
|
||||
self.assert_qmp(result, 'return[1]/fds[1]/opaque', 'image1:w+')
|
||||
self.assert_qmp(result, 'return[2]/fds[0]/opaque', 'image2:r')
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_remove_fd_invalid_fdset(self):
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
fd_image3 = result['return'][0]['fds'][0]['fd']
|
||||
result = self.vm.qmp('remove-fd', fdset_id=3, fd=fd_image3)
|
||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||
self.assert_qmp(result, 'error/desc',
|
||||
'File descriptor named \'fdset-id:3, fd:%d\' not found' % fd_image3)
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_remove_fd_invalid_fd(self):
|
||||
result = self.vm.qmp('query-fdsets')
|
||||
result = self.vm.qmp('remove-fd', fdset_id=2, fd=999)
|
||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||
self.assert_qmp(result, 'error/desc',
|
||||
'File descriptor named \'fdset-id:2, fd:999\' not found')
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_add_fd_invalid_fd(self):
|
||||
result = self.vm.qmp('add-fd', fdset_id=2)
|
||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||
self.assert_qmp(result, 'error/desc',
|
||||
'No file descriptor supplied via SCM_RIGHTS')
|
||||
self.vm.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['raw'])
|
5
tests/qemu-iotests/045.out
Normal file
5
tests/qemu-iotests/045.out
Normal file
@ -0,0 +1,5 @@
|
||||
......
|
||||
----------------------------------------------------------------------
|
||||
Ran 6 tests
|
||||
|
||||
OK
|
215
tests/qemu-iotests/046
Executable file
215
tests/qemu-iotests/046
Executable file
@ -0,0 +1,215 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test concurrent cluster allocations
|
||||
#
|
||||
# Copyright (C) 2012 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 qcow2
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=64k
|
||||
size=128M
|
||||
|
||||
echo
|
||||
echo "== creating backing file for COW tests =="
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
function backing_io()
|
||||
{
|
||||
local offset=$1
|
||||
local sectors=$2
|
||||
local op=$3
|
||||
local pattern=0
|
||||
local cur_sec=0
|
||||
|
||||
for i in $(seq 0 $((sectors - 1))); do
|
||||
cur_sec=$((offset / 65536 + i))
|
||||
pattern=$(( ( (cur_sec % 128) + (cur_sec / 128)) % 128 ))
|
||||
|
||||
echo "$op -P $pattern $((cur_sec * 64))k 64k"
|
||||
done
|
||||
}
|
||||
|
||||
backing_io 0 16 write | $QEMU_IO $TEST_IMG | _filter_qemu_io
|
||||
|
||||
mv $TEST_IMG $TEST_IMG.base
|
||||
|
||||
_make_test_img -b $TEST_IMG.base 6G
|
||||
|
||||
echo
|
||||
echo "== Some concurrent requests touching the same cluster =="
|
||||
|
||||
function overlay_io()
|
||||
{
|
||||
# Allocate middle of cluster 1, then write to somewhere before and after it
|
||||
cat <<EOF
|
||||
break write_aio A
|
||||
aio_write -P 10 0x18000 0x2000
|
||||
wait_break A
|
||||
|
||||
aio_write -P 11 0x12000 0x2000
|
||||
aio_write -P 12 0x1c000 0x2000
|
||||
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
|
||||
# Sequential write case: Alloc middle of cluster 2, then write overlapping
|
||||
# to next cluster
|
||||
cat <<EOF
|
||||
break write_aio A
|
||||
aio_write -P 20 0x28000 0x2000
|
||||
wait_break A
|
||||
aio_write -P 21 0x2a000 0x10000
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
|
||||
# The same with a gap between both requests
|
||||
cat <<EOF
|
||||
break write_aio A
|
||||
aio_write -P 40 0x48000 0x2000
|
||||
wait_break A
|
||||
aio_write -P 41 0x4c000 0x10000
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
|
||||
# Sequential write, but the next cluster is already allocated
|
||||
cat <<EOF
|
||||
write -P 70 0x76000 0x8000
|
||||
aio_flush
|
||||
break write_aio A
|
||||
aio_write -P 60 0x66000 0x2000
|
||||
wait_break A
|
||||
aio_write -P 61 0x6a000 0xe000
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
|
||||
# Sequential write, but the next cluster is already allocated
|
||||
# and phyiscally in the right position
|
||||
cat <<EOF
|
||||
write -P 89 0x80000 0x1000
|
||||
write -P 90 0x96000 0x8000
|
||||
aio_flush
|
||||
discard 0x80000 0x10000
|
||||
aio_flush
|
||||
break write_aio A
|
||||
aio_write -P 80 0x86000 0x2000
|
||||
wait_break A
|
||||
aio_write -P 81 0x8a000 0xe000
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
|
||||
# Sequential write, and the next cluster is compressed
|
||||
cat <<EOF
|
||||
write -P 109 0xa0000 0x1000
|
||||
write -c -P 110 0xb0000 0x10000
|
||||
aio_flush
|
||||
discard 0xa0000 0x10000
|
||||
aio_flush
|
||||
break write_aio A
|
||||
aio_write -P 100 0xa6000 0x2000
|
||||
wait_break A
|
||||
aio_write -P 101 0xaa000 0xe000
|
||||
resume A
|
||||
aio_flush
|
||||
EOF
|
||||
}
|
||||
|
||||
overlay_io | $QEMU_IO blkdebug::$TEST_IMG | _filter_qemu_io |\
|
||||
sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g'
|
||||
|
||||
echo
|
||||
echo "== Verify image content =="
|
||||
|
||||
function verify_io()
|
||||
{
|
||||
echo read -P 0 0 0x10000
|
||||
|
||||
echo read -P 1 0x10000 0x2000
|
||||
echo read -P 11 0x12000 0x2000
|
||||
echo read -P 1 0x14000 0x4000
|
||||
echo read -P 10 0x18000 0x2000
|
||||
echo read -P 1 0x1a000 0x2000
|
||||
echo read -P 12 0x1c000 0x2000
|
||||
echo read -P 1 0x1e000 0x2000
|
||||
|
||||
echo read -P 2 0x20000 0x8000
|
||||
echo read -P 20 0x28000 0x2000
|
||||
echo read -P 21 0x2a000 0x10000
|
||||
echo read -P 3 0x3a000 0x6000
|
||||
|
||||
echo read -P 4 0x40000 0x8000
|
||||
echo read -P 40 0x48000 0x2000
|
||||
echo read -P 4 0x4a000 0x2000
|
||||
echo read -P 41 0x4c000 0x10000
|
||||
echo read -P 5 0x5c000 0x4000
|
||||
|
||||
echo read -P 6 0x60000 0x6000
|
||||
echo read -P 60 0x66000 0x2000
|
||||
echo read -P 6 0x68000 0x2000
|
||||
echo read -P 61 0x6a000 0xe000
|
||||
echo read -P 70 0x78000 0x6000
|
||||
echo read -P 7 0x7e000 0x2000
|
||||
|
||||
echo read -P 8 0x80000 0x6000
|
||||
echo read -P 80 0x86000 0x2000
|
||||
echo read -P 8 0x88000 0x2000
|
||||
echo read -P 81 0x8a000 0xe000
|
||||
echo read -P 90 0x98000 0x6000
|
||||
echo read -P 9 0x9e000 0x2000
|
||||
|
||||
echo read -P 10 0xa0000 0x6000
|
||||
echo read -P 100 0xa6000 0x2000
|
||||
echo read -P 10 0xa8000 0x2000
|
||||
echo read -P 101 0xaa000 0xe000
|
||||
echo read -P 110 0xb8000 0x8000
|
||||
}
|
||||
|
||||
verify_io | $QEMU_IO $TEST_IMG | _filter_qemu_io
|
||||
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
163
tests/qemu-iotests/046.out
Normal file
163
tests/qemu-iotests/046.out
Normal file
@ -0,0 +1,163 @@
|
||||
QA output created by 046
|
||||
|
||||
== creating backing file for COW tests ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
qemu-io> wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 65536
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 131072
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 196608
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 262144
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 327680
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 393216
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 458752
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 524288
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 589824
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 655360
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 720896
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 786432
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 851968
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 917504
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset 983040
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base'
|
||||
|
||||
== Some concurrent requests touching the same cluster ==
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset XXX
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset XXX
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 32768/32768 bytes at offset XXX
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 57344/57344 bytes at offset XXX
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 4096/4096 bytes at offset XXX
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 32768/32768 bytes at offset XXX
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> discard 65536/65536 bytes at offset XXX
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 57344/57344 bytes at offset XXX
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 4096/4096 bytes at offset XXX
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> wrote 65536/65536 bytes at offset XXX
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> discard 65536/65536 bytes at offset XXX
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Suspended request 'A'
|
||||
qemu-io> qemu-io> qemu-io> blkdebug: Resuming request 'A'
|
||||
qemu-io> wrote 8192/8192 bytes at offset XXX
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 57344/57344 bytes at offset XXX
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io>
|
||||
== Verify image content ==
|
||||
qemu-io> read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 65536
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 73728
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 16384/16384 bytes at offset 81920
|
||||
16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 98304
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 106496
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 114688
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 122880
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 32768/32768 bytes at offset 131072
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 163840
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 65536/65536 bytes at offset 172032
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 237568
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 32768/32768 bytes at offset 262144
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 294912
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 303104
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 65536/65536 bytes at offset 311296
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 16384/16384 bytes at offset 376832
|
||||
16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 393216
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 417792
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 425984
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 57344/57344 bytes at offset 434176
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 491520
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 516096
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 524288
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 548864
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 557056
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 57344/57344 bytes at offset 565248
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 622592
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 647168
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 24576/24576 bytes at offset 655360
|
||||
24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 679936
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 8192/8192 bytes at offset 688128
|
||||
8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 57344/57344 bytes at offset 696320
|
||||
56 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> read 32768/32768 bytes at offset 753664
|
||||
32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qemu-io> No errors were found on the image.
|
||||
*** done
|
@ -51,3 +51,5 @@
|
||||
042 rw auto quick
|
||||
043 rw auto backing
|
||||
044 rw auto
|
||||
045 rw auto
|
||||
046 rw auto aio
|
||||
|
@ -79,6 +79,18 @@ class VM(object):
|
||||
self._num_drives += 1
|
||||
return self
|
||||
|
||||
def add_fd(self, fd, fdset, opaque, opts=''):
|
||||
'''Pass a file descriptor to the VM'''
|
||||
options = ['fd=%d' % fd,
|
||||
'set=%d' % fdset,
|
||||
'opaque=%s' % opaque]
|
||||
if opts:
|
||||
options.append(opts)
|
||||
|
||||
self._args.append('-add-fd')
|
||||
self._args.append(','.join(options))
|
||||
return self
|
||||
|
||||
def launch(self):
|
||||
'''Launch the VM and establish a QMP connection'''
|
||||
devnull = open('/dev/null', 'rb')
|
||||
|
@ -15,6 +15,14 @@
|
||||
|
||||
AioContext *ctx;
|
||||
|
||||
/* Wait until there are no more BHs or AIO requests */
|
||||
static void wait_for_aio(void)
|
||||
{
|
||||
while (aio_poll(ctx, true)) {
|
||||
/* Do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple callbacks for testing. */
|
||||
|
||||
typedef struct {
|
||||
@ -78,14 +86,6 @@ static void test_notify(void)
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
}
|
||||
|
||||
static void test_flush(void)
|
||||
{
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
aio_notify(ctx);
|
||||
aio_flush(ctx);
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
}
|
||||
|
||||
static void test_bh_schedule(void)
|
||||
{
|
||||
BHTestData data = { .n = 0 };
|
||||
@ -116,7 +116,7 @@ static void test_bh_schedule10(void)
|
||||
g_assert(aio_poll(ctx, true));
|
||||
g_assert_cmpint(data.n, ==, 2);
|
||||
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data.n, ==, 10);
|
||||
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
@ -164,7 +164,7 @@ static void test_bh_delete_from_cb(void)
|
||||
qemu_bh_schedule(data1.bh);
|
||||
g_assert_cmpint(data1.n, ==, 0);
|
||||
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data1.n, ==, data1.max);
|
||||
g_assert(data1.bh == NULL);
|
||||
|
||||
@ -200,7 +200,7 @@ static void test_bh_delete_from_cb_many(void)
|
||||
g_assert_cmpint(data4.n, ==, 1);
|
||||
g_assert(data1.bh == NULL);
|
||||
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data1.n, ==, data1.max);
|
||||
g_assert_cmpint(data2.n, ==, data2.max);
|
||||
g_assert_cmpint(data3.n, ==, data3.max);
|
||||
@ -219,7 +219,7 @@ static void test_bh_flush(void)
|
||||
qemu_bh_schedule(data.bh);
|
||||
g_assert_cmpint(data.n, ==, 0);
|
||||
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data.n, ==, 1);
|
||||
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
@ -281,7 +281,7 @@ static void test_flush_event_notifier(void)
|
||||
g_assert_cmpint(data.active, ==, 9);
|
||||
g_assert(aio_poll(ctx, false));
|
||||
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data.n, ==, 10);
|
||||
g_assert_cmpint(data.active, ==, 0);
|
||||
g_assert(!aio_poll(ctx, false));
|
||||
@ -325,7 +325,7 @@ static void test_wait_event_notifier_noflush(void)
|
||||
g_assert_cmpint(data.n, ==, 2);
|
||||
|
||||
event_notifier_set(&dummy.e);
|
||||
aio_flush(ctx);
|
||||
wait_for_aio();
|
||||
g_assert_cmpint(data.n, ==, 2);
|
||||
g_assert_cmpint(dummy.n, ==, 1);
|
||||
g_assert_cmpint(dummy.active, ==, 0);
|
||||
@ -346,7 +346,7 @@ static void test_wait_event_notifier_noflush(void)
|
||||
* - sometimes both the AioContext and the glib main loop wake
|
||||
* themselves up. Hence, some "g_assert(!aio_poll(ctx, false));"
|
||||
* are replaced by "while (g_main_context_iteration(NULL, false));".
|
||||
* - there is no exact replacement for aio_flush's blocking wait.
|
||||
* - there is no exact replacement for a blocking wait.
|
||||
* "while (g_main_context_iteration(NULL, true)" seems to work,
|
||||
* but it is not documented _why_ it works. For these tests a
|
||||
* non-blocking loop like "while (g_main_context_iteration(NULL, false)"
|
||||
@ -637,7 +637,6 @@ int main(int argc, char **argv)
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/aio/notify", test_notify);
|
||||
g_test_add_func("/aio/flush", test_flush);
|
||||
g_test_add_func("/aio/bh/schedule", test_bh_schedule);
|
||||
g_test_add_func("/aio/bh/schedule10", test_bh_schedule10);
|
||||
g_test_add_func("/aio/bh/cancel", test_bh_cancel);
|
||||
|
@ -47,11 +47,19 @@ static void qemu_aio_wait_nonblocking(void)
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
/* Wait until all aio and bh activity has finished */
|
||||
static void qemu_aio_wait_all(void)
|
||||
{
|
||||
while (qemu_aio_wait()) {
|
||||
/* Do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
static void test_submit(void)
|
||||
{
|
||||
WorkerTestData data = { .n = 0 };
|
||||
thread_pool_submit(worker_cb, &data);
|
||||
qemu_aio_flush();
|
||||
qemu_aio_wait_all();
|
||||
g_assert_cmpint(data.n, ==, 1);
|
||||
}
|
||||
|
||||
@ -63,7 +71,7 @@ static void test_submit_aio(void)
|
||||
/* The callbacks are not called until after the first wait. */
|
||||
active = 1;
|
||||
g_assert_cmpint(data.ret, ==, -EINPROGRESS);
|
||||
qemu_aio_flush();
|
||||
qemu_aio_wait_all();
|
||||
g_assert_cmpint(active, ==, 0);
|
||||
g_assert_cmpint(data.n, ==, 1);
|
||||
g_assert_cmpint(data.ret, ==, 0);
|
||||
@ -84,7 +92,7 @@ static void co_test_cb(void *opaque)
|
||||
data->ret = 0;
|
||||
active--;
|
||||
|
||||
/* The test continues in test_submit_co, after qemu_aio_flush... */
|
||||
/* The test continues in test_submit_co, after qemu_aio_wait_all... */
|
||||
}
|
||||
|
||||
static void test_submit_co(void)
|
||||
@ -99,9 +107,9 @@ static void test_submit_co(void)
|
||||
g_assert_cmpint(active, ==, 1);
|
||||
g_assert_cmpint(data.ret, ==, -EINPROGRESS);
|
||||
|
||||
/* qemu_aio_flush will execute the rest of the coroutine. */
|
||||
/* qemu_aio_wait_all will execute the rest of the coroutine. */
|
||||
|
||||
qemu_aio_flush();
|
||||
qemu_aio_wait_all();
|
||||
|
||||
/* Back here after the coroutine has finished. */
|
||||
|
||||
@ -184,7 +192,7 @@ static void test_cancel(void)
|
||||
}
|
||||
|
||||
/* Finish execution and execute any remaining callbacks. */
|
||||
qemu_aio_flush();
|
||||
qemu_aio_wait_all();
|
||||
g_assert_cmpint(active, ==, 0);
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (data[i].n == 3) {
|
||||
|
41
vl.c
41
vl.c
@ -886,9 +886,9 @@ static int cleanup_add_fd(QemuOpts *opts, void *opaque)
|
||||
|
||||
static int drive_init_func(QemuOpts *opts, void *opaque)
|
||||
{
|
||||
int *use_scsi = opaque;
|
||||
BlockInterfaceType *block_default_type = opaque;
|
||||
|
||||
return drive_init(opts, *use_scsi) == NULL;
|
||||
return drive_init(opts, *block_default_type) == NULL;
|
||||
}
|
||||
|
||||
static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
|
||||
@ -899,16 +899,11 @@ static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void default_drive(int enable, int snapshot, int use_scsi,
|
||||
BlockInterfaceType type, int index,
|
||||
const char *optstr)
|
||||
static void default_drive(int enable, int snapshot, BlockInterfaceType type,
|
||||
int index, const char *optstr)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
|
||||
if (type == IF_DEFAULT) {
|
||||
type = use_scsi ? IF_SCSI : IF_IDE;
|
||||
}
|
||||
|
||||
if (!enable || drive_get_by_index(type, index)) {
|
||||
return;
|
||||
}
|
||||
@ -917,7 +912,7 @@ static void default_drive(int enable, int snapshot, int use_scsi,
|
||||
if (snapshot) {
|
||||
drive_enable_snapshot(opts, NULL);
|
||||
}
|
||||
if (!drive_init(opts, use_scsi)) {
|
||||
if (!drive_init(opts, type)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -2001,7 +1996,7 @@ static int balloon_parse(const char *arg)
|
||||
return -1;
|
||||
} else {
|
||||
/* create empty opts */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
}
|
||||
qemu_opt_set(opts, "driver", "virtio-balloon");
|
||||
return 0;
|
||||
@ -2251,14 +2246,14 @@ static int virtcon_parse(const char *devname)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bus_opts = qemu_opts_create(device, NULL, 0, NULL);
|
||||
bus_opts = qemu_opts_create_nofail(device);
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
|
||||
} else {
|
||||
qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
|
||||
}
|
||||
|
||||
dev_opts = qemu_opts_create(device, NULL, 0, NULL);
|
||||
dev_opts = qemu_opts_create_nofail(device);
|
||||
qemu_opt_set(dev_opts, "driver", "virtconsole");
|
||||
|
||||
snprintf(label, sizeof(label), "virtcon%d", index);
|
||||
@ -3110,8 +3105,7 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
qemu_opt_set_bool(fsdev, "readonly",
|
||||
qemu_opt_get_bool(opts, "readonly", 0));
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
NULL);
|
||||
device = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
qemu_opt_set(device, "driver", "virtio-9p-pci");
|
||||
qemu_opt_set(device, "fsdev",
|
||||
qemu_opt_get(opts, "mount_tag"));
|
||||
@ -3131,8 +3125,7 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
qemu_opt_set(fsdev, "fsdriver", "synth");
|
||||
|
||||
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
NULL);
|
||||
device = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
qemu_opt_set(device, "driver", "virtio-9p-pci");
|
||||
qemu_opt_set(device, "fsdev", "v_synth");
|
||||
qemu_opt_set(device, "mount_tag", "v_synth");
|
||||
@ -3770,15 +3763,15 @@ int main(int argc, char **argv, char **envp)
|
||||
/* open the virtual block devices */
|
||||
if (snapshot)
|
||||
qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0);
|
||||
if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0)
|
||||
if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,
|
||||
&machine->block_default_type, 1) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
default_drive(default_cdrom, snapshot, machine->use_scsi,
|
||||
IF_DEFAULT, 2, CDROM_OPTS);
|
||||
default_drive(default_floppy, snapshot, machine->use_scsi,
|
||||
IF_FLOPPY, 0, FD_OPTS);
|
||||
default_drive(default_sdcard, snapshot, machine->use_scsi,
|
||||
IF_SD, 0, SD_OPTS);
|
||||
default_drive(default_cdrom, snapshot, machine->block_default_type, 2,
|
||||
CDROM_OPTS);
|
||||
default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
|
||||
default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
|
||||
|
||||
register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user